1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
import { GLib, Variable } from "astal";
import { bind, Subscribable } from "astal/binding";
import { Astal, Gdk, Gtk } from "astal/gtk4";
import AstalNotifd from "gi://AstalNotifd?version=0.1";
const notificationTimeout: number = 5000;
class NotificationHandler implements Subscribable<Gtk.Widget[]> {
private notifications: Variable<Gtk.Widget[]> = Variable([]);
private notificationsMap: Map<number, Gtk.Widget> = new Map();
constructor() {
const notifd = AstalNotifd.get_default();
notifd.connect("notified", (_source, id, _replaced) => {
const n = notifd.get_notification(id);
this.create(n);
setTimeout(() => this.remove(id), notificationTimeout);
});
notifd.connect("resolved", (_source, id, _reason) => {
this.remove(id);
});
}
private rerender() {
this.notifications.set([...this.notificationsMap.values()].reverse());
}
private create(n: AstalNotifd.Notification) {
const notification = Notification(n);
this.notificationsMap.get(n.id)?.emit("destroy");
this.notificationsMap.set(n.id, notification);
this.rerender();
}
private remove(id: number) {
this.notificationsMap.get(id)?.emit("destroy");
this.notificationsMap.delete(id);
this.rerender();
}
subscribe(callback: (value: Gtk.Widget[]) => void): () => void {
return this.notifications.subscribe(callback);
}
get(): Gtk.Widget[] {
return this.notifications.get();
}
}
function getUrgencyClass(n: AstalNotifd.Notification): string {
switch (n.urgency) {
case AstalNotifd.Urgency.LOW:
return "low";
case AstalNotifd.Urgency.CRITICAL:
return "critical";
case AstalNotifd.Urgency.NORMAL:
default:
return "normal";
}
}
function Notification(n: AstalNotifd.Notification) {
const appName = n.appName;
const time = GLib.DateTime.new_from_unix_local(n.time);
return <box cssClasses={["Notification", getUrgencyClass(n)]} vertical>
<box cssClasses={["Header"]}>
<label cssClasses={["Application"]} halign={Gtk.Align.START} label={appName || "Unknown"} />
<label cssClasses={["Time"]} hexpand halign={Gtk.Align.END} label={time.format("%I:%M:%S %p")!} />
<button cssClasses={["Close"]} onClicked={() => n.dismiss()}><image iconName="window-close-symbolic" /></button>
</box>
<Gtk.Separator visible />
<box cssClasses={["Contents"]}>
<box vertical>
<label cssClasses={["Summary"]} halign={Gtk.Align.START} label={n.summary} />
<label cssClasses={["Body"]} useMarkup wrap maxWidthChars={0} justify={Gtk.Justification.FILL} label={n.body} />
</box>
</box>
{n.get_actions().length > 0 && <>
<Gtk.Separator visible />
<box cssClasses={["Actions"]}>
{n.get_actions().map(({ label, id }) => <button hexpand onClicked={() => n.invoke(id)}><label label={label} /></button>)}
</box>
</>}
</box>;
}
export default function(gdkmonitor: Gdk.Monitor) {
const { TOP, RIGHT } = Astal.WindowAnchor;
const notifications = new NotificationHandler();
return <window
// NOTE: is required due to last notification being displayed all the time
visible={bind(notifications).as(v => v.length != 0)}
name={"Notifications"}
cssClasses={["Notifications"]}
gdkmonitor={gdkmonitor}
exclusivity={Astal.Exclusivity.EXCLUSIVE}
anchor={TOP | RIGHT}>
<box vertical>
{bind(notifications)}
</box>
</window>
}
|