Bound the notification app list, and give Focus a real editor
Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Services.Notifications
|
||||
import QtQuick
|
||||
|
||||
import qs.config
|
||||
@@ -8,6 +9,10 @@ import qs.services
|
||||
ShellRoot {
|
||||
id: root
|
||||
|
||||
// A stand-in for a served notification. `urgency` and `expireTimeout` are
|
||||
// spelled out rather than left undefined: the effective-urgency override
|
||||
// and the banner-duration choice both read them, so a fixture that omits
|
||||
// them would be testing the undefined path instead of the ordinary one.
|
||||
function notification(idValue: int, desktopEntryValue: string, appNameValue: string): var {
|
||||
const closeHandlers = [];
|
||||
return {
|
||||
@@ -15,6 +20,9 @@ ShellRoot {
|
||||
desktopEntry: desktopEntryValue,
|
||||
appName: appNameValue,
|
||||
appIcon: "",
|
||||
urgency: NotificationUrgency.Normal,
|
||||
expireTimeout: -1,
|
||||
hints: ({}),
|
||||
transient: false,
|
||||
lastGeneration: false,
|
||||
tracked: false,
|
||||
@@ -42,6 +50,24 @@ ShellRoot {
|
||||
Notifs.fallbackAppRules = {};
|
||||
Notifs.rememberedApplications = {};
|
||||
DesktopPreferences.set("notificationAppRules", {});
|
||||
DesktopPreferences.set("criticalBreaksThrough", false);
|
||||
}
|
||||
|
||||
// One notification through the whole delivery path, reported as the four
|
||||
// counts that distinguish every outcome from every other: muted, filed
|
||||
// quietly, held by Do Not Disturb, or shown.
|
||||
function deliver(n: var): var {
|
||||
Notifs.handleNotification(n);
|
||||
return {
|
||||
tracked: n.tracked,
|
||||
history: Notifs.history.length,
|
||||
popups: Notifs.popups.length,
|
||||
unread: Notifs.unreadCount
|
||||
};
|
||||
}
|
||||
|
||||
function ruleKeys(): var {
|
||||
return Object.keys(DesktopPreferences.get("notificationAppRules"));
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
@@ -108,5 +134,115 @@ ShellRoot {
|
||||
function applications(): string {
|
||||
return JSON.stringify(Notifs.applications);
|
||||
}
|
||||
|
||||
// display: "history" is not muting. It has to reach history and the
|
||||
// unread count and stop short of the banner, which is the one
|
||||
// combination of the four counts that says so.
|
||||
function historyOnly(): string {
|
||||
root.reset();
|
||||
const seed = root.notification(10, "org.history.App.desktop", "History App");
|
||||
Notifs.handleNotification(seed);
|
||||
const appId = Notifs.notificationAppId(seed);
|
||||
|
||||
root.resetNotifications();
|
||||
Notifs.setAppRule(appId, { display: "history" });
|
||||
return JSON.stringify(root.deliver(
|
||||
root.notification(11, "org.history.App.desktop", "History App")));
|
||||
}
|
||||
|
||||
// Forgetting removes the key rather than resetting its fields, so the
|
||||
// next notification writes a fresh default rule and the row returns.
|
||||
function forgetting(): string {
|
||||
root.reset();
|
||||
const seed = root.notification(12, "org.forget.App.desktop", "Forget App");
|
||||
Notifs.handleNotification(seed);
|
||||
const appId = Notifs.notificationAppId(seed);
|
||||
|
||||
Notifs.setAppRule(appId, { sound: false, enabled: false });
|
||||
const before = root.ruleKeys().indexOf(appId) >= 0;
|
||||
Notifs.forgetApp(appId);
|
||||
const after = root.ruleKeys().indexOf(appId) >= 0;
|
||||
// Forgetting something with no rule is a no-op, not a write.
|
||||
const forgotUnknown = Notifs.forgetApp("org.never.Seen.desktop");
|
||||
|
||||
root.resetNotifications();
|
||||
Notifs.handleNotification(
|
||||
root.notification(13, "org.forget.App.desktop", "Forget App"));
|
||||
|
||||
return JSON.stringify({
|
||||
before: before,
|
||||
after: after,
|
||||
returned: root.ruleKeys().indexOf(appId) >= 0,
|
||||
soundAfterReturn: Notifs.appRule(appId).sound,
|
||||
forgotUnknown: forgotUnknown
|
||||
});
|
||||
}
|
||||
|
||||
// The override is one answer shared by the bell, the banner duration,
|
||||
// the breakthrough gate and the card. Only the duration is observable
|
||||
// from here, and it is the one that would silently keep the old value.
|
||||
function urgency(): string {
|
||||
root.reset();
|
||||
const n = root.notification(14, "org.urgent.App.desktop", "Urgent App");
|
||||
Notifs.handleNotification(n);
|
||||
const appId = Notifs.notificationAppId(n);
|
||||
|
||||
const claimed = Notifs.effectiveUrgency(n) === NotificationUrgency.Normal;
|
||||
const normalTimeout = Notifs.notificationTimeoutMs(n) === Settings.notificationTimeoutMs;
|
||||
|
||||
Notifs.setAppRule(appId, { urgency: "critical" });
|
||||
const escalated = Notifs.effectiveUrgency(n) === NotificationUrgency.Critical;
|
||||
const escalatedTimeout =
|
||||
Notifs.notificationTimeoutMs(n) === Settings.notificationTimeoutCriticalMs;
|
||||
|
||||
Notifs.setAppRule(appId, { urgency: "low" });
|
||||
const lowered = Notifs.effectiveUrgency(n) === NotificationUrgency.Low;
|
||||
|
||||
return JSON.stringify({
|
||||
claimed: claimed,
|
||||
escalated: escalated,
|
||||
lowered: lowered,
|
||||
normalTimeout: normalTimeout,
|
||||
escalatedTimeout: escalatedTimeout
|
||||
});
|
||||
}
|
||||
|
||||
// Do Not Disturb with its second exception. Off, everything is held;
|
||||
// on, exactly the effective-critical notifications get through -- and
|
||||
// an application told to be treated as critical is one of them.
|
||||
function breakthrough(): string {
|
||||
root.reset();
|
||||
const seed = root.notification(15, "org.critical.App.desktop", "Critical App");
|
||||
Notifs.handleNotification(seed);
|
||||
const appId = Notifs.notificationAppId(seed);
|
||||
|
||||
function attempt(idValue, claimedUrgency) {
|
||||
root.resetNotifications();
|
||||
Notifs.doNotDisturb = true;
|
||||
const n = root.notification(idValue, "org.critical.App.desktop", "Critical App");
|
||||
n.urgency = claimedUrgency;
|
||||
Notifs.handleNotification(n);
|
||||
return Notifs.popups.length;
|
||||
}
|
||||
|
||||
DesktopPreferences.set("criticalBreaksThrough", false);
|
||||
const held = attempt(16, NotificationUrgency.Critical);
|
||||
|
||||
DesktopPreferences.set("criticalBreaksThrough", true);
|
||||
const through = attempt(17, NotificationUrgency.Critical);
|
||||
const ordinary = attempt(18, NotificationUrgency.Normal);
|
||||
|
||||
Notifs.setAppRule(appId, { urgency: "critical" });
|
||||
const escalated = attempt(19, NotificationUrgency.Normal);
|
||||
|
||||
DesktopPreferences.set("criticalBreaksThrough", false);
|
||||
Notifs.doNotDisturb = false;
|
||||
return JSON.stringify({
|
||||
held: held,
|
||||
through: through,
|
||||
ordinary: ordinary,
|
||||
escalated: escalated
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user