Files
bifrost/src/notifications/notification_manager.py
Storm Dragon f2c4ad1bd6 Add comprehensive soundpack manager with security-first design
- **Soundpack Manager**: Full-featured package discovery, download, and installation system
  - Repository management with HTTPS enforcement and validation
  - Directory listing support (auto-discovers .zip files) + soundpacknames.txt fallback
  - Secure download with size limits, timeout protection, and path sanitization
  - Zip bomb protection: file count, individual size, and total extraction limits
  - Audio file validation using magic numbers (not just extensions)
  - Accessible UI with keyboard navigation and screen reader optimization

- **Auto-refresh system**: Smart timeline updates respecting user activity
  - 300-second default interval + 10-second idle buffer
  - Keyboard activity tracking prevents interrupting active users
  - True new content detection using post IDs instead of counts
  - Preserves cursor position during background refreshes

- **Enhanced notifications**: Fixed spam issues and improved targeting
  - Timeline switching now silent (no notifications for actively viewed content)
  - Initial app load notifications disabled for 2 seconds
  - Generic "New content in timeline" messages instead of misleading post counts
  - Separate handling for auto-refresh vs manual refresh scenarios

- **Load more posts improvements**: Better positioning and user experience
  - New posts load below cursor position instead of above
  - Cursor automatically focuses on first new post for natural reading flow
  - Fixed widget hierarchy issues preventing activation

- **Accessibility enhancements**: Workarounds for screen reader quirks
  - Added list headers to fix Orca single-item list reading issues
  - Improved accessible names and descriptions throughout
  - Non-selectable header items with dynamic counts
  - Better error messages and status updates

- **Settings integration**: Corrected soundpack configuration management
  - Fixed inconsistent config keys (now uses [audio] sound_pack consistently)
  - Added soundpack manager to File menu (Ctrl+Shift+P)
  - Repository persistence and validation

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-20 15:18:50 -04:00

120 lines
3.9 KiB
Python

"""
Desktop notification manager using plyer
"""
from typing import Optional
from plyer import notification
from config.settings import SettingsManager
class NotificationManager:
"""Manages desktop notifications for Bifrost"""
def __init__(self, settings: SettingsManager):
self.settings = settings
def is_enabled(self, notification_type: str = None) -> bool:
"""Check if notifications are enabled globally or for specific type"""
if not self.settings.get_bool('notifications', 'enabled', True):
return False
if notification_type:
return self.settings.get_bool('notifications', notification_type, True)
return True
def show_notification(self, title: str, message: str, notification_type: str = None):
"""Show a desktop notification"""
if not self.is_enabled(notification_type):
return
try:
notification.notify(
title=title,
message=message,
app_name="Bifrost",
timeout=5
)
except Exception as e:
print(f"Failed to show notification: {e}")
def notify_direct_message(self, sender: str, message_preview: str):
"""Show notification for direct message"""
if not self.is_enabled('direct_messages'):
return
self.show_notification(
title=f"Direct message from {sender}",
message=message_preview,
notification_type='direct_messages'
)
def notify_mention(self, sender: str, post_preview: str):
"""Show notification for mention"""
if not self.is_enabled('mentions'):
return
self.show_notification(
title=f"{sender} mentioned you",
message=post_preview,
notification_type='mentions'
)
def notify_boost(self, sender: str, post_preview: str):
"""Show notification for boost/reblog"""
if not self.is_enabled('boosts'):
return
self.show_notification(
title=f"{sender} boosted your post",
message=post_preview,
notification_type='boosts'
)
def notify_favorite(self, sender: str, post_preview: str):
"""Show notification for favorite"""
if not self.is_enabled('favorites'):
return
self.show_notification(
title=f"{sender} favorited your post",
message=post_preview,
notification_type='favorites'
)
def notify_follow(self, follower: str):
"""Show notification for new follower"""
if not self.is_enabled('follows'):
return
self.show_notification(
title="New follower",
message=f"{follower} started following you",
notification_type='follows'
)
def notify_timeline_update(self, count: int, timeline_type: str = "timeline"):
"""Show notification for timeline updates"""
if not self.is_enabled('timeline_updates'):
return
if count == 1:
message = f"1 new post in your {timeline_type}"
else:
message = f"{count} new posts in your {timeline_type}"
self.show_notification(
title="Timeline updated",
message=message,
notification_type='timeline_updates'
)
def notify_new_content(self, timeline_type: str = "timeline"):
"""Show notification for new content without counting posts"""
if not self.is_enabled('timeline_updates'):
return
self.show_notification(
title="Timeline updated",
message=f"New content in your {timeline_type}",
notification_type='timeline_updates'
)