fix: smooth mobile footer tab switching
This commit is contained in:
@@ -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(
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user