Implement seamless auto-loading timeline navigation

- Remove explicit "Load more posts" items from timeline view
- Auto-load more posts when down arrow pressed at timeline end
- Create seamless infinite-scroll experience for better UX
- Maintain focus on newly loaded content for smooth navigation
- Reduce status message display time to 1 second for less interruption
- Update accessibility name to "Timeline Tree" for consistency

This creates a more natural browsing experience where users can
continuously navigate through their timeline without manual loading prompts.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Storm Dragon
2025-07-24 02:05:04 -04:00
parent 2b8382e4eb
commit b3c37ae625

View File

@ -116,7 +116,7 @@ class TimelineView(QTreeWidget):
self.setFocusPolicy(Qt.StrongFocus)
# Set accessible properties
self.setAccessibleName("Timeline")
self.setAccessibleName("Timeline Tree")
self.setAccessibleDescription("Timeline showing posts and conversations")
def set_timeline_type(self, timeline_type: str):
@ -1151,13 +1151,10 @@ class TimelineView(QTreeWidget):
)
def add_load_more_item(self):
"""Add a 'Load more posts' item at the bottom of the timeline"""
load_more_item = QTreeWidgetItem(["Load more posts (Press Enter)"])
load_more_item.setData(0, Qt.UserRole, "load_more") # Special marker
load_more_item.setData(
0, Qt.AccessibleTextRole, "Load more posts from timeline"
)
self.addTopLevelItem(load_more_item)
"""Legacy method - no longer adds visible 'Load more posts' item"""
# Auto-loading on down arrow makes explicit load more items unnecessary
# Keep method for compatibility but don't add any visible items
pass
def load_more_posts(self):
"""Load more posts from the current timeline"""
@ -1200,11 +1197,8 @@ class TimelineView(QTreeWidget):
)
if more_data:
# Remember current "Load more" item position
load_more_index = self.get_load_more_item_index()
# Remove current "Load more" item
self.remove_load_more_item()
# Get current position to add new posts
insert_index = self.topLevelItemCount()
# Add new posts to existing list
self.load_additional_timeline_data(more_data)
@ -1213,24 +1207,18 @@ class TimelineView(QTreeWidget):
self.oldest_post_id = more_data[-1]["id"]
# Add new posts to the tree without rebuilding everything
self.add_new_posts_to_tree(more_data, load_more_index)
self.add_new_posts_to_tree(more_data, insert_index)
# Add new "Load more" item at the end
self.add_load_more_item()
# Focus on the first newly added post so user can arrow down to read them
if (
load_more_index is not None
and load_more_index < self.topLevelItemCount()
):
first_new_item = self.topLevelItem(load_more_index)
# Focus on the first newly added post so user can continue reading
if insert_index < self.topLevelItemCount():
first_new_item = self.topLevelItem(insert_index)
if first_new_item:
self.setCurrentItem(first_new_item)
self.scrollToItem(first_new_item)
if hasattr(self, "parent") and hasattr(self.parent(), "status_bar"):
self.parent().status_bar.showMessage(
f"Loaded {len(more_data)} more posts", 2000
f"Loaded {len(more_data)} more posts", 1000
)
else:
if hasattr(self, "parent") and hasattr(self.parent(), "status_bar"):
@ -1244,20 +1232,12 @@ class TimelineView(QTreeWidget):
)
def get_load_more_item_index(self):
"""Get the index of the 'Load more posts' item"""
for i in range(self.topLevelItemCount()):
item = self.topLevelItem(i)
if item.data(0, Qt.UserRole) == "load_more":
return i
"""Legacy method - no longer needed as there are no load more items"""
return None
def remove_load_more_item(self):
"""Remove the 'Load more posts' item"""
for i in range(self.topLevelItemCount()):
item = self.topLevelItem(i)
if item.data(0, Qt.UserRole) == "load_more":
self.takeTopLevelItem(i)
break
"""Legacy method - no longer needed as there are no load more items"""
pass
def load_additional_timeline_data(self, timeline_data):
"""Load additional timeline data and append to existing posts"""
@ -1371,7 +1351,7 @@ class TimelineView(QTreeWidget):
return
post = item.data(0, Qt.UserRole)
if not post or post == "load_more":
if not post:
return
menu = QMenu(self)
@ -1632,7 +1612,7 @@ class TimelineView(QTreeWidget):
self.logger.error(f"Error updating conversation display: {e}")
def keyPressEvent(self, event: QKeyEvent):
"""Handle keyboard events, including Enter for poll voting and context menu keys"""
"""Handle keyboard events, including Enter for poll voting and automatic loading"""
key = event.key()
current = self.currentItem()
@ -1648,15 +1628,22 @@ class TimelineView(QTreeWidget):
self.customContextMenuRequested.emit(center)
return
# Handle Enter key for polls, load more, and post details
if (key == Qt.Key_Return or key == Qt.Key_Enter) and current:
# Check for special items first
special_data = current.data(0, Qt.UserRole)
if special_data == "load_more":
# Handle down arrow at end of timeline - automatically load more posts
if key == Qt.Key_Down and current:
# Check if we're at the last post in the timeline
current_index = self.indexOfTopLevelItem(current)
total_items = self.topLevelItemCount()
# If we're at the last post, automatically load more
if current_index >= total_items - 1:
# Only auto-load if we actually have an API client and more data might be available
if self.activitypub_client and self.oldest_post_id:
self.load_more_posts()
return
# Check if current item has poll data
# Handle Enter key for post details
if (key == Qt.Key_Return or key == Qt.Key_Enter) and current:
# Check if current item has post data
try:
post_index = self.indexOfTopLevelItem(current)
if 0 <= post_index < len(self.posts):