diff --git a/web/src/components/MessageList.tsx b/web/src/components/MessageList.tsx
index 5dcd3a4..5d77118 100644
--- a/web/src/components/MessageList.tsx
+++ b/web/src/components/MessageList.tsx
@@ -24,6 +24,9 @@ export function MessageList() {
const activeChatId = useChatStore((s) => s.activeChatId);
const messagesByChat = useChatStore((s) => s.messagesByChat);
const typingByChat = useChatStore((s) => s.typingByChat);
+ const hasMoreByChat = useChatStore((s) => s.hasMoreByChat);
+ const loadingMoreByChat = useChatStore((s) => s.loadingMoreByChat);
+ const loadMoreMessages = useChatStore((s) => s.loadMoreMessages);
const unreadBoundaryByChat = useChatStore((s) => s.unreadBoundaryByChat);
const chats = useChatStore((s) => s.chats);
const setReplyToMessage = useChatStore((s) => s.setReplyToMessage);
@@ -61,6 +64,8 @@ export function MessageList() {
}, [chats, forwardQuery]);
const unreadBoundaryCount = activeChatId ? (unreadBoundaryByChat[activeChatId] ?? 0) : 0;
const unreadBoundaryIndex = unreadBoundaryCount > 0 ? Math.max(0, messages.length - unreadBoundaryCount) : -1;
+ const hasMore = Boolean(activeChatId && hasMoreByChat[activeChatId]);
+ const isLoadingMore = Boolean(activeChatId && loadingMoreByChat[activeChatId]);
const selectedMessages = useMemo(
() => messages.filter((m) => selectedIds.has(m.id)),
[messages, selectedIds]
@@ -254,6 +259,17 @@ export function MessageList() {
) : null}
+ {hasMore ? (
+
+
+
+ ) : null}
{messages.map((message, messageIndex) => {
const own = message.sender_id === me?.id;
const replySource = message.reply_to_message_id ? messagesMap.get(message.reply_to_message_id) : null;
diff --git a/web/src/store/chatStore.ts b/web/src/store/chatStore.ts
index 8a261bf..e22f902 100644
--- a/web/src/store/chatStore.ts
+++ b/web/src/store/chatStore.ts
@@ -7,12 +7,15 @@ interface ChatState {
activeChatId: number | null;
messagesByChat: Record
;
draftsByChat: Record;
+ hasMoreByChat: Record;
+ loadingMoreByChat: Record;
typingByChat: Record;
replyToByChat: Record;
unreadBoundaryByChat: Record;
loadChats: (query?: string) => Promise;
setActiveChatId: (chatId: number | null) => void;
loadMessages: (chatId: number) => Promise;
+ loadMoreMessages: (chatId: number) => Promise;
prependMessage: (chatId: number, message: Message) => boolean;
addOptimisticMessage: (params: {
chatId: number;
@@ -48,6 +51,8 @@ export const useChatStore = create((set, get) => ({
activeChatId: null,
messagesByChat: {},
draftsByChat: {},
+ hasMoreByChat: {},
+ loadingMoreByChat: {},
typingByChat: {},
replyToByChat: {},
unreadBoundaryByChat: {},
@@ -71,6 +76,14 @@ export const useChatStore = create((set, get) => ({
...state.unreadBoundaryByChat,
[chatId]: unreadCount
},
+ hasMoreByChat: {
+ ...state.hasMoreByChat,
+ [chatId]: messages.length >= 50
+ },
+ loadingMoreByChat: {
+ ...state.loadingMoreByChat,
+ [chatId]: false
+ },
chats: state.chats.map((chat) => (chat.id === chatId ? { ...chat, unread_count: 0 } : chat))
}));
const lastMessage = sorted[sorted.length - 1];
@@ -78,6 +91,43 @@ export const useChatStore = create((set, get) => ({
void updateMessageStatus(chatId, lastMessage.id, "message_read");
}
},
+ loadMoreMessages: async (chatId) => {
+ if (get().loadingMoreByChat[chatId]) {
+ return;
+ }
+ const existing = get().messagesByChat[chatId] ?? [];
+ if (!existing.length) {
+ await get().loadMessages(chatId);
+ return;
+ }
+ const oldestId = existing[0]?.id;
+ if (!oldestId) {
+ return;
+ }
+ set((state) => ({
+ loadingMoreByChat: { ...state.loadingMoreByChat, [chatId]: true }
+ }));
+ try {
+ const older = await getMessages(chatId, oldestId);
+ const olderSorted = [...older].reverse();
+ const knownIds = new Set(existing.map((m) => m.id));
+ const merged = [...olderSorted.filter((m) => !knownIds.has(m.id)), ...existing];
+ set((state) => ({
+ messagesByChat: {
+ ...state.messagesByChat,
+ [chatId]: merged
+ },
+ hasMoreByChat: {
+ ...state.hasMoreByChat,
+ [chatId]: older.length >= 50
+ }
+ }));
+ } finally {
+ set((state) => ({
+ loadingMoreByChat: { ...state.loadingMoreByChat, [chatId]: false }
+ }));
+ }
+ },
prependMessage: (chatId, message) => {
const old = get().messagesByChat[chatId] ?? [];
if (old.some((m) => m.id === message.id || (message.client_message_id && m.client_message_id === message.client_message_id))) {