diff --git a/desktop/src/app/useLiveHomeFeedActions.test.mjs b/desktop/src/app/useLiveHomeFeedActions.test.mjs new file mode 100644 index 00000000000..4022faca0be --- /dev/null +++ b/desktop/src/app/useLiveHomeFeedActions.test.mjs @@ -0,0 +1,29 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { buildHomeFeedLivePTagFilter } from "./useLiveHomeFeedActions.ts"; +import { + KIND_APPROVAL_REQUEST, + KIND_REMINDER, + KIND_TEXT_NOTE, +} from "../shared/constants/kinds.ts"; + +test("live p-tag filter covers Pulse mentions", () => { + // Regression: the home feed poll pauses while the window is unfocused, so + // Pulse (kind 1) mentions notify in the background only if this always-on + // subscription delivers them. + const filter = buildHomeFeedLivePTagFilter("abc123", 1_000); + assert.ok(filter.kinds.includes(KIND_TEXT_NOTE)); +}); + +test("live p-tag filter keeps approval and reminder coverage", () => { + const filter = buildHomeFeedLivePTagFilter("abc123", 1_000); + assert.ok(filter.kinds.includes(KIND_APPROVAL_REQUEST)); + assert.ok(filter.kinds.includes(KIND_REMINDER)); +}); + +test("live p-tag filter targets the user and is live-only", () => { + const filter = buildHomeFeedLivePTagFilter("abc123", 1_234); + assert.deepEqual(filter["#p"], ["abc123"]); + assert.equal(filter.since, 1_234); +}); diff --git a/desktop/src/app/useLiveHomeFeedActions.ts b/desktop/src/app/useLiveHomeFeedActions.ts index 6829100fa54..4e810648a8f 100644 --- a/desktop/src/app/useLiveHomeFeedActions.ts +++ b/desktop/src/app/useLiveHomeFeedActions.ts @@ -7,9 +7,30 @@ import { KIND_APPROVAL_REQUEST, KIND_EVENT_REMINDER, KIND_REMINDER, + KIND_TEXT_NOTE, } from "@/shared/constants/kinds"; -const HOME_FEED_ACTION_KINDS = [KIND_APPROVAL_REQUEST, KIND_REMINDER] as const; +/** + * Kinds that must trigger a home-feed refresh when they arrive p-tagging the + * user. The home feed poll pauses while the window is unfocused, so any + * feed-driven alert — Pulse mentions (kind 1), approval requests, reminders — + * only reaches the notification pipeline in the background through this + * always-on subscription. + */ +export const HOME_FEED_LIVE_P_TAG_KINDS = [ + KIND_TEXT_NOTE, + KIND_APPROVAL_REQUEST, + KIND_REMINDER, +] as const; + +export function buildHomeFeedLivePTagFilter(pubkey: string, since: number) { + return { + kinds: [...HOME_FEED_LIVE_P_TAG_KINDS], + "#p": [pubkey], + limit: 50, + since, + }; +} const LIVE_HOME_FEED_RETRY_BASE_MS = 1_000; const LIVE_HOME_FEED_RETRY_MAX_MS = 30_000; @@ -64,12 +85,7 @@ export function useLiveHomeFeedActions( void Promise.allSettled([ relayClient.subscribeLive( - { - kinds: [...HOME_FEED_ACTION_KINDS], - "#p": [normalizedPubkey], - limit: 50, - since, - }, + buildHomeFeedLivePTagFilter(normalizedPubkey, since), handleLiveHomeFeedEvent, ), relayClient.subscribeLive(