feat(agsV2): add notif-center popup
All checks were successful
Discord / discord commits (push) Has been skipped

This commit is contained in:
matt1432 2024-10-16 23:56:05 -04:00
parent 50fc642f03
commit 4c7fd11ee3
6 changed files with 268 additions and 23 deletions

View file

@ -1,4 +1,4 @@
import { Astal, Gdk, Gtk, Widget } from 'astal/gtk3'; import { App, Astal, Gdk, Gtk, Widget } from 'astal/gtk3';
import { bind, Variable } from 'astal'; import { bind, Variable } from 'astal';
import AstalHyprland from 'gi://AstalHyprland?version=0.1'; import AstalHyprland from 'gi://AstalHyprland?version=0.1';
@ -150,9 +150,10 @@ export default ({
</revealer> </revealer>
); );
return ( const win = (
<window <window
name={`bar-${monitor}`} name={`bar-${monitor}`}
namespace={`bar-${monitor}`}
layer={Astal.Layer.OVERLAY} layer={Astal.Layer.OVERLAY}
gdkmonitor={gdkmonitor} gdkmonitor={gdkmonitor}
anchor={anchor} anchor={anchor}
@ -178,5 +179,9 @@ export default ({
</box> </box>
</eventbox> </eventbox>
</window> </window>
); ) as Widget.Window;
App.add_window(win);
return win;
}; };

View file

@ -0,0 +1,141 @@
import { bind, timeout } from 'astal';
import { App, Gtk, Widget } from 'astal/gtk3';
import AstalNotifd from 'gi://AstalNotifd?version=0.1';
const Notifications = AstalNotifd.get_default();
import { Notification, HasNotifs } from './notification';
import { NotifGestureWrapper } from './gesture';
const addNotif = (box: Widget.Box, notifObj: AstalNotifd.Notification) => {
if (notifObj) {
const NewNotif = Notification({
id: notifObj.id,
slide_in_from: 'Right',
});
if (NewNotif) {
box.pack_end(NewNotif, false, false, 0);
box.show_all();
}
}
};
const NotificationList = () => (
<box
vertical
vexpand
valign={Gtk.Align.START}
visible={bind(HasNotifs)}
// It needs to be bigger than the notifs to not jiggle
css="min-width: 550px;"
setup={(self) => {
Notifications.get_notifications().forEach((n) => {
addNotif(self, n);
});
self
.hook(Notifications, 'notified', (_, id) => {
if (id) {
const notifObj = Notifications.get_notification(id);
if (notifObj) {
addNotif(self, notifObj);
}
}
})
.hook(Notifications, 'resolved', (_, id) => {
const notif = (self.get_children() as NotifGestureWrapper[])
.find((ch) => ch.id === id);
if (notif?.sensitive) {
notif.slideAway('Right');
}
});
}}
/>
);
const ClearButton = () => (
<button
className="clear"
sensitive={bind(HasNotifs)}
onButtonReleaseEvent={() => {
Notifications.get_notifications().forEach((notif) => {
notif.dismiss();
});
timeout(1000, () => {
App.get_window('win-notif-center')?.set_visible(false);
});
}}
>
<box>
<label label="Clear " />
<icon icon={bind(Notifications, 'notifications')
.as((notifs) => notifs.length > 0 ?
'user-trash-full-symbolic' :
'user-trash-symbolic')}
/>
</box>
</button>
);
const Header = () => (
<box className="header">
<label
label="Notifications"
hexpand
xalign={0}
/>
<ClearButton />
</box>
);
const Placeholder = () => (
<revealer
transitionType={Gtk.RevealerTransitionType.CROSSFADE}
revealChild={bind(HasNotifs).as((v) => !v)}
>
<box
className="placeholder"
vertical
valign={Gtk.Align.CENTER}
halign={Gtk.Align.CENTER}
vexpand
hexpand
>
<icon icon="notification-disabled-symbolic" />
<label label="Your inbox is empty" />
</box>
</revealer>
);
export default () => (
<box
className="notification-center widget"
vertical
>
<Header />
<box className="notification-wallpaper-box">
<scrollable
className="notification-list-box"
hscroll={Gtk.PolicyType.NEVER}
vscroll={Gtk.PolicyType.AUTOMATIC}
>
<box
className="notification-list"
vertical
>
<NotificationList />
<Placeholder />
</box>
</scrollable>
</box>
</box>
);

View file

