import { desktopEntrySubs } from "@/utils/icons";
import { bind, GLib, register, timeout, Variable } from "astal";
import { Astal, Gtk, Widget } from "astal/gtk3";
import { notifpopups as config } from "config";
import AstalNotifd from "gi://AstalNotifd";
const urgencyToString = (urgency: AstalNotifd.Urgency) => {
switch (urgency) {
case AstalNotifd.Urgency.LOW:
return "low";
case AstalNotifd.Urgency.NORMAL:
return "normal";
case AstalNotifd.Urgency.CRITICAL:
return "critical";
}
};
const getTime = (time: number) => {
const messageTime = GLib.DateTime.new_from_unix_local(time);
const now = GLib.DateTime.new_now_local();
const todayDay = now.get_day_of_year();
if (config.agoTime) {
const diff = now.difference(messageTime) / 1e6;
if (diff < 60) return "Now";
if (diff < 3600) {
const d = Math.floor(diff / 60);
return `${d} min${d === 1 ? "" : "s"} ago`;
}
if (diff < 86400) {
const d = Math.floor(diff / 3600);
return `${d} hour${d === 1 ? "" : "s"} ago`;
}
} else if (messageTime.get_day_of_year() === todayDay) {
const aMinuteAgo = GLib.DateTime.new_now_local().add_seconds(-60);
return aMinuteAgo !== null && messageTime.compare(aMinuteAgo) > 0 ? "Now" : messageTime.format("%H:%M")!;
}
if (messageTime.get_day_of_year() === todayDay - 1) return "Yesterday";
return messageTime.format("%d/%m")!;
};
const AppIcon = ({ appIcon, desktopEntry }: { appIcon: string; desktopEntry: string }) => {
// Try app icon
let icon = Astal.Icon.lookup_icon(appIcon) && appIcon;
// Try desktop entry
if (!icon) {
if (desktopEntrySubs.hasOwnProperty(desktopEntry)) icon = desktopEntrySubs[desktopEntry];
else if (Astal.Icon.lookup_icon(desktopEntry)) icon = desktopEntry;
}
return icon ? : null;
};
const Image = ({ icon }: { icon: string }) => {
if (GLib.file_test(icon, GLib.FileTest.EXISTS))
return (
);
if (Astal.Icon.lookup_icon(icon)) return ;
return null;
};
@register()
export default class Notification extends Widget.Box {
readonly #revealer;
#destroyed = false;
constructor({ notification, popup }: { notification: AstalNotifd.Notification; popup?: boolean }) {
super({ className: "notification" });
const time = Variable(getTime(notification.time)).poll(60000, () => getTime(notification.time));
this.#revealer = (
{notification.image && }
{notification.body && (
)}
{!popup && (
)}
) as Widget.Revealer;
this.add(this.#revealer);
// Init animation
const width = this.get_preferred_width()[1];
this.css = `margin-left: ${width}px; margin-right: -${width}px;`;
timeout(1, () => {
this.#revealer.revealChild = true;
this.css = `transition: 300ms cubic-bezier(0.05, 0.9, 0.1, 1.1); margin-left: 0; margin-right: 0;`;
});
// Close popup after timeout if transient or expire enabled in config
if (popup && (config.expire || notification.transient))
timeout(
notification.expireTimeout > 0
? notification.expireTimeout
: notification.urgency === AstalNotifd.Urgency.CRITICAL
? 10000
: 5000,
() => this.destroyWithAnims()
);
}
destroyWithAnims() {
if (this.#destroyed) return;
this.#destroyed = true;
const animTime = 120;
const animMargin = this.get_allocated_width();
this.css = `transition: ${animTime}ms cubic-bezier(0.85, 0, 0.15, 1);
margin-left: ${animMargin}px; margin-right: -${animMargin}px;`;
timeout(animTime, () => {
this.#revealer.revealChild = false;
timeout(this.#revealer.transitionDuration, () => this.destroy());
});
}
}