Fix multiple notification and UI bugs to improve user experience

- Fix priority-based notification sounds to prevent mentions playing timeline update sounds
- Fix reply functionality to use full @username@domain.com format for proper federation
- Fix missing reply sound notifications by adding reply to priority system
- Fix duplicate reply count display to prevent "(1 replies) (2 replies)" accumulation
- Fix poll voting to automatically refresh timeline and prevent duplicate vote attempts

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Storm Dragon
2025-07-21 17:45:56 -04:00
parent ff32d6a10b
commit b80ae3b0e2
3 changed files with 72 additions and 8 deletions

View File

@ -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

View File

@ -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)

View File

@ -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()
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()