diff --git a/git_monitor/notifier.py b/git_monitor/notifier.py index 69af373..dce0d3b 100644 --- a/git_monitor/notifier.py +++ b/git_monitor/notifier.py @@ -1,45 +1,93 @@ +import threading from git_monitor.logger import logger +# Modern Windows 10/11 toasts with buttons +try: + from windows_toasts import WindowsToaster, ToastText2, ToastActivatedEventArgs, ToastButton + WINDOWS_TOASTS_AVAILABLE = True + toaster = WindowsToaster('Git Monitor') +except ImportError: + logger.warning("windows-toasts not found. Interactive buttons will not be available.") + WINDOWS_TOASTS_AVAILABLE = False + +# Fallbacks try: from plyer import notification PLYER_AVAILABLE = True except ImportError: - logger.warning("plyer not found. Will try win10toast.") PLYER_AVAILABLE = False try: from win10toast import ToastNotifier WIN10TOAST_AVAILABLE = True - toaster = ToastNotifier() + legacy_toaster = ToastNotifier() except ImportError: WIN10TOAST_AVAILABLE = False class Notifier: def notify(self, title, message): + """Simple notification (no buttons).""" logger.info(f"Notification: {title} - {message}") - # Try plyer first + if WINDOWS_TOASTS_AVAILABLE: + try: + toast = ToastText2() + toast.headline = title + toast.body = message + toaster.show_toast(toast) + return + except Exception as e: + logger.error(f"windows-toasts simple notification failed: {e}") + + # Fallback to older libraries if PLYER_AVAILABLE: try: - notification.notify( - title=title, - message=message, - app_name="Git Monitor" - ) + notification.notify(title=title, message=message, app_name="Git Monitor") return - except Exception as e: - logger.error(f"Plyer notification failed: {e}. Trying fallback...") + except: pass - # Fallback to win10toast if WIN10TOAST_AVAILABLE: try: - # threaded=True prevents blocking the app - toaster.show_toast(title, message, duration=5, threaded=True) + legacy_toaster.show_toast(title, message, duration=5, threaded=True) return - except Exception as e: - logger.error(f"win10toast notification failed: {e}") + except: pass - # Final fallback to stdout - print(f"[{title}] {message}") + def notify_interactive(self, title, message, on_save, on_later): + """Interactive notification with 'Save now' and 'Ask in 5 min' buttons.""" + logger.info(f"Interactive Notification: {title} - {message}") + + if not WINDOWS_TOASTS_AVAILABLE: + logger.warning("Interactive notifications not available, performing default action (Save).") + on_save() + return + + try: + toast = ToastText2() + toast.headline = title + toast.body = message + + # Action buttons + toast.AddAction(ToastButton("Zapisz teraz", "save")) + toast.AddAction(ToastButton("Zapytaj mnie za 5 min", "later")) + + # Handler for button clicks + def on_activated(args: ToastActivatedEventArgs): + if args.arguments == "save": + logger.info("User clicked 'Zapisz teraz'") + on_save() + elif args.arguments == "later": + logger.info("User clicked 'Zapytaj mnie za 5 min'") + on_later() + else: + # Default click on toast body + on_save() + + toast.on_activated = on_activated + toaster.show_toast(toast) + + except Exception as e: + logger.error(f"Interactive notification error: {e}") + # Fallback to immediate save if notification fails + on_save() notifier = Notifier()