terry-staging #13

Merged
terry merged 4 commits from terry-staging into main 2026-05-30 14:05:28 +00:00
2 changed files with 55 additions and 19 deletions
Showing only changes of commit cc58ee8aac - Show all commits

View File

@@ -112,6 +112,17 @@ function streamKey(params: PostStreamParams): string {
return buildRealUrl(params); return buildRealUrl(params);
} }
function cacheFirstPage(
params: PostStreamParams,
page: PostListResponse,
): void {
streamCache.set(streamKey(params), {
items: itemsOrEmpty(page.items),
cursor: page.nextCursor,
hasMore: !!page.nextCursor,
});
}
/** /**
* Warm the cache for a stream view before the user navigates to it, so opening * Warm the cache for a stream view before the user navigates to it, so opening
* the page shows content immediately instead of starting to load on arrival. * the page shows content immediately instead of starting to load on arrival.
@@ -119,20 +130,31 @@ function streamKey(params: PostStreamParams): string {
*/ */
export function prefetchPostStream(params: PostStreamParams): void { export function prefetchPostStream(params: PostStreamParams): void {
if (USE_MOCK) return; if (USE_MOCK) return;
const key = streamKey(params);
if (streamCache.has(key)) return;
const url = buildRealUrl(params); const url = buildRealUrl(params);
if (readJSONCache<PostListResponse>(url)) return; const cachedPage = readJSONCache<PostListResponse>(url);
getJSON<PostListResponse>(url).catch(() => {}); if (cachedPage) {
cacheFirstPage(params, cachedPage);
return;
}
getJSON<PostListResponse>(url)
.then((page) => cacheFirstPage(params, page))
.catch(() => {});
} }
export function usePostStream(params: PostStreamParams): PostStreamResult { export function usePostStream(params: PostStreamParams): PostStreamResult {
const [items, setItems] = useState<Post[]>([]); const initialCached = streamCache.get(streamKey(params));
const [hasMore, setHasMore] = useState(true); const [items, setItems] = useState<Post[]>(() => initialCached?.items ?? []);
const [isLoading, setIsLoading] = useState(false); const [hasMore, setHasMore] = useState(() => initialCached?.hasMore ?? true);
const [isLoading, setIsLoading] = useState(() => !initialCached);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const reqIdRef = useRef(0); const reqIdRef = useRef(0);
const cursorRef = useRef<string | undefined>(undefined); const cursorRef = useRef<string | undefined>(initialCached?.cursor);
const hasMoreRef = useRef(true); const hasMoreRef = useRef(initialCached?.hasMore ?? true);
const loadingRef = useRef(false); const loadingRef = useRef(false);
const fetchPage = useCallback( const fetchPage = useCallback(

View File

@@ -764,9 +764,8 @@ function BottomNavIcon({
icon: "home" | "document" | "bookmark" | "update"; icon: "home" | "document" | "bookmark" | "update";
active: boolean; active: boolean;
}) { }) {
const src = active const activeSrc = `${NAVBAR_ICON_BASE}/${icon}-active.svg`;
? `${NAVBAR_ICON_BASE}/${icon}-active.svg` const inactiveSrc = `${NAVBAR_ICON_BASE}/${icon}-inactive.svg`;
: `${NAVBAR_ICON_BASE}/${icon}-inactive.svg`;
return ( return (
<Link <Link
to={to} to={to}
@@ -775,15 +774,30 @@ function BottomNavIcon({
active ? "text-ark-gold" : "text-[#908F92]", active ? "text-ark-gold" : "text-[#908F92]",
].join(" ")} ].join(" ")}
> >
<span className="relative h-6 w-6" aria-hidden>
<img <img
src={src} src={inactiveSrc}
alt="" alt=""
className="mx-auto h-6 w-6 object-contain" className={`absolute inset-0 h-6 w-6 object-contain ${
active ? "opacity-0" : "opacity-100"
}`}
width={24} width={24}
height={24} height={24}
loading="lazy" loading="eager"
decoding="async" decoding="sync"
/> />
<img
src={activeSrc}
alt=""
className={`absolute inset-0 h-6 w-6 object-contain ${
active ? "opacity-100" : "opacity-0"
}`}
width={24}
height={24}
loading="eager"
decoding="sync"
/>
</span>
<span className="leading-tight">{label}</span> <span className="leading-tight">{label}</span>
</Link> </Link>
); );