From e61fa1d0cfde675af1fdc4940424521a83304ce5 Mon Sep 17 00:00:00 2001 From: Timothy Cardoza Date: Wed, 25 Mar 2026 09:09:16 -0500 Subject: [PATCH] subscription recovery --- src/store/index.ts | 53 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/store/index.ts b/src/store/index.ts index 5c2bda9..14edac0 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -15,10 +15,62 @@ export const getFavoriteTag = () => { const BASE_LIST_FIELDS = 'id,collectionId,collectionName,created,updated,title,startDate,dueDate,priority,status,completed,tags,labelTags,shareRefs,noteRefs,createdBy,user,size,recurrence,deletedAt'; const NOTE_LIST_FIELDS = 'id,created,updated,title,key,tags,isPrivate,user,tasks,deletedAt'; let updatedByFieldSupport: boolean | null = null; +let realtimeRecoveryAttached = false; +let realtimeRecoveryTimer: number | undefined; +let lastRealtimeRecoveryAt = 0; const getTaskListFields = () => updatedByFieldSupport ? `${BASE_LIST_FIELDS},updatedBy` : BASE_LIST_FIELDS; +const scheduleRealtimeRecovery = (reason: string, delayMs = 400) => { + if (typeof window === "undefined") return; + if (!pb.authStore.isValid) return; + + const nowTs = Date.now(); + if (nowTs - lastRealtimeRecoveryAt < 10000) return; + + if (realtimeRecoveryTimer) { + window.clearTimeout(realtimeRecoveryTimer); + } + + realtimeRecoveryTimer = window.setTimeout(async () => { + realtimeRecoveryTimer = undefined; + if (!pb.authStore.isValid || store.isInitializing) return; + lastRealtimeRecoveryAt = Date.now(); + console.log(`[REALTIME] Recovering subscriptions after ${reason}`); + try { + await initStore(); + } catch (err) { + console.warn("[REALTIME] Recovery failed:", err); + } + }, delayMs); +}; + +const ensureRealtimeRecovery = () => { + if (realtimeRecoveryAttached || typeof window === "undefined") return; + realtimeRecoveryAttached = true; + + const recoverIfDisconnected = (reason: string) => { + if (!pb.authStore.isValid) return; + if (pb.realtime.isConnected) return; + scheduleRealtimeRecovery(reason); + }; + + document.addEventListener("visibilitychange", () => { + if (document.visibilityState === "visible") { + recoverIfDisconnected("visibilitychange"); + } + }); + + window.addEventListener("focus", () => recoverIfDisconnected("focus")); + window.addEventListener("online", () => recoverIfDisconnected("online")); + + pb.realtime.onDisconnect = () => { + if (!pb.authStore.isValid) return; + scheduleRealtimeRecovery("realtime disconnect", 1000); + }; +}; + const getStorageKey = () => { const userId = pb.authStore.model?.id; return userId ? `${STORAGE_KEY_PREFIX}_data_${userId}` : null; @@ -2139,6 +2191,7 @@ export const runProdDataMigration = async () => { export const initStore = async () => { + ensureRealtimeRecovery(); if (!pb.authStore.isValid || store.isInitializing) return; setStore("isInitializing", true);