From 1da40523fc00b9963012abe067a6c4910c7578b6 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Thu, 1 Oct 2026 12:35:15 -0700 Subject: Zaps and reactions get their own notification switches. The notifier now watches kind-7 reactions p-tagged at the user (opt-in by an explicit flag, so older apps that can't word them stay quiet) and sends zap receipts as 'zap' to apps that report the category, 'payment' to older ones. Coinos zap receipts carry their amount into the push. The service worker names the reactor when it knows them and stays quiet for a muted one. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01VP5Ljqk7EYcm5QQAbzDwog --- src/sw-nwc.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'src/sw-nwc.js') diff --git a/src/sw-nwc.js b/src/sw-nwc.js index c4a52e0..ddcdbb5 100644 --- a/src/sw-nwc.js +++ b/src/sw-nwc.js @@ -23,7 +23,7 @@ self.addEventListener('push', (e) => { // Money landing is exactly when a forwarding rule wants to run, and this // wake-up is the only moment the worker gets. Failures are recorded in // the mirror; the notification still goes out either way. - if (data.reason === 'payment') { + if (data.reason === 'payment' || data.reason === 'zap') { try { await bgAutoWithdraw({ log: (m) => console.log('[sw-aw]', m) }); } catch (err) { console.warn('[sw-aw] failed:', err && err.message); } } @@ -40,19 +40,30 @@ self.addEventListener('push', (e) => { if (verdict && verdict.name) dmName = verdict.name; } catch (err) { console.warn('[sw-dm] could not classify:', err && err.message); } } + // A reaction names who, when this device knows them, and is dropped + // when they are muted here — the notifier can't know either. + let who = null; + if (data.reason === 'reaction' && data.from) { + const boxes = await allInboxes().catch(() => []); + if (boxes.some((b) => (b.muted || []).includes(data.from))) return; + who = boxes.map((b) => b.names && b.names[data.from]).find(Boolean) || null; + } + const emoji = !data.emoji || data.emoji === '+' ? '\u2764\ufe0f' : /^:[\w-]+:$/.test(data.emoji) ? data.emoji : data.emoji.slice(0, 8); const T = { payment: ['Payment received', data.amountSat ? `+${Number(data.amountSat).toLocaleString()} sats. Open coinos to see it.` : 'Open coinos to see it.'], dm: dmName ? [dmName, 'sent you a message.'] : ['New message', 'You have a new private message.'], chat: ['New chat activity', 'There are new messages in your communities.'], mention: [data.reply ? 'New reply to your post' : 'You were mentioned', data.text || 'Open coinos to see it.'], + zap: ['Zap received', data.amountSat ? `+${Number(data.amountSat).toLocaleString()} sats. Open coinos to see it.` : 'Someone zapped you. Open coinos to see it.'], + reaction: [who ? `${who} reacted ${emoji}` : `New reaction ${emoji}`, 'to your post. Open coinos to see it.'], }; const [title, body] = T[data.reason] || T.chat; // a reply lands you in the Notifications list, where it is - const target = data.reason === 'mention' ? { url: './?open=notifs', view: 'notifs' } : { url: './' }; + const target = data.reason === 'mention' || data.reason === 'reaction' ? { url: './?open=notifs', view: 'notifs' } : { url: './' }; await self.registration.showNotification(title, { body, icon: 'icon-192.png', badge: 'badge-96.png', - tag: 'notify-' + (data.reason || 'chat'), renotify: data.reason === 'payment' || data.reason === 'mention', + tag: 'notify-' + (data.reason || 'chat'), renotify: ['payment', 'zap', 'mention'].includes(data.reason), data: target, }); })()); -- cgit v1.2.3