From a5506689f726cfebb6188176951a78e65349194e Mon Sep 17 00:00:00 2001 From: nayan9617 Date: Tue, 7 Apr 2026 00:15:39 +0530 Subject: [PATCH 1/6] fix: reset relay sockets on visibility change to prevent auth hangs on sleep/wake When browser tab is hidden (sleep), tear down all relay socket connections completely. This forces fresh socket creation and clean auth handshake on wake, preventing stuck AuthStatus.PendingResponse states. - Add documentVisibility store tracking document.visibilityState - Call Pool.get().remove(url) for relay sockets during hidden teardown - Integrate socket reset in syncUserData, syncSpaces, syncDMs - Add explanatory comments for visibility guards and race conditions Fixes: App stuck in "Authenticating" after Mac sleep/wake cycle Tested with: wss://news.utxo.one/ --- .next/_events_51442.json | 1 + .next/dev/cache/.rscinfo | 1 + .next/dev/cache/turbopack/8d0f77bfa/CURRENT | Bin 0 -> 4 bytes .next/dev/logs/next-development.log | 0 .next/dev/package.json | 3 + src/app/core/state.ts | 18 ++ src/app/core/sync.ts | 213 +++++++++++++++----- 7 files changed, 181 insertions(+), 55 deletions(-) create mode 100644 .next/_events_51442.json create mode 100644 .next/dev/cache/.rscinfo create mode 100644 .next/dev/cache/turbopack/8d0f77bfa/CURRENT create mode 100644 .next/dev/logs/next-development.log create mode 100644 .next/dev/package.json diff --git a/.next/_events_51442.json b/.next/_events_51442.json new file mode 100644 index 00000000..86bc8b31 --- /dev/null +++ b/.next/_events_51442.json @@ -0,0 +1 @@ +[{"eventName":"NEXT_CLI_SESSION_STOPPED","payload":{"nextVersion":"16.2.2","nodeVersion":"v24.7.0","cliCommand":"dev","durationMilliseconds":6291,"turboFlag":true,"pagesDir":false,"appDir":true,"isRspack":false}}] \ No newline at end of file diff --git a/.next/dev/cache/.rscinfo b/.next/dev/cache/.rscinfo new file mode 100644 index 00000000..3ee33f06 --- /dev/null +++ b/.next/dev/cache/.rscinfo @@ -0,0 +1 @@ +{"encryption.key":"mT8zxPCClGbcV2DiRln03XdmvHCQ+orzt0Q2KKaiDd4=","encryption.expire_at":1776709211601} \ No newline at end of file diff --git a/.next/dev/cache/turbopack/8d0f77bfa/CURRENT b/.next/dev/cache/turbopack/8d0f77bfa/CURRENT new file mode 100644 index 0000000000000000000000000000000000000000..593f4708db84ac8fd0f5cc47c634f38c013fe9e4 GIT binary patch literal 4 LcmZQzU|;|M00aO5 literal 0 HcmV?d00001 diff --git a/.next/dev/logs/next-development.log b/.next/dev/logs/next-development.log new file mode 100644 index 00000000..e69de29b diff --git a/.next/dev/package.json b/.next/dev/package.json new file mode 100644 index 00000000..c9a44226 --- /dev/null +++ b/.next/dev/package.json @@ -0,0 +1,3 @@ +{ + "type": "commonjs" +} \ No newline at end of file diff --git a/src/app/core/state.ts b/src/app/core/state.ts index 69672d04..98be1b8c 100644 --- a/src/app/core/state.ts +++ b/src/app/core/state.ts @@ -156,6 +156,24 @@ import {readFeed} from "@lib/feeds" export const fromCsv = (s: string) => (s || "").split(",").filter(identity) +// Keep sync logic informed when the tab sleeps or wakes so subscriptions can be torn down cleanly. +export const documentVisibility = readable( + typeof document !== "undefined" ? document.visibilityState : "visible", + set => { + if (typeof document === "undefined") return + + const onVisibilityChange = () => { + set(document.visibilityState) + } + + document.addEventListener("visibilitychange", onVisibilityChange) + + return () => { + document.removeEventListener("visibilitychange", onVisibilityChange) + } + }, +) + export const ROOM = "h" export const PROTECTED = ["-"] diff --git a/src/app/core/sync.ts b/src/app/core/sync.ts index 131730a9..bb4f1e6e 100644 --- a/src/app/core/sync.ts +++ b/src/app/core/sync.ts @@ -6,6 +6,7 @@ import {PollResponse} from "nostr-tools/kinds" import { getListTags, getRelayTagValues, + type List, WRAP, ROOM_META, ROOM_DELETE, @@ -22,7 +23,7 @@ import { getTagValue, } from "@welshman/util" import type {Filter, TrustedEvent} from "@welshman/util" -import {request, requestOne, Difference, DifferenceEvent} from "@welshman/net" +import {request, requestOne, Difference, DifferenceEvent, Pool} from "@welshman/net" import { pubkey, loadRelay, @@ -45,6 +46,7 @@ import { MESSAGE_KINDS, CONTENT_KINDS, INDEXER_RELAYS, + documentVisibility, loadSettings, loadGroupList, userSpaceUrls, @@ -68,6 +70,10 @@ type SyncOpts = { onEvent?: (event: TrustedEvent) => void } +const resetRelaySocket = (url: string) => { + Pool.get().remove(url) +} + const pullOneWithFallback = async ( url: string, filter: Filter, @@ -86,6 +92,12 @@ const pullOneWithFallback = async ( const shouldFallback = !hasNegentropy(url) || (await new Promise(resolve => { + if (signal.aborted) { + resolve(false) + return + } + + // If visibility flips while the negentropy diff is opening, skip the fallback path and let teardown win. const diff = new Difference({relay: url, filter, events: cachedEvents, signal}) diff.on(DifferenceEvent.Error, () => { @@ -111,9 +123,7 @@ export const pullWithFallback = async ({url, signal, filters, onEvent}: SyncOpts if (signal.aborted) return - for (const filter of filters) { - pullOneWithFallback(url, filter, signal, onEvent) - } + await Promise.all(filters.map(filter => pullOneWithFallback(url, filter, signal, onEvent))) } const listen = ({url, signal, filters, onEvent}: SyncOpts) => { @@ -197,7 +207,7 @@ const syncUserRoomMembership = (url: string, h: string) => { const syncUserData = () => { const unsubscribersByKey = new Map() - const unsubscribeGroupList = userGroupList.subscribe($userGroupList => { + const syncGroupList = ($userGroupList: List | undefined) => { if ($userGroupList) { const keys = new Set() @@ -226,37 +236,76 @@ const syncUserData = () => { } } } - }) + } - const unsubscribeRelayList = userRelayList.subscribe($userRelayList => { - if ($userRelayList) { - loadBlossomServerList($userRelayList.event.pubkey) - loadBlockedRelayList($userRelayList.event.pubkey) - loadFollowList($userRelayList.event.pubkey) - loadGroupList($userRelayList.event.pubkey) - loadMuteList($userRelayList.event.pubkey) - loadProfile($userRelayList.event.pubkey) - loadSettings($userRelayList.event.pubkey) - loadFeedsForPubkey($userRelayList.event.pubkey) - } - }) + const syncRelayList = ($userRelayList: List | undefined) => { + const pubkey = $userRelayList?.event?.pubkey - const unsubscribeFollows = userFollowList.subscribe(async $userFollowList => { + if (!pubkey) return + + loadBlossomServerList(pubkey) + loadBlockedRelayList(pubkey) + loadFollowList(pubkey) + loadGroupList(pubkey) + loadMuteList(pubkey) + loadProfile(pubkey) + loadSettings(pubkey) + loadFeedsForPubkey(pubkey) + } + + const syncFollowList = () => { for (const pubkeys of chunk(10, get(bootstrapPubkeys))) { - // This isn't urgent, avoid clogging other stuff up - await sleep(1000) + void (async () => { + // This isn't urgent, avoid clogging other stuff up + await sleep(1000) - await Promise.all( - pubkeys.flatMap(pk => [ - loadRelayList(pk), - loadGroupList(pk), - loadProfile(pk), - loadFollowList(pk), - loadMuteList(pk), - ]), - ) + await Promise.all( + pubkeys.flatMap(pk => [ + loadRelayList(pk), + loadGroupList(pk), + loadProfile(pk), + loadFollowList(pk), + loadMuteList(pk), + ]), + ) + })() } - }) + } + + const unsubscribeGroupList = derived([userGroupList, documentVisibility], identity).subscribe( + ([$userGroupList, $visibility]) => { + if ($visibility === "hidden") { + const urls = new Set(Array.from(unsubscribersByKey.keys()).map(key => key.split("'")[0])) + + unsubscribersByKey.forEach(call) + unsubscribersByKey.clear() + + for (const url of urls) { + resetRelaySocket(url) + } + + return + } + + syncGroupList($userGroupList) + }, + ) + + const unsubscribeRelayList = derived([userRelayList, documentVisibility], identity).subscribe( + ([$userRelayList, $visibility]) => { + if ($visibility === "hidden") return + + syncRelayList($userRelayList) + }, + ) + + const unsubscribeFollows = derived([userFollowList, documentVisibility], identity).subscribe( + ([$userFollowList, $visibility]) => { + if ($visibility === "hidden") return + + syncFollowList() + }, + ) return () => { unsubscribersByKey.forEach(call) @@ -321,11 +370,26 @@ const syncSpace = (url: string, rooms: string[]) => { } const syncSpaces = () => { - const store = derived([userGroupList, page], identity) + const store = derived([userGroupList, page, documentVisibility], identity) const unsubscribersByUrl = new Map() const roomsByUrl = new Map() - const unsubscribe = store.subscribe(([$userGroupList, $page]) => { + const unsubscribe = store.subscribe(([$userGroupList, $page, $visibility]) => { + if ($visibility === "hidden") { + // Hidden tabs should drop every live space subscription so we restart from a clean slate on wake. + for (const url of unsubscribersByUrl.keys()) { + resetRelaySocket(url) + } + + for (const unsubscribe of unsubscribersByUrl.values()) { + unsubscribe() + } + + unsubscribersByUrl.clear() + roomsByUrl.clear() + return + } + const urls = new Set(getSpaceUrlsFromGroupList($userGroupList)) if ($page.params.relay) { @@ -338,6 +402,7 @@ const syncSpaces = () => { unsubscribersByUrl.delete(url) roomsByUrl.delete(url) unsubscribe() + resetRelaySocket(url) } } @@ -357,8 +422,9 @@ const syncSpaces = () => { }) return () => { - for (const unsubscriber of unsubscribersByUrl.values()) { + for (const [url, unsubscriber] of unsubscribersByUrl.entries()) { unsubscriber() + resetRelaySocket(url) } unsubscribe() @@ -383,11 +449,51 @@ const syncDMs = () => { const unsubscribersByUrl = new Map() let currentPubkey: string | undefined + let currentShouldUnwrap = false + // Late relay-list promises can resolve after a hide/show cycle, so keep the last visible state here. + let currentVisibility: DocumentVisibilityState = "visible" - const unsubscribeAll = () => { + const unsubscribeAll = (resetSockets = false) => { for (const [url, unsubscribe] of unsubscribersByUrl.entries()) { unsubscribersByUrl.delete(url) unsubscribe() + + if (resetSockets) { + resetRelaySocket(url) + } + } + } + + const syncPubkey = ($pubkey: string | undefined, $shouldUnwrap: boolean) => { + if ($pubkey !== currentPubkey) { + unsubscribeAll() + } + + if ($pubkey && $shouldUnwrap) { + loadRelayList($pubkey) + .then(() => loadMessagingRelayList($pubkey)) + .then($l => { + if ( + $l && + currentVisibility === "visible" && + currentPubkey === $pubkey && + currentShouldUnwrap === $shouldUnwrap + ) { + subscribeAll($pubkey, getRelayTagValues(getListTags($l))) + } + }) + } + + currentPubkey = $pubkey + currentShouldUnwrap = $shouldUnwrap + } + + const syncList = ($userMessagingRelayList: List | undefined) => { + const $pubkey = pubkey.get() + const $shouldUnwrap = shouldUnwrap.get() + + if ($pubkey && $shouldUnwrap) { + subscribeAll($pubkey, getRelayTagValues(getListTags($userMessagingRelayList))) } } @@ -408,33 +514,30 @@ const syncDMs = () => { } } - // When pubkey changes, re-sync - const unsubscribePubkey = derived([pubkey, shouldUnwrap], identity).subscribe( - ([$pubkey, $shouldUnwrap]) => { - if ($pubkey !== currentPubkey) { - unsubscribeAll() + // When pubkey or visibility changes, re-sync + const unsubscribePubkey = derived([pubkey, shouldUnwrap, documentVisibility], identity).subscribe( + ([$pubkey, $shouldUnwrap, $visibility]) => { + currentVisibility = $visibility + + if ($visibility === "hidden") { + unsubscribeAll(true) + return } - // If we have a pubkey, refresh our user's relay list then sync our subscriptions - if ($pubkey && $shouldUnwrap) { - loadRelayList($pubkey) - .then(() => loadMessagingRelayList($pubkey)) - .then($l => subscribeAll($pubkey, getRelayTagValues(getListTags($l)))) - } - - currentPubkey = $pubkey + syncPubkey($pubkey, $shouldUnwrap) }, ) // When user messaging relays change, update synchronization - const unsubscribeList = userMessagingRelayList.subscribe($userMessagingRelayList => { - const $pubkey = pubkey.get() - const $shouldUnwrap = shouldUnwrap.get() + const unsubscribeList = derived([userMessagingRelayList, documentVisibility], identity).subscribe( + ([$userMessagingRelayList, $visibility]) => { + currentVisibility = $visibility - if ($pubkey && $shouldUnwrap) { - subscribeAll($pubkey, getRelayTagValues(getListTags($userMessagingRelayList))) - } - }) + if ($visibility === "hidden") return + + syncList($userMessagingRelayList) + }, + ) return () => { unsubscribeAll() -- 2.52.0 From e710fc256120875b14577963599d4df3a03dee51 Mon Sep 17 00:00:00 2001 From: nayan9617 Date: Tue, 7 Apr 2026 00:26:30 +0530 Subject: [PATCH 2/6] refactor: revert socket reset workaround for adapter layer investigation --- src/app/core/sync.ts | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/src/app/core/sync.ts b/src/app/core/sync.ts index bb4f1e6e..c4d607c4 100644 --- a/src/app/core/sync.ts +++ b/src/app/core/sync.ts @@ -23,7 +23,7 @@ import { getTagValue, } from "@welshman/util" import type {Filter, TrustedEvent} from "@welshman/util" -import {request, requestOne, Difference, DifferenceEvent, Pool} from "@welshman/net" +import {request, requestOne, Difference, DifferenceEvent} from "@welshman/net" import { pubkey, loadRelay, @@ -70,10 +70,6 @@ type SyncOpts = { onEvent?: (event: TrustedEvent) => void } -const resetRelaySocket = (url: string) => { - Pool.get().remove(url) -} - const pullOneWithFallback = async ( url: string, filter: Filter, @@ -275,15 +271,9 @@ const syncUserData = () => { const unsubscribeGroupList = derived([userGroupList, documentVisibility], identity).subscribe( ([$userGroupList, $visibility]) => { if ($visibility === "hidden") { - const urls = new Set(Array.from(unsubscribersByKey.keys()).map(key => key.split("'")[0])) - unsubscribersByKey.forEach(call) unsubscribersByKey.clear() - for (const url of urls) { - resetRelaySocket(url) - } - return } @@ -377,10 +367,6 @@ const syncSpaces = () => { const unsubscribe = store.subscribe(([$userGroupList, $page, $visibility]) => { if ($visibility === "hidden") { // Hidden tabs should drop every live space subscription so we restart from a clean slate on wake. - for (const url of unsubscribersByUrl.keys()) { - resetRelaySocket(url) - } - for (const unsubscribe of unsubscribersByUrl.values()) { unsubscribe() } @@ -402,7 +388,6 @@ const syncSpaces = () => { unsubscribersByUrl.delete(url) roomsByUrl.delete(url) unsubscribe() - resetRelaySocket(url) } } @@ -422,9 +407,8 @@ const syncSpaces = () => { }) return () => { - for (const [url, unsubscriber] of unsubscribersByUrl.entries()) { + for (const unsubscriber of unsubscribersByUrl.values()) { unsubscriber() - resetRelaySocket(url) } unsubscribe() @@ -453,14 +437,10 @@ const syncDMs = () => { // Late relay-list promises can resolve after a hide/show cycle, so keep the last visible state here. let currentVisibility: DocumentVisibilityState = "visible" - const unsubscribeAll = (resetSockets = false) => { + const unsubscribeAll = () => { for (const [url, unsubscribe] of unsubscribersByUrl.entries()) { unsubscribersByUrl.delete(url) unsubscribe() - - if (resetSockets) { - resetRelaySocket(url) - } } } @@ -520,7 +500,7 @@ const syncDMs = () => { currentVisibility = $visibility if ($visibility === "hidden") { - unsubscribeAll(true) + unsubscribeAll() return } -- 2.52.0 From 35c27090c1c7d1aa5b4d6d76e77d1241f90f86a6 Mon Sep 17 00:00:00 2001 From: nayan9617 Date: Tue, 7 Apr 2026 23:02:58 +0530 Subject: [PATCH 3/6] Guard fallback pulls after abort --- src/app/core/sync.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/app/core/sync.ts b/src/app/core/sync.ts index c4d607c4..f992bcb5 100644 --- a/src/app/core/sync.ts +++ b/src/app/core/sync.ts @@ -76,6 +76,8 @@ const pullOneWithFallback = async ( signal: AbortSignal, onEvent?: (event: TrustedEvent) => void, ) => { + if (signal.aborted) return + const cachedEvents = repository.query([filter]).filter(isSignedEvent) const since = last(cachedEvents.slice(10))?.created_at || 0 @@ -93,7 +95,7 @@ const pullOneWithFallback = async ( return } - // If visibility flips while the negentropy diff is opening, skip the fallback path and let teardown win. + // If teardown wins while the diff is opening, skip the fallback path and let cleanup stay in control. const diff = new Difference({relay: url, filter, events: cachedEvents, signal}) diff.on(DifferenceEvent.Error, () => { @@ -129,6 +131,8 @@ const listen = ({url, signal, filters, onEvent}: SyncOpts) => { } const pullAndListen = (options: SyncOpts) => { + if (options.signal.aborted) return + pullWithFallback(options) listen(options) } -- 2.52.0 From 2144bffc4135f1d30e8c447ab1a83dfa6f869cfa Mon Sep 17 00:00:00 2001 From: nayan9617 Date: Wed, 8 Apr 2026 16:20:35 +0530 Subject: [PATCH 4/6] Align sync as per the review --- .gitignore | 1 + src/app/core/state.ts | 18 --------- src/app/core/sync.ts | 94 ++++++++++--------------------------------- 3 files changed, 23 insertions(+), 90 deletions(-) diff --git a/.gitignore b/.gitignore index 0981428d..39569863 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ node_modules/ .pnpm-store/ build/ .svelte-kit/ +.next/ # Rust/Tauri *target/ diff --git a/src/app/core/state.ts b/src/app/core/state.ts index 98be1b8c..69672d04 100644 --- a/src/app/core/state.ts +++ b/src/app/core/state.ts @@ -156,24 +156,6 @@ import {readFeed} from "@lib/feeds" export const fromCsv = (s: string) => (s || "").split(",").filter(identity) -// Keep sync logic informed when the tab sleeps or wakes so subscriptions can be torn down cleanly. -export const documentVisibility = readable( - typeof document !== "undefined" ? document.visibilityState : "visible", - set => { - if (typeof document === "undefined") return - - const onVisibilityChange = () => { - set(document.visibilityState) - } - - document.addEventListener("visibilitychange", onVisibilityChange) - - return () => { - document.removeEventListener("visibilitychange", onVisibilityChange) - } - }, -) - export const ROOM = "h" export const PROTECTED = ["-"] diff --git a/src/app/core/sync.ts b/src/app/core/sync.ts index f992bcb5..3811f90f 100644 --- a/src/app/core/sync.ts +++ b/src/app/core/sync.ts @@ -1,12 +1,12 @@ import {page} from "$app/stores" import type {Unsubscriber} from "svelte/store" -import {derived, get} from "svelte/store" -import {last, call, ifLet, assoc, chunk, sleep, identity, WEEK, ago} from "@welshman/lib" +import {get} from "svelte/store" +import {last, call, ifLet, assoc, chunk, sleep, WEEK, ago} from "@welshman/lib" import {PollResponse} from "nostr-tools/kinds" +import {merged} from "@welshman/store" import { getListTags, getRelayTagValues, - type List, WRAP, ROOM_META, ROOM_DELETE, @@ -22,7 +22,7 @@ import { unionFilters, getTagValue, } from "@welshman/util" -import type {Filter, TrustedEvent} from "@welshman/util" +import type {Filter, List, PublishedList, TrustedEvent} from "@welshman/util" import {request, requestOne, Difference, DifferenceEvent} from "@welshman/net" import { pubkey, @@ -46,7 +46,6 @@ import { MESSAGE_KINDS, CONTENT_KINDS, INDEXER_RELAYS, - documentVisibility, loadSettings, loadGroupList, userSpaceUrls, @@ -238,7 +237,7 @@ const syncUserData = () => { } } - const syncRelayList = ($userRelayList: List | undefined) => { + const syncRelayList = ($userRelayList: PublishedList | undefined) => { const pubkey = $userRelayList?.event?.pubkey if (!pubkey) return @@ -272,34 +271,17 @@ const syncUserData = () => { } } - const unsubscribeGroupList = derived([userGroupList, documentVisibility], identity).subscribe( - ([$userGroupList, $visibility]) => { - if ($visibility === "hidden") { - unsubscribersByKey.forEach(call) - unsubscribersByKey.clear() + const unsubscribeGroupList = merged([userGroupList]).subscribe(([$userGroupList]) => { + syncGroupList($userGroupList) + }) - return - } + const unsubscribeRelayList = merged([userRelayList]).subscribe(([$userRelayList]) => { + syncRelayList($userRelayList) + }) - syncGroupList($userGroupList) - }, - ) - - const unsubscribeRelayList = derived([userRelayList, documentVisibility], identity).subscribe( - ([$userRelayList, $visibility]) => { - if ($visibility === "hidden") return - - syncRelayList($userRelayList) - }, - ) - - const unsubscribeFollows = derived([userFollowList, documentVisibility], identity).subscribe( - ([$userFollowList, $visibility]) => { - if ($visibility === "hidden") return - - syncFollowList() - }, - ) + const unsubscribeFollows = merged([userFollowList]).subscribe(() => { + syncFollowList() + }) return () => { unsubscribersByKey.forEach(call) @@ -364,22 +346,11 @@ const syncSpace = (url: string, rooms: string[]) => { } const syncSpaces = () => { - const store = derived([userGroupList, page, documentVisibility], identity) + const store = merged([userGroupList, page]) const unsubscribersByUrl = new Map() const roomsByUrl = new Map() - const unsubscribe = store.subscribe(([$userGroupList, $page, $visibility]) => { - if ($visibility === "hidden") { - // Hidden tabs should drop every live space subscription so we restart from a clean slate on wake. - for (const unsubscribe of unsubscribersByUrl.values()) { - unsubscribe() - } - - unsubscribersByUrl.clear() - roomsByUrl.clear() - return - } - + const unsubscribe = store.subscribe(([$userGroupList, $page]) => { const urls = new Set(getSpaceUrlsFromGroupList($userGroupList)) if ($page.params.relay) { @@ -438,8 +409,6 @@ const syncDMs = () => { let currentPubkey: string | undefined let currentShouldUnwrap = false - // Late relay-list promises can resolve after a hide/show cycle, so keep the last visible state here. - let currentVisibility: DocumentVisibilityState = "visible" const unsubscribeAll = () => { for (const [url, unsubscribe] of unsubscribersByUrl.entries()) { @@ -457,12 +426,7 @@ const syncDMs = () => { loadRelayList($pubkey) .then(() => loadMessagingRelayList($pubkey)) .then($l => { - if ( - $l && - currentVisibility === "visible" && - currentPubkey === $pubkey && - currentShouldUnwrap === $shouldUnwrap - ) { + if ($l && currentPubkey === $pubkey && currentShouldUnwrap === $shouldUnwrap) { subscribeAll($pubkey, getRelayTagValues(getListTags($l))) } }) @@ -498,27 +462,13 @@ const syncDMs = () => { } } - // When pubkey or visibility changes, re-sync - const unsubscribePubkey = derived([pubkey, shouldUnwrap, documentVisibility], identity).subscribe( - ([$pubkey, $shouldUnwrap, $visibility]) => { - currentVisibility = $visibility - - if ($visibility === "hidden") { - unsubscribeAll() - return - } - - syncPubkey($pubkey, $shouldUnwrap) - }, - ) + const unsubscribePubkey = merged([pubkey, shouldUnwrap]).subscribe(([$pubkey, $shouldUnwrap]) => { + syncPubkey($pubkey, $shouldUnwrap) + }) // When user messaging relays change, update synchronization - const unsubscribeList = derived([userMessagingRelayList, documentVisibility], identity).subscribe( - ([$userMessagingRelayList, $visibility]) => { - currentVisibility = $visibility - - if ($visibility === "hidden") return - + const unsubscribeList = merged([userMessagingRelayList]).subscribe( + ([$userMessagingRelayList]) => { syncList($userMessagingRelayList) }, ) -- 2.52.0 From 55497b79499f8545957105a969854564601bc04a Mon Sep 17 00:00:00 2001 From: nayan9617 Date: Wed, 8 Apr 2026 16:28:14 +0530 Subject: [PATCH 5/6] Keep follow sync trickling --- src/app/core/sync.ts | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/app/core/sync.ts b/src/app/core/sync.ts index 3811f90f..84eee092 100644 --- a/src/app/core/sync.ts +++ b/src/app/core/sync.ts @@ -252,25 +252,29 @@ const syncUserData = () => { loadFeedsForPubkey(pubkey) } - const syncFollowList = () => { + const syncFollowList = async (signal: AbortSignal) => { for (const pubkeys of chunk(10, get(bootstrapPubkeys))) { - void (async () => { - // This isn't urgent, avoid clogging other stuff up - await sleep(1000) + if (signal.aborted) return - await Promise.all( - pubkeys.flatMap(pk => [ - loadRelayList(pk), - loadGroupList(pk), - loadProfile(pk), - loadFollowList(pk), - loadMuteList(pk), - ]), - ) - })() + // This isn't urgent, avoid clogging other stuff up + await sleep(1000) + + if (signal.aborted) return + + await Promise.all( + pubkeys.flatMap(pk => [ + loadRelayList(pk), + loadGroupList(pk), + loadProfile(pk), + loadFollowList(pk), + loadMuteList(pk), + ]), + ) } } + let bootstrapFollowController = new AbortController() + const unsubscribeGroupList = merged([userGroupList]).subscribe(([$userGroupList]) => { syncGroupList($userGroupList) }) @@ -280,10 +284,13 @@ const syncUserData = () => { }) const unsubscribeFollows = merged([userFollowList]).subscribe(() => { - syncFollowList() + bootstrapFollowController.abort() + bootstrapFollowController = new AbortController() + void syncFollowList(bootstrapFollowController.signal) }) return () => { + bootstrapFollowController.abort() unsubscribersByKey.forEach(call) unsubscribeGroupList() unsubscribeRelayList() -- 2.52.0 From 3dd5a0915f60ce57b2c6ccadf0dd71a4da47d224 Mon Sep 17 00:00:00 2001 From: nayan9617 Date: Wed, 8 Apr 2026 21:55:57 +0530 Subject: [PATCH 6/6] Stop tracking .next artifacts --- .next/_events_51442.json | 1 - .next/dev/cache/.rscinfo | 1 - .next/dev/cache/turbopack/8d0f77bfa/CURRENT | Bin 4 -> 0 bytes .next/dev/logs/next-development.log | 0 .next/dev/package.json | 3 --- 5 files changed, 5 deletions(-) delete mode 100644 .next/_events_51442.json delete mode 100644 .next/dev/cache/.rscinfo delete mode 100644 .next/dev/cache/turbopack/8d0f77bfa/CURRENT delete mode 100644 .next/dev/logs/next-development.log delete mode 100644 .next/dev/package.json diff --git a/.next/_events_51442.json b/.next/_events_51442.json deleted file mode 100644 index 86bc8b31..00000000 --- a/.next/_events_51442.json +++ /dev/null @@ -1 +0,0 @@ -[{"eventName":"NEXT_CLI_SESSION_STOPPED","payload":{"nextVersion":"16.2.2","nodeVersion":"v24.7.0","cliCommand":"dev","durationMilliseconds":6291,"turboFlag":true,"pagesDir":false,"appDir":true,"isRspack":false}}] \ No newline at end of file diff --git a/.next/dev/cache/.rscinfo b/.next/dev/cache/.rscinfo deleted file mode 100644 index 3ee33f06..00000000 --- a/.next/dev/cache/.rscinfo +++ /dev/null @@ -1 +0,0 @@ -{"encryption.key":"mT8zxPCClGbcV2DiRln03XdmvHCQ+orzt0Q2KKaiDd4=","encryption.expire_at":1776709211601} \ No newline at end of file diff --git a/.next/dev/cache/turbopack/8d0f77bfa/CURRENT b/.next/dev/cache/turbopack/8d0f77bfa/CURRENT deleted file mode 100644 index 593f4708db84ac8fd0f5cc47c634f38c013fe9e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4 LcmZQzU|;|M00aO5 diff --git a/.next/dev/logs/next-development.log b/.next/dev/logs/next-development.log deleted file mode 100644 index e69de29b..00000000 diff --git a/.next/dev/package.json b/.next/dev/package.json deleted file mode 100644 index c9a44226..00000000 --- a/.next/dev/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "type": "commonjs" -} \ No newline at end of file -- 2.52.0