Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save matthew-gerstman/d70fefe003d3db47e01c262d019ba0df to your computer and use it in GitHub Desktop.

Select an option

Save matthew-gerstman/d70fefe003d3db47e01c262d019ba0df to your computer and use it in GitHub Desktop.
OBV-1846: Message pagination implementation plan for threads with >200 messages
# OBV-1846: Message Pagination — Remaining Work
## Problem
Threads with >200 messages have older messages permanently inaccessible.
Mirror hydration loads 200 messages per thread. There is no UI to load more.
## Architecture
### Current data flow
1. **Hydration** (`/hydrate/project/:scopeId`) loads 200 most recent messages per thread
2. **useThreadSync** loads 200 messages for threads granted mid-session
3. **useThreadMessages** reads from Mirror — no fallback to API
4. **ChatThread** has unused props: `onLoadMore`, `isLoadingMore`, `hasMoreMessages` (line 89-91)
### API already supports pagination
- `chatApi.getMessages(threadId, { limit, skip, orderDirection })` — frontend client
- `messageService.findMany(threadId, { limit, skip, orderBy })` — server service
- `GET /threads/:threadId/messages?limit=N&skip=N&orderDirection=desc` — REST endpoint
## Implementation Plan
### 1. API: Return `hasMore` flag
**File: `apps/api/src/routes/messages.ts`** — GET `/` handler
Fetch `limit + 1` rows. If result exceeds limit, `hasMore = true`.
Return `{ messages, hasMore }` instead of raw array.
### 2. Dashboard API client: Update return type
**File: `dashboard/src/api/chat.api.ts`** — `getMessages`
Add `getMessagesPaginated` method returning `{ messages: Message[], hasMore: boolean }`.
Keep `getMessages` returning `Message[]` for backward compatibility.
### 3. New hook: `useLoadOlderMessages`
**New file: `dashboard/src/features/chat/chat-with-events/hooks/use-load-older-messages.ts`**
```typescript
interface UseLoadOlderMessagesArgs {
threadId: string | null | undefined
projectId: string
mirror: MirrorInstance
/** Number of messages currently in Mirror for this thread */
currentMessageCount: number
/** The hydration/initial load limit (200) — if count >= this, assume more exist */
initialLoadLimit: number
}
interface UseLoadOlderMessagesResult {
loadOlderMessages: () => Promise<void>
isLoadingMore: boolean
hasOlderMessages: boolean
}
```
- Tracks `hasMore` state: initially true if `currentMessageCount >= initialLoadLimit`
- `loadOlderMessages()`: calls `chatApi.getMessagesPaginated(threadId, { limit: 50, skip: currentCount, orderDirection: 'desc' })`
- Upserts loaded messages into Mirror via `mirror.upsertResource()`
- Updates `hasMore` from API response
### 4. Wire through component tree
**Files to modify (prop threading):**
- `dashboard/src/features/chat/chat-with-events.tsx` — call hook, pass props
- `dashboard/src/features/chat/components/chat-content.tsx` — pass through
- `dashboard/src/features/chat/chat-with-events/elements/chat-body.element.tsx` — pass through
- `dashboard/src/features/chat/components/chat-thread/chat-thread.tsx` — wire existing props to element
- `dashboard/src/features/chat/components/chat-thread/chat-thread.element.tsx` — render button
### 5. UI: "Load older messages" button
**File: `dashboard/src/features/chat/components/chat-thread/chat-thread.element.tsx`**
Add button at top of message list (inside `contentRef` div, before `ChatGroupedMessageParts`):
- Text: "Load older messages" (or spinner when loading)
- Preserve scroll position: record `scrollHeight` before load, restore offset after
- Use existing `scrollContainerRef` for position management
### 6. Scroll position preservation
When loading older messages at the top:
```typescript
const container = scrollContainerRef.current
const prevScrollHeight = container.scrollHeight
await loadOlderMessages()
// After messages render, adjust scroll to maintain visual position
requestAnimationFrame(() => {
const newScrollHeight = container.scrollHeight
container.scrollTop += newScrollHeight - prevScrollHeight
})
```
## Key files reference
- `dashboard/src/features/chat/chat-with-events/hooks/use-thread-sync.ts` — initial message loading
- `dashboard/src/hooks/use-thread-messages.ts` — message display from Mirror
- `dashboard/src/api/chat.api.ts` — API client with existing pagination params
- `apps/api/src/services/message.service.ts` — `findMany` with limit/skip/orderBy
- `apps/api/src/routes/messages.ts` — REST endpoint
- `apps/api/src/hydration/resolvers.ts` — hydration limit (currently 200)
- `dashboard/src/features/chat/components/chat-thread/hooks.ts` — scroll management
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment