mirror of
https://github.com/Theaninova/TheaninovOS.git
synced 2026-01-04 23:02:48 +00:00
299 lines
11 KiB
JavaScript
299 lines
11 KiB
JavaScript
// This file is for the actual widget for each single notification
|
|
|
|
const { GLib, Gdk, Gtk } = imports.gi;
|
|
import { Utils, Widget } from '../../imports.js';
|
|
const { lookUpIcon, timeout } = Utils;
|
|
const { Box, EventBox, Icon, Scrollable, Label, Button, Revealer } = Widget;
|
|
import { MaterialIcon } from "./materialicon.js";
|
|
import { setupCursorHover } from "./cursorhover.js";
|
|
|
|
const NotificationIcon = (notifObject) => {
|
|
// { appEntry, appIcon, image }, urgency = 'normal'
|
|
if (notifObject.image) {
|
|
return Box({
|
|
valign: 'center',
|
|
hexpand: false,
|
|
className: 'notif-icon',
|
|
style: `
|
|
background-image: url("${notifObject.image}");
|
|
background-size: auto 100%;
|
|
background-repeat: no-repeat;
|
|
background-position: center;
|
|
`,
|
|
});
|
|
}
|
|
|
|
let icon = 'NO_ICON';
|
|
if (lookUpIcon(notifObject.appIcon))
|
|
icon = notifObject.appIcon;
|
|
if (lookUpIcon(notifObject.appEntry))
|
|
icon = notifObject.appEntry;
|
|
|
|
return Box({
|
|
valign: 'center',
|
|
hexpand: false,
|
|
className: 'notif-icon',
|
|
setup: box => {
|
|
if (icon != 'NO_ICON') box.pack_start(Icon({
|
|
icon, size: 30,
|
|
halign: 'center', hexpand: true,
|
|
valign: 'center',
|
|
setup: () => {
|
|
box.toggleClassName(`notif-icon-material-${notifObject.urgency}`, true);
|
|
},
|
|
}), false, true, 0);
|
|
else box.pack_start(MaterialIcon(`${notifObject.urgency == 'critical' ? 'release_alert' : 'chat'}`, 'hugeass', {
|
|
hexpand: true,
|
|
setup: () => box.toggleClassName(`notif-icon-material-${notifObject.urgency}`, true),
|
|
}), false, true, 0)
|
|
}
|
|
});
|
|
};
|
|
|
|
export default ({
|
|
notifObject,
|
|
isPopup = false,
|
|
props = {},
|
|
} = {}) => {
|
|
const command = (isPopup ?
|
|
() => notifObject.dismiss() :
|
|
() => notifObject.close()
|
|
)
|
|
const destroyWithAnims = () => {
|
|
widget.sensitive = false;
|
|
notificationBox.setStyle(rightAnim1);
|
|
Utils.timeout(200, () => {
|
|
wholeThing.revealChild = false;
|
|
});
|
|
Utils.timeout(400, () => {
|
|
command();
|
|
wholeThing.destroy();
|
|
});
|
|
}
|
|
const widget = EventBox({
|
|
onHover: (self) => {
|
|
self.window.set_cursor(Gdk.Cursor.new_from_name(display, 'grab'));
|
|
if (!wholeThing._hovered)
|
|
wholeThing._hovered = true;
|
|
},
|
|
onHoverLost: (self) => {
|
|
self.window.set_cursor(null);
|
|
if (wholeThing._hovered)
|
|
wholeThing._hovered = false;
|
|
if(isPopup) {
|
|
command();
|
|
}
|
|
},
|
|
onMiddleClick: (self) => {
|
|
destroyWithAnims();
|
|
}
|
|
});
|
|
const wholeThing = Revealer({
|
|
properties: [
|
|
['id', notifObject.id],
|
|
['close', undefined],
|
|
['hovered', false],
|
|
['dragging', false],
|
|
['destroyWithAnims', () => destroyWithAnims]
|
|
],
|
|
revealChild: false,
|
|
transition: 'slide_down',
|
|
transitionDuration: 200,
|
|
child: Box({ // Box to make sure css-based spacing works
|
|
homogeneous: true,
|
|
})
|
|
});
|
|
|
|
const display = Gdk.Display.get_default();
|
|
const notificationContent = Box({
|
|
...props,
|
|
className: `${isPopup ? 'popup-' : ''}notif-${notifObject.urgency} spacing-h-10`,
|
|
children: [
|
|
NotificationIcon(notifObject),
|
|
Box({
|
|
valign: 'center',
|
|
vertical: true,
|
|
hexpand: true,
|
|
children: [
|
|
Box({
|
|
children: [
|
|
Label({
|
|
xalign: 0,
|
|
className: 'txt-small txt-semibold titlefont',
|
|
justify: Gtk.Justification.LEFT,
|
|
hexpand: true,
|
|
maxWidthChars: 24,
|
|
ellipsize: 3,
|
|
wrap: true,
|
|
useMarkup: notifObject.summary.startsWith('<'),
|
|
label: notifObject.summary,
|
|
}),
|
|
]
|
|
}),
|
|
Label({
|
|
xalign: 0,
|
|
className: 'txt-smallie notif-body-${urgency}',
|
|
useMarkup: true,
|
|
xalign: 0,
|
|
justify: Gtk.Justification.LEFT,
|
|
wrap: true,
|
|
label: notifObject.body,
|
|
}),
|
|
]
|
|
}),
|
|
Box({
|
|
className: 'spacing-h-5',
|
|
children: [
|
|
Label({
|
|
valign: 'center',
|
|
className: 'txt-smaller txt-semibold',
|
|
justify: Gtk.Justification.RIGHT,
|
|
setup: (label) => {
|
|
const messageTime = GLib.DateTime.new_from_unix_local(notifObject.time);
|
|
if (messageTime.get_day_of_year() == GLib.DateTime.new_now_local().get_day_of_year()) {
|
|
label.label = messageTime.format('%H:%M');
|
|
}
|
|
else if (messageTime.get_day_of_year() == GLib.DateTime.new_now_local().get_day_of_year() - 1) {
|
|
label.label = messageTime.format('%H:%M\nYesterday');
|
|
}
|
|
else {
|
|
label.label = messageTime.format('%H:%M\n%d/%m');
|
|
}
|
|
}
|
|
}),
|
|
Button({
|
|
className: 'notif-close-btn',
|
|
onClicked: () => {
|
|
destroyWithAnims()
|
|
},
|
|
child: MaterialIcon('close', 'large', {
|
|
valign: 'center',
|
|
}),
|
|
setup: (button) => setupCursorHover(button),
|
|
}),
|
|
]
|
|
}),
|
|
|
|
// what is this? i think it should be at the bottom not on the right
|
|
// Box({
|
|
// className: 'actions',
|
|
// children: actions.map(action => Button({
|
|
// className: 'action-button',
|
|
// onClicked: () => Notifications.invoke(id, action.id),
|
|
// hexpand: true,
|
|
// child: Label(action.label),
|
|
// })),
|
|
// }),
|
|
]
|
|
})
|
|
|
|
// Gesture stuff
|
|
|
|
const gesture = Gtk.GestureDrag.new(widget);
|
|
var initialDir = 0;
|
|
// in px
|
|
const startMargin = 0;
|
|
const dragThreshold = 100;
|
|
// in rem
|
|
const maxOffset = 10.227;
|
|
const endMargin = 20.455;
|
|
const disappearHeight = 6.818;
|
|
const leftAnim1 = `transition: 200ms cubic-bezier(0.05, 0.7, 0.1, 1);
|
|
margin-left: -${Number(maxOffset + endMargin)}rem;
|
|
margin-right: ${Number(maxOffset + endMargin)}rem;
|
|
opacity: 0;`;
|
|
|
|
const rightAnim1 = `transition: 200ms cubic-bezier(0.05, 0.7, 0.1, 1);
|
|
margin-left: ${Number(maxOffset + endMargin)}rem;
|
|
margin-right: -${Number(maxOffset + endMargin)}rem;
|
|
opacity: 0;`;
|
|
|
|
const notificationBox = Box({
|
|
properties: [
|
|
['leftAnim1', leftAnim1],
|
|
['rightAnim1', rightAnim1],
|
|
['ready', false],
|
|
],
|
|
homogeneous: true,
|
|
children: [notificationContent],
|
|
connections: [
|
|
[gesture, self => {
|
|
var offset = gesture.get_offset()[1];
|
|
if (initialDir == 0 && offset != 0)
|
|
initialDir = (offset > 0 ? 1 : -1)
|
|
|
|
if (offset > 0) {
|
|
if (initialDir < 0)
|
|
self.setStyle(`margin-left: 0px; margin-right: 0px;`);
|
|
else
|
|
self.setStyle(`
|
|
margin-left: ${Number(offset + startMargin)}px;
|
|
margin-right: -${Number(offset + startMargin)}px;
|
|
`);
|
|
}
|
|
else if (offset < 0) {
|
|
if (initialDir > 0)
|
|
self.setStyle(`margin-left: 0px; margin-right: 0px;`);
|
|
else {
|
|
offset = Math.abs(offset);
|
|
self.setStyle(`
|
|
margin-right: ${Number(offset + startMargin)}px;
|
|
margin-left: -${Number(offset + startMargin)}px;
|
|
`);
|
|
}
|
|
}
|
|
|
|
wholeThing._dragging = Math.abs(offset) > 10;
|
|
|
|
if (widget.window)
|
|
widget.window.set_cursor(Gdk.Cursor.new_from_name(display, 'grabbing'));
|
|
}, 'drag-update'],
|
|
|
|
[gesture, self => {
|
|
if (!self._ready) {
|
|
wholeThing.revealChild = true;
|
|
self._ready = true;
|
|
return;
|
|
}
|
|
|
|
const offset = gesture.get_offset()[1];
|
|
|
|
if (Math.abs(offset) > dragThreshold && offset * initialDir > 0) {
|
|
if (offset > 0) {
|
|
self.setStyle(rightAnim1);
|
|
widget.sensitive = false;
|
|
}
|
|
else {
|
|
self.setStyle(leftAnim1);
|
|
widget.sensitive = false;
|
|
}
|
|
Utils.timeout(200, () => {
|
|
wholeThing.revealChild = false
|
|
});
|
|
Utils.timeout(400, () => {
|
|
command();
|
|
wholeThing.destroy();
|
|
});
|
|
}
|
|
else {
|
|
self.setStyle(`transition: margin 200ms cubic-bezier(0.05, 0.7, 0.1, 1), opacity 200ms cubic-bezier(0.05, 0.7, 0.1, 1);
|
|
margin-left: ${startMargin}px;
|
|
margin-right: ${startMargin}px;
|
|
margin-bottom: unset; margin-top: unset;
|
|
opacity: 1;`);
|
|
if (widget.window)
|
|
widget.window.set_cursor(Gdk.Cursor.new_from_name(display, 'grab'));
|
|
|
|
wholeThing._dragging = false;
|
|
}
|
|
initialDir = 0;
|
|
}, 'drag-end'],
|
|
|
|
],
|
|
})
|
|
widget.add(notificationBox);
|
|
wholeThing.child.children = [widget];
|
|
|
|
return wholeThing;
|
|
}
|