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 AstalHyprland from 'gi://AstalHyprland?version=0.1';
|
||||
|
@ -150,9 +150,10 @@ export default ({
|
|||
</revealer>
|
||||
);
|
||||
|
||||
return (
|
||||
const win = (
|
||||
<window
|
||||
name={`bar-${monitor}`}
|
||||
namespace={`bar-${monitor}`}
|
||||
layer={Astal.Layer.OVERLAY}
|
||||
gdkmonitor={gdkmonitor}
|
||||
anchor={anchor}
|
||||
|
@ -178,5 +179,9 @@ export default ({
|
|||
</box>
|
||||
</eventbox>
|
||||
</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 is_popup: boolean;
|
||||
|
||||
private timer_object: AstalIO.Time | undefined;
|
||||
|
||||
public popup_timer: number;
|
||||
|
@ -108,7 +110,11 @@ export class NotifGestureWrapper extends Widget.EventBox {
|
|||
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)
|
||||
.css = side === 'Left' ? slideLeft : slideRight;
|
||||
|
||||
|
@ -120,16 +126,17 @@ export class NotifGestureWrapper extends Widget.EventBox {
|
|||
|
||||
setTimeout(() => {
|
||||
// Kill notif if specified
|
||||
if (force) {
|
||||
if (!this.is_popup) {
|
||||
Notifications.get_notification(this.id)?.dismiss();
|
||||
|
||||
// Update HasNotifs
|
||||
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();
|
||||
}
|
||||
|
||||
// Make sure we cleanup any references to this instance
|
||||
this.timer_object?.cancel();
|
||||
NotifGestureWrapper.popups.delete(this.id);
|
||||
|
||||
// Update HasNotifs
|
||||
HasNotifs.set(Notifications.get_notifications().length > 0);
|
||||
|
||||
// Get rid of disappeared widget
|
||||
this.destroy();
|
||||
|
@ -146,13 +153,12 @@ export class NotifGestureWrapper extends Widget.EventBox {
|
|||
}: NotifGestureWrapperProps) {
|
||||
super();
|
||||
|
||||
setup_notif(this);
|
||||
|
||||
this.id = id;
|
||||
this.slide_in_from = slide_in_from;
|
||||
this.dragging = false;
|
||||
|
||||
this.popup_timer = popup_timer;
|
||||
this.is_popup = this.popup_timer !== 0;
|
||||
this.timer_update(this.popup_timer);
|
||||
|
||||
// OnClick
|
||||
|
@ -312,6 +318,8 @@ export class NotifGestureWrapper extends Widget.EventBox {
|
|||
/>
|
||||
</revealer>,
|
||||
);
|
||||
|
||||
setup_notif(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import { Astal } from 'astal/gtk3';
|
|||
import PopupWindow from '../misc/popup-window';
|
||||
|
||||
import Popups from './popups';
|
||||
import Center from './center';
|
||||
|
||||
|
||||
export const NotifPopups = () => (
|
||||
|
@ -21,6 +22,6 @@ export const NotifCenter = () => (
|
|||
name="notif-center"
|
||||
anchor={Astal.WindowAnchor.TOP | Astal.WindowAnchor.RIGHT}
|
||||
>
|
||||
<box css="min-height: 100px; min-width: 100px; background: black;" />
|
||||
<Center />
|
||||
</PopupWindow>
|
||||
);
|
||||
|
|
|
@ -17,11 +17,9 @@ import SmoothProgress from '../misc/smooth-progress';
|
|||
// to know when there are notifs or not
|
||||
export const HasNotifs = Variable(false);
|
||||
|
||||
const setTime = (time: number): string => {
|
||||
return GLib.DateTime
|
||||
.new_from_unix_local(time)
|
||||
.format('%H:%M') ?? '';
|
||||
};
|
||||
const setTime = (time: number): string => GLib.DateTime
|
||||
.new_from_unix_local(time)
|
||||
.format('%H:%M') ?? '';
|
||||
|
||||
const NotifIcon = ({ notifObj }: {
|
||||
notifObj: AstalNotifd.Notification
|
||||
|
@ -93,6 +91,7 @@ const BlockedApps = [
|
|||
export const Notification = ({
|
||||
id = 0,
|
||||
popup_timer = 0,
|
||||
slide_in_from = 'Left' as 'Left' | 'Right',
|
||||
}): ReturnType<typeof NotifGestureWrapper> | undefined => {
|
||||
const notifObj = Notifications.get_notification(id);
|
||||
|
||||
|
@ -114,10 +113,16 @@ export const Notification = ({
|
|||
<NotifGestureWrapper
|
||||
id={id}
|
||||
popup_timer={popup_timer}
|
||||
slide_in_from={slide_in_from}
|
||||
setup_notif={(self) => {
|
||||
self.connect('timer-update', () => {
|
||||
progress.fraction = self.popup_timer / 5;
|
||||
});
|
||||
if (self.is_popup) {
|
||||
self.connect('timer-update', () => {
|
||||
progress.fraction = self.popup_timer / 5;
|
||||
});
|
||||
}
|
||||
else {
|
||||
progress.destroy();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<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