Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ Thumbs.db
*.bak
*.tmp

# Roadmap / Ideas
**/Roadmap.md

# Python cache
*.pyc
__pycache__/
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
## [Unreleased]

## Added

1. Add day separator before the first message of a day.


---

## [v0.7.0]

### Added
Expand Down
48 changes: 41 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,34 @@
# memory.text

[![Python](https://img.shields.io/badge/python-3.12%2B-4B8BBE?style=for-the-badge&logo=python&logoColor=%23FFE873)](https://www.python.org/)
[![Qt](https://img.shields.io/badge/Qt-%2CDE85.svg?style=for-the-badge&logo=Qt&logoColor=white)](https://wiki.qt.io/Qt_for_Python)
[![LICENSE: MIT](https://img.shields.io/badge/LICENSE-MIT-green?style=for-the-badge)](LICENSE)

A simple app to revisit your text messages.
A desktop application that reimagines your chat history into a book-like reading experience, with chapters, navigation, and structure instead of endless scrolling.

---

## Vision

Chat apps are great for communication but terrible for revisiting conversations, with no navigation and require an awful lot of scrolling.
This app explores a different approach, treating chats as something you can navigate and read, not just scroll through.

Think:
- Jumping to specific days or moments
- Navigating like chapters in a book
- Bookmarks
- Rediscovering conversations as memories

## Preview

> Current UI — early development (focused on chat rendering and core structure)

<p align="center">
<img src="./assets/screenshots/chat_view.png" height="600">
</p>



## Installation

Requires Python >=3.12
Expand All @@ -20,18 +42,30 @@ pip install git+https://gitlab.com/sharmasiddhant/memory.text.git
## Features

- Import WhatsApp chat exports.
- Native Lightweight Qt UI.
- Native lightweight Qt UI.
- Chats sidebars
- Message bubbles
- Stickers

## Roadmap

- [ ] Option to modify/delete currently imported chats, for instance, modify username or timezone.
- [ ] Sort and filter by timestamps
### Navigation & Structure

- [ ] Day markers
- [ ] Jump to a specific date
- [ ] Timeline-style navigation

### Filter and Search

- [ ] Search messages
- [ ] Filter by participants
- [ ] Search for text in messages
- [ ] Add tags to messages
- [ ] Sort/filter by timestamps

### Organization

- [ ] Edit imported chat metadata (title, username, timezone)
- [ ] Tags for messages
- [ ] Bookmark important moments


---
Expand All @@ -42,7 +76,7 @@ pip install git+https://gitlab.com/sharmasiddhant/memory.text.git

If you'd like to explore, improve, fix something, report bugs, or suggest any feature ideas, you are welcome to contribute.

To get started, you can have a look at the [issue tracker](https://github.com/shsiddhant/memory.texts/issues). If you want to report a bug or make a feature request, please open a [new issue](https://github.com/shsiddhant/memory.texts/issues/new/choose) using an appropriate template.
To get started, you can have a look at the [issue tracker](https://github.com/shsiddhant/memory.text/issues). If you want to report a bug or make a feature request, please open a [new issue](https://github.com/shsiddhant/memory.text/issues/new/choose) using an appropriate template.

See [CONTRIBUTING](CONTRIBUTING.md) for a detailed overview of the contributing guidelines.

Expand Down
Binary file added assets/screenshots/chat_view.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 19 additions & 3 deletions src/memorytext/delegates/message_list.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from PySide6.QtWidgets import QStyledItemDelegate
from PySide6.QtCore import Qt, QRect, QSize
from PySide6.QtGui import QFont, QFontMetrics, QColor, QPixmap
from PySide6.QtGui import QFont, QFontMetrics, QColor, QPixmap, QPainter
from memorytext.models.roles import Roles
from memorytext.config import USER_DIR

Expand All @@ -16,13 +16,15 @@
class MessageDelegate(QStyledItemDelegate):
def __init__(self, parent=None):
super().__init__(parent)
self.day_text_height = 20
self.day_text_spacing = 20
self.padding = 15
self.radius = 10
self.sender_height = 20
self.spacing = 5
self.sticker_size = QSize(128, 128)

def paint(self, painter, option, index):
def paint(self, painter: QPainter, option, index):
painter.save()

# Data
Expand All @@ -33,6 +35,8 @@ def paint(self, painter, option, index):
is_same_sender = index.data(Roles.IsSameSenderRole)
alignment = index.data(Qt.ItemDataRole.TextAlignmentRole)
attachment = index.data(Roles.AttachmentRole)
is_day_separator = index.data(Roles.IsDaySeparatorRole)
day_text = index.data(Roles.DayTextRole)
path = None

if attachment and attachment.startswith("STK"):
Expand Down Expand Up @@ -61,6 +65,15 @@ def paint(self, painter, option, index):
sender_metrics = QFontMetrics(sender_font)
ts_metrics = QFontMetrics(ts_font)

current_top = content_rect.top()

if is_day_separator:
day_text_font = base_font
day_text_font.setBold(True)
painter.setFont(day_text_font)
painter.drawText(content_rect, day_text, Qt.AlignmentFlag.AlignHCenter)
current_top += self.day_text_height + 2 * self.day_text_spacing

text_rect = metrics.boundingRect(
0, 0, inner_width, 10000, Qt.TextFlag.TextWordWrap, text
)
Expand Down Expand Up @@ -91,7 +104,7 @@ def paint(self, painter, option, index):
)
bubble_color = USER_COLOR if is_user else OTHER_COLOR
bubble_rect = QRect(
bubble_left, option.rect.top() + self.spacing, bubble_width, bubble_height
bubble_left, current_top + self.spacing, bubble_width, bubble_height
)

painter.setRenderHint(painter.RenderHint.Antialiasing)
Expand Down Expand Up @@ -171,6 +184,7 @@ def sizeHint(self, option, index):
text = str(index.data(Qt.ItemDataRole.DisplayRole))
is_same_sender = index.data(Roles.IsSameSenderRole)
attachment = index.data(Roles.AttachmentRole)
is_date_separator = index.data(Roles.IsDaySeparatorRole)

max_width = option.rect.width() * 0.5
inner_width = max_width - 2 * self.padding
Expand All @@ -185,6 +199,8 @@ def sizeHint(self, option, index):
0, 0, inner_width, 10000, Qt.TextFlag.TextWordWrap, text
)
height = text_rect.height() + self.padding * 2 + ts_metrics.height()
if is_date_separator:
height += self.day_text_height + 2 * self.day_text_spacing
if not is_same_sender:
height += self.sender_height
if attachment and attachment.startswith("STK"):
Expand Down
11 changes: 11 additions & 0 deletions src/memorytext/models/message_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from typing import TYPE_CHECKING

from PySide6.QtCore import QAbstractListModel, QPersistentModelIndex, Qt, QModelIndex
from memorytext.util import get_day_string_from_timestamp
from memorytext.services import chat_service
from memorytext.models.roles import Roles

Expand Down Expand Up @@ -59,6 +60,16 @@ def data(self, index, role=Qt.ItemDataRole.DisplayRole): # pyright: ignore[repo
if role == Roles.AttachmentRole:
return m.attachment

# Day Separator
if role == Roles.IsDaySeparatorRole:
return (
row == 0 or m.timestamp.date() != self._items[row - 1].timestamp.date()
)

# Day Text
if role == Roles.DayTextRole:
return get_day_string_from_timestamp(m.timestamp)

return None

def flags(self, index):
Expand Down
2 changes: 2 additions & 0 deletions src/memorytext/models/roles.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ class Roles(IntEnum):
SenderRole = Qt.ItemDataRole.UserRole + 2
TimestampRole = Qt.ItemDataRole.UserRole + 3
AttachmentRole = Qt.ItemDataRole.UserRole + 5
IsDaySeparatorRole = Qt.ItemDataRole.UserRole + 6
DayTextRole = Qt.ItemDataRole.UserRole + 7

# Chat List Model
ParticipantsRole = Qt.ItemDataRole.UserRole + 4
10 changes: 10 additions & 0 deletions src/memorytext/util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from __future__ import annotations
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from datetime import datetime


def get_day_string_from_timestamp(timestamp: datetime):
fmt = "%A, %b %d, %Y"
return timestamp.strftime(fmt)
Loading