mirror of
https://github.com/Theaninova/TheaninovOS.git
synced 2025-12-12 19:46:20 +00:00
312 lines
9.6 KiB
JavaScript
312 lines
9.6 KiB
JavaScript
import Widget from "resource:///com/github/Aylur/ags/widget.js"
|
|
import {lookUpIcon} from "resource:///com/github/Aylur/ags/utils.js"
|
|
import GLib from "gi://GLib"
|
|
import Gtk from "gi://Gtk"
|
|
import Gdk from "gi://Gdk?version=3.0"
|
|
import {MaterialIcon} from "./materialicon.js"
|
|
import {setupCursorHover} from "./cursorhover.js"
|
|
|
|
/**
|
|
* @typedef {{appIcon: string; appEntry: string; image?: string; urgency: 'critical'}} NotificationObject
|
|
*/
|
|
|
|
/**
|
|
* @param notificationObject {NotificationObject}
|
|
* @returns {import('types/widgets/box').default}
|
|
* @constructor
|
|
*/
|
|
const NotificationIcon = notificationObject => {
|
|
if (notificationObject.image) {
|
|
return Widget.Box({
|
|
valign: Gtk.Align.CENTER,
|
|
hexpand: false,
|
|
className: "notif-icon",
|
|
style:
|
|
`background-image: url("${notificationObject.image}");` +
|
|
"background-size: auto 100%;" +
|
|
"background-repeat: no-repeat;" +
|
|
"background-position: center;",
|
|
})
|
|
}
|
|
|
|
let icon = "NO_ICON"
|
|
if (lookUpIcon(notificationObject.appIcon)) icon = notificationObject.appIcon
|
|
if (lookUpIcon(notificationObject.appEntry)) icon = notificationObject.appEntry
|
|
|
|
return Widget.Box({
|
|
valign: Gtk.Align.CENTER,
|
|
hexpand: false,
|
|
className: "notif-icon",
|
|
setup: box => {
|
|
if (icon !== "NO_ICON")
|
|
box.pack_start(
|
|
Widget.Icon({
|
|
icon,
|
|
size: 30,
|
|
halign: Gtk.Align.CENTER,
|
|
hexpand: true,
|
|
valign: Gtk.Align.CENTER,
|
|
setup: () => {
|
|
box.toggleClassName(`notif-icon-material-${notificationObject.urgency}`, true)
|
|
},
|
|
}),
|
|
false,
|
|
true,
|
|
0,
|
|
)
|
|
else
|
|
box.pack_start(
|
|
MaterialIcon(`${notificationObject.urgency === "critical" ? "release_alert" : "chat"}`, "hugeass", {
|
|
hexpand: true,
|
|
setup: () => box.toggleClassName(`notif-icon-material-${notificationObject.urgency}`, true),
|
|
}),
|
|
false,
|
|
true,
|
|
0,
|
|
)
|
|
},
|
|
})
|
|
}
|
|
|
|
/**
|
|
* @param options {{notifObject?: NotificationObject; isPopup?: boolean, props?: object}}
|
|
* @returns {import('types/widgets/revealer').default}
|
|
*/
|
|
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 = 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 = Widget.Revealer({
|
|
properties: [
|
|
["id", notifObject.id],
|
|
["close", undefined],
|
|
["hovered", false],
|
|
["dragging", false],
|
|
["destroyWithAnims", () => destroyWithAnims],
|
|
],
|
|
revealChild: false,
|
|
transition: "slide_down",
|
|
transitionDuration: 200,
|
|
child: Widget.Box({
|
|
// Box to make sure css-based spacing works
|
|
homogeneous: true,
|
|
}),
|
|
})
|
|
|
|
const display = Gdk.Display.get_default()
|
|
const notificationContent = Widget.Box({
|
|
...props,
|
|
className: `${isPopup ? "popup-" : ""}notif-${notifObject.urgency} spacing-h-10`,
|
|
children: [
|
|
NotificationIcon(notifObject),
|
|
Widget.Box({
|
|
valign: "center",
|
|
vertical: true,
|
|
hexpand: true,
|
|
children: [
|
|
Widget.Box({
|
|
children: [
|
|
Widget.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,
|
|
}),
|
|
],
|
|
}),
|
|
Widget.Label({
|
|
xalign: 0,
|
|
className: "txt-smallie notif-body-${urgency}",
|
|
useMarkup: true,
|
|
justify: Gtk.Justification.LEFT,
|
|
wrap: true,
|
|
label: notifObject.body,
|
|
}),
|
|
],
|
|
}),
|
|
Widget.Box({
|
|
className: "spacing-h-5",
|
|
children: [
|
|
Widget.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")
|
|
}
|
|
},
|
|
}),
|
|
Widget.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)
|
|
let 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 = Widget.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] ?? NaN
|
|
|
|
if (Math.abs(offset) > dragThreshold && offset * initialDir > 0) {
|
|
if (offset > 0) {
|
|
self.set_style(rightAnim1)
|
|
widget.sensitive = false
|
|
} else {
|
|
self.set_style(leftAnim1)
|
|
widget.sensitive = false
|
|
}
|
|
Utils.timeout(200, () => {
|
|
wholeThing.revealChild = false
|
|
})
|
|
Utils.timeout(400, () => {
|
|
command()
|
|
wholeThing.destroy()
|
|
})
|
|
} else {
|
|
self.set_style(`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
|
|
}
|