Skip to content

feat(sidebar): add recent conversation feed - #1041

Open
sudo-eugene wants to merge 1 commit into
siteboon:mainfrom
TheWebEng:codex/recent-conversations-feed
Open

feat(sidebar): add recent conversation feed#1041
sudo-eugene wants to merge 1 commit into
siteboon:mainfrom
TheWebEng:codex/recent-conversations-feed

Conversation

@sudo-eugene

@sudo-eugene sudo-eugene commented Jul 18, 2026

Copy link
Copy Markdown

Related to #904

What changed

The Conversations tab now opens a flat, globally ordered feed instead of repeating the project hierarchy. Project and provider information remain visible as row context, while typing two or more characters still switches to the existing full-message search.

Why

The previous tab label suggested a distinct conversation-oriented view, but selecting it still presented the same project-first structure as the Projects tab. That made the control difficult to understand and forced people to remember which project contained a recent conversation before they could resume it.

Building the feed by flattening the sessions already loaded for each project would produce incomplete or incorrectly ordered results because project histories are paginated independently. This change therefore orders and paginates visible conversations at the database boundary, where the complete history is available. Archived sessions and sessions owned by archived projects stay out of the active feed.

The project hierarchy remains unchanged in Projects. In Conversations, the project name becomes secondary metadata rather than another navigation layer, preserving orientation without obscuring the primary task: returning to recent work.

Before and after

Before After
recent-conversations-before recent-conversations-after

Test plan

  • npm run build
  • npm run typecheck
  • npm run lint — completed with 0 errors; existing repository warnings remain
  • npx --no-install tsx --tsconfig server/tsconfig.json --test server/modules/database/tests/sessions.db.integration.test.ts — 4 tests passed
  • Verified locally that the feed is globally sorted, loads from 40 to 80 rows without duplicates, preserves full-message search, and opens the selected conversation

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Recent conversations can now be fetched through a paginated provider API, normalized by the sessions service, managed by the sidebar controller, and rendered with loading, error, empty, selection, and load-more states. Shared compact age formatting is used across session views.

Changes

Recent conversations

Layer / File(s) Summary
Session data and provider API
server/modules/database/repositories/sessions.db.ts, server/modules/database/tests/sessions.db.integration.test.ts, server/modules/providers/...
Adds visibility-filtered global pagination, project metadata normalization, bounded query parsing, the /sessions/recent endpoint, and integration coverage.
Recent conversation controller state
src/utils/api.js, src/components/sidebar/hooks/useSidebarController.ts, src/components/sidebar/types/types.ts
Adds the authenticated API call, paginated controller state, stale-response protection, refresh behavior, and recent conversation types.
Sidebar recent conversation rendering
src/components/sidebar/view/..., src/i18n/locales/en/sidebar.json
Adds recent conversation UI states, selection and load-more handling, sidebar wiring, and localized strings.
Shared compact age formatting
src/components/sidebar/utils/utils.ts, src/components/sidebar/view/subcomponents/SidebarContent.tsx, src/components/sidebar/view/subcomponents/SidebarSessionItem.tsx
Centralizes compact elapsed-time formatting and applies it to sidebar session displays.

Sequence Diagram(s)

sequenceDiagram
  participant Sidebar as SidebarRecentConversations
  participant Controller as useSidebarController
  participant API as api.recentConversations
  participant Route as provider recent route
  participant Service as sessionsService.listRecentSessions
  participant Database as sessionsDb.getRecentSessionsPage
  Sidebar->>Controller: reload or load more conversations
  Controller->>API: request limit and offset
  API->>Route: GET /api/providers/sessions/recent
  Route->>Service: listRecentSessions(limit, offset)
  Service->>Database: getRecentSessionsPage(limit, offset)
  Database-->>Service: sessions and total
  Service-->>Route: conversations and hasMore
  Route-->>API: success response
  API-->>Controller: paginated payload
  Controller-->>Sidebar: render conversation states and rows
Loading

Suggested reviewers: blackmammoth, viper151

Poem

I’m a rabbit with pages to turn,
Recent sessions hop and return.
Through the sidebar they race,
With timestamps keeping pace—
Load more, little carrots, and learn!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding a recent conversation feed to the sidebar.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
server/modules/providers/provider.routes.ts (1)

367-373: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use strict integer parsing for query parameters.

Number.parseInt tolerates floating-point values (truncating them) and trailing non-numeric characters (e.g., '10.5' or '10abc' parses as 10 without failing). For stricter query validation, consider using Number(raw) and Number.isInteger().

