feat(notifications): inline reply from a browser notification (#203)
CI / Build & Quality Checks (push) Successful in 1m38s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 7s
CI / Trigger Desktop Build (push) Successful in 9s
CI / Playwright smoke (e2e) (push) Successful in 2m50s
CI / Build & Quality Checks (push) Successful in 1m38s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 7s
CI / Trigger Desktop Build (push) Successful in 9s
CI / Playwright smoke (e2e) (push) Successful in 2m50s
Message notifications shown through the service worker now carry a text-input
'Reply' action (Chrome desktop/Android). On notificationclick with
action==='reply' the SW sends the typed text itself — it already holds the
newest session's access token for authenticated media — as m.room.message
(threaded when the notification was for a thread), so it works with the tab in
the background or closed; a failed send shows a 'Reply not sent' notification
that opens the room. Not offered for encrypted rooms (the SW cannot encrypt).
The sender lives in swReply.ts so it is unit-tested; verified headless that
the SW notification carries actions + {roomId, threadId}.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
/// <reference lib="WebWorker" />
|
||||
|
||||
import { precacheAndRoute, type PrecacheEntry } from 'workbox-precaching';
|
||||
import { sendNotificationReply } from './swReply';
|
||||
|
||||
export type {};
|
||||
declare const self: ServiceWorkerGlobalScope & {
|
||||
@@ -147,6 +148,32 @@ self.addEventListener('notificationclick', (event: NotificationEvent) => {
|
||||
const data = event.notification.data ?? {};
|
||||
const path = typeof data.path === 'string' ? data.path : undefined;
|
||||
|
||||
// [Gitea #203] Reply action: send it and stop — no need to raise the app.
|
||||
if (event.action === 'reply' && typeof data.roomId === 'string') {
|
||||
const text = (event as NotificationEvent & { reply?: string }).reply?.trim();
|
||||
if (text) {
|
||||
event.waitUntil(
|
||||
sendNotificationReply(
|
||||
Array.from(sessions.values()).pop(),
|
||||
data.roomId,
|
||||
data.threadId,
|
||||
text,
|
||||
).then((ok) => {
|
||||
if (!ok) {
|
||||
// Let the user know it didn't go out, and keep their text reachable.
|
||||
return self.registration.showNotification('Reply not sent', {
|
||||
body: `Couldn't send "${text.slice(0, 60)}" — open the app to retry.`,
|
||||
tag: `swreply-fail-${data.roomId}`,
|
||||
data: { path },
|
||||
});
|
||||
}
|
||||
return undefined;
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
event.waitUntil(
|
||||
(async () => {
|
||||
const windowClients = (
|
||||
|
||||
Reference in New Issue
Block a user