@ -53,6 +53,8 @@ export class NotifGestureWrapper extends Widget.EventBox {
readonly slide_in_from: 'Left' | 'Right'; readonly slide_in_from: 'Left' | 'Right';
readonly is_popup: boolean;
private timer_object: AstalIO.Time | undefined; private timer_object: AstalIO.Time | undefined;
public popup_timer: number; public popup_timer: number;
@ -108,7 +110,11 @@ export class NotifGestureWrapper extends Widget.EventBox {
return false; return false;
} }
public slideAway(side: 'Left' | 'Right', force = false) { public slideAway(side: 'Left' | 'Right') {
if (!this.sensitive) {
return;
}
((this.get_child() as Widget.Revealer).get_child() as Widget.Box) ((this.get_child() as Widget.Revealer).get_child() as Widget.Box)
.css = side === 'Left' ? slideLeft : slideRight; .css = side === 'Left' ? slideLeft : slideRight;
@ -120,16 +126,17 @@ export class NotifGestureWrapper extends Widget.EventBox {
setTimeout(() => { setTimeout(() => {
// Kill notif if specified // Kill notif if specified
if (force) { if (!this.is_popup) {
Notifications.get_notification(this.id)?.dismiss(); Notifications.get_notification(this.id)?.dismiss();
}
// Make sure we cleanup any references to this instance
this.timer_object?.cancel();
NotifGestureWrapper.popups.delete(this.id);
// Update HasNotifs // Update HasNotifs
HasNotifs.set(Notifications.get_notifications().length > 0); HasNotifs.set(Notifications.get_notifications().length > 0);
}
else {
// Make sure we cleanup any references to this instance
NotifGestureWrapper.popups.delete(this.id);
this.timer_object?.cancel();
}
// Get rid of disappeared widget // Get rid of disappeared widget
this.destroy(); this.destroy();
@ -146,13 +153,12 @@ export class NotifGestureWrapper extends Widget.EventBox {
}: NotifGestureWrapperProps) { }: NotifGestureWrapperProps) {
super(); super();
setup_notif(this);
this.id = id; this.id = id;
this.slide_in_from = slide_in_from; this.slide_in_from = slide_in_from;
this.dragging = false; this.dragging = false;
this.popup_timer = popup_timer; this.popup_timer = popup_timer;
this.is_popup = this.popup_timer !== 0;
this.timer_update(this.popup_timer); this.timer_update(this.popup_timer);
// OnClick // OnClick
@ -312,6 +318,8 @@ export class NotifGestureWrapper extends Widget.EventBox {
/> />
</revealer>, </revealer>,
); );
setup_notif(this);
} }
} }

View file

@ -3,6 +3,7 @@ import { Astal } from 'astal/gtk3';
import PopupWindow from '../misc/popup-window'; import PopupWindow from '../misc/popup-window';
import Popups from './popups'; import Popups from './popups';
import Center from './center';
export const NotifPopups = () => ( export const NotifPopups = () => (
@ -21,6 +22,6 @@ export const NotifCenter = () => (
name="notif-center" name="notif-center"
anchor={Astal.WindowAnchor.TOP | Astal.WindowAnchor.RIGHT} anchor={Astal.WindowAnchor.TOP | Astal.WindowAnchor.RIGHT}
> >
<box css="min-height: 100px; min-width: 100px; background: black;" /> <Center />
</PopupWindow> </PopupWindow>
); );

View file

@ -17,11 +17,9 @@ import SmoothProgress from '../misc/smooth-progress';
// to know when there are notifs or not // to know when there are notifs or not
export const HasNotifs = Variable(false); export const HasNotifs = Variable(false);
const setTime = (time: number): string => { const setTime = (time: number): string => GLib.DateTime
return GLib.DateTime
.new_from_unix_local(time) .new_from_unix_local(time)
.format('%H:%M') ?? ''; .format('%H:%M') ?? '';
};
const NotifIcon = ({ notifObj }: { const NotifIcon = ({ notifObj }: {
notifObj: AstalNotifd.Notification notifObj: AstalNotifd.Notification
@ -93,6 +91,7 @@ const BlockedApps = [
export const Notification = ({ export const Notification = ({
id = 0, id = 0,
popup_timer = 0, popup_timer = 0,
slide_in_from = 'Left' as 'Left' | 'Right',
}): ReturnType<typeof NotifGestureWrapper> | undefined => { }): ReturnType<typeof NotifGestureWrapper> | undefined => {
const notifObj = Notifications.get_notification(id); const notifObj = Notifications.get_notification(id);
@ -114,10 +113,16 @@ export const Notification = ({
<NotifGestureWrapper <NotifGestureWrapper
id={id} id={id}
popup_timer={popup_timer} popup_timer={popup_timer}
slide_in_from={slide_in_from}
setup_notif={(self) => { setup_notif={(self) => {
if (self.is_popup) {
self.connect('timer-update', () => { self.connect('timer-update', () => {
progress.fraction = self.popup_timer / 5; progress.fraction = self.popup_timer / 5;
}); });
}
else {
progress.destroy();
}
}} }}
> >
<box vertical className={`notification ${notifObj.urgency} widget`}> <box vertical className={`notification ${notifObj.urgency} widget`}>

View file

@ -45,3 +45,88 @@
} }
} }
} }
.notification-center {
margin-top: 0;
min-height: 700px;
min-width: 580px;
* {
font-size: 16px;
}
.header {
padding: 10px;
margin-bottom: 9px;
label {
font-size: 22px;
}
}
.notification-list-box {
padding: 0 12px;
border-radius: 30px;
border-top: 2px solid $accent-color;
viewport {
all: unset;
}
scrollbar {
all: unset;
border-radius: 8px;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
* {
all: unset;
}
&:hover {
border-radius: 8px;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
}
scrollbar.vertical {
transition: 200ms;
background-color: rgba(23, 23, 23, 0.3);
&:hover {
background-color: rgba(23, 23, 23, 0.7);
slider {
background-color: rgba(238, 238, 238, 0.7);
min-width: .6em;
}
}
slider {
background-color: rgba(238, 238, 238, 0.5);
border-radius: 9px;
min-width: .4em;
min-height: 2em;
transition: 200ms;
}
}
}
.placeholder {
color: white;
icon {
font-size: 7em;
text-shadow: 2px 2px 2px rgba(0, 0, 0, 0.6);
-gtk-icon-shadow: 2px 2px 2px rgba(0, 0, 0, 0.6);
}
label {
font-size: 1.2em;
text-shadow: 2px 2px 2px rgba(0, 0, 0, 0.6);
-gtk-icon-shadow: 2px 2px 2px rgba(0, 0, 0, 0.6);
}
}
}