diff --git a/.gitignore b/.gitignore
index b3af7a9..56c57f6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,9 @@ Thumbs.db
*.bak
*.tmp
+# Roadmap / Ideas
+**/Roadmap.md
+
# Python cache
*.pyc
__pycache__/
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b14de23..9c34701 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,12 @@
+## [Unreleased]
+
+## Added
+
+1. Add day separator before the first message of a day.
+
+
+---
+
## [v0.7.0]
### Added
diff --git a/README.md b/README.md
index c3aa087..113ccb5 100644
--- a/README.md
+++ b/README.md
@@ -2,12 +2,34 @@
# memory.text
[](https://www.python.org/)
+[](https://wiki.qt.io/Qt_for_Python)
[](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)
+
+
+
+
+
+
+
## Installation
Requires Python >=3.12
@@ -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
---
@@ -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.
diff --git a/assets/screenshots/chat_view.png b/assets/screenshots/chat_view.png
new file mode 100644
index 0000000..63c3f5e
Binary files /dev/null and b/assets/screenshots/chat_view.png differ
diff --git a/src/memorytext/delegates/message_list.py b/src/memorytext/delegates/message_list.py
index 217a21c..8cccedd 100644
--- a/src/memorytext/delegates/message_list.py
+++ b/src/memorytext/delegates/message_list.py
@@ -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
@@ -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
@@ -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"):
@@ -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
)
@@ -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)
@@ -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
@@ -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"):
diff --git a/src/memorytext/models/message_list.py b/src/memorytext/models/message_list.py
index 3d41ab7..9f62621 100644
--- a/src/memorytext/models/message_list.py
+++ b/src/memorytext/models/message_list.py
@@ -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
@@ -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):
diff --git a/src/memorytext/models/roles.py b/src/memorytext/models/roles.py
index 91b2a7f..598f6d9 100644
--- a/src/memorytext/models/roles.py
+++ b/src/memorytext/models/roles.py
@@ -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
diff --git a/src/memorytext/util.py b/src/memorytext/util.py
new file mode 100644
index 0000000..cbca4d5
--- /dev/null
+++ b/src/memorytext/util.py
@@ -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)