♻️ Proposed refactor
-  const parsed = Number.parseInt(raw, 10);
-  if (Number.isNaN(parsed) || parsed < minimum || parsed > maximum) {
+  const parsed = Number(raw);
+  if (!Number.isInteger(parsed) || parsed < minimum || parsed > maximum) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/modules/providers/provider.routes.ts` around lines 367 - 373, Update
the query-parameter validation around the parsed value to convert raw input with
Number(raw) and reject any result that is not an integer, while preserving the
existing minimum and maximum bounds and AppError details. Replace the
Number.parseInt-based check so values such as decimals and trailing characters
are invalid.
server/modules/database/repositories/sessions.db.ts (1)

335-336: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Consider removing julianday() for better performance.

If updated_at and created_at are stored as standard ISO 8601 strings (which sort correctly lexicographically), wrapping them in julianday() is unnecessary. It forces SQLite to evaluate the function for every row during the sort phase and prevents the query engine from utilizing an index on those columns.

Removing it allows for potential full-index scans if an index is added in the future.

♻️ Proposed refactor
-         ORDER BY julianday(COALESCE(sessions.updated_at, sessions.created_at)) DESC,
+         ORDER BY COALESCE(sessions.updated_at, sessions.created_at) DESC,
                   sessions.session_id DESC
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/modules/database/repositories/sessions.db.ts` around lines 335 - 336,
Update the session query’s ORDER BY clause to sort COALESCE(sessions.updated_at,
sessions.created_at) directly, removing the julianday() wrapper while preserving
descending timestamp order and the sessions.session_id DESC tie-breaker.
server/modules/providers/services/sessions.service.ts (1)

119-129: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Leverage the existing DB join to avoid N+1 lookups.

The sessionsDb.getRecentSessionsPage query already performs a LEFT JOIN on the projects table for visibility filtering. Instead of querying projectsDb.getProjectPath for each unique project in a loop, you can add projects.project_id and projects.custom_project_name to the SELECT clause in the DB query.

This would eliminate the N+1 lookups and simplify the mapping logic here, completely removing the need for projectCache.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/modules/providers/services/sessions.service.ts` around lines 119 -
129, Update sessionsDb.getRecentSessionsPage to select projects.project_id and
projects.custom_project_name from its existing projects LEFT JOIN, then use
those joined fields directly when mapping page.sessions in the sessions service.
Remove the projectsDb.getProjectPath calls, projectCache, and related
per-project lookup logic while preserving the current null behavior for sessions
without a project.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@server/modules/database/repositories/sessions.db.ts`:
- Around line 335-336: Update the session query’s ORDER BY clause to sort
COALESCE(sessions.updated_at, sessions.created_at) directly, removing the
julianday() wrapper while preserving descending timestamp order and the
sessions.session_id DESC tie-breaker.

In `@server/modules/providers/provider.routes.ts`:
- Around line 367-373: Update the query-parameter validation around the parsed
value to convert raw input with Number(raw) and reject any result that is not an
integer, while preserving the existing minimum and maximum bounds and AppError
details. Replace the Number.parseInt-based check so values such as decimals and
trailing characters are invalid.

In `@server/modules/providers/services/sessions.service.ts`:
- Around line 119-129: Update sessionsDb.getRecentSessionsPage to select
projects.project_id and projects.custom_project_name from its existing projects
LEFT JOIN, then use those joined fields directly when mapping page.sessions in
the sessions service. Remove the projectsDb.getProjectPath calls, projectCache,
and related per-project lookup logic while preserving the current null behavior
for sessions without a project.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 23820a98-d0d5-462a-8348-df94a6076b1d

📥 Commits

Reviewing files that changed from the base of the PR and between 27eaf01 and a3a6d33.

📒 Files selected for processing (13)
  • server/modules/database/repositories/sessions.db.ts
  • server/modules/database/tests/sessions.db.integration.test.ts
  • server/modules/providers/provider.routes.ts
  • server/modules/providers/services/sessions.service.ts
  • src/components/sidebar/hooks/useSidebarController.ts
  • src/components/sidebar/types/types.ts
  • src/components/sidebar/utils/utils.ts
  • src/components/sidebar/view/Sidebar.tsx
  • src/components/sidebar/view/subcomponents/SidebarContent.tsx
  • src/components/sidebar/view/subcomponents/SidebarRecentConversations.tsx
  • src/components/sidebar/view/subcomponents/SidebarSessionItem.tsx
  • src/i18n/locales/en/sidebar.json
  • src/utils/api.js

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant