From 267ecb5c46cb33f5fbcce695e4e7fe6b0bd279f9 Mon Sep 17 00:00:00 2001 From: Eli Mallon Date: Wed, 23 Sep 2026 13:48:44 -0700 Subject: [PATCH] app: retry profile lookups that failed Asking for each DID once stopped the refetch loop, but also stopped a lookup that failed outright (a network error) from ever being retried while the screen stayed mounted. getProfiles now says whether the lookup worked, and useAvatars forgets the DIDs of one that didn't. A DID the app view simply doesn't know still isn't asked for again. Co-Authored-By: Claude Opus 5.5 (1M context) --- js/app/hooks/useAvatars.tsx | 7 +++++-- js/app/store/slices/blueskySlice.ts | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/js/app/hooks/useAvatars.tsx b/js/app/hooks/useAvatars.tsx index 4a0f8b487..324ca97c7 100644 --- a/js/app/hooks/useAvatars.tsx +++ b/js/app/hooks/useAvatars.tsx @@ -11,7 +11,8 @@ export default function useAvatars(dids: string[]) { // Ask for each DID once. Callers pass a fresh array every render, and a DID // the Bluesky app view doesn't know (an account on another PDS with no // Bluesky profile) never lands in the cache, so keying on the array, or - // re-asking for whatever is missing, refetches in a loop. + // re-asking for whatever is missing, refetches in a loop. A lookup that + // failed outright is forgotten, so a later render asks again. const requested = useRef(new Set()); const missing = dids .filter((did) => !(did in profiles) && !requested.current.has(did)) @@ -21,7 +22,9 @@ export default function useAvatars(dids: string[]) { if (!missing) return; const toFetch = missing.split(","); toFetch.forEach((did) => requested.current.add(did)); - getProfiles(toFetch); + getProfiles(toFetch).then((ok) => { + if (!ok) toFetch.forEach((did) => requested.current.delete(did)); + }); }, [missing]); return profiles; diff --git a/js/app/store/slices/blueskySlice.ts b/js/app/store/slices/blueskySlice.ts index d87d9be3d..93cbcbf89 100644 --- a/js/app/store/slices/blueskySlice.ts +++ b/js/app/store/slices/blueskySlice.ts @@ -80,7 +80,8 @@ export interface BlueskySlice { refreshSessionScope: () => Promise; logout: () => Promise; getProfile: (actor: string) => Promise; - getProfiles: (actors: string[]) => Promise; + // resolves false if the lookup failed, as opposed to finding nothing + getProfiles: (actors: string[]) => Promise; oauthCallback: (url: string) => Promise; setReturnRoute: (route: { name: string; params?: any } | null) => void; showLoginModal: boolean; @@ -456,7 +457,7 @@ export const createBlueskySlice: StateCreator< // an unchanged cache must stay the same object, or every subscriber // re-renders for nothing if (Object.keys(parsedProfiles).length === 0) { - return; + return true; } set((s) => ({ profileCache: { @@ -464,8 +465,10 @@ export const createBlueskySlice: StateCreator< ...parsedProfiles, }, })); + return true; } catch (error) { console.error("getProfiles error", error); + return false; } }, -- 2.51.2