-
Notifications
You must be signed in to change notification settings - Fork 5
feat: Add multi-chat delete functionality #352
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Deploying maple with
|
| Latest commit: |
3fadde1
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://04995de0.maple-ca8.pages.dev |
| Branch Preview URL: | https://feature-multi-chat-delete.maple-ca8.pages.dev |
Greptile SummaryThis PR adds multi-chat deletion functionality with a polished UX that matches modern mobile and desktop patterns. Users can enter selection mode by clicking "Select" in the dropdown menu or long-pressing on mobile, select up to 20 chats with visual checkboxes, and batch delete with confirmation. Key changes:
The implementation is clean and follows existing patterns in the codebase. Selection mode UX could be slightly snappier by immediately entering mode when clicking "Select" rather than relying on Confidence Score: 4/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant User
participant Sidebar
participant ChatHistoryList
participant BulkDeleteDialog
participant OpenSecret API
participant LocalState
User->>ChatHistoryList: Click "Select" in dropdown
ChatHistoryList->>Sidebar: onSelectionChange(Set([chatId]))
Sidebar->>Sidebar: useEffect detects selectedIds.size > 0
Sidebar->>Sidebar: setIsSelectionMode(true)
Sidebar->>ChatHistoryList: Pass isSelectionMode=true
ChatHistoryList->>User: Show checkboxes & selection UI
User->>ChatHistoryList: Click additional chats (up to 20)
ChatHistoryList->>ChatHistoryList: toggleSelection(chatId)
ChatHistoryList->>Sidebar: onSelectionChange(new Set)
User->>Sidebar: Click "Delete" button
Sidebar->>Window: dispatchEvent("openbulkdelete")
Window->>ChatHistoryList: Event listener triggered
ChatHistoryList->>BulkDeleteDialog: setIsBulkDeleteDialogOpen(true)
BulkDeleteDialog->>User: Show confirmation dialog
User->>BulkDeleteDialog: Confirm deletion
BulkDeleteDialog->>ChatHistoryList: onConfirm()
ChatHistoryList->>ChatHistoryList: handleBulkDelete()
alt API Conversations
ChatHistoryList->>OpenSecret API: batchDeleteConversations(ids)
OpenSecret API-->>ChatHistoryList: {data: [{id, deleted}]}
ChatHistoryList->>ChatHistoryList: Remove deleted from state
end
alt Archived Chats
loop For each archived chat
ChatHistoryList->>LocalState: deleteChat(id)
LocalState-->>ChatHistoryList: Success
end
ChatHistoryList->>ChatHistoryList: invalidateQueries("archivedChats")
end
alt Current chat deleted
ChatHistoryList->>Window: Update URL & dispatch "newchat"
end
ChatHistoryList->>Sidebar: onExitSelectionMode()
Sidebar->>Sidebar: Clear selection state
Sidebar->>ChatHistoryList: isSelectionMode=false
|
📝 WalkthroughWalkthroughAdds multi-chat selection and bulk-delete UI and logic: a new BulkDeleteDialog component, selection mode with checkboxes and mobile long-press in ChatHistoryList, and Sidebar controls to manage selections and trigger bulk deletion. Also bumps Changes
Sequence DiagramsequenceDiagram
participant User
participant Sidebar
participant ChatHistoryList
participant BulkDeleteDialog
participant API
rect rgb(240,248,255)
Note over User,ChatHistoryList: Enter selection
User->>ChatHistoryList: Long-press item / click to select
ChatHistoryList->>ChatHistoryList: Toggle selection, update selectedIds
ChatHistoryList->>Sidebar: onSelectionChange(selectedIds)
Sidebar->>Sidebar: Enter selection mode / render control bar
end
rect rgb(255,250,240)
User->>Sidebar: Click Delete
Sidebar->>BulkDeleteDialog: Open (count = selectedIds.size)
User->>BulkDeleteDialog: Confirm
BulkDeleteDialog->>Sidebar: onConfirm
Sidebar->>API: Delete selected conversations (batch) / archived items individually
API-->>Sidebar: Deletion result
Sidebar->>ChatHistoryList: onExitSelectionMode()
ChatHistoryList->>ChatHistoryList: Clear selections, exit mode
ChatHistoryList->>Sidebar: onSelectionChange(empty set)
Sidebar->>Sidebar: Exit selection mode / refresh UI
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches✅ Passed checks (4 passed)
✨ Finishing touches
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
frontend/src/components/ChatHistoryList.tsx (3)
26-34:selectedIdsandonSelectionChangeshould either be optional or have defaults.The props
selectedIdsandonSelectionChangeare required butisSelectionModedefaults tofalse. If the component is used without selection support, this could cause runtime issues.🔎 Suggested fix: Make selection props optional with defaults
interface ChatHistoryListProps { currentChatId?: string; searchQuery?: string; isMobile?: boolean; isSelectionMode?: boolean; onExitSelectionMode?: () => void; - selectedIds: Set<string>; - onSelectionChange: (ids: Set<string>) => void; + selectedIds?: Set<string>; + onSelectionChange?: (ids: Set<string>) => void; }Then update the destructuring to provide defaults:
isSelectionMode = false, onExitSelectionMode, - selectedIds, - onSelectionChange + selectedIds = new Set(), + onSelectionChange = () => {} }: ChatHistoryListProps) {
344-362: Consider providing user feedback when selection limit is reached.The
toggleSelectionsilently returns whenMAX_SELECTIONis hit. Users may not understand why additional selections aren't working.A toast notification or visual indicator when the 20-chat limit is reached would improve UX.
364-427: Bulk delete logic handles partial failures well, but users won't see error feedback.The implementation correctly:
- Separates archived vs API conversations
- Only removes successfully deleted items from state (Line 383-385)
- Cleans up selection state after completion
However, if
batchDeleteConversationsfails or partially fails, errors are only logged to console. Consider surfacing errors to users via toast notification.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
frontend/bun.lockis excluded by!**/*.lock
📒 Files selected for processing (4)
frontend/package.jsonfrontend/src/components/BulkDeleteDialog.tsxfrontend/src/components/ChatHistoryList.tsxfrontend/src/components/Sidebar.tsx
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use path aliases (@/*maps to./src/*) for imports in TypeScript/React files
Use 2-space indentation, double quotes, and enforce 100-character line limit in TypeScript/React code
Maintain strict TypeScript and avoid usinganytype
Use PascalCase for component names and camelCase for variables and function names
Use functional components with React hooks instead of class components
Use React context for global state management and TanStack Query for server state management
Runjust format,just lint, andjust buildafter making TypeScript/React changes to ensure code quality and compilation
Files:
frontend/src/components/BulkDeleteDialog.tsxfrontend/src/components/Sidebar.tsxfrontend/src/components/ChatHistoryList.tsx
🧠 Learnings (3)
📚 Learning: 2025-12-08T19:55:33.330Z
Learnt from: CR
Repo: OpenSecretCloud/Maple PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-08T19:55:33.330Z
Learning: Applies to **/*.{ts,tsx} : Use React context for global state management and TanStack Query for server state management
Applied to files:
frontend/src/components/Sidebar.tsxfrontend/src/components/ChatHistoryList.tsx
📚 Learning: 2025-12-08T19:55:33.330Z
Learnt from: CR
Repo: OpenSecretCloud/Maple PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-08T19:55:33.330Z
Learning: Applies to src/components/ui/**/*.{ts,tsx} : Use existing shadcn/ui components from `src/components/ui/` for UI elements instead of creating custom components
Applied to files:
frontend/src/components/Sidebar.tsx
📚 Learning: 2025-12-08T19:55:33.330Z
Learnt from: CR
Repo: OpenSecretCloud/Maple PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-08T19:55:33.330Z
Learning: Applies to **/*.{ts,tsx} : Use functional components with React hooks instead of class components
Applied to files:
frontend/src/components/Sidebar.tsxfrontend/src/components/ChatHistoryList.tsx
🧬 Code graph analysis (2)
frontend/src/components/BulkDeleteDialog.tsx (1)
frontend/src/components/ui/alert-dialog.tsx (8)
AlertDialog(104-104)AlertDialogContent(108-108)AlertDialogHeader(109-109)AlertDialogTitle(111-111)AlertDialogDescription(112-112)AlertDialogFooter(110-110)AlertDialogCancel(114-114)AlertDialogAction(113-113)
frontend/src/components/ChatHistoryList.tsx (3)
frontend/src/components/ui/checkbox.tsx (1)
Checkbox(26-26)frontend/src/components/ui/dropdown-menu.tsx (4)
DropdownMenu(170-170)DropdownMenuTrigger(171-171)DropdownMenuContent(172-172)DropdownMenuItem(173-173)frontend/src/components/BulkDeleteDialog.tsx (1)
BulkDeleteDialog(20-57)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: build-android
- GitHub Check: build-ios
- GitHub Check: build-macos (universal-apple-darwin)
- GitHub Check: Cloudflare Pages
🔇 Additional comments (11)
frontend/src/components/BulkDeleteDialog.tsx (1)
20-57: Well-structured confirmation dialog.Clean implementation with proper handling:
- Pluralization logic for count-based messaging
isDeletingstate disables buttons to prevent double-submite.preventDefault()ensures the dialog stays open during async deletion- Destructive styling clearly indicates the action severity
frontend/src/components/Sidebar.tsx (4)
33-47: Selection state management looks good.The auto-activation of selection mode when items are selected (e.g., via long-press from ChatHistoryList) provides smooth UX. The
exitSelectionModecallback properly clears both states.
49-53: Event-based approach for triggering bulk delete dialog is reasonable.This decouples the Sidebar from the dialog implementation in ChatHistoryList. The guard condition prevents unnecessary events.
190-230: Selection mode header UI is well-implemented.Clear visual distinction between modes with appropriate controls. The cancel button, selection count, and destructive delete button provide good UX.
255-263: Props correctly wire selection state between Sidebar and ChatHistoryList.The lifted state pattern allows coordinated selection management across components.
frontend/src/components/ChatHistoryList.tsx (5)
429-447: Long-press handlers are correctly implemented.The 500ms threshold is appropriate, and the timer cleanup prevents memory leaks and ghost activations.
449-458: Event listener properly coordinates with Sidebar's delete trigger.Cleanup is correctly handled in the effect's return function.
590-617: Selection UI integration is well-executed.Good implementation choices:
- Click behavior adapts based on
isSelectionMode- Checkbox stops propagation to prevent double-toggle
- Long-press handlers are guarded by
isMobilecheck- Visual feedback with
bg-primary/10for selected items
676-744: Archived chats section doesn't support bulk selection.This may be intentional, but users might expect consistent behavior. Consider adding selection support to archived chats in a follow-up, or document this limitation.
764-770: BulkDeleteDialog correctly integrated.All props are properly connected to component state and handlers.
frontend/package.json (1)
20-20: LGTM – v1.5.3 exportsbatchDeleteConversationsand is correctly used in ChatHistoryList.tsx.
|
@TestFlight build |
|
🚀 TestFlight deployment triggered! Check the Actions tab for progress. |
|
✅ TestFlight deployment completed successfully! |
38ed7b4 to
61bfbeb
Compare
|
@TestFlight build |
|
🚀 TestFlight deployment triggered! Check the Actions tab for progress. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/components/ChatHistoryList.tsx (1)
677-745: Archived chats excluded from selection mode – handleBulkDelete contains unreachable code.The archived chats section (lines 677-745) does not render selection checkboxes or integrate with selection mode, making it impossible for archived chat IDs to end up in
selectedIds. Consequently, the archived chat handling logic inhandleBulkDelete(lines 373-398) is unreachable dead code that will never execute.This appears intentional—archived chats are managed separately via the dropdown menu (Rename Chat, Delete Chat options). Either add a code comment in
handleBulkDeleteexplaining the archived chat filtering is defensive/historical, or remove the unreachable archived chat branch to clarify intent.
🧹 Nitpick comments (3)
frontend/src/components/Sidebar.tsx (1)
190-231: Selection mode UI is well-designed.Good UX considerations:
- Clear visual distinction between modes
- "max" indicator when limit (20) is reached
- Delete button disabled when no items selected
- Proper
aria-labelfor accessibility on the cancel buttonOne minor note: the magic number
20on Line 204 should ideally reference the same constant (MAX_SELECTION) defined inChatHistoryList.tsxto avoid duplication. Consider extracting it to a shared location.frontend/src/components/ChatHistoryList.tsx (2)
344-362: Selection toggle logic is correct.The implementation properly creates a new
Setto avoid mutation and correctly enforces theMAX_SELECTIONlimit.Consider adding user feedback (e.g., a toast notification) when the selection limit is reached, as the silent
returnmay leave users confused about why additional items can't be selected.
365-427: Bulk delete implementation handles the happy path well.Good practices:
- Separates archived chats from API conversations for different handling
- Uses batch API (
batchDeleteConversations) for efficiency- Correctly filters only successfully deleted items from local state using
result.data- Properly handles navigation when current chat is deleted
A few considerations:
Error feedback: On Line 414, errors are logged but no user feedback is provided. Consider adding a toast notification for failures.
Partial failure resilience: The sequential loop for archived chats (Lines 390-394) could fail midway. The code continues and clears selection regardless, which may leave the UI in an inconsistent state if some deletions failed.
🔎 Suggested improvement for error handling
// Delete archived chats individually + const failedArchivedIds: string[] = []; for (const id of archivedIds) { if (localState?.deleteChat) { - await localState.deleteChat(id); + try { + await localState.deleteChat(id); + } catch (err) { + console.error(`Failed to delete archived chat ${id}:`, err); + failedArchivedIds.push(id); + } } } + + // Optionally notify user of partial failures + if (failedArchivedIds.length > 0) { + // Consider showing a toast: `${failedArchivedIds.length} chat(s) failed to delete` + }
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
frontend/bun.lockis excluded by!**/*.lock
📒 Files selected for processing (4)
frontend/package.jsonfrontend/src/components/BulkDeleteDialog.tsxfrontend/src/components/ChatHistoryList.tsxfrontend/src/components/Sidebar.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/package.json
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use path aliases (@/*maps to./src/*) for imports in TypeScript/React files
Use 2-space indentation, double quotes, and enforce 100-character line limit in TypeScript/React code
Maintain strict TypeScript and avoid usinganytype
Use PascalCase for component names and camelCase for variables and function names
Use functional components with React hooks instead of class components
Use React context for global state management and TanStack Query for server state management
Runjust format,just lint, andjust buildafter making TypeScript/React changes to ensure code quality and compilation
Files:
frontend/src/components/BulkDeleteDialog.tsxfrontend/src/components/ChatHistoryList.tsxfrontend/src/components/Sidebar.tsx
🧠 Learnings (3)
📚 Learning: 2025-12-08T19:55:33.330Z
Learnt from: CR
Repo: OpenSecretCloud/Maple PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-08T19:55:33.330Z
Learning: Applies to **/*.{ts,tsx} : Use React context for global state management and TanStack Query for server state management
Applied to files:
frontend/src/components/ChatHistoryList.tsxfrontend/src/components/Sidebar.tsx
📚 Learning: 2025-12-08T19:55:33.330Z
Learnt from: CR
Repo: OpenSecretCloud/Maple PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-08T19:55:33.330Z
Learning: Applies to **/*.{ts,tsx} : Use functional components with React hooks instead of class components
Applied to files:
frontend/src/components/ChatHistoryList.tsxfrontend/src/components/Sidebar.tsx
📚 Learning: 2025-12-08T19:55:33.330Z
Learnt from: CR
Repo: OpenSecretCloud/Maple PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-08T19:55:33.330Z
Learning: Applies to src/components/ui/**/*.{ts,tsx} : Use existing shadcn/ui components from `src/components/ui/` for UI elements instead of creating custom components
Applied to files:
frontend/src/components/Sidebar.tsx
🧬 Code graph analysis (3)
frontend/src/components/BulkDeleteDialog.tsx (1)
frontend/src/components/ui/alert-dialog.tsx (8)
AlertDialog(104-104)AlertDialogContent(108-108)AlertDialogHeader(109-109)AlertDialogTitle(111-111)AlertDialogDescription(112-112)AlertDialogFooter(110-110)AlertDialogCancel(114-114)AlertDialogAction(113-113)
frontend/src/components/ChatHistoryList.tsx (3)
frontend/src/components/ui/checkbox.tsx (1)
Checkbox(26-26)frontend/src/components/ui/dropdown-menu.tsx (4)
DropdownMenu(170-170)DropdownMenuTrigger(171-171)DropdownMenuContent(172-172)DropdownMenuItem(173-173)frontend/src/components/BulkDeleteDialog.tsx (1)
BulkDeleteDialog(20-57)
frontend/src/components/Sidebar.tsx (3)
frontend/src/components/ui/button.tsx (1)
Button(62-62)frontend/src/components/icons/X.tsx (1)
X(3-23)frontend/src/components/ChatHistoryList.tsx (1)
ChatHistoryList(53-774)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Greptile Review
- GitHub Check: build-macos (universal-apple-darwin)
- GitHub Check: build-linux
- GitHub Check: build-android
- GitHub Check: build-ios
- GitHub Check: Cloudflare Pages
🔇 Additional comments (9)
frontend/src/components/BulkDeleteDialog.tsx (1)
1-57: Well-structured bulk delete confirmation dialog.The component follows React best practices with proper TypeScript typing, uses the path alias correctly (
@/components/ui/alert-dialog), and implements good UX patterns:
- Disables both buttons during deletion to prevent double-clicks
- Correctly pluralizes "chat(s)" based on count
- Uses
e.preventDefault()to prevent dialog auto-close before async operation completesfrontend/src/components/Sidebar.tsx (4)
1-9: Imports look correct.The
Xicon is correctly imported fromlucide-react(the close/cancel icon), distinct from the customX.tsxcomponent inicons/which is the Twitter/X logo. Good distinction.
37-42: Selection mode auto-activation logic is correct.This effect enables the long-press flow: when
ChatHistoryListsetsselectedIdsviaonSelectionChange, this effect automatically enters selection mode. The dependency array correctly usesselectedIds.sizeas an optimization since only the count matters for this logic.
49-53: Window event for triggering bulk delete dialog.Using
window.dispatchEventfor cross-component communication works but is less type-safe than alternatives like React context. This pattern is already established in the codebase (e.g.,"newchat","conversationselected"events), so it's consistent with existing patterns.
257-265: Props correctly passed to ChatHistoryList.The selection-related state is appropriately lifted to
Sidebarand passed down, enabling coordinated selection UI in the sidebar header and list items.frontend/src/components/ChatHistoryList.tsx (4)
26-34: Props interface correctly extended for selection mode.The use of
Set<string>forselectedIdsis appropriate for efficient membership checks. MakingselectedIdsandonSelectionChangerequired ensures this is a controlled component pattern.
450-458: Event listener setup is correct.The effect properly adds and removes the event listener, and the dependency on
selectedIds.sizeensures the handler always has access to the current selection state.
591-618: Selection mode click handling and checkbox UI are well-implemented.Good implementation details:
- Dual-mode click handler correctly distinguishes between selection toggle and navigation
- Long-press handlers are properly attached for mobile
- Checkbox includes
stopPropagationto prevent double-toggle from the parent's onClickonContextMenuis prevented to avoid conflicts with mobile long-press
765-771: BulkDeleteDialog correctly integrated.Props are properly wired to component state and handlers. The controlled dialog pattern (always rendered, visibility controlled by
openprop) is correctly implemented.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additional Comments (1)
-
frontend/src/components/ChatHistoryList.tsx, line 694-741 (link)logic: Archived chats don't support selection mode - users can't include archived chats in bulk deletions. Apply the same selection mode pattern used for regular conversations (checkbox, conditional click handler, Select menu item)
4 files reviewed, 1 comment
61bfbeb to
3fadde1
Compare
- Add 'Select' option to chat dropdown menu to enter selection mode - Support selecting up to 20 chats for bulk deletion - Use batch delete API from @opensecret/react 1.5.3 - Long-press on mobile enters selection mode - Selection mode shows count and Delete button in sidebar header Closes #351 Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
4 files reviewed, 1 comment
| <DropdownMenuItem onClick={() => onSelectionChange(new Set([conv.id]))}> | ||
| <CheckSquare className="mr-2 h-4 w-4" /> | ||
| <span>Select</span> | ||
| </DropdownMenuItem> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: The "Select" action doesn't immediately show selection mode UI because Sidebar detects selection mode via useEffect watching selectedIds.size. Consider explicitly triggering selection mode when clicking "Select" for instant visual feedback.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Prompt To Fix With AI
This is a comment left during a code review.
Path: frontend/src/components/ChatHistoryList.tsx
Line: 654:657
Comment:
**style:** The "Select" action doesn't immediately show selection mode UI because `Sidebar` detects selection mode via `useEffect` watching `selectedIds.size`. Consider explicitly triggering selection mode when clicking "Select" for instant visual feedback.
<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>
How can I resolve this? If you propose a fix, please make it concise.|
✅ TestFlight deployment completed successfully! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
frontend/src/components/BulkDeleteDialog.tsx (1)
27-30: Consider removinge.preventDefault()as it may be unnecessary.The
AlertDialogActioncomponent likely handles button click events correctly without requiringpreventDefault(). Unless there's a specific reason (e.g., preventing form submission), this line can be removed to simplify the code.🔎 Simplified implementation
- const handleConfirm = (e: React.MouseEvent) => { - e.preventDefault(); - onConfirm(); - }; + const handleConfirm = () => { + onConfirm(); + };Or even simpler, pass
onConfirmdirectly:<AlertDialogAction - onClick={handleConfirm} + onClick={onConfirm} disabled={isDeleting}
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
frontend/bun.lockis excluded by!**/*.lock
📒 Files selected for processing (4)
frontend/package.jsonfrontend/src/components/BulkDeleteDialog.tsxfrontend/src/components/ChatHistoryList.tsxfrontend/src/components/Sidebar.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/package.json
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use path aliases (@/*maps to./src/*) for imports in TypeScript/React files
Use 2-space indentation, double quotes, and enforce 100-character line limit in TypeScript/React code
Maintain strict TypeScript and avoid usinganytype
Use PascalCase for component names and camelCase for variables and function names
Use functional components with React hooks instead of class components
Use React context for global state management and TanStack Query for server state management
Runjust format,just lint, andjust buildafter making TypeScript/React changes to ensure code quality and compilation
Files:
frontend/src/components/BulkDeleteDialog.tsxfrontend/src/components/ChatHistoryList.tsxfrontend/src/components/Sidebar.tsx
🧠 Learnings (3)
📚 Learning: 2025-12-08T19:55:33.330Z
Learnt from: CR
Repo: OpenSecretCloud/Maple PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-08T19:55:33.330Z
Learning: Applies to **/*.{ts,tsx} : Use React context for global state management and TanStack Query for server state management
Applied to files:
frontend/src/components/ChatHistoryList.tsxfrontend/src/components/Sidebar.tsx
📚 Learning: 2025-12-08T19:55:33.330Z
Learnt from: CR
Repo: OpenSecretCloud/Maple PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-08T19:55:33.330Z
Learning: Applies to **/*.{ts,tsx} : Use functional components with React hooks instead of class components
Applied to files:
frontend/src/components/ChatHistoryList.tsxfrontend/src/components/Sidebar.tsx
📚 Learning: 2025-12-08T19:55:33.330Z
Learnt from: CR
Repo: OpenSecretCloud/Maple PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-08T19:55:33.330Z
Learning: Applies to src/components/ui/**/*.{ts,tsx} : Use existing shadcn/ui components from `src/components/ui/` for UI elements instead of creating custom components
Applied to files:
frontend/src/components/Sidebar.tsx
🧬 Code graph analysis (2)
frontend/src/components/BulkDeleteDialog.tsx (1)
frontend/src/components/ui/alert-dialog.tsx (8)
AlertDialog(104-104)AlertDialogContent(108-108)AlertDialogHeader(109-109)AlertDialogTitle(111-111)AlertDialogDescription(112-112)AlertDialogFooter(110-110)AlertDialogCancel(114-114)AlertDialogAction(113-113)
frontend/src/components/Sidebar.tsx (2)
frontend/src/components/icons/X.tsx (1)
X(3-23)frontend/src/components/ChatHistoryList.tsx (1)
ChatHistoryList(53-784)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Greptile Review
- GitHub Check: build-ios
- GitHub Check: build-android
- GitHub Check: build-macos (universal-apple-darwin)
- GitHub Check: build-linux
- GitHub Check: Cloudflare Pages
🔇 Additional comments (9)
frontend/src/components/BulkDeleteDialog.tsx (1)
20-57: LGTM! Clean confirmation dialog implementation.The component follows React best practices with clear prop types, proper pluralization, and good UX through disabled states and loading indicators. The implementation aligns well with the project's patterns using shadcn/ui components.
frontend/src/components/Sidebar.tsx (3)
33-53: LGTM! Selection mode state management is well-structured.The auto-entry effect (lines 38-42) provides good UX for mobile long-press, and the state cleanup in
exitSelectionModeis properly memoized. The custom event dispatch pattern works effectively for component communication.
190-231: LGTM! Selection mode UI is intuitive and well-implemented.The conditional rendering between selection and normal modes is clear, with good UX touches like showing "max" at 20 selections (line 204) and properly disabling the delete button when no items are selected (line 212).
257-265: LGTM! Selection props correctly passed to ChatHistoryList.The component properly threads selection state and callbacks to
ChatHistoryList, enabling the parent-child coordination for bulk selection and deletion workflows.frontend/src/components/ChatHistoryList.tsx (5)
344-362: LGTM! Selection toggle logic with proper limit enforcement.The
MAX_SELECTIONlimit of 20 (line 344) is enforced correctly, and the Set manipulation for toggling selections is clean and efficient.
449-457: LGTM! Long press timer cleanup properly implemented.This cleanup effect addresses the previous review comment by ensuring the long-press timer is cleared on unmount, preventing the callback from running after the component is unmounted.
430-447: LGTM! Long press handlers well-implemented for mobile UX.The 500ms threshold (line 437) is appropriate for mobile long-press detection, and the handlers correctly integrate with the cleanup effect to prevent memory leaks.
586-674: LGTM! Conversation item rendering correctly adapts to selection mode.The conditional rendering cleanly separates selection mode behavior from normal mode:
- Click behavior toggles selection vs. navigation (lines 602-608)
- Checkbox and adjusted layout in selection mode (lines 619-628)
- Dropdown hidden during selection (line 638)
- "Select" option in dropdown menu provides alternative entry to selection mode (line 654)
The implementation provides good UX on both desktop and mobile.
775-781: LGTM! BulkDeleteDialog properly integrated.The dialog is correctly wired with appropriate state and handlers, completing the bulk delete flow.
Summary
Add the ability to select and delete multiple chats at once.
Changes
@opensecret/react1.5.3BulkDeleteDialogcomponent for confirming bulk deletionsHow to use
On mobile, you can also long-press a chat to enter selection mode.
Closes #351
Summary by CodeRabbit
New Features
Chores
✏️ Tip: You can customize this high-level summary in your review settings.