Add stale cache for public data

This commit is contained in:
TerryM
2026-05-28 23:09:18 +08:00
parent 5ae9647465
commit 320739f91b
8 changed files with 263 additions and 83 deletions

View File

@@ -1,7 +1,7 @@
import { ChevronLeft, ChevronRight } from "lucide-react";
import { Link, useLocation } from "react-router-dom";
import { useEffect, useRef, useState } from "react";
import { getJSON, itemsOrEmpty, type Category } from "../../api";
import { getJSON, itemsOrEmpty, readJSONCache, type Category } from "../../api";
import { CategoryIcon } from "../../components/CategoryIcon";
import { FigmaBanner } from "../../components/FigmaBanner";
import {
@@ -61,41 +61,80 @@ export function Home() {
const [recScroll, setRecScroll] = useState({ ratio: 1, progress: 0 });
useEffect(() => {
let cancelled = false;
const langParam = encodeURIComponent(langQuery(lang));
const languageParam = encodeURIComponent(sourceLanguageQuery(lang));
const catQ = `?lang=${langParam}`;
const postQ = `?lang=${langParam}&language=${languageParam}`;
const categoriesUrl = `/api/categories${catQ}`;
const recommendedUrl = `/api/posts/recommended${postQ}&limit=12`;
const latestUrl = `/api/posts${postQ}&sort=latest&limit=12`;
const popularUrl = `/api/posts${postQ}&sort=popular&limit=5`;
const applyHomeData = (
c: Category[],
r: { items: Post[] },
l: { items: Post[] },
p: { items: Post[] },
) => {
const categoryItems = itemsOrEmpty(c);
setCats(categoryItems);
setRec(
itemsOrEmpty(r.items).map((post) =>
postToResource(post, lang, categoryItems),
),
);
const latestItems = itemsOrEmpty(l.items);
setLatestPosts(latestItems);
setLatest(
latestItems.map((post) => postToResource(post, lang, categoryItems)),
);
const popularItems = itemsOrEmpty<Post>(p.items);
setPopularPosts(popularItems);
setPopular(
popularItems.map((post) => postToResource(post, lang, categoryItems)),
);
};
setErr(null);
const cachedCategories = readJSONCache<Category[]>(categoriesUrl);
const cachedRecommended = readJSONCache<{ items: Post[] }>(recommendedUrl);
const cachedLatest = readJSONCache<{ items: Post[] }>(latestUrl);
const cachedPopular = readJSONCache<{ items: Post[] }>(popularUrl);
const showedCached = !!(
cachedCategories &&
cachedRecommended &&
cachedLatest
);
if (showedCached) {
applyHomeData(
cachedCategories,
cachedRecommended,
cachedLatest,
cachedPopular ?? { items: [] },
);
}
Promise.all([
getJSON<Category[]>(`/api/categories${catQ}`),
getJSON<{ items: Post[] }>(`/api/posts/recommended${postQ}&limit=12`),
getJSON<{ items: Post[] }>(`/api/posts${postQ}&sort=latest&limit=12`),
getJSON<{ items: Post[] }>(
`/api/posts${postQ}&sort=popular&limit=5`,
).catch((): { items: Post[] } => ({ items: [] })),
getJSON<Category[]>(categoriesUrl),
getJSON<{ items: Post[] }>(recommendedUrl),
getJSON<{ items: Post[] }>(latestUrl),
getJSON<{ items: Post[] }>(popularUrl).catch(
(): { items: Post[] } => cachedPopular ?? { items: [] },
),
])
.then(([c, r, l, p]) => {
setCats(itemsOrEmpty(c));
setRec(
itemsOrEmpty(r.items).map((post) =>
postToResource(post, lang, itemsOrEmpty(c)),
),
);
const latestItems = itemsOrEmpty(l.items);
setLatestPosts(latestItems);
setLatest(
latestItems.map((post) =>
postToResource(post, lang, itemsOrEmpty(c)),
),
);
const popularItems = itemsOrEmpty<Post>(p.items);
setPopularPosts(popularItems);
setPopular(
popularItems.map((post) =>
postToResource(post, lang, itemsOrEmpty(c)),
),
);
if (cancelled) return;
applyHomeData(c, r, l, p);
})
.catch((e) => setErr(String(e)));
.catch((e) => {
if (!cancelled && !showedCached) setErr(String(e));
});
return () => {
cancelled = true;
};
}, [lang]);
const iconKeyForResource = (r: PostBackedResource) =>