feat: add interactive notifications with buttons using windows-toasts
This commit is contained in:
@@ -1,45 +1,93 @@
|
|||||||
|
import threading
|
||||||
from git_monitor.logger import logger
|
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:
|
try:
|
||||||
from plyer import notification
|
from plyer import notification
|
||||||
PLYER_AVAILABLE = True
|
PLYER_AVAILABLE = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
logger.warning("plyer not found. Will try win10toast.")
|
|
||||||
PLYER_AVAILABLE = False
|
PLYER_AVAILABLE = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from win10toast import ToastNotifier
|
from win10toast import ToastNotifier
|
||||||
WIN10TOAST_AVAILABLE = True
|
WIN10TOAST_AVAILABLE = True
|
||||||
toaster = ToastNotifier()
|
legacy_toaster = ToastNotifier()
|
||||||
except ImportError:
|
except ImportError:
|
||||||
WIN10TOAST_AVAILABLE = False
|
WIN10TOAST_AVAILABLE = False
|
||||||
|
|
||||||
class Notifier:
|
class Notifier:
|
||||||
def notify(self, title, message):
|
def notify(self, title, message):
|
||||||
|
"""Simple notification (no buttons)."""
|
||||||
logger.info(f"Notification: {title} - {message}")
|
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:
|
if PLYER_AVAILABLE:
|
||||||
try:
|
try:
|
||||||
notification.notify(
|
notification.notify(title=title, message=message, app_name="Git Monitor")
|
||||||
title=title,
|
|
||||||
message=message,
|
|
||||||
app_name="Git Monitor"
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
except Exception as e:
|
except: pass
|
||||||
logger.error(f"Plyer notification failed: {e}. Trying fallback...")
|
|
||||||
|
|
||||||
# Fallback to win10toast
|
|
||||||
if WIN10TOAST_AVAILABLE:
|
if WIN10TOAST_AVAILABLE:
|
||||||
try:
|
try:
|
||||||
# threaded=True prevents blocking the app
|
legacy_toaster.show_toast(title, message, duration=5, threaded=True)
|
||||||
toaster.show_toast(title, message, duration=5, threaded=True)
|
|
||||||
return
|
return
|
||||||
except Exception as e:
|
except: pass
|
||||||
logger.error(f"win10toast notification failed: {e}")
|
|
||||||
|
|
||||||
# Final fallback to stdout
|
def notify_interactive(self, title, message, on_save, on_later):
|
||||||
print(f"[{title}] {message}")
|
"""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()
|
notifier = Notifier()
|
||||||
|
|||||||
Reference in New Issue
Block a user