diff --git a/src/views/content-library-view.jsx b/src/views/content-library-view.jsx index a020c34a..e7233e3e 100644 --- a/src/views/content-library-view.jsx +++ b/src/views/content-library-view.jsx @@ -290,6 +290,16 @@ function Favorites({view, hashParams, tinode, onToast}) { const [items, setItems] = React.useState(() => readCachedData('favorites:' + type) || null); const [cursor, setCursor] = React.useState(''); const [hasMore, setHasMore] = React.useState(false); + // A failed initial load (no cached list to fall back on) previously left this stuck on "加载中…" + // forever -- the catch handler only fired a toast, which fades in 2s, and `items` never left null + // so the loading branch below never had anything else to show. Same fix already applied to this + // file's own DownloadPage/GroupFiles(group-management-view.jsx)/Profile(contact-management-view.jsx) + // for the identical reason; Favorites/History were missed. Concretely hit live: favoriting a large + // (out-of-band) file attachment makes the *server* 500 on GET /favorites?type=all from then on + // (a real backend bug, tracked separately -- not something the frontend can prevent), and every + // subsequent visit to "全部" silently hung on the loading spinner with only a 2s toast, no way to + // tell "still loading" from "never going to load", no retry affordance. + const [loadError, setLoadError] = React.useState(false); const [fileUrls, setFileUrls] = React.useState({}); const [sourceNames, setSourceNames] = React.useState({}); // An in-flight request isn't guaranteed to resolve before a newer one its own type/query change @@ -302,6 +312,7 @@ function Favorites({view, hashParams, tinode, onToast}) { const requestId = ++requestIdRef.current; getFavorites({type, query, cursor: append ? cursor : ''}).then(data => { if (requestIdRef.current !== requestId) return; + setLoadError(false); setItems(prev => { const list = append ? (prev || []).concat(data.items || []) : (data.items || []); if (!query) writeCachedData('favorites:' + type, list); @@ -309,9 +320,14 @@ function Favorites({view, hashParams, tinode, onToast}) { }); setCursor(data.next_cursor || ''); setHasMore(!!data.next_cursor); - }).catch(err => onToast(err.message || '加载失败')); + }).catch(err => { + if (requestIdRef.current !== requestId) return; + onToast(err.message || '加载失败'); + if (!append) setLoadError(true); + }); }, [type, query, cursor]); React.useEffect(() => { + setLoadError(false); setItems((!query && readCachedData('favorites:' + type)) || null); load(false); }, [type, query]); @@ -380,7 +396,7 @@ function Favorites({view, hashParams, tinode, onToast}) { // 类型列表内的勾选,没有跨 tab 累加的设计)。 onClick={() => { setType(id); setSelected([]); if (TYPE_ROUTE[id] && !selecting) navigateReplace(TYPE_ROUTE[id]); }}>{label})}
- {items === null ?

加载中…

: + {items === null ?

{loadError ? '加载失败,请重试' : '加载中…'}

: list.length ? <>{list.map(item => toggle(item.favorite_id)} @@ -614,6 +630,10 @@ function History({view, hashParams, myUserId, onToast}) { const [items, setItems] = React.useState(() => (cacheKey && readCachedData(cacheKey)) || null); const [cursor, setCursor] = React.useState(''); const [hasMore, setHasMore] = React.useState(false); + // Same "stuck on 加载中… forever" gap as Favorites' loadError above, same fix -- a failed initial + // load with nothing cached to fall back on left this on the loading branch permanently, with only + // a 2s toast (already faded by the time anyone looks back) as the only sign anything went wrong. + const [loadError, setLoadError] = React.useState(false); const [mediaUrls, setMediaUrls] = React.useState({}); const [senderNames, setSenderNames] = React.useState({}); // Same stale-response guard as Favorites' load() above and for the same reason -- a slower @@ -628,15 +648,21 @@ function History({view, hashParams, myUserId, onToast}) { getTopicMedia(topic, type, append ? cursor : ''); call.then(data => { if (requestIdRef.current !== requestId) return; + setLoadError(false); setItems(prev => { const list = append ? (prev || []).concat(data.items || []) : (data.items || []); if (!query && cacheKey) writeCachedData(cacheKey, list); return list; }); setCursor(data.next_cursor || ''); setHasMore(!!data.next_cursor); - }).catch(err => onToast(err.message || '加载失败')); + }).catch(err => { + if (requestIdRef.current !== requestId) return; + onToast(err.message || '加载失败'); + if (!append) setLoadError(true); + }); }, [topic, view, query, cursor]); React.useEffect(() => { + setLoadError(false); setItems((!query && cacheKey && readCachedData(cacheKey)) || null); load(false); }, [topic, view, query]); @@ -687,7 +713,7 @@ function History({view, hashParams, myUserId, onToast}) { {view === 'history-search' ? : null}
- {items === null ?

加载中…

: !list.length ? + {items === null ?

{loadError ? '加载失败,请重试' : '加载中…'}

: !list.length ?
{view === 'history-search' ? 'search_off' : 'inbox'}暂无内容
: view === 'history-search' ?
{/* 同一处"问题13"修复暴露的缺口,History 自己的一份,只影响 type==='text':非 text 类型