diff --git a/src/accessibility/accessible_tree.py b/src/accessibility/accessible_tree.py index 7116672..4ca33e4 100644 --- a/src/accessibility/accessible_tree.py +++ b/src/accessibility/accessible_tree.py @@ -316,7 +316,13 @@ class AccessibleTreeWidget(QTreeWidget): if item.childCount() > 0: original_text = item.text(0) child_count = item.childCount() - accessible_text = f"{original_text} ({child_count} replies, {state})" + + # Remove any existing reply count information to prevent duplication + # Pattern matches " (X replies, collapsed/expanded)" at the end of text + import re + clean_text = re.sub(r' \(\d+ replies?, (?:collapsed|expanded)\)$', '', original_text) + + accessible_text = f"{clean_text} ({child_count} replies, {state})" item.setData(0, Qt.AccessibleTextRole, accessible_text) # Emit signal for potential sound feedback diff --git a/src/main_window.py b/src/main_window.py index 44b904c..8be9b85 100644 --- a/src/main_window.py +++ b/src/main_window.py @@ -631,8 +631,8 @@ class MainWindow(QMainWindow): def reply_to_post(self, post): """Reply to a specific post""" dialog = ComposeDialog(self.account_manager, self) - # Pre-fill with reply mention - dialog.text_edit.setPlainText(f"@{post.account.username} ") + # Pre-fill with reply mention using full fediverse handle + dialog.text_edit.setPlainText(f"@{post.account.acct} ") # Move cursor to end cursor = dialog.text_edit.textCursor() cursor.movePosition(QTextCursor.MoveOperation.End) diff --git a/src/widgets/timeline_view.py b/src/widgets/timeline_view.py index eb34a36..3327d80 100644 --- a/src/widgets/timeline_view.py +++ b/src/widgets/timeline_view.py @@ -163,11 +163,17 @@ class TimelineView(AccessibleTreeWidget): if self.timeline_type == "notifications": # Handle notifications data structure + # Track notification types to determine priority-based sound + notification_types_found = set() + for notification_data in timeline_data: try: notification_type = notification_data['type'] sender = notification_data['account']['display_name'] or notification_data['account']['username'] + # Track notification types for priority-based sound selection + notification_types_found.add(notification_type) + # Notifications with status (mentions, boosts, favorites) if 'status' in notification_data: post = Post.from_api_dict(notification_data['status']) @@ -182,21 +188,21 @@ class TimelineView(AccessibleTreeWidget): if notification_type == 'mention': self.notification_manager.notify_mention(sender, content_preview) - self.sound_manager.play_mention() elif notification_type == 'reblog': self.notification_manager.notify_boost(sender, content_preview) - self.sound_manager.play_boost() elif notification_type == 'favourite': self.notification_manager.notify_favorite(sender, content_preview) - self.sound_manager.play_favorite() elif notification_type == 'follow': # Handle follow notifications without status (skip if initial load) if not self.skip_notifications: self.notification_manager.notify_follow(sender) - self.sound_manager.play_follow() except Exception as e: print(f"Error parsing notification: {e}") continue + + # Play priority-based sound for notification batch (skip if initial load) + if notification_types_found and not self.skip_notifications: + self._play_priority_notification_sound(notification_types_found) elif self.timeline_type == "conversations": # Handle conversations data structure for conv_data in timeline_data: @@ -1056,6 +1062,10 @@ class TimelineView(AccessibleTreeWidget): # Play success sound self.sound_manager.play_success() + # Refresh the entire timeline to ensure poll state is properly updated + # and prevent duplicate voting attempts + self.refresh() + except Exception as e: print(f"Failed to submit poll vote: {e}") # Play error sound @@ -1196,4 +1206,52 @@ class TimelineView(AccessibleTreeWidget): except Exception as e: print(f"Failed to show post details: {e}") - self.sound_manager.play_error() \ No newline at end of file + self.sound_manager.play_error() + + def _play_priority_notification_sound(self, notification_types): + """Play the highest priority sound from a set of notification types. + + Priority order (highest to lowest): + 1. direct_message (direct messages) + 2. follow (follow notifications) + 3. mention (mentions) + 4. reply (replies to user's posts) + 4. reblog (boosts) + 5. favourite (favorites) + """ + # Define priority order (higher number = higher priority) + priority_map = { + 'direct_message': 5, + 'follow': 4, + 'mention': 3, + 'reply': 2, # Reply notifications should play reply sound + 'reblog': 2, + 'favourite': 1 + } + + # Find the highest priority notification type + highest_priority = 0 + highest_type = None + + for notification_type in notification_types: + priority = priority_map.get(notification_type, 0) + if priority > highest_priority: + highest_priority = priority + highest_type = notification_type + + # Play appropriate sound for highest priority notification + if highest_type == 'direct_message': + self.sound_manager.play_direct_message() + elif highest_type == 'follow': + self.sound_manager.play_follow() + elif highest_type == 'mention': + self.sound_manager.play_mention() + elif highest_type == 'reply': + self.sound_manager.play_reply() + elif highest_type == 'reblog': + self.sound_manager.play_boost() + elif highest_type == 'favourite': + self.sound_manager.play_favorite() + else: + # Fallback for unknown notification types + self.sound_manager.play_notification() \ No newline at end of file