From 46551255412f33b403107d2b5e2202a0a81b5549 Mon Sep 17 00:00:00 2001 From: asepharyana Date: Sun, 16 Aug 2026 16:21:10 +0700 Subject: [PATCH] feat(frontend): clearer load-older spinner + cap history pages (Messages) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Show an explicit Loader2 spinner row ("Loading older…") while the next page fetches, instead of a disabled button. - Cap appended older pages at MAX_OLDER_PAGES=10 (500 messages) so a long scroll-up never pulls the entire history; show a "capped" hint pointing to search. Reset the counter when guild/channel changes. --- .../src/app/(dashboard)/messages/view.tsx | 49 ++++++++++++------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/services/frontend/src/app/(dashboard)/messages/view.tsx b/services/frontend/src/app/(dashboard)/messages/view.tsx index 0215e92..e9cab1b 100644 --- a/services/frontend/src/app/(dashboard)/messages/view.tsx +++ b/services/frontend/src/app/(dashboard)/messages/view.tsx @@ -10,7 +10,7 @@ import { Search, ShieldAlert, } from "lucide-react"; -import { useEffect, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import { useAmbient } from "@/components/ambient/ambient-context"; import { Avatar, @@ -78,6 +78,10 @@ export function MessagesView({ const [channelId, setChannelId] = useState(null); const [selected, setSelected] = useState(null); const [query, setQuery] = useState(""); + // Guard against loading the entire history on a long scroll: cap how many + // older pages we append. Each page is 50 messages (backend limit default). + const MAX_OLDER_PAGES = 10; + const [loadedPages, setLoadedPages] = useState(0); const { data: messages, @@ -94,6 +98,17 @@ export function MessagesView({ const detail = useMessageDetail(selected); const ambient = useAmbient(); + // Fetch the next (older) page via cursor and bump the loaded-page counter. + const loadOlder = useCallback(async () => { + if (!guildId || !hasMore || loadedPages >= MAX_OLDER_PAGES) return; + await loadMore.mutateAsync({ + guildId, + channelId: channelId ?? undefined, + cursor: nextCursor ?? "", + }); + setLoadedPages((n) => n + 1); + }, [guildId, channelId, hasMore, loadedPages, nextCursor, loadMore]); + useEffect(() => { ambient.set(query ? "amber" : "signal", 0.3, query ? "search" : "messages"); }, [query, ambient]); @@ -113,6 +128,7 @@ export function MessagesView({ setGuildId(g); setChannelId(c); setSelected(null); + setLoadedPages(0); }} />
@@ -151,21 +167,23 @@ export function MessagesView({
{!searching && (
- {hasMore ? ( + {loadMore.isPending ? ( + + + Loading older… + + ) : hasMore && loadedPages < MAX_OLDER_PAGES ? ( + ) : loadedPages >= MAX_OLDER_PAGES ? ( + + capped at {MAX_OLDER_PAGES} older pages · use search for more + ) : ( messages && messages.length > 0 && ( @@ -181,12 +199,9 @@ export function MessagesView({ onScroll={(e) => { // Auto-load older messages when the user scrolls to the top. if (searching || !hasMore || loadMore.isPending) return; + if (loadedPages >= MAX_OLDER_PAGES) return; if (e.currentTarget.scrollTop <= 8) { - loadMore.mutate({ - guildId: guildId ?? "", - channelId: channelId ?? undefined, - cursor: nextCursor ?? "", - }); + loadOlder(); } }} >