feat(frontend): watches, webhooks and alerts panels read humanly, with chat picker, url filter, test send and delivery log
This commit is contained in:
1 parent
6a4fd6ad47
commit
ea3a7c0a61
19 files changed
+3699
-458
No files matched your search
@@ -0,0 +1,120 @@
|
||||
import {
|
||||
type AlertItem,
|
||||
type LiveAlertEvent,
|
||||
unseenAlerts,
|
||||
} from "#lib/api/watches.js";
|
||||
import { alertLine, alertTitle } from "#lib/format/watches.js";
|
||||
import { events } from "#lib/stores/events.svelte.js";
|
||||
import { browser } from "$app/env";
|
||||
import { goto } from "$app/navigation";
|
||||
|
||||
const NOTIFY_KEY = "bg.alerts.notify";
|
||||
|
||||
export const ALERTS_PARAM = "alerts";
|
||||
|
||||
type Listener = (alert: AlertItem) => void;
|
||||
|
||||
function notifySupported(): boolean {
|
||||
return browser && "Notification" in window;
|
||||
}
|
||||
|
||||
function createAlerts() {
|
||||
const listeners = new Set<Listener>();
|
||||
let unseen = $state(0);
|
||||
let notify = $state(
|
||||
notifySupported() &&
|
||||
localStorage.getItem(NOTIFY_KEY) === "1" &&
|
||||
Notification.permission === "granted"
|
||||
);
|
||||
let account: number | null = null;
|
||||
|
||||
async function refresh() {
|
||||
if (account === null) {
|
||||
return;
|
||||
}
|
||||
const current = account;
|
||||
const count = await unseenAlerts().catch(() => null);
|
||||
if (count !== null && current === account) {
|
||||
unseen = count;
|
||||
}
|
||||
}
|
||||
|
||||
function popUp(alert: AlertItem) {
|
||||
if (!(notify && document.hidden)) {
|
||||
return;
|
||||
}
|
||||
const popup = new Notification(alertTitle(alert), {
|
||||
body: alertLine(alert),
|
||||
tag: `bg-alert-${alert.id}`,
|
||||
});
|
||||
popup.onclick = () => {
|
||||
window.focus();
|
||||
goto(alert.path);
|
||||
popup.close();
|
||||
};
|
||||
}
|
||||
|
||||
function apply(event: LiveAlertEvent) {
|
||||
({ unseen } = event);
|
||||
if (event.alert === null) {
|
||||
return;
|
||||
}
|
||||
for (const listener of listeners) {
|
||||
listener(event.alert);
|
||||
}
|
||||
popUp(event.alert);
|
||||
}
|
||||
|
||||
events.subscribe((event) => {
|
||||
if (event.type === "alert") {
|
||||
apply(event);
|
||||
}
|
||||
});
|
||||
events.onReconnect(() => {
|
||||
refresh();
|
||||
});
|
||||
|
||||
return {
|
||||
get notify(): boolean {
|
||||
return notify;
|
||||
},
|
||||
get notifySupported(): boolean {
|
||||
return notifySupported();
|
||||
},
|
||||
onAlert(listener: Listener): () => void {
|
||||
listeners.add(listener);
|
||||
return () => listeners.delete(listener);
|
||||
},
|
||||
read(count = 1) {
|
||||
unseen = Math.max(0, unseen - count);
|
||||
},
|
||||
readAll() {
|
||||
unseen = 0;
|
||||
},
|
||||
async setNotify(on: boolean): Promise<boolean> {
|
||||
if (!notifySupported()) {
|
||||
return false;
|
||||
}
|
||||
const allowed =
|
||||
on &&
|
||||
(Notification.permission === "granted" ||
|
||||
(await Notification.requestPermission()) === "granted");
|
||||
notify = allowed;
|
||||
localStorage.setItem(NOTIFY_KEY, allowed ? "1" : "0");
|
||||
return allowed === on;
|
||||
},
|
||||
track(accountId: number | null) {
|
||||
if (accountId === account) {
|
||||
return;
|
||||
}
|
||||
account = accountId;
|
||||
unseen = 0;
|
||||
refresh();
|
||||
},
|
||||
get unseen(): number {
|
||||
return unseen;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const alerts = createAlerts();
|
||||
@@ -13,6 +13,7 @@ export type RightPanel =
|
||||
| "stories-all"
|
||||
| "policy"
|
||||
| "watches"
|
||||
| "webhooks"
|
||||
| "alerts"
|
||||
| "shares"
|
||||
| "bots"
|
||||
|
||||
Reference in new issue
Block a user