-
Notifications
You must be signed in to change notification settings - Fork 582
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
add multi-email input component and refactor compose email form #386
base: main
Are you sure you want to change the base?
Conversation
@yashsharma999 is attempting to deploy a commit to the Inbox Zero Team on Vercel. A member of the Team first needs to authorize it. |
WalkthroughThis PR updates the email composition functionality. In the Changes
Sequence Diagram(s)sequenceDiagram
participant User as User
participant Form as ComposeEmailForm
participant MailBox as ComposeMailBox
participant Server as Server
User->>Form: Enter email details (to, cc, bcc)
Form->>MailBox: Render email input fields with toggles for Cc/Bcc
User->>MailBox: Toggle Cc/Bcc fields as needed
MailBox->>MailBox: Update internal display state
User->>Form: Submit form
Form->>Form: Convert email arrays to comma-separated strings
Form->>Server: Send processed email data
sequenceDiagram
participant User as User
participant MEI as MultiEmailInput
participant Reg as Form Registration
User->>MEI: Type email address(es)
MEI->>MEI: Validate emails using regex and update state
MEI->>Reg: Pass comma-separated email values for form submission
Assessment against linked issues
Possibly related PRs
Poem
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
apps/web/app/(app)/compose/ComposeEmailForm.tsxOops! Something went wrong! :( ESLint: 9.23.0 ESLint couldn't find an eslint.config.(js|mjs|cjs) file. From ESLint v9.0.0, the default configuration file is now eslint.config.js. https://eslint.org/docs/latest/use/configure/migration-guide If you still have problems after following the migration guide, please stop by apps/web/app/(app)/compose/MultiEmailInput.tsxOops! Something went wrong! :( ESLint: 9.23.0 ESLint couldn't find an eslint.config.(js|mjs|cjs) file. From ESLint v9.0.0, the default configuration file is now eslint.config.js. https://eslint.org/docs/latest/use/configure/migration-guide If you still have problems after following the migration guide, please stop by apps/web/app/(app)/compose/ComposeMailBox.tsxOops! Something went wrong! :( ESLint: 9.23.0 ESLint couldn't find an eslint.config.(js|mjs|cjs) file. From ESLint v9.0.0, the default configuration file is now eslint.config.js. https://eslint.org/docs/latest/use/configure/migration-guide If you still have problems after following the migration guide, please stop by ✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
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 (
|
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 (10)
apps/web/app/(app)/compose/MultiEmailInput.tsx (4)
69-70
: Remove console.log statement.There's a debugging console.log statement that should be removed before merging to production.
const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => { if ((e.key === " " || e.key === "Tab") && inputValue) { - console.log("came here,", e.key); e.preventDefault(); addEmail(inputValue);
19-21
: Consider using more specific types instead ofany
.The
register
anderror
props are typed asany
, which reduces type safety. Consider using more specific types based on react-hook-form's types.interface MultiEmailInputProps { name: string; label: string; placeholder?: string; className?: string; - register?: any; - error?: any; + register?: UseFormRegister<any>; + error?: FieldError; }You'll need to import these types from react-hook-form:
import { UseFormRegister, FieldError } from "react-hook-form";
36-38
: Consider using a more comprehensive email validation regex.The current email validation is basic and might not catch all edge cases. Consider using a more comprehensive regex for validating emails.
const isValidEmail = (email: string) => { - return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); + return /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(email); };
104-119
: Use email as key instead of index.Using array indices as React keys can lead to unexpected behavior when items are removed from the middle of the list. Since email addresses are unique in this context, use them as keys instead.
{emails.map((email, index) => ( <div - key={index} + key={email} className="flex w-fit items-center gap-1 rounded-md bg-primary/10 px-2 py-1 text-sm text-primary" > <span>{email}</span> <button type="button" onClick={() => removeEmail(index)} className="flex h-4 w-4 items-center justify-center rounded-full hover:bg-primary/20" aria-label={`Remove ${email}`} > <X className="h-3 w-3" /> </button> </div> ))}apps/web/app/(app)/compose/ComposeEmailForm.tsx (1)
29-30
: Useimport type
for type-only imports.Since
SendEmailBody
is only used as a type, it should be imported usingimport type
to ensure it's removed during compilation.import ComposeMailBox from "@/app/(app)/compose/ComposeMailBox"; -import { SendEmailBody } from "@/utils/gmail/mail"; +import type { SendEmailBody } from "@/utils/gmail/mail";🧰 Tools
🪛 Biome (1.9.4)
[error] 30-30: All these imports are only used as types.
Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.(lint/style/useImportType)
apps/web/app/(app)/compose/ComposeMailBox.tsx (5)
2-2
: Remove unused import.The
useEffect
import is not used in this component and should be removed.-import React, { useEffect, useState } from "react"; +import React, { useState } from "react";
15-16
: Consider using more specific types instead ofany
.The
register
anderrors
props are typed asany
, which reduces type safety. Consider using more specific types based on react-hook-form's types.type ComposeMailBoxProps = { to: string; cc?: string; bcc?: string; - register: any; - errors?: any; + register: UseFormRegister<any>; + errors?: { + to?: FieldError; + cc?: FieldError; + bcc?: FieldError; + }; };You'll need to import these types from react-hook-form:
import { UseFormRegister, FieldError } from "react-hook-form";
41-41
: Use string literal instead of template literal.No template interpolation is needed here, so a simple string literal would be more appropriate.
-<div className={`relative flex flex-col gap-2 rounded-md border p-2`}> +<div className="relative flex flex-col gap-2 rounded-md border p-2">🧰 Tools
🪛 Biome (1.9.4)
[error] 41-41: Do not use template literals if interpolation and special-character handling are not needed.
Unsafe fix: Replace with string literal
(lint/style/noUnusedTemplateLiteral)
101-116
: Simplify array filtering logic.The current implementation creates an array with potentially falsy elements, filters them, and then checks again whether each button exists. This can be simplified.
-{[ - !showCC && { type: CarbonCopyType.CC, width: "w-8" }, - !showBCC && { type: CarbonCopyType.BCC, width: "w-10" }, -] - .filter(Boolean) - .map( - (button) => - button && ( - <ToggleButton - key={button.type} - label={button.type} - className={button.width} - onClick={() => toggleCarbonCopy(button.type)} - /> - ), - )} +{[ + { type: CarbonCopyType.CC, width: "w-8", show: !showCC }, + { type: CarbonCopyType.BCC, width: "w-10", show: !showBCC }, +] + .filter(button => button.show) + .map(button => ( + <ToggleButton + key={button.type} + label={button.type} + className={button.width} + onClick={() => toggleCarbonCopy(button.type)} + /> + ))}
135-135
: Use string literal instead of template literal.No template interpolation is needed here, so a simple string literal would be more appropriate.
-className={cn(`h-6 w-8 text-[10px]`, className)} +className={cn("h-6 w-8 text-[10px]", className)}🧰 Tools
🪛 Biome (1.9.4)
[error] 135-135: Do not use template literals if interpolation and special-character handling are not needed.
Unsafe fix: Replace with string literal
(lint/style/noUnusedTemplateLiteral)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
apps/web/app/(app)/compose/ComposeEmailForm.tsx
(5 hunks)apps/web/app/(app)/compose/ComposeMailBox.tsx
(1 hunks)apps/web/app/(app)/compose/MultiEmailInput.tsx
(1 hunks)
🧰 Additional context used
🧬 Code Definitions (2)
apps/web/app/(app)/compose/ComposeEmailForm.tsx (1)
apps/web/app/(app)/compose/ComposeMailBox.tsx (1)
ComposeMailBox
(19-87)
apps/web/app/(app)/compose/ComposeMailBox.tsx (1)
apps/web/app/(app)/compose/MultiEmailInput.tsx (1)
MultiEmailInput
(23-136)
🪛 Biome (1.9.4)
apps/web/app/(app)/compose/ComposeEmailForm.tsx
[error] 30-30: All these imports are only used as types.
Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.
(lint/style/useImportType)
apps/web/app/(app)/compose/ComposeMailBox.tsx
[error] 41-41: Do not use template literals if interpolation and special-character handling are not needed.
Unsafe fix: Replace with string literal
(lint/style/noUnusedTemplateLiteral)
[error] 135-135: Do not use template literals if interpolation and special-character handling are not needed.
Unsafe fix: Replace with string literal
(lint/style/noUnusedTemplateLiteral)
🔇 Additional comments (5)
apps/web/app/(app)/compose/ComposeEmailForm.tsx (4)
70-71
: Verify intentional removal of reply context.The default values for
cc
andbcc
are now hardcoded to empty strings, ignoring potential values fromreplyingToEmail
. Ensure this is intentional and not an oversight, as it might lead to loss of context when replying to emails.defaultValues: { replyToEmail: replyingToEmail, subject: replyingToEmail?.subject, to: replyingToEmail?.to, - cc: "", - bcc: "", + cc: replyingToEmail?.cc || "", + bcc: replyingToEmail?.bcc || "", messageHtml: replyingToEmail?.draftHtml, },
80-82
: Good addition to handle array email fields.The modifications to handle arrays for email fields is a good improvement for compatibility with the new multi-email input component.
135-137
: Good adaptation for the new email format.The adjustment to the
selectedEmailAddressses
logic to handle both string and array formats is appropriate and ensures backward compatibility.
315-321
: Well integrated new email component.The new
ComposeMailBox
component is well integrated into the form, passing all the necessary props for proper functioning.apps/web/app/(app)/compose/ComposeMailBox.tsx (1)
19-87
: Good implementation of the email composition box.The component effectively manages the visibility of CC and BCC fields with clean toggle functionality. The dynamic positioning of toggle buttons based on content is a nice UX touch.
🧰 Tools
🪛 Biome (1.9.4)
[error] 41-41: Do not use template literals if interpolation and special-character handling are not needed.
Unsafe fix: Replace with string literal
(lint/style/noUnusedTemplateLiteral)
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 (8)
apps/web/app/(app)/compose/ComposeMailBox.tsx (5)
11-17
: Improve type definitions for better type safety.The
register
anderrors
props are currently typed asany
, which reduces type safety and IDE assistance. Consider using more specific types for these props.- type ComposeMailBoxProps = { - to: string; - cc?: string; - bcc?: string; - register: any; - errors?: any; - }; + type ComposeMailBoxProps = { + to: string; + cc?: string; + bcc?: string; + register: UseFormRegister<any>; + errors?: FieldErrors<any>; + };You would need to import these types from react-hook-form:
import { UseFormRegister, FieldErrors } from "react-hook-form";
19-35
: Props destructuring is incomplete.You're destructuring some props in line 20 but still using
props
directly in other parts of the component. Consider destructuring all the props you need for better readability.- export default function ComposeMailBox(props: ComposeMailBoxProps) { - const { register, to, errors } = props; + export default function ComposeMailBox({ + register, + to, + cc, + bcc, + errors + }: ComposeMailBoxProps) {
37-38
: Consider renaming state variable for clarity.The variable
moveToggleButtonsToNewLine
doesn't actually move buttons but determines whether to render them on a new line. A more accurate name would improve readability.- const moveToggleButtonsToNewLine = - carbonCopy.cc || carbonCopy.bcc || (to && to.length > 0); + const shouldRenderButtonsBelow = + carbonCopy.cc || carbonCopy.bcc || (to && to.length > 0);Update the references to this variable in the JSX as well.
40-41
: Fix template literal usage.Template literals are used without any interpolation or special characters, which is flagged by static analysis.
- <div className={`relative flex flex-col gap-2 rounded-md border p-2`}> + <div className="relative flex flex-col gap-2 rounded-md border p-2">🧰 Tools
🪛 Biome (1.9.4)
[error] 41-41: Do not use template literals if interpolation and special-character handling are not needed.
Unsafe fix: Replace with string literal
(lint/style/noUnusedTemplateLiteral)
119-138
: Fix template literal usage in ToggleButton.Template literals are used without any interpolation or special characters, which is flagged by static analysis.
- className={cn(`h-6 w-8 text-[10px]`, className)} + className={cn("h-6 w-8 text-[10px]", className)}🧰 Tools
🪛 Biome (1.9.4)
[error] 132-132: Do not use template literals if interpolation and special-character handling are not needed.
Unsafe fix: Replace with string literal
(lint/style/noUnusedTemplateLiteral)
apps/web/app/(app)/compose/MultiEmailInput.tsx (3)
8-15
: Improve type definitions for better type safety.The
register
anderror
props are typed asany
, which reduces type safety. Consider using more specific types for form handling.interface MultiEmailInputProps { name: string; label: string; placeholder?: string; className?: string; - register?: any; - error?: any; + register?: UseFormRegister<any>; + error?: FieldError; }You would need to import these types from react-hook-form:
import { UseFormRegister, FieldError } from "react-hook-form";
29-34
: Consider extracting the email validation regex to a utility function.The email validation regex is complex and would be better maintained as a utility function that can be reused across the application.
Create a utility file for validation functions (e.g.,
utils/validation.ts
):export const isValidEmail = (email: string): boolean => { return /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test( email, ); };Then import and use it in your component:
- // Email validation regex - const isValidEmail = (email: string) => { - return /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test( - email, - ); - }; + import { isValidEmail } from "@/utils/validation";
125-125
: Consider handling initial values more explicitly.The component doesn't seem to handle initial values that might be passed from parent components. This could be important when editing an existing email.
Add an effect to handle initial values:
+ useEffect(() => { + // If an initial value is provided as a comma-separated string + if (register && register(name).value && typeof register(name).value === 'string') { + const initialEmails = register(name).value.split(',').filter(email => email.trim() && isValidEmail(email.trim())); + if (initialEmails.length > 0) { + setEmails(initialEmails); + } + } + }, [name, register]);Don't forget to import
useEffect
if it's not already imported.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
apps/web/app/(app)/compose/ComposeEmailForm.tsx
(5 hunks)apps/web/app/(app)/compose/ComposeMailBox.tsx
(1 hunks)apps/web/app/(app)/compose/MultiEmailInput.tsx
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/web/app/(app)/compose/ComposeEmailForm.tsx
🧰 Additional context used
🧬 Code Definitions (1)
apps/web/app/(app)/compose/ComposeMailBox.tsx (1)
apps/web/app/(app)/compose/MultiEmailInput.tsx (1)
MultiEmailInput
(17-131)
🪛 Biome (1.9.4)
apps/web/app/(app)/compose/ComposeMailBox.tsx
[error] 41-41: Do not use template literals if interpolation and special-character handling are not needed.
Unsafe fix: Replace with string literal
(lint/style/noUnusedTemplateLiteral)
[error] 132-132: Do not use template literals if interpolation and special-character handling are not needed.
Unsafe fix: Replace with string literal
(lint/style/noUnusedTemplateLiteral)
🔇 Additional comments (5)
apps/web/app/(app)/compose/ComposeMailBox.tsx (2)
40-86
: The component structure looks well-organized.The component has a clean structure with conditional rendering of email input fields based on the visibility state. The layout adapts based on the presence of emails in the "To" field, which provides a good user experience.
🧰 Tools
🪛 Biome (1.9.4)
[error] 41-41: Do not use template literals if interpolation and special-character handling are not needed.
Unsafe fix: Replace with string literal
(lint/style/noUnusedTemplateLiteral)
89-117
: The ToggleButtonsWrapper logic looks good.The helper component effectively handles the visibility of toggle buttons based on the current state, filtering out buttons that shouldn't be shown. The use of a map function with filtering makes this code concise and readable.
apps/web/app/(app)/compose/MultiEmailInput.tsx (3)
36-62
: Well-implemented email management functions.The functions to add and remove emails are well implemented with proper validation and state management. The code ensures that:
- Only valid emails are added
- Duplicate emails are prevented
- The input field is cleared after adding
- Form state is updated appropriately
64-81
: Good keyboard interaction handling.The keyboard handlers implement a natural user experience:
- Adding emails with space or tab
- Deleting the last email with backspace when the input is empty
- Adding the current input when the field loses focus
This makes the component intuitive and efficient to use.
83-129
: Well-structured UI with good accessibility.The UI implementation is clean and accessible with:
- Proper labeling
- Visual indication of errors
- Clear representation of added emails
- Keyboard navigation support
- Aria labels for remove buttons
The component layout adjusts appropriately based on the state of the emails list.
closes #368
https://www.loom.com/share/641f7db9d9294edabfecbcc1c8180810?sid=20501053-759b-4b2f-93a8-09bf24e9bf4e
Summary by CodeRabbit