feat: scroll-to-top loads older messages
Both ChatArea and DMChat now detect when you scroll near the top and fetch the next 50 older messages. Scroll position is preserved so you don't lose your place. Works for both server channels and DM conversations.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useRef, useState, useMemo } from "react";
|
||||
import { useEffect, useRef, useState, useMemo, useCallback } from "react";
|
||||
import { useMessageStore } from "../stores/message.ts";
|
||||
import { useChannelStore } from "../stores/channel.ts";
|
||||
import { useServerStore } from "../stores/server.ts";
|
||||
@@ -115,7 +115,9 @@ export function ChatArea() {
|
||||
activeChannelId ? s.messagesByChannel[activeChannelId] || [] : [],
|
||||
);
|
||||
const isLoading = useMessageStore((s) => s.isLoading);
|
||||
const isLoadingOlder = useMessageStore((s) => s.isLoadingOlder);
|
||||
const fetchMessages = useMessageStore((s) => s.fetchMessages);
|
||||
const fetchOlderMessages = useMessageStore((s) => s.fetchOlderMessages);
|
||||
const sendMessage = useMessageStore((s) => s.sendMessage);
|
||||
const membersByServer = useMemberStore((s) => s.membersByServer);
|
||||
const threads = useThreadStore((s) => activeChannelId ? s.threadsByParent[activeChannelId] || [] : []);
|
||||
@@ -130,6 +132,7 @@ export function ChatArea() {
|
||||
const [activeThread, setActiveThread] = useState<{ id: string; name: string } | null>(null);
|
||||
const [profileUserId, setProfileUserId] = useState<string | null>(null);
|
||||
const bottomRef = useRef<HTMLDivElement>(null);
|
||||
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const toggleSelectedMessage = useMessageStore((s) => s.toggleSelectedMessage);
|
||||
@@ -161,6 +164,20 @@ export function ChatArea() {
|
||||
}
|
||||
}, [activeChannelId, messages.length, isLoading]);
|
||||
|
||||
const handleScroll = useCallback(() => {
|
||||
const el = scrollContainerRef.current;
|
||||
if (!el || !activeChannelId || isLoadingOlder) return;
|
||||
if (el.scrollTop < 100) {
|
||||
const prevHeight = el.scrollHeight;
|
||||
fetchOlderMessages(activeChannelId).then(() => {
|
||||
// ponytail: maintain scroll position after prepending older messages
|
||||
requestAnimationFrame(() => {
|
||||
el.scrollTop = el.scrollHeight - prevHeight;
|
||||
});
|
||||
});
|
||||
}
|
||||
}, [activeChannelId, isLoadingOlder, fetchOlderMessages]);
|
||||
|
||||
useEffect(() => {
|
||||
bottomRef.current?.scrollIntoView({ behavior: "auto" });
|
||||
}, [messages]);
|
||||
@@ -352,7 +369,8 @@ export function ChatArea() {
|
||||
) : (
|
||||
<>
|
||||
<div className="flex-1 flex flex-col min-w-0">
|
||||
<div className="flex-1 overflow-y-auto p-3 space-y-1 font-mono text-sm">
|
||||
<div ref={scrollContainerRef} onScroll={handleScroll} className="flex-1 overflow-y-auto p-3 space-y-1 font-mono text-sm">
|
||||
{isLoadingOlder && <p className="text-center text-gb-fg-f text-xs">[loading older messages...]</p>}
|
||||
{isLoading && <p className="text-gb-fg-f">[loading...]</p>}
|
||||
{!isLoading && messages.length === 0 && (
|
||||
<p className="text-gb-fg-f">[no messages in this channel]</p>
|
||||
|
||||
Reference in New Issue
Block a user