feat(src/services): add config persistence, reload handling and notification pruning on config change

Add ability to persist current configuration to disk and trigger reloads, expose set() and save() methods, monitor the config directory for changes, and adjust notification history based on config modifications.

- New Features
  - Implement `save()` method to write config to file and trigger reload
  - Add `set()` method for in‑memory config updates
  - Extend file monitoring to include parent directory
  - Rename `get_all_config` to `get_config_all`
- Enhancements
  - Refine reload logic to correctly capture previous state via `_last_notified_config`
  - Trigger reloads on additional file‑monitor events (CREATED, DELETED)
  - Prune cached notifications according to config changes (`notification_ignored_apps`, `notification_limited_apps_history`)
- Bug Fixes
  - Resolve race condition where reload‑pending flag could be left set
  - Ensure callbacks are notified on every reload request
  - Fix stale comparison of config values in `has_changed`
This commit is contained in:
sankalp committed 2026-05-03 22:06:23 +05:45
1 parent 791400f437
commit 244a913d96
2 files changed
+87 -14

No files matched your search

+52 -4
View File
@@ -11,6 +11,7 @@ from fabric.notifications import (
from fabric.utils import GdkPixbuf, logger, os, time
import shared.data as data
from services.config import config, on_config_change
NOTIFICATION_CACHE_FILE = f"{data.CACHE_DIR}/notification_history.json"
@@ -222,11 +223,43 @@ class CachedNotifications(Notifications):
self.load_cached_notifications()
# Connect to the notification_added signal to cache new notifications
# Note: self here refers to the CachedNotifications service, which inherits from Notifications
# So we connect to our own notification_added signal
super().notification_added.connect(self.on_notification_added)
# Listen for config changes to prune history if needed
on_config_change(self._on_config_changed)
def _on_config_changed(self, new_config, old_config):
# If ignored apps changed, remove them from history
if config().has_changed("notification_ignored_apps", old_config):
ignored_apps = new_config.get("notification_ignored_apps", [])
ids_to_remove = [
cid
for cid, cnotif in self._cached_notifications.items()
if cnotif.app_name in ignored_apps
]
for cid in ids_to_remove:
self.remove_cached_notification(cid)
if ids_to_remove:
logger.debug(
f"Hot-reload: Removed {len(ids_to_remove)} notifications from ignored apps"
)
# If limited apps changed, prune duplicates
if config().has_changed("notification_limited_apps_history", old_config):
limited_apps = new_config.get("notification_limited_apps_history", [])
for app in limited_apps:
app_notifs = [
(cid, cnotif.timestamp)
for cid, cnotif in self._cached_notifications.items()
if cnotif.app_name == app
]
if len(app_notifs) > 1:
# Sort by timestamp descending and keep only the first (latest)
app_notifs.sort(key=lambda x: x[1], reverse=True)
for cid, _ in app_notifs[1:]:
self.remove_cached_notification(cid)
logger.debug(f"Hot-reload: Pruned history for limited app {app}")
def load_cached_notifications(self) -> dict[int, CachedNotification]:
"""Load cached notifications from a JSON file (deserialization)."""
try:
@@ -309,7 +342,8 @@ class CachedNotifications(Notifications):
)
# Check if this app should be ignored for history (don't cache)
if notification.app_name in data.NOTIFICATION_IGNORED_APPS_HISTORY:
ignored_apps = config().get("notification_ignored_apps", [])
if notification.app_name in ignored_apps:
# Don't cache notifications from ignored apps, but still allow popup display
logger.debug(
f"Ignoring notification from {notification.app_name} (in ignore list)"
@@ -347,6 +381,20 @@ class CachedNotifications(Notifications):
f"Caching new notification: ID={notification.id}, App={notification.app_name}, Summary={notification.summary[:50]}..."
)
# Handle limited history apps - remove previous notifications from same app
limited_apps = config().get("notification_limited_apps_history", [])
if notification.app_name in limited_apps:
ids_to_remove = [
cid
for cid, cnotif in self._cached_notifications.items()
if cnotif.app_name == notification.app_name
]
for cid in ids_to_remove:
self.remove_cached_notification(cid)
logger.debug(
f"Pruned {len(ids_to_remove)} old notifications for limited app {notification.app_name}"
)
# GUARANTEED STORAGE: Always create and store notification to history first
cache_id = self._next_cache_id
self._next_cache_id += 1