From 2983831de033cbaf0db0d39fe132e6929d077729 Mon Sep 17 00:00:00 2001 From: Roscoe Rubin-Rottenberg Date: Sun, 12 Apr 2026 13:35:14 -0400 Subject: [PATCH] feat: reply notification root uris --- data-plane/indexing/plugins/reply.ts | 4 ++++ data-plane/indexing/processor.ts | 2 ++ utils/push.ts | 26 +++++++++++++++++++++++--- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/data-plane/indexing/plugins/reply.ts b/data-plane/indexing/plugins/reply.ts index fdac905..70451cc 100644 --- a/data-plane/indexing/plugins/reply.ts +++ b/data-plane/indexing/plugins/reply.ts @@ -167,6 +167,7 @@ const notifsForInsert = (obj: IndexedReply) => { recordCid: string; sortAt: string; reasonSubject?: string; + threadRootUri?: string; }> = []; const notified = new Set([obj.reply.authorDid]); const maybeNotify = (notif: { @@ -177,6 +178,7 @@ const notifsForInsert = (obj: IndexedReply) => { recordCid: string; sortAt: string; reasonSubject?: string; + threadRootUri?: string; }) => { if (!notified.has(notif.did)) { notified.add(notif.did); @@ -211,6 +213,7 @@ const notifsForInsert = (obj: IndexedReply) => { recordUri: obj.reply.uri, recordCid: obj.reply.cid, sortAt: obj.reply.createdAt, + threadRootUri: obj.reply.reply?.root.uri, }); // found hidden reply, don't notify any higher ancestors if (threadgateHiddenReplies.includes(ancestorUri.toString())) break; @@ -232,6 +235,7 @@ const notifsForInsert = (obj: IndexedReply) => { recordUri: descendent.uri, recordCid: descendent.cid, sortAt: descendent.sortAt, + threadRootUri: obj.reply.reply?.root.uri, }); } } diff --git a/data-plane/indexing/processor.ts b/data-plane/indexing/processor.ts index d0d466e..58eabb0 100644 --- a/data-plane/indexing/processor.ts +++ b/data-plane/indexing/processor.ts @@ -41,6 +41,7 @@ type Notif = { recordCid: string; sortAt: string; reasonSubject?: string; + threadRootUri?: string; }; export class RecordProcessor { @@ -374,6 +375,7 @@ export class RecordProcessor { author: notif.author, recordUri: notif.recordUri, reasonSubject: notif.reasonSubject, + threadRootUri: notif.threadRootUri, }); }); } diff --git a/utils/push.ts b/utils/push.ts index f9856ea..4020ea3 100644 --- a/utils/push.ts +++ b/utils/push.ts @@ -8,6 +8,7 @@ export interface PushPayload { author: string; recordUri: string; reasonSubject?: string; + threadRootUri?: string; } export interface PushConfig { @@ -254,13 +255,15 @@ export class PushService { recordUri: payload.recordUri, ...(payload.reasonSubject && { reasonSubject: payload.reasonSubject }), + ...(payload.threadRootUri && + { threadRootUri: payload.threadRootUri }), }, }, }; // Add platform-specific options if (token.platform === "ios") { - const threadId = this.getThreadId(payload); + const threadId = this.getIosThreadId(payload); message.message.apns = { headers: { "apns-priority": "10", @@ -274,7 +277,7 @@ export class PushService { }, }; } else if (token.platform === "android") { - const threadId = this.getThreadId(payload); + const threadId = this.getAndroidTag(payload); message.message.android = { priority: "high", notification: { @@ -510,10 +513,27 @@ export class PushService { return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); } - private getThreadId(payload: PushPayload): string { + private getIosThreadId(payload: PushPayload): string { if (payload.reason === "follow") { return "follows"; } + if (payload.reason === "reply") { + return payload.threadRootUri ?? payload.reasonSubject ?? + payload.recordUri; + } + if (payload.reasonSubject) { + return payload.reasonSubject; + } + return payload.recordUri; + } + + private getAndroidTag(payload: PushPayload): string { + if (payload.reason === "follow") { + return "follows"; + } + if (payload.reason === "reply") { + return payload.reasonSubject ?? payload.recordUri; + } if (payload.reasonSubject) { return payload.reasonSubject; } -- 2.51.2