[AGS] Add new notifications setup
This commit is contained in:
		
							
								
								
									
										6
									
								
								config/astal/components/notifications-opt/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								config/astal/components/notifications-opt/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| # Source | ||||
| This has been copied from [matshell](https://github.com/Neurarian/matshell) | ||||
|  | ||||
| It is not yet used, as it has not been adapted yet to feature a notification history. | ||||
|  | ||||
| Potentially, a notification centre will be added to make this here work better. Styling is also missing | ||||
							
								
								
									
										32
									
								
								config/astal/components/notifications-opt/main.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								config/astal/components/notifications-opt/main.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| import { Astal } from "astal/gtk4"; | ||||
| import Notifd from "gi://AstalNotifd"; | ||||
| import Hyprland from "gi://AstalHyprland"; | ||||
| import { bind } from "astal"; | ||||
| import { NotificationWidget } from "./modules/Notification"; | ||||
| import { hyprToGdk } from "../../util/hyprland"; | ||||
|  | ||||
| export default function Notifications() { | ||||
|     const notifd = Notifd.get_default(); | ||||
|     const hyprland = Hyprland.get_default(); | ||||
|     const { TOP, RIGHT } = Astal.WindowAnchor; | ||||
|  | ||||
|     return ( | ||||
|         <window | ||||
|             name="notifications" | ||||
|             gdkmonitor={bind(hyprland, "focusedMonitor").as( | ||||
|                 (focused: Hyprland.Monitor) => hyprToGdk(focused), | ||||
|             )} | ||||
|             anchor={TOP | RIGHT} | ||||
|             visible={bind(notifd, "notifications").as( | ||||
|                 (notifications) => notifications.length > 0, | ||||
|             )} | ||||
|             child={ | ||||
|                 <box vertical={true} cssClasses={["notifications"]}> | ||||
|                     {bind(notifd, "notifications").as((notifications) => | ||||
|                         notifications.map((n) => <NotificationWidget notification={n} />), | ||||
|                     )} | ||||
|                 </box> | ||||
|             } | ||||
|         /> | ||||
|     ); | ||||
| } | ||||
							
								
								
									
										25
									
								
								config/astal/components/notifications-opt/modules/Icon.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								config/astal/components/notifications-opt/modules/Icon.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| import { Gtk } from "astal/gtk4"; | ||||
| import Notifd from "gi://AstalNotifd"; | ||||
| import { fileExists, isIcon } from "../../../util/notifd"; | ||||
|  | ||||
|  | ||||
| export function NotificationIcon(notification: Notifd.Notification) { | ||||
|     if (notification.image || notification.appIcon || notification.desktopEntry) { | ||||
|         const icon = notification.image || notification.appIcon || notification.desktopEntry; | ||||
|         if (fileExists(icon)) { | ||||
|             return ( | ||||
|                 <box expand={false} valign={Gtk.Align.CENTER}> | ||||
|                     <image file={icon} /> | ||||
|                 </box> | ||||
|             ); | ||||
|         } else if (isIcon(icon)) { | ||||
|             return ( | ||||
|                 <box expand={false} valign={Gtk.Align.CENTER}> | ||||
|                     <image iconName={icon} /> | ||||
|                 </box> | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|     return null; | ||||
| } | ||||
|  | ||||
| @@ -0,0 +1,126 @@ | ||||
| import { bind } from "astal"; | ||||
| import { Gtk } from "astal/gtk4"; | ||||
| import Notifd from "gi://AstalNotifd"; | ||||
| import { NotificationIcon } from "./Icon"; | ||||
| import { createTimeoutManager } from "../../../util/notifd"; | ||||
|  | ||||
| export function NotificationWidget({ | ||||
|     notification, | ||||
| }: { | ||||
|     notification: Notifd.Notification; | ||||
| }) { | ||||
|     const { START, CENTER, END } = Gtk.Align; | ||||
|     const actions = notification.actions || []; | ||||
|     const TIMEOUT_DELAY = 3000; | ||||
|  | ||||
|     // Keep track of notification validity | ||||
|     const notifd = Notifd.get_default(); | ||||
|     const timeoutManager = createTimeoutManager( | ||||
|         () => notification.dismiss(), | ||||
|         TIMEOUT_DELAY, | ||||
|     ); | ||||
|     return ( | ||||
|         <box | ||||
|             setup={(self) => { | ||||
|                 // Set up timeout | ||||
|                 timeoutManager.setupTimeout(); | ||||
|                 const clickGesture = Gtk.GestureClick.new(); | ||||
|                 clickGesture.set_button(0); // 0 means any button | ||||
|                 clickGesture.connect("pressed", (gesture, _) => { | ||||
|                     try { | ||||
|                         // Get which button was pressed (1=left, 2=middle, 3=right) | ||||
|                         const button = gesture.get_current_button(); | ||||
|  | ||||
|                         if (button === 1) { | ||||
|                             // PRIMARY/LEFT | ||||
|                             if (actions.length > 0) n.invoke(actions[0]); | ||||
|                         } else if (button === 2) { | ||||
|                             // MIDDLE | ||||
|                             notifd.notifications?.forEach((n) => { | ||||
|                                 n.dismiss(); | ||||
|                             }); | ||||
|                         } else if (button === 3) { | ||||
|                             // SECONDARY/RIGHT | ||||
|                             notification.dismiss(); | ||||
|                         } | ||||
|                     } catch (error) { | ||||
|                         console.error(error); | ||||
|                     } | ||||
|                 }); | ||||
|                 self.add_controller(clickGesture); | ||||
|  | ||||
|                 self.connect("unrealize", () => { | ||||
|                     timeoutManager.cleanup(); | ||||
|                 }); | ||||
|             }} | ||||
|             onHoverEnter={timeoutManager.handleHover} | ||||
|             onHoverLeave={timeoutManager.handleHoverLost} | ||||
|             vertical | ||||
|             vexpand={false} | ||||
|             cssClasses={["notification", `${urgency(notification)}`]} | ||||
|             name={notification.id.toString()} | ||||
|         > | ||||
|             <box cssClasses={["header"]}> | ||||
|                 <label | ||||
|                     cssClasses={["app-name"]} | ||||
|                     halign={CENTER} | ||||
|                     label={bind(notification, "app_name")} | ||||
|                 /> | ||||
|                 <label | ||||
|                     cssClasses={["time"]} | ||||
|                     hexpand | ||||
|                     halign={END} | ||||
|                     label={time(notification.time)} | ||||
|                 /> | ||||
|             </box> | ||||
|             <Gtk.Separator /> | ||||
|             <box cssClasses={["content"]}> | ||||
|                 <box | ||||
|                     cssClasses={["thumb"]} | ||||
|                     visible={Boolean(NotificationIcon(notification))} | ||||
|                     halign={CENTER} | ||||
|                     valign={CENTER} | ||||
|                     vexpand={true} | ||||
|                 > | ||||
|                     {NotificationIcon(notification)} | ||||
|                 </box> | ||||
|                 <box | ||||
|                     vertical | ||||
|                     cssClasses={["text-content"]} | ||||
|                     hexpand={true} | ||||
|                     halign={CENTER} | ||||
|                     valign={CENTER} | ||||
|                 > | ||||
|                     <label | ||||
|                         cssClasses={["title"]} | ||||
|                         valign={CENTER} | ||||
|                         wrap={false} | ||||
|                         label={bind(notification, "summary")} | ||||
|                     /> | ||||
|                     {notification.body && ( | ||||
|                         <label | ||||
|                             cssClasses={["body"]} | ||||
|                             valign={CENTER} | ||||
|                             wrap={true} | ||||
|                             maxWidthChars={50} | ||||
|                             label={bind(notification, "body")} | ||||
|                         /> | ||||
|                     )} | ||||
|                 </box> | ||||
|             </box> | ||||
|             {actions.length > 0 && ( | ||||
|                 <box cssClasses={["actions"]}> | ||||
|                     {actions.map(({ label, action }) => ( | ||||
|                         <button | ||||
|                             hexpand | ||||
|                             cssClasses={["action-button"]} | ||||
|                             onClicked={() => notification.invoke(action)} | ||||
|                         > | ||||
|                             <label label={label} halign={CENTER} hexpand /> | ||||
|                         </button> | ||||
|                     ))} | ||||
|                 </box> | ||||
|             )} | ||||
|         </box> | ||||
|     ); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user