Fix notification and sound system issues with comprehensive improvements

- **Fixed duplicate sound events**: Prevent success/shutdown sounds from playing multiple times during timeline switches and app quit
- **Added event coordination patterns**: Implemented flags and source tracking to prevent circular UI event chains
- **Completed sound event coverage**: Added missing sound methods (play_favorite, play_follow, play_unfollow, play_direct_message, play_post) and wired them to user actions
- **Enhanced notification system**: Fixed desktop notifications ignoring enable/disable settings by adding missing notifications section to default config
- **Improved timeline-specific sounds**: Conversations now use direct_message sound instead of generic timeline_update sound
- **Added duplicate code prevention guidelines**: Comprehensive CLAUDE.md section with patterns, checklist, and testing requirements to prevent future duplicate implementations
- **Sound pack compatibility**: Full support for both default pack (private_message, post_sent) and Doom pack (direct_message, post) naming conventions with intelligent fallback

All sound events now properly trigger on user actions: boost/favorite posts, follow/unfollow users, notifications, timeline updates, and lifecycle events.

🤖 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 07:53:36 -04:00
parent 014f288524
commit 3c3b50bdb9
6 changed files with 158 additions and 10 deletions

View File

@ -446,9 +446,9 @@ class MainWindow(QMainWindow):
def on_timeline_tab_changed(self, index):
"""Handle timeline tab change"""
self.switch_timeline(index)
self.switch_timeline(index, from_tab_change=True)
def switch_timeline(self, index):
def switch_timeline(self, index, from_tab_change=False):
"""Switch to timeline by index with loading feedback"""
timeline_names = ["Home", "Messages", "Mentions", "Local", "Federated", "Bookmarks", "Followers", "Following"]
timeline_types = ["home", "conversations", "notifications", "local", "federated", "bookmarks", "followers", "following"]
@ -457,8 +457,13 @@ class MainWindow(QMainWindow):
timeline_name = timeline_names[index]
timeline_type = timeline_types[index]
# Set tab to match if called from keyboard shortcut
if self.timeline_tabs.currentIndex() != index:
# Prevent duplicate calls for the same timeline
if hasattr(self, '_current_timeline_switching') and self._current_timeline_switching:
return
self._current_timeline_switching = True
# Set tab to match if called from keyboard shortcut (but not if already from tab change)
if not from_tab_change and self.timeline_tabs.currentIndex() != index:
self.timeline_tabs.setCurrentIndex(index)
# Announce loading
@ -478,6 +483,10 @@ class MainWindow(QMainWindow):
if hasattr(self.timeline, 'sound_manager'):
self.timeline.sound_manager.play_error()
self.status_bar.showMessage(f"Failed to load {timeline_name} timeline: {str(e)}", 3000)
finally:
# Reset the flag after a brief delay to allow the operation to complete
from PySide6.QtCore import QTimer
QTimer.singleShot(100, lambda: setattr(self, '_current_timeline_switching', False))
def get_selected_post(self):
"""Get the currently selected post from timeline"""
@ -591,6 +600,9 @@ class MainWindow(QMainWindow):
else:
client.reblog_status(post.id)
self.status_bar.showMessage("Post boosted", 2000)
# Play boost sound for successful boost
if hasattr(self.timeline, 'sound_manager'):
self.timeline.sound_manager.play_boost()
# Refresh timeline to show updated state
self.timeline.refresh()
except Exception as e:
@ -610,6 +622,9 @@ class MainWindow(QMainWindow):
else:
client.favourite_status(post.id)
self.status_bar.showMessage("Post favorited", 2000)
# Play favorite sound for successful favorite
if hasattr(self.timeline, 'sound_manager'):
self.timeline.sound_manager.play_favorite()
# Refresh timeline to show updated state
self.timeline.refresh()
except Exception as e:
@ -630,6 +645,7 @@ class MainWindow(QMainWindow):
def quit_application(self):
"""Quit the application with shutdown sound"""
self._shutdown_sound_played = True # Mark that we're handling the shutdown sound
if hasattr(self.timeline, 'sound_manager'):
self.timeline.sound_manager.play_shutdown()
# Wait briefly for sound to start playing
@ -761,6 +777,9 @@ class MainWindow(QMainWindow):
client.follow_account(post.account.id)
username = post.account.display_name or post.account.username
self.status_bar.showMessage(f"Followed {username}", 2000)
# Play follow sound for successful follow
if hasattr(self.timeline, 'sound_manager'):
self.timeline.sound_manager.play_follow()
except Exception as e:
self.status_bar.showMessage(f"Follow failed: {str(e)}", 3000)
@ -776,6 +795,9 @@ class MainWindow(QMainWindow):
client.unfollow_account(post.account.id)
username = post.account.display_name or post.account.username
self.status_bar.showMessage(f"Unfollowed {username}", 2000)
# Play unfollow sound for successful unfollow
if hasattr(self.timeline, 'sound_manager'):
self.timeline.sound_manager.play_unfollow()
except Exception as e:
self.status_bar.showMessage(f"Unfollow failed: {str(e)}", 3000)
@ -858,14 +880,17 @@ class MainWindow(QMainWindow):
client.follow_account(target_account['id'])
display_name = target_account.get('display_name') or target_account['username']
self.status_bar.showMessage(f"Followed {display_name}", 2000)
# Play follow sound for successful follow
if hasattr(self.timeline, 'sound_manager'):
self.timeline.sound_manager.play_follow()
except Exception as e:
self.status_bar.showMessage(f"Follow failed: {str(e)}", 3000)
def closeEvent(self, event):
"""Handle window close event"""
# Play shutdown sound if not already played through quit_application
if hasattr(self.timeline, 'sound_manager'):
# Only play shutdown sound if not already played through quit_application
if not hasattr(self, '_shutdown_sound_played') and hasattr(self.timeline, 'sound_manager'):
self.timeline.sound_manager.play_shutdown()
# Wait briefly for sound to complete
from PySide6.QtCore import QTimer, QEventLoop