Skip to content

Commit c5a9dfe

Browse files
committed
fix(store): refresh a stale catalog on revisit
The catalog latched after one good load and never refetched for the life of the client session, hiding freshly published modules (the theme launch was invisible until a restart). Revisits now refetch in the background past a 5-minute TTL, keeping the last good catalog on failure and never flashing a loading state over the grid.
1 parent b45071c commit c5a9dfe

2 files changed

Lines changed: 20 additions & 7 deletions

File tree

modules/store/metadata.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "store",
33
"tags": ["ui"],
4-
"version": "1.0.2",
4+
"version": "1.0.3",
55
"authors": ["spicetify"],
66
"description": "Browse, install, and manage v3 modules from vaults",
77
"entries": {

modules/store/page.tsx

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -701,23 +701,28 @@ function StorePage(props: { api: PageApi }): ReactElement {
701701
const [, bumpCounts] = React.useReducer((n: number) => n + 1, 0);
702702
const loadedRef = React.useRef(false);
703703
const loadingRef = React.useRef(false);
704+
const loadedAtRef = React.useRef(0);
704705
const autoDisabledRevoked = React.useRef(new Set<string>());
705706
const resetTimer = React.useRef<ReturnType<typeof setTimeout> | null>(null);
706707
const importInput = React.useRef<HTMLInputElement>(null);
707708

708709
const refreshRegistry = () => setRegistryEpoch((n) => n + 1);
709710

710-
const load = React.useCallback(async () => {
711+
const load = React.useCallback(async (background = false) => {
711712
// A failed load must retry on the next visit, not latch.
712713
if (loadingRef.current) return;
713714
loadingRef.current = true;
714-
setStatus("loading vaults…");
715+
// A background refresh keeps the current grid on screen instead
716+
// of flashing a loading state over it.
717+
if (!background) setStatus("loading vaults…");
715718
let next: Catalog = { modules: [], revoked: {}, ok: false };
716719
try {
717720
next = await loadCatalog();
721+
if (background && !next.ok) return; // keep the last good catalog
718722
// Only a load where some vault answered may latch; an offline
719723
// page keeps retrying on later visits.
720724
loadedRef.current = next.ok;
725+
if (next.ok) loadedAtRef.current = Date.now();
721726
setCatalog(next);
722727
setStatus(
723728
next.ok
@@ -738,12 +743,20 @@ function StorePage(props: { api: PageApi }): ReactElement {
738743
void load();
739744
}, [load]);
740745

741-
// A revisit re-renders from live registry state, or retries the
742-
// catalog when no vault has answered yet.
746+
// A revisit re-renders from live registry state, retries the catalog
747+
// when no vault has answered yet, and refreshes a stale catalog in
748+
// the background: modules publish continuously, so a latched catalog
749+
// must not outlive the session (that hid freshly published themes
750+
// until a client restart).
751+
const CATALOG_TTL_MS = 5 * 60 * 1000;
743752
React.useEffect(() => {
744753
props.api.onRevisit = () => {
745-
if (loadedRef.current) refreshRegistry();
746-
else void load();
754+
if (!loadedRef.current) {
755+
void load();
756+
return;
757+
}
758+
refreshRegistry();
759+
if (Date.now() - loadedAtRef.current > CATALOG_TTL_MS) void load(true);
747760
};
748761
return () => {
749762
props.api.onRevisit = null;

0 commit comments

Comments
 (0)