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";
|
|
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
|
|
}
|