refactor(ags notifs): clarify code, add way to slide in from right, fix clear button, etc.
This commit is contained in:
parent
ed3f8f6eb1
commit
ad02510b10
4 changed files with 144 additions and 157 deletions
|
@ -1,6 +1,8 @@
|
|||
import Applications from 'resource:///com/github/Aylur/ags/service/applications.js';
|
||||
import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js';
|
||||
import Variable from 'resource:///com/github/Aylur/ags/variable.js';
|
||||
import { Box, Icon, Label, Button } from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import { lookUpIcon, execAsync } from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
import { lookUpIcon, execAsync, timeout } from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
|
||||
import GLib from 'gi://GLib';
|
||||
|
||||
|
@ -87,10 +89,20 @@ const NotificationIcon = notif => {
|
|||
});
|
||||
};
|
||||
|
||||
export default ({
|
||||
// Make a variable to connect to for Widgets
|
||||
// to know when there are notifs or not
|
||||
export const HasNotifs = Variable(false);
|
||||
|
||||
export const Notification = ({
|
||||
notif,
|
||||
slideIn = 'Left',
|
||||
command = () => {},
|
||||
} = {}) => {
|
||||
if (!notif)
|
||||
return;
|
||||
|
||||
HasNotifs.value = Notifications.notifications.length > 0;
|
||||
|
||||
const BlockedApps = [
|
||||
'Spotify',
|
||||
];
|
||||
|
@ -100,9 +112,11 @@ export default ({
|
|||
return;
|
||||
}
|
||||
|
||||
return Gesture({
|
||||
// Init notif
|
||||
const notifWidget = Gesture({
|
||||
maxOffset: 200,
|
||||
command: () => command(),
|
||||
slideIn,
|
||||
properties: [
|
||||
['hovered', false],
|
||||
['id', notif.id],
|
||||
|
@ -115,77 +129,93 @@ export default ({
|
|||
if (w._hovered)
|
||||
w._hovered = false;
|
||||
},
|
||||
|
||||
child: Box({
|
||||
className: `notification ${notif.urgency}`,
|
||||
vexpand: false,
|
||||
// Notification
|
||||
child: Box({
|
||||
vertical: true,
|
||||
children: [
|
||||
// Content
|
||||
Box({
|
||||
children: [
|
||||
NotificationIcon(notif),
|
||||
Box({
|
||||
hexpand: true,
|
||||
vertical: true,
|
||||
children: [
|
||||
// Top of Content
|
||||
Box({
|
||||
children: [
|
||||
Label({
|
||||
className: 'title',
|
||||
xalign: 0,
|
||||
justification: 'left',
|
||||
hexpand: true,
|
||||
maxWidthChars: 24,
|
||||
truncate: 'end',
|
||||
wrap: true,
|
||||
label: notif.summary,
|
||||
useMarkup: notif.summary.startsWith('<'),
|
||||
}),
|
||||
Label({
|
||||
className: 'time',
|
||||
valign: 'start',
|
||||
label: setTime(notif.time),
|
||||
}),
|
||||
EventBox({
|
||||
reset: false,
|
||||
child: Button({
|
||||
className: 'close-button',
|
||||
valign: 'start',
|
||||
onClicked: () => notif.close(),
|
||||
child: Icon('window-close-symbolic'),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Label({
|
||||
className: 'description',
|
||||
hexpand: true,
|
||||
useMarkup: true,
|
||||
xalign: 0,
|
||||
justification: 'left',
|
||||
label: notif.body,
|
||||
wrap: true,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
// Actions
|
||||
Box({
|
||||
className: 'actions',
|
||||
children: notif.actions.map(action => Button({
|
||||
className: 'action-button',
|
||||
onClicked: () => notif.invoke(action.id),
|
||||
hexpand: true,
|
||||
child: Label(action.label),
|
||||
})),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
// Notif methods
|
||||
notifWidget.slideAway = side => {
|
||||
notifWidget.child.setStyle(notifWidget.child[`_slide${side}`]);
|
||||
notifWidget.sensitive = false;
|
||||
timeout(400, () => {
|
||||
notifWidget.child.setStyle(notifWidget.child[`_squeeze${side}`]);
|
||||
timeout(500, () => {
|
||||
HasNotifs.value = Notifications.notifications.length > 0;
|
||||
notifWidget.get_parent().remove(notifWidget);
|
||||
notifWidget.destroy();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Add body to notif
|
||||
notifWidget.child.add(Box({
|
||||
className: `notification ${notif.urgency}`,
|
||||
vexpand: false,
|
||||
// Notification
|
||||
child: Box({
|
||||
vertical: true,
|
||||
children: [
|
||||
// Content
|
||||
Box({
|
||||
children: [
|
||||
NotificationIcon(notif),
|
||||
Box({
|
||||
hexpand: true,
|
||||
vertical: true,
|
||||
children: [
|
||||
// Top of Content
|
||||
Box({
|
||||
children: [
|
||||
Label({
|
||||
className: 'title',
|
||||
xalign: 0,
|
||||
justification: 'left',
|
||||
hexpand: true,
|
||||
maxWidthChars: 24,
|
||||
truncate: 'end',
|
||||
wrap: true,
|
||||
label: notif.summary,
|
||||
useMarkup: notif.summary.startsWith('<'),
|
||||
}),
|
||||
Label({
|
||||
className: 'time',
|
||||
valign: 'start',
|
||||
label: setTime(notif.time),
|
||||
}),
|
||||
EventBox({
|
||||
reset: false,
|
||||
child: Button({
|
||||
className: 'close-button',
|
||||
valign: 'start',
|
||||
onClicked: () => notif.close(),
|
||||
child: Icon('window-close-symbolic'),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Label({
|
||||
className: 'description',
|
||||
hexpand: true,
|
||||
useMarkup: true,
|
||||
xalign: 0,
|
||||
justification: 'left',
|
||||
label: notif.body,
|
||||
wrap: true,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
// Actions
|
||||
Box({
|
||||
className: 'actions',
|
||||
children: notif.actions.map(action => Button({
|
||||
className: 'action-button',
|
||||
onClicked: () => notif.invoke(action.id),
|
||||
hexpand: true,
|
||||
child: Label(action.label),
|
||||
})),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
}));
|
||||
return notifWidget;
|
||||
};
|
||||
|
|
|
@ -1,45 +1,16 @@
|
|||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
||||
import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js';
|
||||
import { Button, Label, Box, Icon, Scrollable, Revealer } from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import { timeout } from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
|
||||
import Notification from './base.js';
|
||||
import { Notification, HasNotifs } from './base.js';
|
||||
import PopupWindow from '../misc/popup.js';
|
||||
import EventBox from '../misc/cursorbox.js';
|
||||
|
||||
|
||||
// Needs to be wrapped to still have onHover when disabled
|
||||
const ClearButton = () => EventBox({
|
||||
child: Button({
|
||||
onPrimaryClickRelease: button => {
|
||||
button._popups.children.forEach(ch => {
|
||||
ch.child.setStyle(ch.child._slideLeft);
|
||||
ch.sensitive = false;
|
||||
});
|
||||
|
||||
button._notifList.children.forEach(ch => {
|
||||
if (ch.child) {
|
||||
ch.child.setStyle(ch.child._slideRight);
|
||||
ch.sensitive = false;
|
||||
}
|
||||
timeout(500, () => {
|
||||
button._notifList.remove(ch);
|
||||
Notifications.clear();
|
||||
});
|
||||
});
|
||||
},
|
||||
properties: [
|
||||
['notifList'],
|
||||
['popups'],
|
||||
],
|
||||
connections: [[Notifications, button => {
|
||||
if (!button._notifList)
|
||||
button._notifList = NotificationList;
|
||||
|
||||
if (!button._popups)
|
||||
button._popups = App.getWindow('notifications').child.children[0].child;
|
||||
|
||||
button.sensitive = Notifications.notifications.length > 0;
|
||||
}]],
|
||||
onPrimaryClickRelease: () => Notifications.clear(),
|
||||
binds: [['sensitive', HasNotifs]],
|
||||
child: Box({
|
||||
children: [
|
||||
Label('Clear '),
|
||||
|
@ -66,16 +37,21 @@ const Header = () => Box({
|
|||
],
|
||||
});
|
||||
|
||||
const NotificationList = Box({
|
||||
const NotificationList = () => Box({
|
||||
vertical: true,
|
||||
valign: 'start',
|
||||
vexpand: true,
|
||||
valign: 'start',
|
||||
binds: [['visible', HasNotifs]],
|
||||
connections: [
|
||||
[Notifications, (box, id) => {
|
||||
if (box.children.length == 0) {
|
||||
for (const notif of Notifications.notifications) {
|
||||
if (!notif)
|
||||
return;
|
||||
|
||||
const NewNotif = Notification({
|
||||
notif,
|
||||
slideIn: 'Right',
|
||||
command: () => notif.close(),
|
||||
});
|
||||
|
||||
|
@ -90,6 +66,7 @@ const NotificationList = Box({
|
|||
|
||||
const NewNotif = Notification({
|
||||
notif,
|
||||
slideIn: 'Right',
|
||||
command: () => notif.close(),
|
||||
});
|
||||
|
||||
|
@ -103,23 +80,17 @@ const NotificationList = Box({
|
|||
[Notifications, (box, id) => {
|
||||
for (const ch of box.children) {
|
||||
if (ch._id == id) {
|
||||
ch.child.setStyle(ch.child._slideRight);
|
||||
ch.sensitive = false;
|
||||
timeout(500, () => box.remove(ch));
|
||||
ch.slideAway('Right');
|
||||
return;
|
||||
}
|
||||
}
|
||||
}, 'closed'],
|
||||
|
||||
[Notifications, box => box.visible = Notifications.notifications.length > 0],
|
||||
],
|
||||
});
|
||||
|
||||
const Placeholder = () => Revealer({
|
||||
transition: 'crossfade',
|
||||
connections: [[Notifications, box => {
|
||||
box.revealChild = Notifications.notifications.length === 0;
|
||||
}]],
|
||||
binds: [['revealChild', HasNotifs, 'value', value => !value]],
|
||||
child: Box({
|
||||
className: 'placeholder',
|
||||
vertical: true,
|
||||
|
@ -149,7 +120,7 @@ const NotificationCenterWidget = () => Box({
|
|||
className: 'notification-list',
|
||||
vertical: true,
|
||||
children: [
|
||||
NotificationList,
|
||||
NotificationList(),
|
||||
Placeholder(),
|
||||
],
|
||||
}),
|
||||
|
|
|
@ -7,6 +7,7 @@ const display = Gdk.Display.get_default();
|
|||
|
||||
|
||||
export default ({
|
||||
slideIn = 'Left',
|
||||
maxOffset = 150,
|
||||
startMargin = 0,
|
||||
endMargin = 300,
|
||||
|
@ -87,6 +88,7 @@ export default ({
|
|||
`);
|
||||
}
|
||||
|
||||
// Put a threshold on if a click is actually dragging
|
||||
self.get_parent()._dragging = Math.abs(offset) > 10;
|
||||
|
||||
if (widget.window)
|
||||
|
@ -94,9 +96,9 @@ export default ({
|
|||
}, 'drag-update'],
|
||||
|
||||
[gesture, self => {
|
||||
// ?
|
||||
// Make it slide in on init
|
||||
if (!self._ready) {
|
||||
self.setStyle(slideLeft);
|
||||
self.setStyle(slideIn === 'Left' ? slideLeft : slideRight);
|
||||
|
||||
timeout(500, () => self.setStyle(`${TRANSITION} margin: unset; opacity: 1;`));
|
||||
timeout(1000, () => self._ready = true);
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js';
|
||||
import { Box, Revealer, Window } from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import { interval, timeout } from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
import { Box, Window } from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import { interval } from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
|
||||
import GLib from 'gi://GLib';
|
||||
|
||||
import Notification from './base.js';
|
||||
import { Notification } from './base.js';
|
||||
|
||||
|
||||
// TODO: clean up code
|
||||
const Popups = () => Box({
|
||||
vertical: true,
|
||||
properties: [
|
||||
|
@ -21,6 +20,7 @@ const Popups = () => Box({
|
|||
});
|
||||
|
||||
if (NewNotif) {
|
||||
// use this instead of add to put it at the top
|
||||
box.pack_end(NewNotif, false, false, 0);
|
||||
box.show_all();
|
||||
}
|
||||
|
@ -30,28 +30,21 @@ const Popups = () => Box({
|
|||
['dismiss', (box, id, force = false) => {
|
||||
for (const ch of box.children) {
|
||||
if (ch._id == id) {
|
||||
// If notif isn't hovered or was closed, slide away
|
||||
if (!ch._hovered || force) {
|
||||
ch.child.setStyle(ch.child._slideLeft);
|
||||
ch.sensitive = false;
|
||||
timeout(400, () => {
|
||||
ch.child.setStyle(ch.child._squeezeLeft);
|
||||
timeout(500, () => box.remove(ch));
|
||||
});
|
||||
ch.slideAway('Left');
|
||||
return;
|
||||
}
|
||||
|
||||
// If notif is hovered, delay close
|
||||
else if (ch._hovered) {
|
||||
ch.interval = interval(2000, () => {
|
||||
if (!ch._hovered) {
|
||||
if (ch.interval) {
|
||||
ch.child.setStyle(ch.child._slideLeft);
|
||||
ch.sensitive = false;
|
||||
timeout(400, () => {
|
||||
ch.child.setStyle(ch.child._squeezeLeft);
|
||||
timeout(500, () => box.remove(ch));
|
||||
});
|
||||
GLib.source_remove(ch.interval);
|
||||
ch.interval = undefined;
|
||||
}
|
||||
if (!ch._hovered && ch.interval) {
|
||||
ch.slideAway('Left');
|
||||
|
||||
GLib.source_remove(ch.interval);
|
||||
ch.interval = undefined;
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -66,22 +59,13 @@ const Popups = () => Box({
|
|||
],
|
||||
});
|
||||
|
||||
const PopupList = ({ transition = 'none' } = {}) => Box({
|
||||
className: 'notifications-popup-list',
|
||||
style: 'padding: 1px',
|
||||
children: [
|
||||
Revealer({
|
||||
transition,
|
||||
connections: [[Notifications, self => {
|
||||
self.revealChild = self.child.children.length > 0;
|
||||
}]],
|
||||
child: Popups(),
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
export default () => Window({
|
||||
name: 'notifications',
|
||||
anchor: ['top', 'left'],
|
||||
child: PopupList(),
|
||||
child: Box({
|
||||
style: `min-height:1px;
|
||||
min-width:1px;
|
||||
padding: 1px;`,
|
||||
children: [Popups()],
|
||||
}),
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue