summaryrefslogtreecommitdiff
path: root/accounts/gkleen@sif/shell/quickshell/Services/NotificationManager.qml
blob: 001ffcf50d934cde237ae3aac0b929403c912d3c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
pragma Singleton

import QtQml
import Quickshell
import Quickshell.Services.Notifications

Singleton {
  id: root

  readonly property bool active: !lockscreenActive && !displayInhibited
  property bool lockscreenActive: false
  property bool displayInhibited: false
  property alias trackedNotifications: server.trackedNotifications
  readonly property var groups: {
    function matchesGroupKey(notif, groupKey) {
      var matches = true;
      for (const prop in groupKey.test) {
	if (notif[prop] !== groupKey.test[prop]) {
	  matches = false;
	  break;
	}
      }
      return matches;
    }

    var groups = new Map();
    var notifs = new Array();
    for (const [ix, notif] of server.trackedNotifications.values.entries()) {
      var didGroup = false;
      for (const groupKey of root.groupKeys) {
	if (!matchesGroupKey(notif, groupKey))
	  continue;

        const key = JSON.stringify({
	  "key": groupKey,
	  "values": Object.assign({}, ...(Array.from(groupKey["group-by"]).map(prop => {
	    var res = {};
	    res[prop] = notif[prop];
	    return res;
	  })))
	});
	if (!groups.has(key))
	  groups.set(key, new Array());
	groups.get(key).push({ "ix": ix, "notif": notif });
	didGroup = true;
	break;
      }

      if (!didGroup)
        notifs.push([{ "ix": ix, "notif": notif }]);
    }
    notifs.push(...groups.values());
    notifs.sort((as, bs) => Math.min(...(as.map(o => o.ix))) - Math.min(...(bs.map(o => o.ix))));
    return notifs.map(ns => ns.map(n => n.notif));
  }

  property var groupKeys: [
    { "test": { "appName": "Element" }, "group-by": [ "summary" ] }
  ];

  property var history: []

  Component {
    id: expirationTimer

    QtObject {
      id: timer

      required property QtObject parent
      required property int expirationTime
      property list<QtObject> data: [
	Timer {
	  running: root.active
	  interval: timer.expirationTime
	  onTriggered: timer.parent.expire()
	}
      ]
    }
  }

  Component {
    id: notificationLock

    RetainableLock {}
  }

  readonly property SystemClock clock: SystemClock {
    precision: SystemClock.Minutes
  }

  function formatTime(time) {
    const now = root.clock.date;
    const diff = now - time;
    const minutes = Math.ceil(diff / 60000);
    const hours = Math.floor(minutes / 60);

    if (hours < 1) {
      if (minutes < 1)
        return "now";
      if (minutes == 1)
        return "1 minute";
      return `${minutes} minutes`;
    }

    const nowDate = new Date(now.getFullYear(), now.getMonth(), now.getDate())
    const timeDate = new Date(time.getFullYear(), time.getMonth(), time.getDate())
    const days = Math.floor((nowDate - timeDate) / (1000 * 86400))

    const timeStr = time.toLocaleTimeString(Qt.locale(), "HH:mm");

    if (days === 0)
      return timeStr;
    if (days === 1)
      return `yesterday ${timeStr}`;

    const dateStr = time.toLocaleTimeString(Qt.locale(), "YYYY-MM-DD");
    return `${dateStr} ${timeStr}`;
  }

  NotificationServer {
    id: server

    bodySupported: true
    actionsSupported: true
    actionIconsSupported: true
    imageSupported: true
    bodyMarkupSupported: true
    bodyImagesSupported: true

    onNotification: notification => {
      var timeout = notification.expireTimeout * 1000;
      if (notification.appName == "poweralertd")
        timeout = 2000;
      if (timeout > 0) {
        Object.defineProperty(notification, "expirationTimer", { configurable: true, enumerable: true, writable: true });
	notification.expirationTimer = expirationTimer.createObject(notification, { parent: notification, expirationTime: timeout });
      }
      Object.defineProperty(notification, "receivedTime", { configurable: true, enumerable: true, writable: true });
      notification.receivedTime = root.clock.date;
      notification.closed.connect((reason) => server.onNotificationClosed(notification, reason));
      notification.tracked = true;
    }

    function onNotificationClosed(notification, reason) {
      root.history.push({
        lock: notificationLock.createObject(root, { locked: true, object: notification }),
	notification: notification
      });
    }
  }
}