feat(frontend): load older messages in Messages view (cursor pagination)
Wire the existing useLoadMore + useMessagesHasMore pagination hooks into the Messages view: add a "↑ Load older messages" button at the top of the list and auto-load the next (older) page when the user scrolls to the top. Backend messages.list already returns a created_at-based nextCursor (DESC order), so older pages are just subsequent cursors. Newest-first live feed is preserved; the load-older control is hidden during search.
This commit is contained in:
@@ -27,9 +27,11 @@ import {
|
|||||||
} from "@/components/shared";
|
} from "@/components/shared";
|
||||||
import { GuildChannelPicker } from "@/components/shared/guild-picker";
|
import { GuildChannelPicker } from "@/components/shared/guild-picker";
|
||||||
import {
|
import {
|
||||||
|
useLoadMore,
|
||||||
useMessageDetail,
|
useMessageDetail,
|
||||||
useMessageSearch,
|
useMessageSearch,
|
||||||
useMessages,
|
useMessages,
|
||||||
|
useMessagesHasMore,
|
||||||
useMessagesWsSync,
|
useMessagesWsSync,
|
||||||
} from "@/hooks";
|
} from "@/hooks";
|
||||||
import {
|
import {
|
||||||
@@ -82,6 +84,11 @@ export function MessagesView({
|
|||||||
isLoading,
|
isLoading,
|
||||||
error,
|
error,
|
||||||
} = useMessages(guildId ?? "", channelId ?? undefined);
|
} = useMessages(guildId ?? "", channelId ?? undefined);
|
||||||
|
// Cursor to the next (older) page + whether more history exists.
|
||||||
|
const { data: pageInfo } = useMessagesHasMore(guildId ?? "", channelId ?? undefined);
|
||||||
|
const nextCursor = pageInfo?.cursor ?? null;
|
||||||
|
const hasMore = pageInfo?.hasMore ?? false;
|
||||||
|
const loadMore = useLoadMore();
|
||||||
useMessagesWsSync(ws, guildId ?? "");
|
useMessagesWsSync(ws, guildId ?? "");
|
||||||
const search = useMessageSearch(query, query.trim().length >= 2);
|
const search = useMessageSearch(query, query.trim().length >= 2);
|
||||||
const detail = useMessageDetail(selected);
|
const detail = useMessageDetail(selected);
|
||||||
@@ -141,8 +148,49 @@ export function MessagesView({
|
|||||||
description="Pick a guild to begin, or run a search."
|
description="Pick a guild to begin, or run a search."
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="max-h-[60vh] space-y-1.5 overflow-y-auto pr-1">
|
<div>
|
||||||
{list.map((m) => (
|
{!searching && (
|
||||||
|
<div className="mb-2 flex items-center justify-center gap-2">
|
||||||
|
{hasMore ? (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() =>
|
||||||
|
loadMore.mutate({
|
||||||
|
guildId: guildId ?? "",
|
||||||
|
channelId: channelId ?? undefined,
|
||||||
|
cursor: nextCursor ?? "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
disabled={loadMore.isPending}
|
||||||
|
className="rounded-full border border-hairline bg-white/[0.03] px-3 py-1 text-xs text-ink-soft transition-colors hover:bg-white/[0.06] disabled:opacity-60"
|
||||||
|
>
|
||||||
|
{loadMore.isPending ? "Loading older…" : "↑ Load older messages"}
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
messages &&
|
||||||
|
messages.length > 0 && (
|
||||||
|
<span className="mono text-[0.65rem] text-ink-faint">
|
||||||
|
beginning of history
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div
|
||||||
|
className="max-h-[60vh] space-y-1.5 overflow-y-auto pr-1"
|
||||||
|
onScroll={(e) => {
|
||||||
|
// Auto-load older messages when the user scrolls to the top.
|
||||||
|
if (searching || !hasMore || loadMore.isPending) return;
|
||||||
|
if (e.currentTarget.scrollTop <= 8) {
|
||||||
|
loadMore.mutate({
|
||||||
|
guildId: guildId ?? "",
|
||||||
|
channelId: channelId ?? undefined,
|
||||||
|
cursor: nextCursor ?? "",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{list.map((m) => (
|
||||||
<button
|
<button
|
||||||
key={m.id}
|
key={m.id}
|
||||||
type="button"
|
type="button"
|
||||||
@@ -181,6 +229,7 @@ export function MessagesView({
|
|||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</GlassPanel>
|
</GlassPanel>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user