Add getCachedResources command with background fetch and periodic refresh#2271
Add getCachedResources command with background fetch and periodic refresh#2271captaincrazybro wants to merge 16 commits into
Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sonner auto-dismisses on action click so manual tracking is unnecessary. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds startBackgroundFetch (10-attempt retry), getCachedResources (on-demand fallback), registers the command, fires background fetch on activate, and updates openGetResources to notify on fetch failure. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…es` command Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Wire up error+retry UI: replace dead isPlatformError branch with resources===undefined check; add %resources_fetch_failed% label and Retry button backed by retryCount state - Remove stale useData imports from both web views - Replace fetchMutex.isLocked() TOCTOU guard with hasFetchStarted flag - Change executionToken from non-null assertion to | undefined with guards - Replace local sleep() with wait() from platform-bible-utils - Extract fetchAndCacheResources() helper (provider fetched internally); simplify startBackgroundFetchResources, getCachedResources, and interval - Remove unnecessary async/await wrappers on sendCommand callbacks - Add inner-mutex cachedResources check in getCachedResources - Add 12-hour setInterval to periodically refresh cache via fetchAndCacheResources Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
The first clause communicates nothing the user doesn’t already know from:
It also reads a bit like an internal log message rather than user-facing UI text. Also, we do not use the word "fetch" anywhere else in our UI. (FWIW, it is used a few times -- including in this context -- in P9.) It sounds developer-ish to me. I’d lean toward “load” rather than “fetch” for user-facing UI. We use "load" a lot in our UI. An alternative might be "access" or "connect to". Paratext 9 already has a pretty good string that might serve us well: Other possibilities:
|
|
This could be stated more clearly to remove doubt about what happens in the case of a failed re-fetch: Code snippet: /**
* Returns DBL resources from memory cache.
*
* If no cached value exists, attempts to fetch them.
* Failed refresh attempts do NOT clear existing cached data.
*
* @returns Cached DBL resources, or `undefined` if none have been
* successfully fetched.
*/ |
tombogle
left a comment
There was a problem hiding this comment.
@tombogle reviewed 6 files and all commit messages.
Reviewable status: all files reviewed, 2 unresolved discussions (waiting on captaincrazybro).
|
With this approach of using cached resources, what happens in the case where the Internet is down or (when we add a setting for this*) Internet activity is disabled, and then it later comes back or is re-enabled? Presumably the user can click retry manually, but is there any way we can detect that condition automatically and try to re-fetch?
|
|
One fundamental question I have is whether |
Code Review Summary
Branch:
feat-cached-resourcesBase:
origin/mainDate: 2026-05-14
Review model: Claude Sonnet 4.6
Files changed: 11
Overview
This branch adds a
getCachedResourcescommand to theplatform-get-resourcesextension. On activation, the extension kicks off a background fetch (up to 10 retries, 1 second apart) and caches the result in both memory and persistent user storage. The new command returns the cache immediately if populated; if the cache is empty it performs an on-demand fetch. A 12-hoursetIntervalalso firesfetchAndCacheResourcesperiodically to keep the cache fresh. TheGet ResourcesandHomeweb views were migrated from a liveuseDatasubscription to a one-timeusePromisefetch using the new command.Several correctness issues were found and fixed during the review: the error-feedback path was non-functional (dead
isPlatformErrorbranch plus unused localization strings for the planned retry UI), both web views had staleuseDataimports, the background-fetch guard had a TOCTOU window, andexecutionTokenused a non-null assertion. Minor issues fixed include deduplicating the fetch-and-cache sequence into afetchAndCacheResourceshelper, removing an unnecessarysleephelper, simplifyingasync/awaitwrappers, and adding an inner-mutex cache check.API Changes
'platformGetResources.getCachedResources': () => Promise<DblResourceData[] | undefined>— new command added topapi-shared-types.CommandHandlersDblResourceDataimported into thepapi-shared-typesmodule declaration (needed for the new command's return type)lib/platform-bible-react,lib/platform-bible-utils, orlib/papi-dts/papi.d.tsFindings
Critical — Must address before merge
None.
Important — Should address before merge
isPlatformErrorbranch + missing error UI inget-resources.web-view.tsx(fixed during review: replacedisPlatformError(resources)— which can never be true after theusePromisemigration — withresources === undefined && !isLoadingResources; wired up%resources_fetch_failed%error label and a Retry button backed by aretryCountstate variable that re-triggersusePromise)%resources_fetch_failed%and%resources_retry%defined inlocalizedStrings.jsonbut never referenced (fixed during review: both strings are now used in the new error+retry UI)useDataimport inget-resources.web-view.tsxandhome.web-view.tsx(fixed during review: removeduseDatafrom the@papi/frontend/reactimport in both files)startBackgroundFetchResourcesbetweenisLocked()andrunExclusive()(fixed during review: replaced thefetchMutex.isLocked()guard with a module-levelhasFetchStartedboolean set synchronously before enteringrunExclusive)executionTokendeclared with non-null assertion (!) (fixed during review: changed toExecutionToken | undefined; bothwriteUserDatacall sites guarded withif (executionToken))[All five important findings were fixed during the interview.]
Minor — Consider
sleepfunction duplicateswaitfromplatform-bible-utils(fixed during review: removedsleep, replaced withwait(1000))startBackgroundFetchResourcesandgetCachedResources(fixed during review: extractedfetchAndCacheResources()helper that fetches the provider internally; all callers simplified)async () => await sendCommand(...)wrappers in both web views (fixed during review: simplified to() => papi.commands.sendCommand(...))getCachedResourceswhen background fetch completes first (fixed during review: addedif (cachedResources !== undefined) return cachedResourcesinsiderunExclusivecallback)(intentional — author confirmed this is the desired behavior for the cached-resources approach)useData→usePromisebehavioral change: was a live subscription, now a one-time fetch on mountNo tests for the retry loop, persistence-load-on-startup, and on-demand fetch paths(acknowledged — consistent with the pre-existing lack of tests in this extension; not blocking)Template Propagation
Shared Regions Modified
None.
Extension Config Changes
None.
Positive Observations
platform-get-resources.d.tswith a clear@returnsJSDoc covering both the success andundefinedcasesMutexfromplatform-bible-utilscorrectly coordinates the background, on-demand, and scheduled fetch paths, preventing duplicate in-flight network requestspapi.storage.readUserData/writeUserDatameans the UI can show stale-but-fast data immediately on next launcheslint-disablecommentscontext.registrations.add(...), ensuring proper cleanup on deactivationInterview Notes
Purpose: Implement a
getCachedResourcescommand — returns cached DBL resources if available; on startup, tries to fetch and cache; if called with no cache, also triggers an on-demand fetch. A 12-hour interval was added during review to periodically refresh the cache.Key decisions:
useData(live subscription) replaced withusePromise(one-time fetch on mount) — intentional; the cached-resource model means the UI doesn't need to react to live data updateshasFetchStartedflag used to prevent double background fetches — eliminates the window between lock check and lock acquisitionexecutionTokenpersistence guards are best-effort — in-memory cache still populates if the token is unavailablefetchAndCacheResourcesdirectly (notgetCachedResources) so it always re-fetches rather than no-oping on a populated cacheAuthor understanding: Clear throughout. The author explained the cache/retry model without prompting and made targeted decisions for every finding.
In-Review Quality Check
npm run typechecknpm run lintnpm run formatnpm testLint fixes applied (all in in-review files):
get-resources.web-view.tsx— Added explanatory comment beforereact-hooks/exhaustive-depsdisable (retryCountis an intentional re-fetch trigger)main.ts— Inlineeslint-disable-linecomments converted toeslint-disable-next-linewith explanation commentsmain.ts—no-continue: retry loopcontinuerefactored toif (provider) { ... }guard block (later removed when provider fetch moved into helper)main.ts—no-type-assertion:JSON.parse(cached) as DblResourceData[]refactored to typed assignmentmain.ts— Added explanatory comment for floating-promiseeslint-disableinsetIntervalcallbackmain.ts— Prettier formatting applied tocontext.registrations.adddispose objectSuggested Review Focus
origin/mainrather thanorigin/ai/mainper the CLAUDE.md AI branch rules — verify the PR targets the correct base before merginguseData→usePromisebehavioral change: The UI now fetches once on mount rather than subscribing to live updates. If credentials become available after mount and the cache was empty, the UI won't refresh without a user-initiated retry — confirm this is acceptablefetchAndCacheResourcesunconditionally every 12 hours under the mutex. Confirm the interval duration and the decision to always re-fetch (not usegetCachedResourceswith its cache-hit early return)This change is