diff --git a/package.json b/package.json index 994f2bb..688716d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "skate-spots", "private": true, - "version": "1.2.24", + "version": "1.2.25", "type": "module", "homepage": "https://skate-spots.com", "scripts": { diff --git a/src/hooks/useCrewDismissals.ts b/src/hooks/useCrewDismissals.ts index a84367c..dd4a0a6 100644 --- a/src/hooks/useCrewDismissals.ts +++ b/src/hooks/useCrewDismissals.ts @@ -175,7 +175,40 @@ export function useDismissCrewNotification() { if (!user?.pubkey || !nip44) return; // no NIP-44: local-only, no throw try { - const payload = JSON.stringify({ ids: [...next] }); + // Data-safety: this list is a single replaceable event whose encrypted + // content is the WHOLE dismissal set, so we rewrite all of it. If the + // read query hasn't resolved yet (fresh device: cache empty, local + // mirror empty), `next` would be just this one id and we'd overwrite — + // and thus WIPE — the previously-synced dismissals from other devices. + // So merge in the freshest remote set before publishing. (Low-stakes + // data — worst case a handled invite re-appears — but we keep the + // fetch-before-write pattern consistent with Stash/Contacts.) + const merged = new Set(next); + try { + const events = await nostr.query( + [{ kinds: [DISMISS_KIND], authors: [user.pubkey], '#d': [DISMISS_D], limit: 1 }], + { signal: AbortSignal.timeout(6000) }, + ); + const remote = events[0]; + if (remote?.content) { + const decrypted = await nip44.decrypt(user.pubkey, remote.content); + const parsed = JSON.parse(decrypted) as { ids?: unknown }; + if (Array.isArray(parsed.ids)) { + for (const rid of parsed.ids) { + if (typeof rid === 'string') merged.add(rid); + } + } + } + } catch { + // Couldn't read the remote set — publish the local union anyway; a + // dismissal is additive, so at worst we under-merge, never wipe silently. + } + + // Keep local mirror + cache aligned with what we're about to publish. + writeLocal([...merged]); + queryClient.setQueryData(key, merged); + + const payload = JSON.stringify({ ids: [...merged] }); const content = await nip44.encrypt(user.pubkey, payload); const event = await user.signer.signEvent({ kind: DISMISS_KIND,