feat: Auto-Save , Update and Delete Drafts#1807
feat: Auto-Save , Update and Delete Drafts#1807AnjanyKumarJaiswal wants to merge 25 commits intoMail-0:stagingfrom
Conversation
…yKumarJaiswal/mail0 into feat/autoSave_Email_drafts
…yKumarJaiswal/mail0 into feat/autoSave_Email_drafts
…yKumarJaiswal/mail0 into feat/autoSave_Email_drafts
Bug ReportName: Inconsistent attachment handling between Google and Microsoft drivers Severity: High Example test case:
Description: Comments? Email us. Your free trial ends in 6 days. |
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
WalkthroughThis change implements full draft management for mail, including the ability to update and delete drafts across the frontend and backend. It introduces new methods and mutations for updating and deleting drafts, updates the UI to support auto-saving and discarding drafts, and refactors control flow for thread and draft data refreshes. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant EmailComposer
participant TRPC_Client
participant draftsRouter
participant AgentRpcDO
participant ZeroAgent
participant MailManager
User->>EmailComposer: Edit or discard draft
EmailComposer->>TRPC_Client: Call update/delete mutation
TRPC_Client->>draftsRouter: update/delete mutation
draftsRouter->>AgentRpcDO: updateDraft/deleteDraft
AgentRpcDO->>ZeroAgent: updateDraft/deleteDraft
ZeroAgent->>MailManager: updateDraft/deleteDraft
MailManager-->>ZeroAgent: Result
ZeroAgent-->>AgentRpcDO: Result
AgentRpcDO-->>draftsRouter: Result
draftsRouter-->>TRPC_Client: Result
TRPC_Client-->>EmailComposer: Result
EmailComposer-->>User: UI updates (auto-save, discard, etc.)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~90 minutes Possibly related PRs
Suggested reviewers
Poem
Note ⚡️ Unit Test Generation is now available in beta!Learn more here, or try it out under "Finishing Touches" below. ✨ Finishing Touches
🧪 Generate 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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
Bug ReportIncomplete data passed to
Comments? Email us. Your free trial ends in 6 days. |
There was a problem hiding this comment.
Actionable comments posted: 7
🔭 Outside diff range comments (2)
apps/mail/components/create/email-composer.tsx (1)
589-629: Refactor saveDraft to reduce complexity and improve consistencyThe current implementation has several issues:
- Unnecessarily complex nested try-catch blocks
- Inconsistent error handling between update and create paths
onDraftUpdateis only called on update, not on create- Inconsistent toast message formatting
- if(draftId){ - try{ - const response = await updateDraft(draftData); - if(response?.id){ - setDraftId(response?.id); - onDraftUpdate?.(); - toast.success("Your Draft has been Successfully Saved") - } - else{ - console.error("Failed Setting up Draft Id") - toast.error("Failed Setting up Draft Id") - } - } catch(error){ - console.error("Failed to create draft:",error) - toast.error("Failed to create draft") - } - } else { - try{ - const response = await createDraft(draftData); - if(response?.id){ - setDraftId(response?.id); - toast.success("Your Draft has been Successfully Saved") - } else{ - console.error("Failed Setting up Draft Id") - toast.error("Failed Setting up Draft Id") - } - }catch (error) { - console.error("Failed to create draft:", error); - toast.error("Failed to create draft"); - } - } + const response = await (draftId ? updateDraft(draftData) : createDraft(draftData)); + + if (response?.id) { + setDraftId(response.id); + onDraftUpdate?.(); + toast.success('Draft saved successfully'); + } else { + throw new Error('Failed to get draft ID from response'); + }apps/server/src/trpc/routes/drafts.ts (1)
45-51: Add missing await and fix formattingThe
listDraftscall is missingawait, which will return a Promise instead of the actual result.const { activeConnection } = ctx; const agent = await getZeroAgent(activeConnection.id); const { q, maxResults, pageToken } = input; - const res = agent.listDrafts({ q, maxResults: maxResults, pageToken }) as Awaited<ReturnType<MailManager['listDrafts']>>; - return res; + return await agent.listDrafts({ q, maxResults, pageToken }) as Awaited<ReturnType<MailManager['listDrafts']>>; }),
🧹 Nitpick comments (11)
apps/server/src/routes/agent/index.ts (2)
513-518: Fix formatting and style issues.The method implementation is correct but has several formatting issues that violate the coding guidelines.
Apply this diff to fix the formatting:
- async updateDraft( draftData: CreateDraftData){ - if(!this.driver){ - throw new Error('No driver available') - } + async updateDraft(draftData: CreateDraftData) { + if (!this.driver) { + throw new Error('No driver available'); + }
520-525: Fix formatting and style issues.The method implementation is correct but has the same formatting issues as
updateDraft.Apply this diff to fix the formatting:
- async deleteDraft(id: string){ - if(!this.driver){ - throw new Error('No driver available') - } + async deleteDraft(id: string) { + if (!this.driver) { + throw new Error('No driver available'); + }apps/server/src/routes/agent/rpc.ts (2)
113-115: Fix formatting for consistency.The delegation logic is correct, but the formatting should match the established code style.
Apply this diff to fix the formatting:
- async updateDraft(draftData: CreateDraftData){ + async updateDraft(draftData: CreateDraftData) {
117-119: Fix formatting for consistency.The delegation logic is correct, but the formatting should match the established code style.
Apply this diff to fix the formatting:
- async deleteDraft(draftId: string){ + async deleteDraft(draftId: string) {apps/mail/components/create/create-email.tsx (1)
177-181: Simplify the onClick handler.The functionality is correct, but the inline function can be simplified.
Apply this diff to simplify:
- <button onClick={ - () => { - refetchThreads() - } - } + <button onClick={refetchThreads}apps/mail/components/create/email-composer.tsx (4)
77-77: Clean up commented codeRemove the commented
onDeleteDraftsprop at line 116 since it's not being used.onClose, onDraftUpdate, - // onDeleteDrafts, className,Also applies to: 115-116
128-129: Remove unused state variableThe
isDeleteDraftstate variable is set in the code but never used. Consider removing it to reduce unnecessary state management.const [isSavingDraft, setIsSavingDraft] = useState(false); - const [isDeleteDraft, setIsDeleteDraft] = useState(false); const [{ isFetching, refetch: refetchThreads }] = useThreads();
260-261: Fix formatting to follow coding guidelinesEnsure consistent spacing and follow the 2-space indentation rule.
const { mutateAsync: createDraft } = useMutation(trpc.drafts.create.mutationOptions()); - const {mutateAsync: updateDraft} = useMutation(trpc.drafts.update.mutationOptions()); - const {mutateAsync: deleteDraft} = useMutation(trpc.drafts.delete.mutationOptions()); + const { mutateAsync: updateDraft } = useMutation(trpc.drafts.update.mutationOptions()); + const { mutateAsync: deleteDraft } = useMutation(trpc.drafts.delete.mutationOptions());
1636-1636: Use single quotes for consistencyFollow the coding guideline to use single quotes for strings.
- <Trash className='w-5 h-5 rounded-md'/>Discard + <Trash className='w-5 h-5 rounded-md' />Discardapps/server/src/lib/driver/google.ts (1)
782-795: Fix formatting issuesAdd space before opening brace to follow coding style.
- public deleteDraft(draftId: string){ + public deleteDraft(draftId: string) { return this.withErrorHandler( 'deleteDraft', - async () =>{ + async () => { if (!draftId) throw new Error('Missing draft ID to delete'); const res = await this.gmail.users.drafts.delete({ userId: 'me', id: draftId, - }) + }); return res.data; - } , {draftId} + }, { draftId } ); }apps/server/src/trpc/routes/drafts.ts (1)
30-36: Simplify code and fix formattingThe intermediate variable is unnecessary, and there's a missing semicolon.
get: activeDriverProcedure.input(z.object({ id: z.string() })).query(async ({ input, ctx }) => { const { activeConnection } = ctx; const agent = await getZeroAgent(activeConnection.id); const { id } = input; - const res = await agent.getDraft(id) as Awaited<ReturnType<MailManager['getDraft']>> - return res; + return await agent.getDraft(id) as Awaited<ReturnType<MailManager['getDraft']>>; }),
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
apps/mail/components/create/create-email.tsx(5 hunks)apps/mail/components/create/email-composer.tsx(13 hunks)apps/server/src/lib/driver/google.ts(4 hunks)apps/server/src/lib/driver/microsoft.ts(1 hunks)apps/server/src/lib/driver/types.ts(1 hunks)apps/server/src/routes/agent/index.ts(1 hunks)apps/server/src/routes/agent/rpc.ts(1 hunks)apps/server/src/trpc/routes/drafts.ts(2 hunks)
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (AGENT.md)
**/*.{js,jsx,ts,tsx}: Use 2-space indentation
Use single quotes
Limit lines to 100 characters in width
Semicolons are required
Files:
apps/server/src/routes/agent/index.tsapps/mail/components/create/create-email.tsxapps/server/src/lib/driver/types.tsapps/server/src/lib/driver/microsoft.tsapps/server/src/trpc/routes/drafts.tsapps/server/src/lib/driver/google.tsapps/mail/components/create/email-composer.tsxapps/server/src/routes/agent/rpc.ts
**/*.{js,jsx,ts,tsx,css}
📄 CodeRabbit Inference Engine (AGENT.md)
Use Prettier with sort-imports and Tailwind plugins
Files:
apps/server/src/routes/agent/index.tsapps/mail/components/create/create-email.tsxapps/server/src/lib/driver/types.tsapps/server/src/lib/driver/microsoft.tsapps/server/src/trpc/routes/drafts.tsapps/server/src/lib/driver/google.tsapps/mail/components/create/email-composer.tsxapps/server/src/routes/agent/rpc.ts
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (AGENT.md)
Enable TypeScript strict mode
Files:
apps/server/src/routes/agent/index.tsapps/mail/components/create/create-email.tsxapps/server/src/lib/driver/types.tsapps/server/src/lib/driver/microsoft.tsapps/server/src/trpc/routes/drafts.tsapps/server/src/lib/driver/google.tsapps/mail/components/create/email-composer.tsxapps/server/src/routes/agent/rpc.ts
🧠 Learnings (9)
📓 Common learnings
Learnt from: retrogtx
PR: Mail-0/Zero#1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
Learnt from: AnjanyKumarJaiswal
PR: Mail-0/Zero#1732
File: apps/mail/components/create/email-composer.tsx:634-657
Timestamp: 2025-07-15T03:31:14.991Z
Learning: In draft deletion operations, using setTimeout with a delay (like 500ms) before showing success toast notifications improves UX by allowing UI state changes (like closing composers and clearing IDs) to complete before displaying the toast, preventing jarring immediate toast appearances that could disappear quickly during interface transitions.
apps/server/src/routes/agent/index.ts (1)
Learnt from: retrogtx
PR: #1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
apps/mail/components/create/create-email.tsx (3)
Learnt from: retrogtx
PR: #1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
Learnt from: retrogtx
PR: #1328
File: apps/mail/lib/hotkeys/mail-list-hotkeys.tsx:202-209
Timestamp: 2025-06-18T17:26:50.918Z
Learning: In apps/mail/lib/hotkeys/mail-list-hotkeys.tsx, the switchCategoryByIndex function using hardcoded indices for category hotkeys does not break when users reorder categories, contrary to the theoretical index-shifting issue. The actual implementation has constraints or mechanisms that prevent hotkey targeting issues.
Learnt from: AnjanyKumarJaiswal
PR: #1732
File: apps/mail/components/create/email-composer.tsx:634-657
Timestamp: 2025-07-15T03:31:14.991Z
Learning: In draft deletion operations, using setTimeout with a delay (like 500ms) before showing success toast notifications improves UX by allowing UI state changes (like closing composers and clearing IDs) to complete before displaying the toast, preventing jarring immediate toast appearances that could disappear quickly during interface transitions.
apps/server/src/lib/driver/types.ts (2)
Learnt from: retrogtx
PR: #1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
Learnt from: retrogtx
PR: #1468
File: apps/server/src/trpc/routes/mail.ts:386-391
Timestamp: 2025-06-27T04:59:29.731Z
Learning: In apps/server/src/trpc/routes/mail.ts, the attachment processing logic conditionally handles mixed attachment types - it preserves existing File-like objects with arrayBuffer methods while only converting serialized attachments that need processing through toAttachmentFiles.
apps/server/src/lib/driver/microsoft.ts (2)
Learnt from: retrogtx
PR: #1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
Learnt from: retrogtx
PR: #1468
File: apps/server/src/trpc/routes/mail.ts:386-391
Timestamp: 2025-06-27T04:59:29.731Z
Learning: In apps/server/src/trpc/routes/mail.ts, the attachment processing logic conditionally handles mixed attachment types - it preserves existing File-like objects with arrayBuffer methods while only converting serialized attachments that need processing through toAttachmentFiles.
apps/server/src/trpc/routes/drafts.ts (1)
Learnt from: retrogtx
PR: #1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
apps/server/src/lib/driver/google.ts (2)
Learnt from: retrogtx
PR: #1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
Learnt from: retrogtx
PR: #1468
File: apps/server/src/trpc/routes/mail.ts:386-391
Timestamp: 2025-06-27T04:59:29.731Z
Learning: In apps/server/src/trpc/routes/mail.ts, the attachment processing logic conditionally handles mixed attachment types - it preserves existing File-like objects with arrayBuffer methods while only converting serialized attachments that need processing through toAttachmentFiles.
apps/mail/components/create/email-composer.tsx (2)
Learnt from: AnjanyKumarJaiswal
PR: #1732
File: apps/mail/components/create/email-composer.tsx:634-657
Timestamp: 2025-07-15T03:31:14.991Z
Learning: In draft deletion operations, using setTimeout with a delay (like 500ms) before showing success toast notifications improves UX by allowing UI state changes (like closing composers and clearing IDs) to complete before displaying the toast, preventing jarring immediate toast appearances that could disappear quickly during interface transitions.
Learnt from: retrogtx
PR: #1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
apps/server/src/routes/agent/rpc.ts (1)
Learnt from: retrogtx
PR: #1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (AGENT.md)
**/*.{js,jsx,ts,tsx}: Use 2-space indentation
Use single quotes
Limit lines to 100 characters in width
Semicolons are required
Files:
apps/server/src/routes/agent/index.tsapps/mail/components/create/create-email.tsxapps/server/src/lib/driver/types.tsapps/server/src/lib/driver/microsoft.tsapps/server/src/trpc/routes/drafts.tsapps/server/src/lib/driver/google.tsapps/mail/components/create/email-composer.tsxapps/server/src/routes/agent/rpc.ts
**/*.{js,jsx,ts,tsx,css}
📄 CodeRabbit Inference Engine (AGENT.md)
Use Prettier with sort-imports and Tailwind plugins
Files:
apps/server/src/routes/agent/index.tsapps/mail/components/create/create-email.tsxapps/server/src/lib/driver/types.tsapps/server/src/lib/driver/microsoft.tsapps/server/src/trpc/routes/drafts.tsapps/server/src/lib/driver/google.tsapps/mail/components/create/email-composer.tsxapps/server/src/routes/agent/rpc.ts
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (AGENT.md)
Enable TypeScript strict mode
Files:
apps/server/src/routes/agent/index.tsapps/mail/components/create/create-email.tsxapps/server/src/lib/driver/types.tsapps/server/src/lib/driver/microsoft.tsapps/server/src/trpc/routes/drafts.tsapps/server/src/lib/driver/google.tsapps/mail/components/create/email-composer.tsxapps/server/src/routes/agent/rpc.ts
🧠 Learnings (9)
📓 Common learnings
Learnt from: retrogtx
PR: Mail-0/Zero#1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
Learnt from: AnjanyKumarJaiswal
PR: Mail-0/Zero#1732
File: apps/mail/components/create/email-composer.tsx:634-657
Timestamp: 2025-07-15T03:31:14.991Z
Learning: In draft deletion operations, using setTimeout with a delay (like 500ms) before showing success toast notifications improves UX by allowing UI state changes (like closing composers and clearing IDs) to complete before displaying the toast, preventing jarring immediate toast appearances that could disappear quickly during interface transitions.
apps/server/src/routes/agent/index.ts (1)
Learnt from: retrogtx
PR: #1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
apps/mail/components/create/create-email.tsx (3)
Learnt from: retrogtx
PR: #1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
Learnt from: retrogtx
PR: #1328
File: apps/mail/lib/hotkeys/mail-list-hotkeys.tsx:202-209
Timestamp: 2025-06-18T17:26:50.918Z
Learning: In apps/mail/lib/hotkeys/mail-list-hotkeys.tsx, the switchCategoryByIndex function using hardcoded indices for category hotkeys does not break when users reorder categories, contrary to the theoretical index-shifting issue. The actual implementation has constraints or mechanisms that prevent hotkey targeting issues.
Learnt from: AnjanyKumarJaiswal
PR: #1732
File: apps/mail/components/create/email-composer.tsx:634-657
Timestamp: 2025-07-15T03:31:14.991Z
Learning: In draft deletion operations, using setTimeout with a delay (like 500ms) before showing success toast notifications improves UX by allowing UI state changes (like closing composers and clearing IDs) to complete before displaying the toast, preventing jarring immediate toast appearances that could disappear quickly during interface transitions.
apps/server/src/lib/driver/types.ts (2)
Learnt from: retrogtx
PR: #1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
Learnt from: retrogtx
PR: #1468
File: apps/server/src/trpc/routes/mail.ts:386-391
Timestamp: 2025-06-27T04:59:29.731Z
Learning: In apps/server/src/trpc/routes/mail.ts, the attachment processing logic conditionally handles mixed attachment types - it preserves existing File-like objects with arrayBuffer methods while only converting serialized attachments that need processing through toAttachmentFiles.
apps/server/src/lib/driver/microsoft.ts (2)
Learnt from: retrogtx
PR: #1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
Learnt from: retrogtx
PR: #1468
File: apps/server/src/trpc/routes/mail.ts:386-391
Timestamp: 2025-06-27T04:59:29.731Z
Learning: In apps/server/src/trpc/routes/mail.ts, the attachment processing logic conditionally handles mixed attachment types - it preserves existing File-like objects with arrayBuffer methods while only converting serialized attachments that need processing through toAttachmentFiles.
apps/server/src/trpc/routes/drafts.ts (1)
Learnt from: retrogtx
PR: #1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
apps/server/src/lib/driver/google.ts (2)
Learnt from: retrogtx
PR: #1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
Learnt from: retrogtx
PR: #1468
File: apps/server/src/trpc/routes/mail.ts:386-391
Timestamp: 2025-06-27T04:59:29.731Z
Learning: In apps/server/src/trpc/routes/mail.ts, the attachment processing logic conditionally handles mixed attachment types - it preserves existing File-like objects with arrayBuffer methods while only converting serialized attachments that need processing through toAttachmentFiles.
apps/mail/components/create/email-composer.tsx (2)
Learnt from: AnjanyKumarJaiswal
PR: #1732
File: apps/mail/components/create/email-composer.tsx:634-657
Timestamp: 2025-07-15T03:31:14.991Z
Learning: In draft deletion operations, using setTimeout with a delay (like 500ms) before showing success toast notifications improves UX by allowing UI state changes (like closing composers and clearing IDs) to complete before displaying the toast, preventing jarring immediate toast appearances that could disappear quickly during interface transitions.
Learnt from: retrogtx
PR: #1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
apps/server/src/routes/agent/rpc.ts (1)
Learnt from: retrogtx
PR: #1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
⏰ 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). (1)
- GitHub Check: cubic · AI code reviewer
🔇 Additional comments (8)
apps/mail/components/create/create-email.tsx (3)
13-13: LGTM!The import is correctly added and necessary for the thread refetching functionality.
66-66: LGTM!The hook usage follows the established pattern and correctly extracts the refetch function.
213-215: LGTM!The onDraftUpdate callback correctly ensures draft data is refetched after updates, maintaining UI consistency.
apps/server/src/lib/driver/types.ts (1)
72-77: LGTM!The interface extensions are well-designed and consistent with existing patterns. The
updateDraftmethod follows the same signature pattern ascreateDraft, anddeleteDraftappropriately returnsPromise<void>since deletion operations don't need return data.apps/server/src/lib/driver/microsoft.ts (1)
709-716: LGTM!The refactoring simplifies the method by removing unused variables and fallback logic while maintaining the same functionality. The direct POST approach is cleaner and more straightforward.
apps/mail/components/create/email-composer.tsx (2)
18-18: LGTM! Appropriate icon import for draft deletion UI.The Trash icon import is correctly added and follows the existing import pattern.
631-640: Add missing dependency to useEffectThe
saveDraftfunction is missing from the dependency array, which could lead to stale closures.return () => clearTimeout(autoSaveTimer); - }, [hasUnsavedChanges, saveDraft]); + }, [hasUnsavedChanges, saveDraft]);Note: If this causes too many re-renders, consider wrapping
saveDraftinuseCallbackwith proper dependencies.⛔ Skipped due to learnings
Learnt from: retrogtx PR: Mail-0/Zero#1354 File: apps/mail/components/ui/prompts-dialog.tsx:85-88 Timestamp: 2025-06-20T05:03:16.944Z Learning: In React Hook Form, avoid using useEffect for form state synchronization when the values prop can handle reactive updates automatically. The values prop is specifically designed for this purpose and is more optimal than manual useEffect-based synchronization.Learnt from: AnjanyKumarJaiswal PR: Mail-0/Zero#1732 File: apps/mail/components/create/email-composer.tsx:634-657 Timestamp: 2025-07-15T03:31:14.991Z Learning: In draft deletion operations, using setTimeout with a delay (like 500ms) before showing success toast notifications improves UX by allowing UI state changes (like closing composers and clearing IDs) to complete before displaying the toast, preventing jarring immediate toast appearances that could disappear quickly during interface transitions.apps/server/src/lib/driver/google.ts (1)
628-703: LGTM! Clean separation of draft creation logic.The refactoring properly separates draft creation from updates, making the code more maintainable and following single responsibility principle.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (4)
apps/mail/components/create/email-composer.tsx (4)
128-128: Remove unused state variableThe
isDeleteDraftstate is never used and should be removed along with its setter call.Also applies to: 662-662
642-665: Fix function naming and remove unused variableThe function name should follow camelCase convention and the unused
valuesvariable should be removed.
667-675: Add await for async saveDraft callThe
saveDraftfunction is async but called without await, which could cause race conditions.
701-701: Update function call to use corrected nameThe function call uses the incorrect name
handledeleteDraft.
🧹 Nitpick comments (2)
apps/mail/components/create/email-composer.tsx (2)
116-116: Remove commented-out codeThe commented
onDeleteDraftsprop should be removed if it's not needed.onClose, onDraftUpdate, - // onDeleteDrafts, className,
1636-1636: Fix icon className spacingMinor formatting issue with the Trash icon className.
- <Trash className='w-5 h-5 rounded-md'/>Discard + <Trash className="h-5 w-5" /> + Discard
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
apps/mail/components/create/email-composer.tsx(13 hunks)apps/server/src/lib/driver/google.ts(4 hunks)apps/server/src/lib/driver/types.ts(1 hunks)apps/server/src/routes/agent/index.ts(1 hunks)apps/server/src/routes/agent/rpc.ts(1 hunks)
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (AGENT.md)
**/*.{js,jsx,ts,tsx}: Use 2-space indentation
Use single quotes
Limit lines to 100 characters in width
Semicolons are required
Files:
apps/mail/components/create/email-composer.tsx
**/*.{js,jsx,ts,tsx,css}
📄 CodeRabbit Inference Engine (AGENT.md)
Use Prettier with sort-imports and Tailwind plugins
Files:
apps/mail/components/create/email-composer.tsx
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (AGENT.md)
Enable TypeScript strict mode
Files:
apps/mail/components/create/email-composer.tsx
🧠 Learnings (2)
📓 Common learnings
Learnt from: retrogtx
PR: Mail-0/Zero#1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
Learnt from: AnjanyKumarJaiswal
PR: Mail-0/Zero#1732
File: apps/mail/components/create/email-composer.tsx:634-657
Timestamp: 2025-07-15T03:31:14.991Z
Learning: In draft deletion operations, using setTimeout with a delay (like 500ms) before showing success toast notifications improves UX by allowing UI state changes (like closing composers and clearing IDs) to complete before displaying the toast, preventing jarring immediate toast appearances that could disappear quickly during interface transitions.
apps/mail/components/create/email-composer.tsx (3)
Learnt from: AnjanyKumarJaiswal
PR: #1732
File: apps/mail/components/create/email-composer.tsx:634-657
Timestamp: 2025-07-15T03:31:14.991Z
Learning: In draft deletion operations, using setTimeout with a delay (like 500ms) before showing success toast notifications improves UX by allowing UI state changes (like closing composers and clearing IDs) to complete before displaying the toast, preventing jarring immediate toast appearances that could disappear quickly during interface transitions.
Learnt from: retrogtx
PR: #1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
Learnt from: retrogtx
PR: #1328
File: apps/mail/lib/hotkeys/mail-list-hotkeys.tsx:202-209
Timestamp: 2025-06-18T17:26:50.918Z
Learning: In apps/mail/lib/hotkeys/mail-list-hotkeys.tsx, the switchCategoryByIndex function using hardcoded indices for category hotkeys does not break when users reorder categories, contrary to the theoretical index-shifting issue. The actual implementation has constraints or mechanisms that prevent hotkey targeting issues.
🚧 Files skipped from review as they are similar to previous changes (4)
- apps/server/src/routes/agent/rpc.ts
- apps/server/src/routes/agent/index.ts
- apps/server/src/lib/driver/types.ts
- apps/server/src/lib/driver/google.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (AGENT.md)
**/*.{js,jsx,ts,tsx}: Use 2-space indentation
Use single quotes
Limit lines to 100 characters in width
Semicolons are required
Files:
apps/mail/components/create/email-composer.tsx
**/*.{js,jsx,ts,tsx,css}
📄 CodeRabbit Inference Engine (AGENT.md)
Use Prettier with sort-imports and Tailwind plugins
Files:
apps/mail/components/create/email-composer.tsx
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (AGENT.md)
Enable TypeScript strict mode
Files:
apps/mail/components/create/email-composer.tsx
🧠 Learnings (2)
📓 Common learnings
Learnt from: retrogtx
PR: Mail-0/Zero#1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
Learnt from: AnjanyKumarJaiswal
PR: Mail-0/Zero#1732
File: apps/mail/components/create/email-composer.tsx:634-657
Timestamp: 2025-07-15T03:31:14.991Z
Learning: In draft deletion operations, using setTimeout with a delay (like 500ms) before showing success toast notifications improves UX by allowing UI state changes (like closing composers and clearing IDs) to complete before displaying the toast, preventing jarring immediate toast appearances that could disappear quickly during interface transitions.
apps/mail/components/create/email-composer.tsx (3)
Learnt from: AnjanyKumarJaiswal
PR: #1732
File: apps/mail/components/create/email-composer.tsx:634-657
Timestamp: 2025-07-15T03:31:14.991Z
Learning: In draft deletion operations, using setTimeout with a delay (like 500ms) before showing success toast notifications improves UX by allowing UI state changes (like closing composers and clearing IDs) to complete before displaying the toast, preventing jarring immediate toast appearances that could disappear quickly during interface transitions.
Learnt from: retrogtx
PR: #1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
Learnt from: retrogtx
PR: #1328
File: apps/mail/lib/hotkeys/mail-list-hotkeys.tsx:202-209
Timestamp: 2025-06-18T17:26:50.918Z
Learning: In apps/mail/lib/hotkeys/mail-list-hotkeys.tsx, the switchCategoryByIndex function using hardcoded indices for category hotkeys does not break when users reorder categories, contrary to the theoretical index-shifting issue. The actual implementation has constraints or mechanisms that prevent hotkey targeting issues.
🔇 Additional comments (3)
apps/mail/components/create/email-composer.tsx (3)
18-18: Import addition looks goodThe
Trashicon import is properly added and used in the discard button UI.
260-261: Mutations setup looks goodThe
updateDraftanddeleteDraftmutations are properly configured following the existing TRPC pattern.
589-619: Refactor draft save logic for consistency and proper error handlingThe implementation has several issues:
- Inconsistent indentation (should be 2 spaces)
- Grammar issues in toast messages
- Redundant error handling with outer try-catch that will never execute
- Duplicate error logging and toasting
- if(draftId){ - try{ - const response = await updateDraft(draftData); - if(response?.id){ - setDraftId(response?.id); - onDraftUpdate?.(); - toast.success("Your Draft has been Successfully Saved") - } - else{ - console.error("Failed Setting up Draft Id") - toast.error("Failed Setting up Draft Id") - } - } catch(error){ - console.error("Failed to create draft:",error) - toast.error("Failed to create draft") - } - } else { - try{ - const response = await createDraft(draftData); - if(response?.id){ - setDraftId(response?.id); - toast.success("Your Draft has been Successfully Saved") - } else{ - console.error("Failed Setting up Draft Id") - toast.error("Failed Setting up Draft Id") - } - }catch (error) { - console.error("Failed to create draft:", error); - toast.error("Failed to create draft"); - } - } + if (draftId) { + const response = await updateDraft(draftData); + if (response?.id) { + setDraftId(response.id); + onDraftUpdate?.(); + toast.success('Your draft has been successfully saved'); + } else { + throw new Error('Failed to get draft ID from response'); + } + } else { + const response = await createDraft(draftData); + if (response?.id) { + setDraftId(response.id); + toast.success('Your draft has been successfully saved'); + } else { + throw new Error('Failed to get draft ID from response'); + } + }⛔ Skipped due to learnings
Learnt from: AnjanyKumarJaiswal PR: Mail-0/Zero#1732 File: apps/mail/components/create/email-composer.tsx:634-657 Timestamp: 2025-07-15T03:31:14.991Z Learning: In draft deletion operations, using setTimeout with a delay (like 500ms) before showing success toast notifications improves UX by allowing UI state changes (like closing composers and clearing IDs) to complete before displaying the toast, preventing jarring immediate toast appearances that could disappear quickly during interface transitions.Learnt from: retrogtx PR: Mail-0/Zero#1734 File: apps/server/src/lib/driver/google.ts:211-221 Timestamp: 2025-07-15T06:46:33.349Z Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Bug ReportName: Stale state in auto-save leads to data loss
Description: Comments? Email us. Your free trial ends in 6 days. |
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Bug Report
Comments? Email us. Your free trial ends in 6 days. |
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Bug Report
Comments? Email us. Your free trial ends in 6 days. |
Bug Report
Comments? Email us. Your free trial ends in 5 days. |
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (5)
apps/mail/components/create/email-composer.tsx (5)
128-128: Remove unused state variable.Based on past review feedback, the
isDeleteDraftstate is unused and should be removed.- const [isDeleteDraft, setIsDeleteDraft] = useState(false);
631-639: Fix auto-save effect dependency array.Including
saveDraftin the dependency array causes the effect to re-run on every render sincesaveDraftis not memoized, constantly resetting the auto-save timer.Remove
saveDraftfrom the dependency array or wrap it withuseCallback:- }, [hasUnsavedChanges, saveDraft]); + }, [hasUnsavedChanges]);Alternatively, memoize
saveDraftwithuseCallbackto prevent unnecessary re-renders.
642-661: Fix function naming convention.The function name should follow camelCase convention as flagged in previous reviews.
- const handleDeleteDraft = async () => { + const handleDeleteDraft = async () => {The implementation correctly uses setTimeout for toast delay (following established UX patterns) and properly handles error cases.
663-671: Add await for async saveDraft call.The
saveDraftfunction is async but called without await, which could cause race conditions.Consider whether you want to wait for the save to complete before showing the confirmation dialog:
- const handleClose = () => { + const handleClose = async () => { const hasContent = editor?.getText()?.trim().length > 0; if (hasContent) { - saveDraft(); + await saveDraft(); setShowLeaveConfirmation(true); } else { onClose?.(); } };
697-697: Update function call to use corrected name.Update to use the corrected function name.
const confirmLeave = () => { setShowLeaveConfirmation(false); - handledeleteDraft(); + handleDeleteDraft(); onClose?.(); };
🧹 Nitpick comments (1)
apps/mail/components/create/email-composer.tsx (1)
116-116: Remove commented dead code.The commented
onDeleteDraftsprop should be removed entirely rather than left as dead code.- // onDeleteDrafts,
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
apps/mail/components/create/email-composer.tsx(13 hunks)apps/server/src/routes/agent/index.ts(1 hunks)apps/server/src/trpc/routes/drafts.ts(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/server/src/routes/agent/index.ts
- apps/server/src/trpc/routes/drafts.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (AGENT.md)
**/*.{js,jsx,ts,tsx}: Use 2-space indentation
Use single quotes
Limit lines to 100 characters in width
Semicolons are required
Files:
apps/mail/components/create/email-composer.tsx
**/*.{js,jsx,ts,tsx,css}
📄 CodeRabbit Inference Engine (AGENT.md)
Use Prettier with sort-imports and Tailwind plugins
Files:
apps/mail/components/create/email-composer.tsx
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (AGENT.md)
Enable TypeScript strict mode
Files:
apps/mail/components/create/email-composer.tsx
🧠 Learnings (2)
📓 Common learnings
Learnt from: retrogtx
PR: Mail-0/Zero#1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
Learnt from: AnjanyKumarJaiswal
PR: Mail-0/Zero#1732
File: apps/mail/components/create/email-composer.tsx:634-657
Timestamp: 2025-07-15T03:31:14.991Z
Learning: In draft deletion operations, using setTimeout with a delay (like 500ms) before showing success toast notifications improves UX by allowing UI state changes (like closing composers and clearing IDs) to complete before displaying the toast, preventing jarring immediate toast appearances that could disappear quickly during interface transitions.
apps/mail/components/create/email-composer.tsx (4)
Learnt from: AnjanyKumarJaiswal
PR: #1732
File: apps/mail/components/create/email-composer.tsx:634-657
Timestamp: 2025-07-15T03:31:14.991Z
Learning: In draft deletion operations, using setTimeout with a delay (like 500ms) before showing success toast notifications improves UX by allowing UI state changes (like closing composers and clearing IDs) to complete before displaying the toast, preventing jarring immediate toast appearances that could disappear quickly during interface transitions.
Learnt from: retrogtx
PR: #1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
Learnt from: retrogtx
PR: #1328
File: apps/mail/lib/hotkeys/mail-list-hotkeys.tsx:202-209
Timestamp: 2025-06-18T17:26:50.918Z
Learning: In apps/mail/lib/hotkeys/mail-list-hotkeys.tsx, the switchCategoryByIndex function using hardcoded indices for category hotkeys does not break when users reorder categories, contrary to the theoretical index-shifting issue. The actual implementation has constraints or mechanisms that prevent hotkey targeting issues.
Learnt from: retrogtx
PR: #1354
File: apps/mail/components/ui/prompts-dialog.tsx:85-88
Timestamp: 2025-06-20T05:03:16.944Z
Learning: In React Hook Form, avoid using useEffect for form state synchronization when the values prop can handle reactive updates automatically. The values prop is specifically designed for this purpose and is more optimal than manual useEffect-based synchronization.
🔇 Additional comments (4)
apps/mail/components/create/email-composer.tsx (4)
18-18: LGTM! Import addition supports new discard functionality.The Trash import is correctly added to support the discard button in the leave confirmation dialog.
77-77: Well-structured prop addition for draft update notifications.The optional
onDraftUpdatecallback prop follows established patterns and enables proper parent component notification when drafts are updated.
260-261: Mutation hooks properly implement draft management operations.The
updateDraftanddeleteDrafthooks follow the established TRPC pattern and integrate well with the existing draft creation functionality.
985-985: UI improvements enhance user experience.The spacing adjustments and addition of the Trash icon to the discard button improve visual consistency and user understanding of the action being performed.
Also applies to: 1532-1533, 1632-1633
Bug Report
Comments? Email us. Your free trial ends in 4 days. |
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
apps/server/src/lib/driver/google.ts (1)
754-831: This is like building two identical rockets when you could build one with modular components. We need to fix this massive code duplication!The
updateDraftmethod is basically copy-pasting 90% ofcreateDraft. Plus, you're missing an await on line 798 - that's like forgetting to fuel the rocket before launch.First, fix the missing await:
- const base64Data = attachment.base64; + const base64Data = await attachment.base64;Then, let's be smart about this and extract the common MIME building logic:
private async buildDraftMimeMessage(data: CreateDraftData) { const { html: message, inlineImages } = await sanitizeTipTapHtml(data.message); const msg = createMimeMessage(); msg.setSender('me'); const to = data.to.split(', ').map((recipient: string) => { if (recipient.includes('<')) { const [name, email] = recipient.split('<'); return { addr: email.replace('>', ''), name: name.replace('>', '') }; } return { addr: recipient }; }); msg.setTo(to); if (data.cc) msg.setCc(data.cc.split(', ').map((addr) => ({ addr }))); if (data.bcc) msg.setBcc(data.bcc.split(', ').map((addr) => ({ addr }))); msg.setSubject(data.subject); msg.addMessage({ contentType: 'text/html', data: message || '', }); // Handle inline images if (inlineImages.length > 0) { for (const image of inlineImages) { msg.addAttachment({ inline: true, filename: `${image.cid}`, contentType: image.mimeType, data: image.data, headers: { 'Content-ID': `<${image.cid}>`, 'Content-Disposition': 'inline', }, }); } } // Handle attachments if (data.attachments?.length) { for (const attachment of data.attachments) { const base64Data = await attachment.base64; msg.addAttachment({ filename: attachment.name, contentType: attachment.type, data: base64Data, }); } } return msg.asRaw(); } public async createDraft(data: CreateDraftData) { return this.withErrorHandler('createDraft', async () => { const mimeMessage = await this.buildDraftMimeMessage(data); const encodedMessage = Buffer.from(mimeMessage) .toString('base64') .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=+$/, ''); const res = await this.gmail.users.drafts.create({ userId: 'me', requestBody: { message: { raw: encodedMessage, threadId: data.threadId, }, }, }); return res.data; }, { data }); } public async updateDraft(data: CreateDraftData) { return this.withErrorHandler('updateDraft', async () => { if (!data.id) throw new Error('Missing draft ID for update'); const mimeMessage = await this.buildDraftMimeMessage(data); const encodedMessage = Buffer.from(mimeMessage) .toString('base64') .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=+$/, ''); const res = await this.gmail.users.drafts.update({ userId: 'me', id: data.id, requestBody: { message: { raw: encodedMessage, threadId: data.threadId, }, }, }); return res.data; }, { data }); }This is engineering 101 - don't repeat yourself. One function, multiple uses. Like SpaceX's reusable rockets!
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (2)
apps/server/src/lib/driver/google.ts(4 hunks)apps/server/src/routes/agent/index.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (AGENT.md)
**/*.{js,jsx,ts,tsx}: Use 2-space indentation
Use single quotes
Limit lines to 100 characters in width
Semicolons are required
Files:
apps/server/src/routes/agent/index.tsapps/server/src/lib/driver/google.ts
**/*.{js,jsx,ts,tsx,css}
📄 CodeRabbit Inference Engine (AGENT.md)
Use Prettier with sort-imports and Tailwind plugins
Files:
apps/server/src/routes/agent/index.tsapps/server/src/lib/driver/google.ts
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (AGENT.md)
Enable TypeScript strict mode
Files:
apps/server/src/routes/agent/index.tsapps/server/src/lib/driver/google.ts
🧠 Learnings (3)
📓 Common learnings
Learnt from: retrogtx
PR: Mail-0/Zero#1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
Learnt from: AnjanyKumarJaiswal
PR: Mail-0/Zero#1732
File: apps/mail/components/create/email-composer.tsx:634-657
Timestamp: 2025-07-15T03:31:14.991Z
Learning: In draft deletion operations, using setTimeout with a delay (like 500ms) before showing success toast notifications improves UX by allowing UI state changes (like closing composers and clearing IDs) to complete before displaying the toast, preventing jarring immediate toast appearances that could disappear quickly during interface transitions.
apps/server/src/routes/agent/index.ts (1)
Learnt from: retrogtx
PR: #1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
apps/server/src/lib/driver/google.ts (3)
Learnt from: retrogtx
PR: #1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
Learnt from: retrogtx
PR: #1468
File: apps/server/src/trpc/routes/mail.ts:386-391
Timestamp: 2025-06-27T04:59:29.731Z
Learning: In apps/server/src/trpc/routes/mail.ts, the attachment processing logic conditionally handles mixed attachment types - it preserves existing File-like objects with arrayBuffer methods while only converting serialized attachments that need processing through toAttachmentFiles.
Learnt from: AnjanyKumarJaiswal
PR: #1732
File: apps/mail/components/create/email-composer.tsx:634-657
Timestamp: 2025-07-15T03:31:14.991Z
Learning: In draft deletion operations, using setTimeout with a delay (like 500ms) before showing success toast notifications improves UX by allowing UI state changes (like closing composers and clearing IDs) to complete before displaying the toast, preventing jarring immediate toast appearances that could disappear quickly during interface transitions.
🔇 Additional comments (1)
apps/server/src/routes/agent/index.ts (1)
304-316: Excellent implementation! These methods are clean and follow the established patterns perfectly.The new
updateDraftanddeleteDraftmethods maintain consistency with the existing codebase. Simple, elegant, no unnecessary complexity - just like rockets should be designed.
Bug ReportName: Missing Error Handling for Delete Draft Operation
Comments? Email us. |
There was a problem hiding this comment.
Actionable comments posted: 4
🔭 Outside diff range comments (1)
apps/mail/components/create/email-composer.tsx (1)
982-988: Add explicit type to prevent unintended form submissions.Buttons default to type="submit" which is like having autopilot engage when you didn't ask for it. Be explicit about your intentions.
{onClose && ( <button + type="button" tabIndex={-1} className="flex h-full items-center gap-2 text-sm font-medium text-[#8C8C8C] hover:text-[#A8A8A8]" onClick={handleClose} > <X className="h-3.5 w-3.5 fill-[#9A9A9A]" /> </button> )}
♻️ Duplicate comments (5)
apps/mail/components/create/email-composer.tsx (5)
129-129: Remove unused state variable.The
isDeleteDraftstate is declared but never used in the component. Dead code should be eliminated.- const [isDeleteDraft, setIsDeleteDraft] = useState(false);
590-620: Standardize error messages and toast notifications.The error messages lack consistency in capitalization and punctuation. We need precision in our communications.
setDraftId(response?.id); onDraftUpdate?.(); - toast.success("Your Draft has been Successfully Saved") + toast.success("Your draft has been successfully saved"); } else{ - console.error("Failed Setting up Draft Id") - toast.error("Failed Setting up Draft Id") + console.error("Failed to set up draft ID"); + toast.error("Failed to set up draft ID"); } } catch(error){ - console.error("Failed to create draft:",error) - toast.error("Failed to create draft") + console.error("Failed to update draft:", error); + toast.error("Failed to update draft"); } } else { try{ const response = await createDraft(draftData); if(response?.id){ setDraftId(response?.id); - toast.success("Your Draft has been Successfully Saved") + toast.success("Your draft has been successfully saved"); } else{ - console.error("Failed Setting up Draft Id") - toast.error("Failed Setting up Draft Id") + console.error("Failed to set up draft ID"); + toast.error("Failed to set up draft ID"); } }catch (error) { console.error("Failed to create draft:", error); toast.error("Failed to create draft"); }
632-640: Fix the auto-save effect dependency array.Including the non-memoized
saveDraftfunction causes unnecessary re-renders and timer resets. This is not optimal engineering.return () => clearTimeout(autoSaveTimer); - }, [hasUnsavedChanges, saveDraft]); + }, [hasUnsavedChanges]);Alternatively, wrap
saveDraftwithuseCallbackto properly memoize it.
664-672: Add await for async saveDraft call to prevent race conditions.Calling async functions without await is like launching a rocket without checking if the payload is secured. We need deterministic behavior.
- const handleClose = () => { + const handleClose = async () => { const hasContent = editor?.getText()?.trim().length > 0; if (hasContent) { - saveDraft(); + await saveDraft(); setShowLeaveConfirmation(true); } else { onClose?.(); } };
696-700: Fix the function call to use correct name.You're calling a function that doesn't exist. This will crash faster than a Cybertruck in a demolition derby.
const confirmLeave = () => { setShowLeaveConfirmation(false); - handledeleteDraft(); + handleDeleteDraft(); onClose?.(); };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (4)
apps/mail/components/create/email-composer.tsx(13 hunks)apps/server/src/lib/driver/types.ts(1 hunks)apps/server/src/routes/agent/index.ts(1 hunks)apps/server/src/routes/agent/rpc.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (AGENT.md)
**/*.{js,jsx,ts,tsx}: Use 2-space indentation
Use single quotes
Limit lines to 100 characters in width
Semicolons are required
Files:
apps/server/src/routes/agent/rpc.tsapps/server/src/lib/driver/types.tsapps/server/src/routes/agent/index.tsapps/mail/components/create/email-composer.tsx
**/*.{js,jsx,ts,tsx,css}
📄 CodeRabbit Inference Engine (AGENT.md)
Use Prettier with sort-imports and Tailwind plugins
Files:
apps/server/src/routes/agent/rpc.tsapps/server/src/lib/driver/types.tsapps/server/src/routes/agent/index.tsapps/mail/components/create/email-composer.tsx
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (AGENT.md)
Enable TypeScript strict mode
Files:
apps/server/src/routes/agent/rpc.tsapps/server/src/lib/driver/types.tsapps/server/src/routes/agent/index.tsapps/mail/components/create/email-composer.tsx
🧠 Learnings (5)
📓 Common learnings
Learnt from: retrogtx
PR: Mail-0/Zero#1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
Learnt from: AnjanyKumarJaiswal
PR: Mail-0/Zero#1732
File: apps/mail/components/create/email-composer.tsx:634-657
Timestamp: 2025-07-15T03:31:14.991Z
Learning: In draft deletion operations, using setTimeout with a delay (like 500ms) before showing success toast notifications improves UX by allowing UI state changes (like closing composers and clearing IDs) to complete before displaying the toast, preventing jarring immediate toast appearances that could disappear quickly during interface transitions.
📚 Learning: in apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() met...
Learnt from: retrogtx
PR: Mail-0/Zero#1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
Applied to files:
apps/server/src/routes/agent/rpc.tsapps/server/src/lib/driver/types.tsapps/server/src/routes/agent/index.tsapps/mail/components/create/email-composer.tsx
📚 Learning: in apps/server/src/trpc/routes/mail.ts, the attachment processing logic conditionally handles mixed ...
Learnt from: retrogtx
PR: Mail-0/Zero#1468
File: apps/server/src/trpc/routes/mail.ts:386-391
Timestamp: 2025-06-27T04:59:29.731Z
Learning: In apps/server/src/trpc/routes/mail.ts, the attachment processing logic conditionally handles mixed attachment types - it preserves existing File-like objects with arrayBuffer methods while only converting serialized attachments that need processing through toAttachmentFiles.
Applied to files:
apps/server/src/lib/driver/types.ts
📚 Learning: in draft deletion operations, using settimeout with a delay (like 500ms) before showing success toas...
Learnt from: AnjanyKumarJaiswal
PR: Mail-0/Zero#1732
File: apps/mail/components/create/email-composer.tsx:634-657
Timestamp: 2025-07-15T03:31:14.991Z
Learning: In draft deletion operations, using setTimeout with a delay (like 500ms) before showing success toast notifications improves UX by allowing UI state changes (like closing composers and clearing IDs) to complete before displaying the toast, preventing jarring immediate toast appearances that could disappear quickly during interface transitions.
Applied to files:
apps/mail/components/create/email-composer.tsx
📚 Learning: in react hook form, avoid using useeffect for form state synchronization when the values prop can ha...
Learnt from: retrogtx
PR: Mail-0/Zero#1354
File: apps/mail/components/ui/prompts-dialog.tsx:85-88
Timestamp: 2025-06-20T05:03:16.944Z
Learning: In React Hook Form, avoid using useEffect for form state synchronization when the values prop can handle reactive updates automatically. The values prop is specifically designed for this purpose and is more optimal than manual useEffect-based synchronization.
Applied to files:
apps/mail/components/create/email-composer.tsx
🪛 Biome (2.1.2)
apps/mail/components/create/email-composer.tsx
[error] 982-986: Provide an explicit type prop for the button element.
The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset
(lint/a11y/useButtonType)
🔇 Additional comments (7)
apps/server/src/lib/driver/types.ts (1)
71-76: Interface design as elegant as a Tesla Model S! ⚡This is exactly how you extend an interface - maintaining perfect consistency with existing patterns while adding the functionality we need. The updateDraft method signature mirrors createDraft (brilliant engineering decision), and deleteDraft keeps it simple with void return. It's like adding new features to a rocket without breaking the aerodynamics.
apps/mail/components/create/email-composer.tsx (6)
18-18: LGTM! Icon import for enhanced UX.Adding the Trash icon improves the visual clarity of the discard action. This is the way.
78-78: Props addition looks solid.The optional
onDraftUpdatecallback enables parent components to react to draft changes. Good architectural decision.Also applies to: 116-117
261-262: Mutations setup looks good.Proper implementation of draft update and delete mutations using tRPC. This is how we build the future.
643-662: Draft deletion implementation is excellent.The function correctly implements the learned UX pattern with delayed toast notifications. The 500ms delay prevents jarring UI transitions. This is how we create delightful user experiences.
1642-1642: Nice touch with the Trash icon.Adding visual cues to destructive actions is fundamental UX. Users need clear signals. This is the way.
1543-1543: Layout adjustment approved.Small details matter in creating a polished interface.
Bug ReportName: Inconsistent attachment handling in OutlookMailManager updateDraft Comments? Email us. |
There was a problem hiding this comment.
Actionable comments posted: 1
🔭 Outside diff range comments (1)
apps/mail/components/create/email-composer.tsx (1)
982-988: Button without type is like a rocket without guidance - unpredictableAdd explicit
type="button"to prevent accidental form submissions.{onClose && ( <button + type="button" tabIndex={-1} className="flex h-full items-center gap-2 text-sm font-medium text-[#8C8C8C] hover:text-[#A8A8A8]" onClick={handleClose} > <X className="h-3.5 w-3.5 fill-[#9A9A9A]" /> </button> )}
♻️ Duplicate comments (8)
apps/server/src/routes/agent/index.ts (2)
784-789: Houston, we have the same formatting anomaly detected by our orbital scanners! 🚀The engineering behind this method is absolutely flawless - like a perfect Raptor engine burn sequence. The driver availability check and delegation pattern are as precise as a Falcon Heavy center core landing. But we've got the same spacing irregularities that our previous mission control identified.
791-796: Mission control confirms: same orbital trajectory deviation as before! 🛰️Another stellar implementation that follows our established architectural patterns with the precision of a Dragon capsule docking maneuver. The error handling and delegation are as solid as Starship's heat shield. But we're seeing the same formatting inconsistencies that our ground crew flagged in the previous mission briefing.
apps/mail/components/create/email-composer.tsx (4)
632-640: Your auto-save timer is resetting more often than a SpaceX landing attemptIncluding
saveDraftin the dependency array without memoization causes constant re-renders and timer resets.
664-672: Houston, we have an async problemThe
saveDraftfunction is async but called without await, potentially causing race conditions.
698-698: Function name fail - worse than calling X "Twitter"The function is called with the wrong name. Should be
handleDeleteDraftnothandledeleteDraft.
590-620: This error handling is messier than Twitter's verification systemThe implementation has several issues that were already flagged in previous reviews:
- Inconsistent error messages with poor formatting
- Duplicate try-catch blocks
- Wrong error message in the update branch catch block
apps/server/src/lib/driver/google.ts (2)
754-831: This duplication is not sustainable - we need to think from first principlesThe MIME message construction logic is copy-pasted between
createDraftandupdateDraft. Also missingawaiton line 798.
832-845: This formatting needs to be fixed - consistency mattersThe method has inconsistent spacing and formatting compared to the rest of the codebase.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (3)
apps/mail/components/create/email-composer.tsx(13 hunks)apps/server/src/lib/driver/google.ts(4 hunks)apps/server/src/routes/agent/index.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (AGENT.md)
**/*.{js,jsx,ts,tsx}: Use 2-space indentation
Use single quotes
Limit lines to 100 characters in width
Semicolons are required
Files:
apps/server/src/routes/agent/index.tsapps/mail/components/create/email-composer.tsxapps/server/src/lib/driver/google.ts
**/*.{js,jsx,ts,tsx,css}
📄 CodeRabbit Inference Engine (AGENT.md)
Use Prettier with sort-imports and Tailwind plugins
Files:
apps/server/src/routes/agent/index.tsapps/mail/components/create/email-composer.tsxapps/server/src/lib/driver/google.ts
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (AGENT.md)
Enable TypeScript strict mode
Files:
apps/server/src/routes/agent/index.tsapps/mail/components/create/email-composer.tsxapps/server/src/lib/driver/google.ts
🧠 Learnings (6)
📓 Common learnings
Learnt from: retrogtx
PR: Mail-0/Zero#1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
Learnt from: AnjanyKumarJaiswal
PR: Mail-0/Zero#1732
File: apps/mail/components/create/email-composer.tsx:634-657
Timestamp: 2025-07-15T03:31:14.991Z
Learning: In draft deletion operations, using setTimeout with a delay (like 500ms) before showing success toast notifications improves UX by allowing UI state changes (like closing composers and clearing IDs) to complete before displaying the toast, preventing jarring immediate toast appearances that could disappear quickly during interface transitions.
📚 Learning: in apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() met...
Learnt from: retrogtx
PR: Mail-0/Zero#1734
File: apps/server/src/lib/driver/google.ts:211-221
Timestamp: 2025-07-15T06:46:33.349Z
Learning: In apps/server/src/lib/driver/google.ts, the normalization of "draft" to "drafts" in the count() method is necessary because the navigation item in apps/mail/config/navigation.ts has id: 'drafts' (plural) while the Google API returns "draft" (singular). The nav-main.tsx component matches stats by comparing stat.label with item.id, so the backend must return "drafts" for the draft counter badge to appear in the sidebar.
Applied to files:
apps/server/src/routes/agent/index.tsapps/mail/components/create/email-composer.tsxapps/server/src/lib/driver/google.ts
📚 Learning: in draft deletion operations, using settimeout with a delay (like 500ms) before showing success toas...
Learnt from: AnjanyKumarJaiswal
PR: Mail-0/Zero#1732
File: apps/mail/components/create/email-composer.tsx:634-657
Timestamp: 2025-07-15T03:31:14.991Z
Learning: In draft deletion operations, using setTimeout with a delay (like 500ms) before showing success toast notifications improves UX by allowing UI state changes (like closing composers and clearing IDs) to complete before displaying the toast, preventing jarring immediate toast appearances that could disappear quickly during interface transitions.
Applied to files:
apps/server/src/routes/agent/index.tsapps/mail/components/create/email-composer.tsxapps/server/src/lib/driver/google.ts
📚 Learning: in react hook form, avoid using useeffect for form state synchronization when the values prop can ha...
Learnt from: retrogtx
PR: Mail-0/Zero#1354
File: apps/mail/components/ui/prompts-dialog.tsx:85-88
Timestamp: 2025-06-20T05:03:16.944Z
Learning: In React Hook Form, avoid using useEffect for form state synchronization when the values prop can handle reactive updates automatically. The values prop is specifically designed for this purpose and is more optimal than manual useEffect-based synchronization.
Applied to files:
apps/mail/components/create/email-composer.tsx
📚 Learning: in apps/server/src/trpc/routes/mail.ts, the attachment processing logic conditionally handles mixed ...
Learnt from: retrogtx
PR: Mail-0/Zero#1468
File: apps/server/src/trpc/routes/mail.ts:386-391
Timestamp: 2025-06-27T04:59:29.731Z
Learning: In apps/server/src/trpc/routes/mail.ts, the attachment processing logic conditionally handles mixed attachment types - it preserves existing File-like objects with arrayBuffer methods while only converting serialized attachments that need processing through toAttachmentFiles.
Applied to files:
apps/server/src/lib/driver/google.ts
📚 Learning: in apps/server/src/trpc/routes/mail.ts, the user indicated they are not using iso format for the sch...
Learnt from: retrogtx
PR: Mail-0/Zero#1468
File: apps/server/src/trpc/routes/mail.ts:331-331
Timestamp: 2025-06-28T03:56:09.376Z
Learning: In apps/server/src/trpc/routes/mail.ts, the user indicated they are not using ISO format for the scheduleAt parameter, despite the frontend code showing toISOString() usage in the ScheduleSendPicker component.
Applied to files:
apps/server/src/lib/driver/google.ts
🧬 Code Graph Analysis (1)
apps/mail/components/create/email-composer.tsx (5)
apps/mail/hooks/use-threads.ts (1)
useThreads(15-63)apps/server/src/routes/agent/index.ts (3)
updateDraft(784-789)deleteDraft(791-796)createDraft(777-782)apps/server/src/lib/driver/google.ts (3)
updateDraft(754-831)deleteDraft(832-845)createDraft(678-753)apps/server/src/routes/agent/rpc.ts (3)
updateDraft(128-130)deleteDraft(132-134)createDraft(124-126)apps/mail/components/icons/icons.tsx (1)
Trash(940-955)
🪛 Biome (2.1.2)
apps/mail/components/create/email-composer.tsx
[error] 982-986: Provide an explicit type prop for the button element.
The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset
(lint/a11y/useButtonType)
🔇 Additional comments (3)
apps/mail/components/create/email-composer.tsx (2)
643-662: This draft deletion is cleaner than a Falcon 9 landing - nice work!The implementation correctly follows the UX pattern from learnings (using setTimeout for toast) and properly manages state cleanup.
1642-1642: Trash icon addition - simple but effective, like a Model 3Good UX improvement adding the Trash icon to the discard button for visual clarity.
apps/server/src/lib/driver/google.ts (1)
678-753: First principles thinking applied correctly here!The refactoring from
messages.sendtodrafts.createis the right architectural decision. Also good to see theawaitadded for attachment processing - async operations need proper handling.
|
This PR has merge conflicts and has been open for more than 3 days. It will be automatically closed. Please resolve the conflicts and reopen the PR if you'd like to continue working on it. |
Auto Save , Auto Update and delete drafts
Type of Change
Please delete options that are not relevant.
Areas Affected
Please check all that apply:
Testing Done
Describe the tests you've done:
Security Considerations
For changes involving data or authentication:
Checklist
Additional Notes
Add any other context about the pull request here.
Screenshots/Recordings
mail0.drafts.section-1753286890131.mp4
By submitting this pull request, I confirm that my contribution is made under the terms of the project's license.
Summary by cubic
Added auto-save, update, and delete features for email drafts. Drafts now save automatically as you write, and you can delete drafts at any time.
Summary by CodeRabbit
New Features
Improvements
Bug Fixes