Files
colorshell/ags/widget/Notification.tsx
T
2025-07-23 22:22:18 -03:00

125 lines
5.3 KiB
TypeScript

import { Gdk, Gtk } from "ags/gtk4";
import { Separator } from "./Separator";
import { HistoryNotification, Notifications } from "../scripts/notifications";
import { getAppIcon, getSymbolicIcon } from "../scripts/apps";
import { onCleanup } from "ags";
import AstalNotifd from "gi://AstalNotifd";
import Pango from "gi://Pango?version=1.0";
import GLib from "gi://GLib?version=2.0";
import GObject from "gi://GObject?version=2.0";
import { escapeUnintendedMarkup, pathToURI } from "../scripts/utils";
function getNotificationImage(notif: AstalNotifd.Notification|HistoryNotification): (string|undefined) {
const img = notif.image || notif.appIcon;
if(!img || !img.includes('/'))
return undefined;
return pathToURI(img);
}
export function NotificationWidget({ notification, actionClicked, holdOnHover, showTime, actionClose }: {
notification: AstalNotifd.Notification|number|HistoryNotification;
actionClicked?: (notif: AstalNotifd.Notification|HistoryNotification) => void;
actionClose?: (notif: AstalNotifd.Notification|HistoryNotification) => void;
holdOnHover?: boolean;
showTime?: boolean; // It's showTime :speaking_head: :boom: :bangbang:
}): Gtk.Widget {
notification = (typeof notification === "number") ?
AstalNotifd.get_default().get_notification(notification)
: notification;
const conns: Map<GObject.Object, Array<number>> = new Map();
onCleanup(() =>
conns.forEach((ids, obj) => ids.forEach(id => obj.disconnect(id))));
return <Gtk.Box hexpand class={`notification ${
Notifications.getDefault().getUrgencyString(notification.urgency)
}`} orientation={Gtk.Orientation.VERTICAL} spacing={5}
$={(self) => {
const eventControllerMotion = Gtk.EventControllerMotion.new(),
gestureClick = Gtk.GestureClick.new();
self.add_controller(eventControllerMotion);
self.add_controller(gestureClick);
conns.set(eventControllerMotion, [
eventControllerMotion.connect("enter", () =>
holdOnHover && Notifications.getDefault().holdNotification(notification.id)),
eventControllerMotion.connect("leave", () =>
holdOnHover && notification && Notifications.getDefault().removeNotification(notification.id))
]);
conns.set(gestureClick, [
gestureClick.connect("released", (gesture) => {
gesture.get_current_button() === Gdk.BUTTON_PRIMARY &&
actionClicked?.(notification);
})
]);
}}>
<Gtk.Box class={"top"} hexpand>
<Gtk.Image class="app-icon" $={(self) => {
const icon = getSymbolicIcon(notification.appIcon ?? notification.appName) ??
getSymbolicIcon(notification.appName) ?? getAppIcon(notification.appName);
if(icon) {
self.set_from_icon_name(icon);
return;
}
self.set_visible(false);
}} />
<Gtk.Label class={"app-name"} halign={Gtk.Align.START} hexpand={true}
label={notification.appName || "Application"} />
<Gtk.Label class={"time"} visible={showTime} xalign={1}
label={GLib.DateTime.new_from_unix_local(notification.time).format("%H:%M") ?? ""} />
<Gtk.Button halign={Gtk.Align.END} iconName={"window-close-symbolic"}
class={"close"} onClicked={() => actionClose?.(notification)}/>
</Gtk.Box>
<Separator alpha={.1} orientation={Gtk.Orientation.VERTICAL} />
<Gtk.Box class={"content"}>
{getNotificationImage(notification) &&
<Gtk.Box class={"image"} hexpand={false} vexpand={false}
css={`background-image: url("${getNotificationImage(notification)}");`}
/>
}
<Gtk.Box class={"text"} orientation={Gtk.Orientation.VERTICAL}
vexpand={true}>
<Gtk.Label class={"summary"} useMarkup={true} hexpand xalign={0}
vexpand={false} ellipsize={Pango.EllipsizeMode.END} label={
escapeUnintendedMarkup(notification.summary)}
/>
<Gtk.Label class={"body"} useMarkup={true} xalign={0} wrap={true} hexpand
vexpand wrapMode={Pango.WrapMode.WORD_CHAR} valign={Gtk.Align.START} label={
escapeUnintendedMarkup(notification.body)}
/>
</Gtk.Box>
</Gtk.Box>
<Gtk.Box class={"actions button-row"} hexpand={true} visible={
(notification instanceof AstalNotifd.Notification) &&
(notification.actions.filter(action => action.label.toLowerCase() !== "view").length > 0)
}>
{
(notification instanceof AstalNotifd.Notification) &&
notification.actions.filter(a => a.label.toLowerCase() !== "view").map(action =>
<Gtk.Button class={"action"} label={action.label}
hexpand={true} onClicked={(_) => {
notification.invoke(action.id);
actionClose?.(notification);
}}
/>)
}
</Gtk.Box>
</Gtk.Box> as Gtk.Widget;
}