feat(agsV2): add notif-center popup
All checks were successful
Discord / discord commits (push) Has been skipped
All checks were successful
Discord / discord commits (push) Has been skipped
This commit is contained in:
parent
50fc642f03
commit
4c7fd11ee3
6 changed files with 268 additions and 23 deletions
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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>
|
||||||
|
);
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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`}>
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue