/* * dotfiles - handler.ts * * Created by Janis Hutz 03/21/2025, Licensed under the GPL V3 License * https://janishutz.com, development@janishutz.com * * */ import { Astal, Gtk, Gdk } from "astal/gtk3" import Notifd from "gi://AstalNotifd"; import Notification from "./notifications/notifications"; import { type Subscribable } from "astal/binding"; import { Variable, bind, timeout } from "astal" // Config const TIMEOUT_DELAY = 5000; class Notifier implements Subscribable { private display: Map = new Map(); private notifications: Map = new Map(); private notifd: Notifd.Notifd; private subscriberData: Variable = Variable( [] ); private instanceID: number; private menuOpen: boolean; /** * Sets up the notifier */ constructor( id: number ) { this.instanceID = id; this.menuOpen = false; this.notifd = Notifd.get_default(); this.notifd.ignoreTimeout = true; this.notifd.connect( 'notified', ( _, id ) => { this.add( id ); } ); this.notifd.connect( 'resolved', ( _, id ) => { this.delete( id ); } ); } private notify () { this.subscriberData.set( [ ...this.display.values() ].reverse() ); } private add ( id: number ) { this.notifications.set( id, this.notifd.get_notification( id )! ); this.show( id ); } /** * Show an element on screen * @param id - The id of the element to be shown */ private show ( id: number ) { this.set( id, Notification( { notification: this.notifications.get( id )!, onHoverLost: () => { if ( !this.menuOpen ) { this.hide( id ); } }, setup: () => timeout( TIMEOUT_DELAY, () => { if ( !this.menuOpen ) { this.hide( id ); } } ), id: id, delete: deleteHelper, instanceID: this.instanceID } ) ) } /** * Set a selected widget to be shown * @param id - The id of the element to be referenced for later * @param widget - A GTK widget instance */ private set ( id: number, widget: Gtk.Widget ) { this.display.get( id )?.destroy(); this.display.set( id, widget ); this.notify(); } /** * Hide, not delete notification (= send to notification centre) * @param id - The id of the notification to hide */ private hide ( id: number ) { this.display.get( id )?.destroy(); this.display.delete( id ); this.notify(); } /** * Delete a notification (from notification centre too) * @param id - The id of the notification to hide */ delete ( id: number ) { this.hide( id ); this.notifications.get( id )?.dismiss(); this.notifications.delete( id ); if ( this.notifications.size == 0 ) { this.menuOpen = false; } } openNotificationMenu () { // Show all notifications that have not been cleared if ( this.notifications.size > 0 ) { this.menuOpen = true; this.notifications.forEach( ( _, id ) => { this.show( id ); } ) } } hideNotifications () { this.menuOpen = false; this.notifications.forEach( ( _, id ) => { this.hide( id ); } ); } toggleNotificationMenu () { if ( this.menuOpen ) { this.hideNotifications(); } else { this.openNotificationMenu(); } } clearAllNotifications () { this.menuOpen = false; this.notifications.forEach( ( _, id ) => { this.delete( id ); } ) } clearNewestNotification () { this.delete( [ ...this.notifications.keys() ][0] ); } subscribe(callback: (value: unknown) => void): () => void { return this.subscriberData.subscribe( callback ); } get() { return this.subscriberData.get(); } } const notifiers: Map = new Map(); const deleteHelper = ( id: number, instanceID: number ) => { notifiers.get( instanceID )?.delete( id ); } const openNotificationMenu = ( id: number ) => { notifiers.get( id )?.openNotificationMenu(); } const closeNotificationMenu = ( id: number ) => { notifiers.get( id )?.hideNotifications(); } const toggleNotificationMenu = ( id: number ) => { notifiers.get( id )?.toggleNotificationMenu(); } const clearAllNotifications = ( id: number ) => { notifiers.get( id )?.clearAllNotifications(); } const clearNewestNotifications = ( id: number ) => { notifiers.get( id )?.clearNewestNotification(); } const startNotificationHandler = (id: number, gdkmonitor: Gdk.Monitor) => { const { TOP, RIGHT } = Astal.WindowAnchor const notifier: Notifier = new Notifier( id ); notifiers.set( id, notifier ); return {bind(notifier)} } export default { startNotificationHandler, openNotificationMenu, closeNotificationMenu, clearAllNotifications, clearNewestNotifications, toggleNotificationMenu }