207 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			207 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| /*
 | |
| *               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<number, Gtk.Widget> = new Map();
 | |
|     private notifications: Map<number, Notifd.Notification> = new Map();
 | |
| 
 | |
|     private notifd: Notifd.Notifd;
 | |
|     private subscriberData: Variable<Gtk.Widget[]> = 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<number, Notifier> = 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 <window
 | |
|         className="NotificationHandler"
 | |
|         gdkmonitor={gdkmonitor}
 | |
|         exclusivity={Astal.Exclusivity.EXCLUSIVE}
 | |
|         anchor={TOP | RIGHT}>
 | |
|         <box vertical noImplicitDestroy>
 | |
|             {bind(notifier)}
 | |
|         </box>
 | |
|     </window>
 | |
| }
 | |
| 
 | |
| 
 | |
| export default {
 | |
|     startNotificationHandler,
 | |
|     openNotificationMenu,
 | |
|     closeNotificationMenu,
 | |
|     clearAllNotifications,
 | |
|     clearNewestNotifications,
 | |
|     toggleNotificationMenu
 | |
| }
 |