From 387b25f1e33ec583f2d565e7f82a34cf9d6b078c Mon Sep 17 00:00:00 2001 From: TerryM Date: Tue, 2 Jun 2026 11:39:17 +0800 Subject: [PATCH] feat(stream): friendlier pagination loading + error retry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace the bare '…' loading dots at the bottom of the post stream with a skeleton bubble that matches the initial-load placeholders, so pagination feels like content arriving instead of a frozen indicator. - Localize the retry control via new 'retry' / 'loadMoreFailed' keys across all 7 locales and surface a user-friendly error string instead of the raw exception message. - Retry button now picks reset() vs loadMore() based on item count so a pagination failure only refetches the next page, not the whole stream. - When a banner deep-link can't find its target post and pagination errors, break out of the retry loop and release the scroll lock so the user sees the inline retry instead of an endless freeze. Verified in the browser: zh-CN renders '加载更多资料失败,请检查网络后重试。' with a '重试' button; banner clicks with empty / '#' / 'javascript:' / null linkUrls render no anchor and do not navigate. --- .../messageStream/MessageStream.tsx | 34 ++++++++++++++----- src/locales/en.ts | 3 ++ src/locales/id.ts | 3 ++ src/locales/ja.ts | 3 ++ src/locales/ko.ts | 2 ++ src/locales/ms.ts | 2 ++ src/locales/vi.ts | 2 ++ src/locales/zh-CN.ts | 2 ++ 8 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/components/messageStream/MessageStream.tsx b/src/components/messageStream/MessageStream.tsx index 7cca259..d8b2215 100644 --- a/src/components/messageStream/MessageStream.tsx +++ b/src/components/messageStream/MessageStream.tsx @@ -31,7 +31,7 @@ export function MessageStream({ scope }: MessageStreamProps) { const { items, isLoading, error, hasMore, loadMore, reset } = usePostStream(params); const groups = useGroupedByDay(items, lang); - const retryLabel = lang === "zh-CN" ? "重试" : "Retry"; + const retryLabel = t("retry"); const sentinelRef = useRef(null); const filterBarRef = useRef(null); @@ -213,10 +213,17 @@ export function MessageStream({ scope }: MessageStreamProps) { return; } - // Not loaded yet — keep paging until it appears or the stream is exhausted. + // Not loaded yet — keep paging until it appears or the stream is + // exhausted. If the previous loadMore errored, stop the loop so the user + // sees the inline retry button instead of an endless retry cycle, and + // release the scroll lock so they can interact with the page. + if (error) { + setIsAligningQueryTarget(false); + return; + } if (hasMore && !isLoading) loadMore(); else if (!hasMore && !isLoading) setIsAligningQueryTarget(false); - }, [targetPostId, items, hasMore, isLoading, loadMore]); + }, [targetPostId, items, hasMore, isLoading, error, loadMore]); const updateParam = (key: string, value: string) => { const n = new URLSearchParams(sp); @@ -273,20 +280,29 @@ export function MessageStream({ scope }: MessageStreamProps) { ) : null} {error ? ( -
- {error} +
+ {t("loadMoreFailed")}
) : null} - {isLoading ? ( -
+ {isLoading && !error ? ( +
+ +
) : null} )} diff --git a/src/locales/en.ts b/src/locales/en.ts index 16636d7..173b4cf 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -126,6 +126,9 @@ export const enDict: Dict = { tagsCommaLabel: "Tags (comma-separated)", uploadFile: "Upload", loading: "Loading…", + loadMoreFailed: + "Couldn't load more posts. Check your connection and try again.", + retry: "Retry", paginationPrev: "Previous", paginationNext: "Next", listRange: "Showing {{from}}–{{to}} of {{total}}", diff --git a/src/locales/id.ts b/src/locales/id.ts index 7223cd5..f208572 100644 --- a/src/locales/id.ts +++ b/src/locales/id.ts @@ -126,6 +126,9 @@ export const idDict: Dict = { tagsCommaLabel: "Tag (dipisahkan koma)", uploadFile: "Unggah", loading: "Memuat…", + loadMoreFailed: + "Gagal memuat lebih banyak. Periksa koneksi Anda dan coba lagi.", + retry: "Coba lagi", paginationPrev: "Sebelumnya", paginationNext: "Berikutnya", listRange: "Menampilkan {{from}}–{{to}} dari {{total}}", diff --git a/src/locales/ja.ts b/src/locales/ja.ts index 42c37a8..ae0327b 100644 --- a/src/locales/ja.ts +++ b/src/locales/ja.ts @@ -127,6 +127,9 @@ export const jaDict: Dict = { tagsCommaLabel: "タグ(カンマ区切り)", uploadFile: "アップロード", loading: "読み込み中…", + loadMoreFailed: + "追加の読み込みに失敗しました。接続を確認してやり直してください。", + retry: "再試行", paginationPrev: "前へ", paginationNext: "次へ", listRange: "{{from}}–{{to}} / 全 {{total}} 件", diff --git a/src/locales/ko.ts b/src/locales/ko.ts index 9c45d40..2838d05 100644 --- a/src/locales/ko.ts +++ b/src/locales/ko.ts @@ -126,6 +126,8 @@ export const koDict: Dict = { tagsCommaLabel: "태그 (쉼표로 구분)", uploadFile: "업로드", loading: "로딩 중…", + loadMoreFailed: "더 불러오지 못했습니다. 연결을 확인하고 다시 시도하세요.", + retry: "다시 시도", paginationPrev: "이전", paginationNext: "다음", listRange: "{{from}}–{{to}} / 총 {{total}}건", diff --git a/src/locales/ms.ts b/src/locales/ms.ts index 2e41365..d1b1f31 100644 --- a/src/locales/ms.ts +++ b/src/locales/ms.ts @@ -126,6 +126,8 @@ export const msDict: Dict = { tagsCommaLabel: "Tag (dipisahkan koma)", uploadFile: "Muat naik", loading: "Memuatkan…", + loadMoreFailed: "Gagal memuatkan lagi. Sila semak sambungan dan cuba lagi.", + retry: "Cuba lagi", paginationPrev: "Sebelum", paginationNext: "Seterusnya", listRange: "Menunjukkan {{from}}–{{to}} daripada {{total}}", diff --git a/src/locales/vi.ts b/src/locales/vi.ts index 0fe7df3..f7a7acd 100644 --- a/src/locales/vi.ts +++ b/src/locales/vi.ts @@ -126,6 +126,8 @@ export const viDict: Dict = { tagsCommaLabel: "Thẻ (cách nhau bằng dấu phẩy)", uploadFile: "Tải lên", loading: "Đang tải…", + loadMoreFailed: "Không thể tải thêm bài. Hãy kiểm tra kết nối và thử lại.", + retry: "Thử lại", paginationPrev: "Trước", paginationNext: "Sau", listRange: "Hiển thị {{from}}–{{to}} trên {{total}}", diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index 44924f2..10da479 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -124,6 +124,8 @@ export const zhDict: Dict = { tagsCommaLabel: "标签(逗号分隔)", uploadFile: "上传文件", loading: "加载中…", + loadMoreFailed: "加载更多资料失败,请检查网络后重试。", + retry: "重试", paginationPrev: "上一页", paginationNext: "下一页", listRange: "显示 {{from}}–{{to}},共 {{total}} 条",