refactor(ags): typecheck notif logic

This commit is contained in:
matt1432 2023-12-20 17:14:07 -05:00
parent f7ced94c21
commit 42a168762f
10 changed files with 236 additions and 149 deletions

View file

@ -9,7 +9,10 @@ import { Box, Entry, Icon, Label, ListBox, Revealer, Scrollable } from 'resource
import PopupWindow from '../misc/popup.js'; import PopupWindow from '../misc/popup.js';
import AppItem from './app-item.js'; import AppItem from './app-item.js';
/** @typedef {import('types/service/applications.js').Application} App */ /**
* @typedef {import('types/service/applications.js').Application} App
* @typedef {typeof imports.gi.Gtk.ListBoxRow} ListBoxRow
*/
const Applauncher = ({ window_name = 'applauncher' } = {}) => { const Applauncher = ({ window_name = 'applauncher' } = {}) => {
@ -32,17 +35,23 @@ const Applauncher = ({ window_name = 'applauncher' } = {}) => {
fzfResults = fzf.find(text); fzfResults = fzf.find(text);
// @ts-expect-error // @ts-expect-error
list.set_sort_func((a, b) => { list.set_sort_func(
const row1 = a.get_children()[0]?.attribute.app.name; /**
const row2 = b.get_children()[0]?.attribute.app.name; * @param {ListBoxRow} a
* @param {ListBoxRow} b
*/
(a, b) => {
const row1 = a.get_children()[0]?.attribute.app.name;
const row2 = b.get_children()[0]?.attribute.app.name;
if (!row1 || !row2) { if (!row1 || !row2) {
return 0; return 0;
} }
return fzfResults.indexOf(row1) - return fzfResults.indexOf(row1) -
fzfResults.indexOf(row1) || 0; fzfResults.indexOf(row1) || 0;
}); },
);
}; };
const makeNewChildren = () => { const makeNewChildren = () => {
@ -96,7 +105,7 @@ const Applauncher = ({ window_name = 'applauncher' } = {}) => {
setSort(text); setSort(text);
let visibleApps = 0; let visibleApps = 0;
/** @type Array<typeof imports.gi.Gtk.ListBoxRow> */ /** @type Array<ListBoxRow> */
// @ts-expect-error // @ts-expect-error
const rows = list.get_children(); const rows = list.get_children();

View file

@ -135,6 +135,7 @@ export default () => {
const updateWorkspaces = () => { const updateWorkspaces = () => {
Hyprland.workspaces.forEach((ws) => { Hyprland.workspaces.forEach((ws) => {
const currentWs = self.children.find((ch) => { const currentWs = self.children.find((ch) => {
// @ts-expect-error
return ch.attribute.id === ws.id; return ch.attribute.id === ws.id;
}); });

View file

@ -9,12 +9,13 @@ const ANIM_DURATION = 500;
const TRANSITION = `transition: margin ${ANIM_DURATION}ms ease, const TRANSITION = `transition: margin ${ANIM_DURATION}ms ease,
opacity ${ANIM_DURATION}ms ease;`; opacity ${ANIM_DURATION}ms ease;`;
/** /**
* @typedef {import('types/widgets/overlay').OverlayProps} OverlayProps * @typedef {import('types/widgets/overlay').OverlayProps} OverlayProps
* @typedef {import('types/widgets/overlay').default} Overlay * @typedef {import('types/widgets/overlay').default} Overlay
* */
* @param {OverlayProps & {
/** @param {OverlayProps & {
* setup?: function(Overlay):void * setup?: function(Overlay):void
* }} o * }} o
*/ */

View file

@ -4,12 +4,13 @@ import { EventBox } from 'resource:///com/github/Aylur/ags/widget.js';
const { Gtk } = imports.gi; const { Gtk } = imports.gi;
/** /**
* @typedef {import('types/widgets/eventbox').EventBoxProps} EventBoxProps * @typedef {import('types/widgets/eventbox').EventBoxProps} EventBoxProps
* @typedef {import('types/widgets/eventbox').default} EventBox * @typedef {import('types/widgets/eventbox').default} EventBox
* */
* @param {EventBoxProps & {
/** @param {EventBoxProps & {
* on_primary_click_release?: function(EventBox):void * on_primary_click_release?: function(EventBox):void
* }} o * }} o
*/ */
@ -23,7 +24,7 @@ export default ({
const CanRun = Variable(true); const CanRun = Variable(true);
const Disabled = Variable(false); const Disabled = Variable(false);
let widget; // eslint-disable-line let widget = EventBox();
const wrapper = EventBox({ const wrapper = EventBox({
cursor: 'pointer', cursor: 'pointer',
@ -35,7 +36,7 @@ export default ({
get_child: () => widget.child, get_child: () => widget.child,
/** @param {import('types/widget').Widget} new_child */ /** @param {import('types/widgets/box').default} new_child */
set_child: (new_child) => { set_child: (new_child) => {
widget.child = new_child; widget.child = new_child;
}, },

View file

@ -23,8 +23,8 @@ import { timeout } from 'resource:///com/github/Aylur/ags/utils.js';
export default ({ export default ({
transition = 'slide_down', transition = 'slide_down',
transition_duration = 500, transition_duration = 500,
onOpen = () => { /**/ }, onOpen = () => {/**/},
onClose = () => { /**/ }, onClose = () => {/**/},
// Window props // Window props
name, name,
@ -42,8 +42,12 @@ export default ({
...props, ...props,
attribute: { attribute: {
/**
* @param {typeof imports.gi.Gtk.Allocation} alloc
* @param {'left'|'right'} side
*/
set_x_pos: ( set_x_pos: (
alloc = {}, alloc,
side = 'right', side = 'right',
) => { ) => {
const width = window.get_display() const width = window.get_display()

View file

@ -8,28 +8,35 @@ import { lookUpIcon } from 'resource:///com/github/Aylur/ags/utils.js';
const { GLib } = imports.gi; const { GLib } = imports.gi;
/** @typedef {import('types/service/notifications').Notification} Notification */
/** @param {number} time */
const setTime = (time) => { const setTime = (time) => {
return GLib.DateTime return GLib.DateTime
.new_from_unix_local(time) .new_from_unix_local(time)
.format('%H:%M'); .format('%H:%M');
}; };
const getDragState = (box) => box.get_parent().get_parent() /** @param {import('types/widgets/eventbox').default} box */
.get_parent().get_parent().get_parent()._dragging; const getDragState = (box) => box.get_parent()?.get_parent()
// @ts-expect-error
?.get_parent()?.get_parent()?.get_parent()?.attribute.dragging;
import Gesture from './gesture.js'; import Gesture from './gesture.js';
import CursorBox from '../misc/cursorbox.js'; import CursorBox from '../misc/cursorbox.js';
/** @param {Notification} notif */
const NotificationIcon = (notif) => { const NotificationIcon = (notif) => {
let iconCmd = () => { /**/ }; /** @type function(import('types/widgets/eventbox').default):void */
let iconCmd = () => {/**/};
if (Applications.query(notif.appEntry).length > 0) { if (notif._appEntry && Applications.query(notif._appEntry).length > 0) {
const app = Applications.query(notif.appEntry)[0]; const app = Applications.query(notif._appEntry)[0];
let wmClass = app.app.get_string('StartupWMClass'); let wmClass = app.app.get_string('StartupWMClass');
if (app.app.get_filename().includes('discord')) { if (app.app?.get_filename()?.includes('discord')) {
wmClass = 'discord'; wmClass = 'discord';
} }
@ -45,17 +52,19 @@ const NotificationIcon = (notif) => {
'togglespecialworkspace spot'); 'togglespecialworkspace spot');
} }
else { else {
Hyprland.sendMessage('j/clients').then((out) => { Hyprland.sendMessage('j/clients').then((msg) => {
out = JSON.parse(out); /** @type {Array<import('types/service/hyprland').Client>} */
const clients = JSON.parse(msg);
const classes = []; const classes = [];
for (const key of out) { for (const key of clients) {
if (key.class) { if (key.class) {
classes.push(key.class); classes.push(key.class);
} }
} }
if (classes.includes(wmClass)) { if (wmClass && classes.includes(wmClass)) {
Hyprland.sendMessage('dispatch ' + Hyprland.sendMessage('dispatch ' +
`focuswindow ^(${wmClass})`); `focuswindow ^(${wmClass})`);
} }
@ -81,7 +90,7 @@ const NotificationIcon = (notif) => {
child: Box({ child: Box({
vpack: 'start', vpack: 'start',
hexpand: false, hexpand: false,
className: 'icon img', class_name: 'icon img',
css: ` css: `
background-image: url("${notif.image}"); background-image: url("${notif.image}");
background-size: contain; background-size: contain;
@ -96,13 +105,13 @@ const NotificationIcon = (notif) => {
let icon = 'dialog-information-symbolic'; let icon = 'dialog-information-symbolic';
if (lookUpIcon(notif.appIcon)) { if (lookUpIcon(notif._appIcon)) {
icon = notif.appIcon; icon = notif._appIcon;
} }
if (lookUpIcon(notif.appEntry)) { if (notif._appEntry && lookUpIcon(notif._appEntry)) {
icon = notif.appEntry; icon = notif._appEntry;
} }
@ -112,7 +121,7 @@ const NotificationIcon = (notif) => {
child: Box({ child: Box({
vpack: 'start', vpack: 'start',
hexpand: false, hexpand: false,
className: 'icon', class_name: 'icon',
css: ` css: `
min-width: 78px; min-width: 78px;
min-height: 78px; min-height: 78px;
@ -132,11 +141,18 @@ const NotificationIcon = (notif) => {
// 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);
/**
* @param {{
* notif: Notification
* slideIn?: 'Left'|'Right'
* command?: () => void
* }} o
*/
export const Notification = ({ export const Notification = ({
notif, notif,
slideIn = 'Left', slideIn = 'Left',
command = () => { /**/ }, command = () => { /**/ },
} = {}) => { }) => {
if (!notif) { if (!notif) {
return; return;
} }
@ -145,7 +161,7 @@ export const Notification = ({
'Spotify', 'Spotify',
]; ];
if (BlockedApps.find((app) => app === notif.appName)) { if (BlockedApps.find((app) => app === notif._appName)) {
notif.close(); notif.close();
return; return;
@ -161,8 +177,9 @@ export const Notification = ({
}); });
// Add body to notif // Add body to notif
// @ts-expect-error
notifWidget.child.add(Box({ notifWidget.child.add(Box({
className: `notification ${notif.urgency}`, class_name: `notification ${notif.urgency}`,
vexpand: false, vexpand: false,
// Notification // Notification
@ -174,6 +191,7 @@ export const Notification = ({
Box({ Box({
children: [ children: [
NotificationIcon(notif), NotificationIcon(notif),
Box({ Box({
hexpand: true, hexpand: true,
vertical: true, vertical: true,
@ -185,21 +203,21 @@ export const Notification = ({
// Title // Title
Label({ Label({
className: 'title', class_name: 'title',
xalign: 0, xalign: 0,
justification: 'left', justification: 'left',
hexpand: true, hexpand: true,
maxWidthChars: 24, max_width_chars: 24,
truncate: 'end', truncate: 'end',
wrap: true, wrap: true,
label: notif.summary, label: notif.summary,
useMarkup: notif.summary use_markup: notif.summary
.startsWith('<'), .startsWith('<'),
}), }),
// Time // Time
Label({ Label({
className: 'time', class_name: 'time',
vpack: 'start', vpack: 'start',
label: setTime(notif.time), label: setTime(notif.time),
}), }),
@ -207,9 +225,12 @@ export const Notification = ({
// Close button // Close button
CursorBox({ CursorBox({
child: Button({ child: Button({
className: 'close-button', class_name: 'close-button',
vpack: 'start', vpack: 'start',
onClicked: () => notif.close(),
on_primary_click_release: () =>
notif.close(),
child: Icon('window-close' + child: Icon('window-close' +
'-symbolic'), '-symbolic'),
}), }),
@ -219,9 +240,9 @@ export const Notification = ({
// Description // Description
Label({ Label({
className: 'description', class_name: 'description',
hexpand: true, hexpand: true,
useMarkup: true, use_markup: true,
xalign: 0, xalign: 0,
justification: 'left', justification: 'left',
label: notif.body, label: notif.body,
@ -234,11 +255,13 @@ export const Notification = ({
// Actions // Actions
Box({ Box({
className: 'actions', class_name: 'actions',
children: notif.actions.map((action) => Button({ children: notif.actions.map((action) => Button({
className: 'action-button', class_name: 'action-button',
onClicked: () => notif.invoke(action.id),
hexpand: true, hexpand: true,
on_primary_click_release: () => notif.invoke(action.id),
child: Label(action.label), child: Label(action.label),
})), })),
}), }),

View file

@ -7,7 +7,16 @@ import { timeout } from 'resource:///com/github/Aylur/ags/utils.js';
import { Notification, HasNotifs } from './base.js'; import { Notification, HasNotifs } from './base.js';
import CursorBox from '../misc/cursorbox.js'; import CursorBox from '../misc/cursorbox.js';
/**
* @typedef {import('types/service/notifications').Notification} NotifObj
* @typedef {import('types/widgets/box').default} Box
*/
/**
* @param {Box} box
* @param {NotifObj} notif
*/
const addNotif = (box, notif) => { const addNotif = (box, notif) => {
if (notif) { if (notif) {
const NewNotif = Notification({ const NewNotif = Notification({
@ -27,7 +36,7 @@ const NotificationList = () => Box({
vertical: true, vertical: true,
vexpand: true, vexpand: true,
vpack: 'start', vpack: 'start',
binds: [['visible', HasNotifs]], visible: HasNotifs.bind(),
setup: (self) => { setup: (self) => {
self self
@ -40,31 +49,42 @@ const NotificationList = () => Box({
} }
else if (id) { else if (id) {
addNotif(box, Notifications.getNotification(id)); const notifObj = Notifications.getNotification(id);
if (notifObj) {
addNotif(box, notifObj);
}
} }
}, 'notified') }, 'notified')
.hook(Notifications, (box, id) => { .hook(Notifications, (box, id) => {
const notif = box.children.find((ch) => ch._id === id); // @ts-expect-error
const notif = box.children.find((ch) => ch.attribute.id === id);
if (notif?.sensitive) { if (notif?.sensitive) {
notif.slideAway('Right'); // @ts-expect-error
notif.attribute.slideAway('Right');
} }
}, 'closed'); }, 'closed');
}, },
}); });
// TODO: use cursorbox feature for this
// Needs to be wrapped to still have onHover when disabled // Needs to be wrapped to still have onHover when disabled
const ClearButton = () => CursorBox({ const ClearButton = () => CursorBox({
child: Button({ child: Button({
onPrimaryClickRelease: () => { sensitive: HasNotifs.bind(),
on_primary_click_release: () => {
Notifications.clear(); Notifications.clear();
timeout(1000, () => App.closeWindow('notification-center')); timeout(1000, () => App.closeWindow('notification-center'));
}, },
binds: [['sensitive', HasNotifs]],
child: Box({ child: Box({
children: [ children: [
Label('Clear '), Label('Clear '),
Icon({ Icon({
setup: (self) => { setup: (self) => {
self.hook(Notifications, () => { self.hook(Notifications, () => {
@ -80,7 +100,7 @@ const ClearButton = () => CursorBox({
}); });
const Header = () => Box({ const Header = () => Box({
className: 'header', class_name: 'header',
children: [ children: [
Label({ Label({
label: 'Notifications', label: 'Notifications',
@ -93,14 +113,17 @@ const Header = () => Box({
const Placeholder = () => Revealer({ const Placeholder = () => Revealer({
transition: 'crossfade', transition: 'crossfade',
binds: [['revealChild', HasNotifs, 'value', (value) => !value]], reveal_child: HasNotifs.bind()
.transform((v) => !v),
child: Box({ child: Box({
className: 'placeholder', class_name: 'placeholder',
vertical: true, vertical: true,
vpack: 'center', vpack: 'center',
hpack: 'center', hpack: 'center',
vexpand: true, vexpand: true,
hexpand: true, hexpand: true,
children: [ children: [
Icon('notification-disabled-symbolic'), Icon('notification-disabled-symbolic'),
Label('Your inbox is empty'), Label('Your inbox is empty'),
@ -109,22 +132,27 @@ const Placeholder = () => Revealer({
}); });
export default () => Box({ export default () => Box({
className: 'notification-center', class_name: 'notification-center',
vertical: true, vertical: true,
children: [ children: [
Header(), Header(),
Box({ Box({
className: 'notification-wallpaper-box', class_name: 'notification-wallpaper-box',
children: [ children: [
Scrollable({ Scrollable({
className: 'notification-list-box', class_name: 'notification-list-box',
hscroll: 'never', hscroll: 'never',
vscroll: 'automatic', vscroll: 'automatic',
child: Box({ child: Box({
className: 'notification-list', class_name: 'notification-list',
vertical: true, vertical: true,
children: [ children: [
NotificationList(), NotificationList(),
Placeholder(), Placeholder(),
], ],
}), }),

View file

@ -5,90 +5,97 @@ import { timeout } from 'resource:///com/github/Aylur/ags/utils.js';
import { HasNotifs } from './base.js'; import { HasNotifs } from './base.js';
import Gtk from 'gi://Gtk'; const { Gtk } = imports.gi;
const MAX_OFFSET = 200; const MAX_OFFSET = 200;
const OFFSCREEN = 300; const OFFSCREEN = 300;
const ANIM_DURATION = 500; const ANIM_DURATION = 500;
const SLIDE_MIN_THRESHOLD = 10; const SLIDE_MIN_THRESHOLD = 10;
const TRANSITION = 'transition: margin 0.5s ease, opacity 0.5s ease;';
const SQUEEZED = 'margin-bottom: -70px; margin-top: -70px;';
const MAX_LEFT = `
margin-left: -${Number(MAX_OFFSET + OFFSCREEN)}px;
margin-right: ${Number(MAX_OFFSET + OFFSCREEN)}px;
`;
const MAX_RIGHT = `
margin-left: ${Number(MAX_OFFSET + OFFSCREEN)}px;
margin-right: -${Number(MAX_OFFSET + OFFSCREEN)}px;
`;
const slideLeft = `${TRANSITION} ${MAX_LEFT}
margin-top: 0px;
margin-bottom: 0px;
opacity: 0;`;
const squeezeLeft = `${TRANSITION} ${MAX_LEFT} ${SQUEEZED} opacity: 0;`;
const slideRight = `${TRANSITION} ${MAX_RIGHT}
margin-top: 0px;
margin-bottom: 0px;
opacity: 0;`;
const squeezeRight = `${TRANSITION} ${MAX_RIGHT} ${SQUEEZED} opacity: 0;`;
const defaultStyle = `${TRANSITION} margin: unset; opacity: 1;`;
export default ({ export default ({
id, id,
slideIn = 'Left', slideIn = 'Left',
command = () => { /**/ }, command = () => {/**/},
...props ...props
}) => { }) => {
const widget = EventBox({ const widget = EventBox({
...props, ...props,
cursor: 'grab', cursor: 'grab',
onHover: (self) => { on_hover: (self) => {
if (!self._hovered) { if (!self.attribute.hovered) {
self._hovered = true; self.attribute.hovered = true;
} }
}, },
onHoverLost: (self) => { on_hover_lost: (self) => {
if (self._hovered) { if (self.attribute.hovered) {
self._hovered = false; self.attribute.hovered = false;
} }
}, },
attribute: {
dragging: false,
hovered: false,
ready: false,
id,
/** @param {'Left'|'Right'} side */
slideAway: (side) => {
// Slide away
// @ts-expect-error
widget.child.setCss(side === 'Left' ? slideLeft : slideRight);
// Make it uninteractable
widget.sensitive = false;
timeout(ANIM_DURATION - 100, () => {
// Reduce height after sliding away
// @ts-expect-error
widget.child?.setCss(side === 'Left' ?
squeezeLeft :
squeezeRight);
timeout(ANIM_DURATION, () => {
// Kill notif and update HasNotifs after anim is done
command();
HasNotifs.value = Notifications
.notifications.length > 0;
// @ts-expect-error
widget.get_parent()?.remove;
});
});
},
},
}); });
// Properties
widget._dragging = false;
widget._hovered = false;
widget._id = id;
widget.ready = false;
const gesture = Gtk.GestureDrag.new(widget); const gesture = Gtk.GestureDrag.new(widget);
const TRANSITION = 'transition: margin 0.5s ease, opacity 0.5s ease;';
const SQUEEZED = 'margin-bottom: -70px; margin-top: -70px;';
const MAX_LEFT = `
margin-left: -${Number(MAX_OFFSET + OFFSCREEN)}px;
margin-right: ${Number(MAX_OFFSET + OFFSCREEN)}px;
`;
const MAX_RIGHT = `
margin-left: ${Number(MAX_OFFSET + OFFSCREEN)}px;
margin-right: -${Number(MAX_OFFSET + OFFSCREEN)}px;
`;
const slideLeft = `${TRANSITION} ${MAX_LEFT}
margin-top: 0px;
margin-bottom: 0px;
opacity: 0;`;
const squeezeLeft = `${TRANSITION} ${MAX_LEFT} ${SQUEEZED} opacity: 0;`;
const slideRight = `${TRANSITION} ${MAX_RIGHT}
margin-top: 0px;
margin-bottom: 0px;
opacity: 0;`;
const squeezeRight = `${TRANSITION} ${MAX_RIGHT} ${SQUEEZED} opacity: 0;`;
const defaultStyle = `${TRANSITION} margin: unset; opacity: 1;`;
// Notif methods
widget.slideAway = (side) => {
// Slide away
widget.child.setCss(side === 'Left' ? slideLeft : slideRight);
// Makie it uninteractable
widget.sensitive = false;
timeout(ANIM_DURATION - 100, () => {
// Reduce height after sliding away
widget.child?.setCss(side === 'Left' ? squeezeLeft : squeezeRight);
timeout(ANIM_DURATION, () => {
// Kill notif and update HasNotifs after anim is done
command();
HasNotifs.value = Notifications.notifications.length > 0;
widget.get_parent()?.remove(widget);
});
});
};
widget.add(Box({ widget.add(Box({
css: squeezeLeft, css: squeezeLeft,
setup: (self) => { setup: (self) => {
@ -123,14 +130,16 @@ export default ({
} }
// Put a threshold on if a click is actually dragging // Put a threshold on if a click is actually dragging
widget._dragging = Math.abs(offset) > SLIDE_MIN_THRESHOLD; widget.attribute.dragging =
Math.abs(offset) > SLIDE_MIN_THRESHOLD;
widget.cursor = 'grabbing'; widget.cursor = 'grabbing';
}, 'drag-update') }, 'drag-update')
// On drag end // On drag end
.hook(gesture, () => { .hook(gesture, () => {
// Make it slide in on init // Make it slide in on init
if (!widget.ready) { if (!widget.attribute.ready) {
// Reverse of slideAway, so it started at squeeze, then we go to slide // Reverse of slideAway, so it started at squeeze, then we go to slide
self.setCss(slideIn === 'Left' ? self.setCss(slideIn === 'Left' ?
slideLeft : slideLeft :
@ -140,7 +149,7 @@ export default ({
// Then we go to center // Then we go to center
self.setCss(defaultStyle); self.setCss(defaultStyle);
timeout(ANIM_DURATION, () => { timeout(ANIM_DURATION, () => {
widget.ready = true; widget.attribute.ready = true;
}); });
}); });
@ -152,16 +161,16 @@ export default ({
// If crosses threshold after letting go, slide away // If crosses threshold after letting go, slide away
if (Math.abs(offset) > MAX_OFFSET) { if (Math.abs(offset) > MAX_OFFSET) {
if (offset > 0) { if (offset > 0) {
widget.slideAway('Right'); widget.attribute.slideAway('Right');
} }
else { else {
widget.slideAway('Left'); widget.attribute.slideAway('Left');
} }
} }
else { else {
self.setCss(defaultStyle); self.setCss(defaultStyle);
widget.cursor = 'grab'; widget.cursor = 'grab';
widget._dragging = false; widget.attribute.dragging = false;
} }
}, 'drag-end'); }, 'drag-end');
}, },

View file

@ -16,7 +16,6 @@ export const NotifPopups = () => PopupWindow({
const TOP_MARGIN = 6; const TOP_MARGIN = 6;
// FIXME: opens at wrong place
export const NotifCenter = () => PopupWindow({ export const NotifCenter = () => PopupWindow({
name: 'notification-center', name: 'notification-center',
anchor: ['top', 'right'], anchor: ['top', 'right'],

View file

@ -14,6 +14,7 @@ export default () => Box({
vertical: true, vertical: true,
setup: (self) => { setup: (self) => {
/** @param {number} id */
const addPopup = (id) => { const addPopup = (id) => {
if (!id) { if (!id) {
return; return;
@ -21,38 +22,49 @@ export default () => Box({
const notif = Notifications.getNotification(id); const notif = Notifications.getNotification(id);
const NewNotif = Notification({ if (notif) {
notif, const NewNotif = Notification({
command: () => notif.dismiss(), notif,
}); command: () => notif.dismiss(),
});
if (NewNotif) { if (NewNotif) {
// Use this instead of add to put it at the top // Use this instead of add to put it at the top
self.pack_end(NewNotif, false, false, 0); self.pack_end(NewNotif, false, false, 0);
self.show_all(); self.show_all();
}
} }
}; };
/**
* @param {number} id
* @param {boolean} force
*/
const handleDismiss = (id, force = false) => { const handleDismiss = (id, force = false) => {
const notif = self.children.find((ch) => ch._id === id); // @ts-expect-error
const notif = self.children.find((ch) => ch.attribute.id === id);
if (!notif) { if (!notif) {
return; return;
} }
// If notif isn't hovered or was closed, slide away // If notif isn't hovered or was closed, slide away
if (!notif._hovered || force) { // @ts-expect-error
notif.slideAway('Left'); if (!notif.attribute.hovered || force) {
// @ts-expect-error
notif.attribute.slideAway('Left');
} }
// If notif is hovered, delay close // If notif is hovered, delay close
else if (notif._hovered) { // @ts-expect-error
notif.interval = interval(DELAY, () => { else if (notif.attribute.hovered) {
if (!notif._hovered && notif.interval) { const intervalId = interval(DELAY, () => {
notif.slideAway('Left'); // @ts-expect-error
if (!notif.attribute.hovered && intervalId) {
// @ts-expect-error
notif.attribute.slideAway('Left');
GLib.source_remove(notif.interval); GLib.source_remove(intervalId);
notif.interval = null;
} }
}); });
} }