aboutsummaryrefslogtreecommitdiffstats
path: root/src/features
diff options
context:
space:
mode:
Diffstat (limited to 'src/features')
-rw-r--r--src/features/messages.js36
1 files changed, 36 insertions, 0 deletions
diff --git a/src/features/messages.js b/src/features/messages.js
index 70886d3..5aa8dfe 100644
--- a/src/features/messages.js
+++ b/src/features/messages.js
@@ -23,6 +23,7 @@ import {
communityId, parseInviteLink, makeInviteLink, makeInviteBundleEvent, openInviteBundle,
} from '../concord.js';
import { makeDM, unwrapDM, wrapDM } from '../dm.js';
+import { saveInbox } from '../dm-inbox.js';
import { makeSearcher, resultRows, fallbackAvatar } from '../recipient-search.js';
import { hexToBytes, bytesToHex } from '@noble/hashes/utils';
import { t } from '../i18n.js';
@@ -607,6 +608,37 @@ export function messagesFeature(ctx) {
}
}
+ // Hand the service worker what it needs to tell a friend's DM from a
+ // stranger's: our key (only if we actually hold one), who we follow, and the
+ // names we already know. See dm-inbox.js for the threat model — a remote
+ // signer stores no key here and simply gets unfiltered notifications.
+ let inboxAt = 0;
+ async function syncInbox({ force = false } = {}) {
+ if (!force && Date.now() - inboxAt < 5 * 60_000) return;
+ inboxAt = Date.now();
+ try {
+ const id = await identity();
+ if (!id) return;
+ const evs = await queryOn([...new Set([...PROFILE_RELAYS, ...DM_RELAYS])],
+ { kinds: [3], authors: [id.pubkey] }, 4000);
+ const newest = evs.sort((a, b) => b.created_at - a.created_at)[0];
+ const follows = newest
+ ? [...new Set(newest.tags.filter((x) => x[0] === 'p' && /^[0-9a-f]{64}$/.test(x[1] || '')).map((x) => x[1]))]
+ : [];
+ const known = [...threads.keys()];
+ const names = {};
+ for (const pk of [...follows, ...known]) {
+ const p = profiles.get(pk);
+ if (p && p.name) names[pk] = p.name;
+ }
+ await saveInbox(wallet._cacheKey(), {
+ pubkey: id.pubkey,
+ sk: id.signer instanceof Uint8Array ? bytesToHex(id.signer) : null,
+ follows, known, names, hasList: !!newest, updated: Date.now(),
+ });
+ } catch { inboxAt = 0; }
+ }
+
async function registerPush({ interactive = false } = {}) {
try {
if (typeof Notification === 'undefined' || !('serviceWorker' in navigator) || !('PushManager' in window)) return false;
@@ -1205,6 +1237,9 @@ export function messagesFeature(ctx) {
function homeView() {
startDMs();
+ // Threads you've since replied to should stop being strangers to the
+ // worker; throttled inside, so this is cheap on every render.
+ syncInbox().catch(() => {});
for (const jm of communities()) ensureRoom(jm);
const dmRows = [...threads.entries()]
@@ -1639,6 +1674,7 @@ export function messagesFeature(ctx) {
// header's unread dot can't report a room nobody is listening to.
for (const jm of communities()) { try { ensureRoom(jm); } catch {} }
syncLists().catch(() => {});
+ syncInbox().catch(() => {});
registerPush().catch(() => {}); // silent refresh when permission already granted
},
stop() {