feat(push): extend categories to crew + social; master switch tears down push
- CATEGORY_BY_KIND now maps crew invite (36229) / join-request (31969) -> crew and zap (9735) / reply-mention (1) / comment (1111) -> social; the relay watch includes those kinds. Reactions/reposts are deliberately not pushed (noisy) and newsletter has no push channel. - Master switch off now also disables web push and the native poller. - Verified: a crew-only token is not woken by a zap but is by a crew invite.
This commit is contained in:
parent
4df961b015
commit
9155949fda
2 changed files with 46 additions and 5 deletions
|
|
@ -67,8 +67,9 @@ interface StoredSubscription {
|
|||
}
|
||||
|
||||
/**
|
||||
* Event kind → notification category, mirroring the app's `NotificationCategory`
|
||||
* (only `dms` and `trades` are pushed — social/crew/newsletter are in-app only).
|
||||
* Event kind → notification category, mirroring the app's `NotificationCategory`.
|
||||
* `newsletter` has no push channel (readers poll it); reactions/reposts are
|
||||
* deliberately excluded (too noisy).
|
||||
*/
|
||||
const CATEGORY_BY_KIND: Record<number, string> = {
|
||||
1059: 'dms', // NIP-17 gift wrap
|
||||
|
|
@ -76,6 +77,19 @@ const CATEGORY_BY_KIND: Record<number, string> = {
|
|||
31871: 'trades', // trade attestation (sold / bought)
|
||||
31872: 'trades', // confirm-request
|
||||
34967: 'trades', // shipment status
|
||||
36229: 'crew', // crew invitation
|
||||
31969: 'crew', // crew join request
|
||||
9735: 'social', // zap receipt
|
||||
1: 'social', // reply / mention
|
||||
1111: 'social', // comment
|
||||
};
|
||||
|
||||
/** Where a tap should land, per category. */
|
||||
const URL_BY_CATEGORY: Record<string, string> = {
|
||||
trades: '/#/trades',
|
||||
crew: '/#/crews',
|
||||
social: '/#/',
|
||||
dms: '/#/messages',
|
||||
};
|
||||
|
||||
function allows(categories: string[] | undefined, category: string): boolean {
|
||||
|
|
@ -197,7 +211,22 @@ const OFFER_KIND = 30403; // offer / counter / accepted / declined / wit
|
|||
const TRADE_CONFIRM_KIND = 31871; // counterparty recorded the trade
|
||||
const TRADE_REQUEST_KIND = 31872; // "please confirm our trade"
|
||||
const SHIPMENT_STATUS_KIND = 34967; // shipped / received / issue (self-reported)
|
||||
const MARKET_KINDS = [OFFER_KIND, TRADE_CONFIRM_KIND, TRADE_REQUEST_KIND, SHIPMENT_STATUS_KIND];
|
||||
const CREW_INVITE_KIND = 36229; // crew invitation
|
||||
const CREW_JOIN_REQUEST_KIND = 31969; // crew join request
|
||||
const ZAP_RECEIPT_KIND = 9735; // zap
|
||||
const TEXT_NOTE_KIND = 1; // reply / mention
|
||||
const COMMENT_KIND = 1111; // comment
|
||||
const MARKET_KINDS = [
|
||||
OFFER_KIND,
|
||||
TRADE_CONFIRM_KIND,
|
||||
TRADE_REQUEST_KIND,
|
||||
SHIPMENT_STATUS_KIND,
|
||||
CREW_INVITE_KIND,
|
||||
CREW_JOIN_REQUEST_KIND,
|
||||
ZAP_RECEIPT_KIND,
|
||||
TEXT_NOTE_KIND,
|
||||
COMMENT_KIND,
|
||||
];
|
||||
|
||||
function subscribedPubkeys(): string[] {
|
||||
// Web subscribers AND native poll-token holders — we watch for both.
|
||||
|
|
@ -208,6 +237,10 @@ function subscribedPubkeys(): string[] {
|
|||
|
||||
/** Content-free body — no amounts, no item text (that stays out of the push). */
|
||||
function marketBody(ev: NostrEvent): string {
|
||||
if (ev.kind === CREW_INVITE_KIND) return 'invited you to a crew';
|
||||
if (ev.kind === CREW_JOIN_REQUEST_KIND) return 'asked to join your crew';
|
||||
if (ev.kind === ZAP_RECEIPT_KIND) return 'You got a zap ⚡';
|
||||
if (ev.kind === TEXT_NOTE_KIND || ev.kind === COMMENT_KIND) return 'New reply or mention';
|
||||
if (ev.kind === TRADE_REQUEST_KIND) return 'asked you to confirm a trade';
|
||||
if (ev.kind === TRADE_CONFIRM_KIND) return 'confirmed your trade';
|
||||
if (ev.kind === SHIPMENT_STATUS_KIND) {
|
||||
|
|
@ -274,11 +307,16 @@ function handleEvent(ev: NostrEvent): void {
|
|||
// 2) Marketplace events addressed to a subscriber (public — metadata only;
|
||||
// we only ever send a generic body, never the amount or item).
|
||||
if (MARKET_KINDS.includes(ev.kind)) {
|
||||
const category = CATEGORY_BY_KIND[ev.kind] ?? 'trades';
|
||||
const recipients = [...new Set(ev.tags.filter(([n]) => n === 'p').map(([, v]) => v).filter(Boolean))];
|
||||
for (const r of recipients) {
|
||||
if (r === ev.pubkey) continue; // never notify the author about their own message
|
||||
console.log(`[push] 🔔 market ${ev.kind} ${ev.id.slice(0, 12)}… → ${r.slice(0, 12)}…`);
|
||||
void pushTo(r, { title: 'SkateSpots', body: marketBody(ev), wrapId: ev.id, url: '/#/trades' }, 'trades');
|
||||
console.log(`[push] 🔔 ${category} ${ev.kind} ${ev.id.slice(0, 12)}… → ${r.slice(0, 12)}…`);
|
||||
void pushTo(
|
||||
r,
|
||||
{ title: 'SkateSpots', body: marketBody(ev), wrapId: ev.id, url: URL_BY_CATEGORY[category] ?? '/#/' },
|
||||
category,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,6 +70,9 @@ export function NotificationSettingsCard() {
|
|||
const toggleEnabled = (next: boolean) => {
|
||||
if (!next) {
|
||||
setPrefs((prev) => ({ ...prev, enabled: false }));
|
||||
// One kill switch: turning the master off also stops push + the native poller.
|
||||
void push.disable();
|
||||
void background.disable();
|
||||
return;
|
||||
}
|
||||
if (permission === 'granted') {
|
||||
|
|
|
|||
Loading…
Reference in a new issue