aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-10-01 12:35:15 -0700
committerAdam Soltys <[email protected]>2026-10-01 12:35:15 -0700
commit1da40523fc00b9963012abe067a6c4910c7578b6 (patch)
tree396b8fc8a66989fc56e61ad1370011b40cf93027 /src
parent509d632595399bc355fcf3826875214dc9d3b68c (diff)
downloadcoinosv3-1da40523fc00b9963012abe067a6c4910c7578b6.tar.gz
coinosv3-1da40523fc00b9963012abe067a6c4910c7578b6.zip
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 <[email protected]> Claude-Session: https://claude.ai/code/session_01VP5Ljqk7EYcm5QQAbzDwog
Diffstat (limited to 'src')
-rw-r--r--src/features/messages.js5
-rw-r--r--src/i18n.js2
-rw-r--r--src/locales/ar.json2
-rw-r--r--src/locales/bn.json2
-rw-r--r--src/locales/de.json2
-rw-r--r--src/locales/es.json2
-rw-r--r--src/locales/fr.json2
-rw-r--r--src/locales/hi.json2
-rw-r--r--src/locales/ja.json2
-rw-r--r--src/locales/ko.json2
-rw-r--r--src/locales/pt.json2
-rw-r--r--src/locales/ru.json2
-rw-r--r--src/locales/tr.json2
-rw-r--r--src/locales/zh.json2
-rw-r--r--src/sw-nwc.js17
15 files changed, 44 insertions, 4 deletions
diff --git a/src/features/messages.js b/src/features/messages.js
index 89e9425..4fcff22 100644
--- a/src/features/messages.js
+++ b/src/features/messages.js
@@ -2016,7 +2016,8 @@ export function messagesFeature(ctx) {
// per-category opt-outs travel with the registration so the notifier
// never sends what the user turned off (a suppressed-but-delivered push
// would earn Chrome's generic "updated in background" nag instead)
- const reasons = { payment: s.reasons?.payment !== false, dm: s.reasons?.dm !== false, mention: s.reasons?.mention !== false };
+ const want = (k) => s.reasons?.[k] !== false;
+ const reasons = { payment: want('payment'), zap: want('zap'), dm: want('dm'), mention: want('mention'), reaction: want('reaction') };
// Stamp the network: the same seed makes the same nostr pubkey on mainnet
// AND staging, so without this the notifier fans a mainnet payment push
// out to the staging PWA's subscription too — and tapping it opened
@@ -2076,8 +2077,10 @@ export function messagesFeature(ctx) {
} }, t('notifEnable'))
: h('div', { class: 'small faint' }, t('notifOnDevice')),
rowT(t('notifPayRecv'), 'payment'),
+ rowT(t('notifZap'), 'zap'),
rowT(t('notifDm'), 'dm'),
rowT(t('notifMention'), 'mention'),
+ rowT(t('notifReaction'), 'reaction'),
h('div', { class: 'small faint' }, t('notifChatHint')));
}
diff --git a/src/i18n.js b/src/i18n.js
index 02584b2..951bc8d 100644
--- a/src/i18n.js
+++ b/src/i18n.js
@@ -1208,6 +1208,8 @@ const STR = {
notifPayRecv: 'Payments received',
notifDm: 'Direct messages',
notifMention: 'Replies and mentions on nostr',
+ notifZap: 'Zaps on your posts',
+ notifReaction: 'Reactions to your posts',
notifChatHint: 'Community chat is opt-in per room. Turn it on from a room’s menu.',
notifPaySent: 'Payments sent by connected apps',
notifPaySentHint: 'A heads-up when a connected app (a nostr client zapping, for example) spends from this wallet while it’s closed.',
diff --git a/src/locales/ar.json b/src/locales/ar.json
index 2e914c3..3305722 100644
--- a/src/locales/ar.json
+++ b/src/locales/ar.json
@@ -780,6 +780,8 @@
"notifEnabled": "الإخطارات مكنت",
"notifOnDevice": "الإخطارات على هذا الجهاز.",
"notifPayRecv": "المدفوعات المقبوضة",
+ "notifZap": "زابات على منشوراتك",
+ "notifReaction": "التفاعلات على منشوراتك",
"notifDm": "رسائل مباشرة",
"notifChatHint": "والحديث المجتمعي هو اختيار كل غرفة - شغله من قائمة غرفة.",
"notifPaySent": "المدفوعات المرسلة بواسطة تطبيقات متصلة",
diff --git a/src/locales/bn.json b/src/locales/bn.json
index 44b2700..60ac9ad 100644
--- a/src/locales/bn.json
+++ b/src/locales/bn.json
@@ -780,6 +780,8 @@
"notifEnabled": "সক্রিয়",
"notifOnDevice": "এই ডিভাইসের জন্য সূচনাবার্তা উপস্থিত রয়েছে।",
"notifPayRecv": "মূল্য প্রদান করা হয়েছে",
+ "notifZap": "আপনার পোস্টে জ্যাপ",
+ "notifReaction": "আপনার পোস্টে প্রতিক্রিয়া",
"notifDm": "সরাসরি বার্তা",
"notifChatHint": "কমিউনিটি চ্যাট রুম থেকে শুরু করে রুম পর্যন্ত চলে।",
"notifPaySent": "সংযুক্ত অ্যাপসের মাধ্যমে প্রদান করা হয়",
diff --git a/src/locales/de.json b/src/locales/de.json
index bf827b4..eb11d94 100644
--- a/src/locales/de.json
+++ b/src/locales/de.json
@@ -780,6 +780,8 @@
"notifEnabled": "Benachrichtigungen aktiviert",
"notifOnDevice": "Benachrichtigungen sind für dieses Gerät.",
"notifPayRecv": "Zahlungen",
+ "notifZap": "Zaps auf deine Beiträge",
+ "notifReaction": "Reaktionen auf deine Beiträge",
"notifDm": "Direkte Nachrichten",
"notifChatHint": "Community-Chat ist opt-in pro Zimmer drehen Sie es von einem Raum-Menü.",
"notifPaySent": "Zahlungen durch verbundene Apps",
diff --git a/src/locales/es.json b/src/locales/es.json
index 52e417b..b8e77d5 100644
--- a/src/locales/es.json
+++ b/src/locales/es.json
@@ -780,6 +780,8 @@
"notifEnabled": "Permitidas notificaciones",
"notifOnDevice": "Las notificaciones están en este dispositivo.",
"notifPayRecv": "Pagos recibidos",
+ "notifZap": "Zaps en tus publicaciones",
+ "notifReaction": "Reacciones a tus publicaciones",
"notifDm": "Mensajes directos",
"notifChatHint": "El chat comunitario es opt-in por habitación, enciende desde el menú de la habitación.",
"notifPaySent": "Pagos enviados por aplicaciones conectadas",
diff --git a/src/locales/fr.json b/src/locales/fr.json
index 471eb5b..1cd6e10 100644
--- a/src/locales/fr.json
+++ b/src/locales/fr.json
@@ -780,6 +780,8 @@
"notifEnabled": "Notifications activées",
"notifOnDevice": "Les notifications sont en cours pour cet appareil.",
"notifPayRecv": "Paiements reçus",
+ "notifZap": "Zaps sur vos publications",
+ "notifReaction": "Réactions à vos publications",
"notifDm": "Messages directs",
"notifChatHint": "Le chat communautaire est opt-in par chambre, allumez-le à partir d'un menu de chambre.",
"notifPaySent": "Paiements envoyés par des applications connectées",
diff --git a/src/locales/hi.json b/src/locales/hi.json
index 4fcd47d..c6d133e 100644
--- a/src/locales/hi.json
+++ b/src/locales/hi.json
@@ -780,6 +780,8 @@
"notifEnabled": "अधिसूचना सक्षम",
"notifOnDevice": "इस उपकरण के लिए अधिसूचनाएं हैं।",
"notifPayRecv": "प्राप्त भुगतान",
+ "notifZap": "आपकी पोस्ट पर ज़ैप",
+ "notifReaction": "आपकी पोस्ट पर प्रतिक्रियाएँ",
"notifDm": "प्रत्यक्ष संदेश",
"notifChatHint": "सामुदायिक चैट प्रति कमरे में ऑप्ट-इन है - इसे कमरे के मेनू से बदल दें।",
"notifPaySent": "कनेक्टेड ऐप्स द्वारा भेजे गए भुगतान",
diff --git a/src/locales/ja.json b/src/locales/ja.json
index 2ab8916..e71625b 100644
--- a/src/locales/ja.json
+++ b/src/locales/ja.json
@@ -780,6 +780,8 @@
"notifEnabled": "通知の有効化",
"notifOnDevice": "通知は、このデバイスで行われます。",
"notifPayRecv": "お支払いについて",
+ "notifZap": "投稿へのZap",
+ "notifReaction": "投稿へのリアクション",
"notifDm": "ダイレクトメッセージ",
"notifChatHint": "コミュニティチャットは、部屋のメニューから選択されます。",
"notifPaySent": "接続されたアプリで送信された支払い",
diff --git a/src/locales/ko.json b/src/locales/ko.json
index e125415..99862ef 100644
--- a/src/locales/ko.json
+++ b/src/locales/ko.json
@@ -779,6 +779,8 @@
"notifEnabled": "자주 묻는 질문",
"notifOnDevice": "알림은이 기기에 있습니다.",
"notifPayRecv": "지불받은",
+ "notifZap": "내 글에 받은 잽",
+ "notifReaction": "내 글에 달린 반응",
"notifDm": "직접 메시지",
"notifChatHint": "커뮤니티 채팅은 방당 선택 - 방 메뉴에서 켜집니다.",
"notifPaySent": "연결된 앱으로 전송되는 결제",
diff --git a/src/locales/pt.json b/src/locales/pt.json
index 9f5b2ad..b49b9d2 100644
--- a/src/locales/pt.json
+++ b/src/locales/pt.json
@@ -780,6 +780,8 @@
"notifEnabled": "Notificações habilitadas",
"notifOnDevice": "As notificações estão ligadas para este dispositivo.",
"notifPayRecv": "Pagamentos recebidos",
+ "notifZap": "Zaps nas suas publicações",
+ "notifReaction": "Reações às suas publicações",
"notifDm": "Mensagens diretas",
"notifChatHint": "O chat comunitário é opt-in por quarto – ligue-o do menu de um quarto.",
"notifPaySent": "Pagamentos enviados por aplicativos conectados",
diff --git a/src/locales/ru.json b/src/locales/ru.json
index 4e472fc..720de70 100644
--- a/src/locales/ru.json
+++ b/src/locales/ru.json
@@ -780,6 +780,8 @@
"notifEnabled": "Уведомления разрешены",
"notifOnDevice": "Уведомления для этого устройства есть.",
"notifPayRecv": "Полученные платежи",
+ "notifZap": "Запы на ваши посты",
+ "notifReaction": "Реакции на ваши посты",
"notifDm": "Прямые сообщения",
"notifChatHint": "Общественный чат включается в каждой комнате, включите его из меню комнаты.",
"notifPaySent": "Платежи, отправленные подключенными приложениями",
diff --git a/src/locales/tr.json b/src/locales/tr.json
index e1e9928..55c0c9c 100644
--- a/src/locales/tr.json
+++ b/src/locales/tr.json
@@ -779,6 +779,8 @@
"notifEnabled": "Bildirimler etkinleştirildi",
"notifOnDevice": "Bildirimler bu cihaz için.",
"notifPayRecv": "Ödemeler alındı",
+ "notifZap": "Gönderilerinize gelen zap'ler",
+ "notifReaction": "Gönderilerinize gelen tepkiler",
"notifDm": "Doğrudan mesajlar",
"notifChatHint": "Community chat oda başına opt-in - onu bir odadan menüsünden döndürür.",
"notifPaySent": "Bağlantılı uygulamalar tarafından gönderilen ödemeler",
diff --git a/src/locales/zh.json b/src/locales/zh.json
index 6bb4c50..3c9335c 100644
--- a/src/locales/zh.json
+++ b/src/locales/zh.json
@@ -780,6 +780,8 @@
"notifEnabled": "已启用通知",
"notifOnDevice": "这个装置的通知已经发出",
"notifPayRecv": "实收付款",
+ "notifZap": "帖子收到的 Zap",
+ "notifReaction": "帖子收到的回应",
"notifDm": "直接消息",
"notifChatHint": "每个房间都选择社区聊天,",
"notifPaySent": "由连接的应用程序发送付款",
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,
});
})());