From 5fab76c41fceb868c023a346bd71c5c83e7577ba Mon Sep 17 00:00:00 2001 From: Joshua Yuen Date: Sat, 12 Sep 2026 20:09:54 -0400 Subject: [PATCH] Keep notifications in dropdown after toast timeout and polish toasts --- modules/home/ags/app/style.scss | 11 +++ modules/home/ags/app/widget/Notifications.tsx | 82 +++++++++++++++---- modules/home/ags/app/widget/QuickSettings.tsx | 17 ++-- 3 files changed, 88 insertions(+), 22 deletions(-) diff --git a/modules/home/ags/app/style.scss b/modules/home/ags/app/style.scss index 15c2a62..f722483 100644 --- a/modules/home/ags/app/style.scss +++ b/modules/home/ags/app/style.scss @@ -16,6 +16,7 @@ $hover-bg: transparentize($base02, 0.2); $hover-overlay: transparentize($base05, 0.92); $active-hover: transparentize($primary-bg, 0.15); $active-gradient: linear-gradient(to right, $primary-bg, darken($primary-bg, 4%)); +$toast-bg: transparentize($base02, 0.15); $bar-alpha: 0.7; $bar-bg: transparentize($base01, 1 - $bar-alpha); @@ -582,6 +583,16 @@ scale { &.critical { border-color: $base08; } + + // popup toasts are more opaque than list entries and become + // completely opaque while hovered + &.toast { + background-color: $toast-bg; + + &:hover { + background-color: $base02; + } + } } .notif-header { diff --git a/modules/home/ags/app/widget/Notifications.tsx b/modules/home/ags/app/widget/Notifications.tsx index 04c9d76..1324eb8 100644 --- a/modules/home/ags/app/widget/Notifications.tsx +++ b/modules/home/ags/app/widget/Notifications.tsx @@ -12,6 +12,13 @@ export const dndIcon = Variable.derive([bind(notifd, "dontDisturb")], (dnd) => : "preferences-system-notifications-symbolic", ) +// every notification received this session is kept until it is explicitly +// cleared, so popups that time out remain in the dropdown's list. only +// notifications dismissed directly from their popup skip the list. +export const history = Variable([]) + +export const notifCount = bind(history).as((ns) => ns.length) + export function DndToggle() { return ( @@ -137,12 +186,15 @@ export default function Notifications(gdkmonitor: Gdk.Monitor) { marginTop={54} marginRight={8} visible={bind(notifd, "notifications").as((ns) => ns.length > 0)} + onNotifyVisible={(self) => { + if (!self.visible) hovered.clear() + }} > {bind(notifd, "notifications").as((ns) => [...ns] .sort((a, b) => b.time - a.time) - .map((n) => NotificationCard(n)), + .map((n) => NotificationCard(n, true)), )} diff --git a/modules/home/ags/app/widget/QuickSettings.tsx b/modules/home/ags/app/widget/QuickSettings.tsx index 1ac75ce..4bf557d 100644 --- a/modules/home/ags/app/widget/QuickSettings.tsx +++ b/modules/home/ags/app/widget/QuickSettings.tsx @@ -1,7 +1,6 @@ import Gtk from "gi://Gtk?version=3.0" import GLib from "gi://GLib" import AstalWp from "gi://AstalWp" -import Notifd from "gi://AstalNotifd" import { execAsync, Variable, bind } from "astal" import Dropdown from "./Dropdown" import { wifiIcon, WifiSection } from "./Wifi" @@ -9,13 +8,17 @@ import { bluetoothIndicator, BluetoothSection } from "./Bluetooth" import { BrightnessRow } from "./Brightness" import { MediaSection } from "./Media" import { batteryIcon, batteryPercent, BatterySection } from "./Battery" -import { NotificationCard, DndToggle, dndIcon } from "./Notifications" +import { + clearNotifications, + dndIcon, + DndToggle, + history, + notifCount, + NotificationCard, +} from "./Notifications" const speaker = AstalWp.get_default().audio.default_speaker! const mic = AstalWp.get_default().audio.default_microphone -const notifd = Notifd.get_default() - -const notifCount = bind(notifd, "notifications").as((ns) => ns.length) const speakerIcon = Variable.derive( [bind(speaker, "volume"), bind(speaker, "mute")], @@ -162,7 +165,7 @@ export default function QuickSettings(gdkmonitor: Gdk.Monitor) { @@ -173,7 +176,7 @@ export default function QuickSettings(gdkmonitor: Gdk.Monitor) { label="no notifications" visible={notifCount.as((n) => n === 0)} /> - {bind(notifd, "notifications").as((ns) => + {bind(history).as((ns) => [...ns] .sort((a, b) => b.time - a.time) .map((n) => NotificationCard(n)),