reply composer auto focus

This commit is contained in:
Nizzy
2025-05-04 20:11:19 -07:00
parent f478f2ea9d
commit 6e36cebc9e
3 changed files with 34 additions and 6 deletions

View File

@@ -25,10 +25,9 @@ import { serializeFiles } from '@/lib/schemas';
import { Input } from '@/components/ui/input';
import { EditorContent } from '@tiptap/react';
import { useForm } from 'react-hook-form';
import { useRef, useState } from 'react';
import { useRef, useState, useEffect } from 'react';
import { useQueryState } from 'nuqs';
import pluralize from 'pluralize';
import { useEffect } from 'react';
import { toast } from 'sonner';
import { z } from 'zod';
@@ -56,6 +55,7 @@ interface EmailComposerProps {
}) => Promise<void>;
onClose?: () => void;
className?: string;
autofocus?: boolean;
}
const isValidEmail = (email: string): boolean => {
@@ -86,6 +86,7 @@ export function EmailComposer({
onSendEmail,
onClose,
className,
autofocus = false,
}: EmailComposerProps) {
const [showCc, setShowCc] = useState(initialCc.length > 0);
const [showBcc, setShowBcc] = useState(initialBcc.length > 0);
@@ -231,15 +232,25 @@ export function EmailComposer({
},
onModEnter: () => {
void handleSend();
return true;
},
onAttachmentsChange: (files) => {
handleAttachment(files);
},
placeholder: 'Start your email here',
autofocus,
});
// Add effect to focus editor when component mounts
useEffect(() => {
if (autofocus && editor) {
const timeoutId = setTimeout(() => {
editor.commands.focus('end');
}, 100);
return () => clearTimeout(timeoutId);
}
}, [editor, autofocus]);
const handleSend = async () => {
try {
setIsLoading(true);

View File

@@ -12,7 +12,7 @@ import { serializeFiles } from '@/lib/schemas';
import { useDraft } from '@/hooks/use-drafts';
import { useTranslations } from 'next-intl';
import { useQueryState } from 'nuqs';
import { useEffect } from 'react';
import { useEffect, useState } from 'react';
import { Sender } from '@/types';
import { toast } from 'sonner';
@@ -183,6 +183,16 @@ export default function ReplyCompose({ messageId }: ReplyComposeProps) {
};
}, [mode, enableScope, disableScope]);
// Add effect to handle initial focus
const [shouldFocus, setShouldFocus] = useState(true);
useEffect(() => {
if (mode) {
setShouldFocus(true);
} else {
setShouldFocus(false);
}
}, [mode]);
if (!mode || !emailData) return null;
if (draftId && isDraftLoading) {
@@ -210,19 +220,18 @@ export default function ReplyCompose({ messageId }: ReplyComposeProps) {
if (recipient.name) {
to.push(recipient.name);
}
return to;
}, []),
cc: message.cc?.reduce<string[]>((cc, recipient) => {
if (recipient.name) {
cc.push(recipient.name);
}
return cc;
}, []),
subject: message.subject,
};
})}
autofocus={shouldFocus}
/>
</div>
);

View File

@@ -157,6 +157,7 @@ const useComposeEditor = ({
onTab,
myInfo,
sender,
autofocus = false,
}: {
initialValue?: Record<string, unknown> | string | null;
isReadOnly?: boolean;
@@ -181,6 +182,7 @@ const useComposeEditor = ({
name?: string;
email?: string;
};
autofocus?: boolean;
}) => {
const extensions = [
...defaultExtensions,
@@ -218,11 +220,17 @@ const useComposeEditor = ({
return useEditor({
editable: !isReadOnly,
autofocus: autofocus ? 'end' : false,
onCreate: ({ editor }) => {
if (onLengthChange) {
const content = editor.getText();
void onLengthChange(content.length);
}
if (autofocus) {
setTimeout(() => {
editor.commands.focus('end');
}, 100);
}
},
onUpdate: ({ editor }) => {
if (onChange) {