From 4a556a76a7d6d9b6cc0f4c183a9fefcf2f414936 Mon Sep 17 00:00:00 2001 From: Benjamin Shafii Date: Wed, 28 Aug 2024 13:57:58 +0200 Subject: [PATCH] feat: allow cmd+enter to send message --- .../convo/[convoId]/_components/reply-box.tsx | 55 +++++++++++++++---- .../convo/_components/create-convo-form.tsx | 22 +++++++- 2 files changed, 65 insertions(+), 12 deletions(-) diff --git a/apps/web/src/app/[orgShortcode]/convo/[convoId]/_components/reply-box.tsx b/apps/web/src/app/[orgShortcode]/convo/[convoId]/_components/reply-box.tsx index d637b5c1..b567da6f 100644 --- a/apps/web/src/app/[orgShortcode]/convo/[convoId]/_components/reply-box.tsx +++ b/apps/web/src/app/[orgShortcode]/convo/[convoId]/_components/reply-box.tsx @@ -37,10 +37,10 @@ import { useDraft } from '@/src/stores/draft-store'; import { Editor } from '@/src/components/editor'; import { type TypeId } from '@u22n/utils/typeid'; import { useDebounce } from '@uidotdev/usehooks'; -import { useAtom, useAtomValue } from 'jotai'; import { platform } from '@/src/lib/trpc'; import { cn } from '@/src/lib/utils'; import { ms } from '@u22n/utils/ms'; +import { useAtom } from 'jotai'; import { toast } from 'sonner'; type ReplyBoxProps = { @@ -58,7 +58,8 @@ export function ReplyBox({ const [editorText, setEditorText] = useState(draft.content); const orgShortcode = useOrgShortcode(); const spaceShortcode = useSpaceShortcode(false); - const replyTo = useAtomValue(replyToMessageAtom); + const [replyTo] = useAtom(replyToMessageAtom); + const [emailIdentity, setEmailIdentity] = useAtom(emailIdentityAtom); const addConvoToCache = useUpdateConvoMessageList$Cache(); const updateConvoData = useUpdateConvoData$Cache(); const editorRef = useRef(null); @@ -116,8 +117,6 @@ export function ReplyBox({ } ); - const [emailIdentity, setEmailIdentity] = useAtom(emailIdentityAtom); - const { attachments, openFilePicker, @@ -148,7 +147,11 @@ export function ReplyBox({ const handleReply = useCallback( async (type: 'comment' | 'message') => { - if (!replyTo) return; + const currentReplyTo = replyTo; + if (!currentReplyTo) { + console.error('ReplyBox: replyTo is null'); + return; + } if (hasExternalParticipants && emailIdentity === null) { toast.error('Please select an email identity to send the message as.'); return; @@ -158,7 +161,7 @@ export function ReplyBox({ attachments: getTrpcUploadFormat(), orgShortcode, message: editorText, - replyToMessagePublicId: replyTo, + replyToMessagePublicId: currentReplyTo, messageType: type, sendAsEmailIdentityPublicId: emailIdentity ?? undefined }); @@ -190,6 +193,7 @@ export function ReplyBox({ onReply(); }, [ + replyTo, addConvoToCache, convoId, editorText, @@ -198,13 +202,45 @@ export function ReplyBox({ hasExternalParticipants, onReply, orgShortcode, - replyTo, replyToConvo, spaceShortcode, updateConvoData ] ); + const handleSendMessage = useCallback(() => { + if ( + !replyTo || + isEditorEmpty || + (hasExternalParticipants && !emailIdentity) || + replyToConvoLoading + ) { + return; + } + return handleReply('message'); + }, [ + replyTo, + isEditorEmpty, + hasExternalParticipants, + emailIdentity, + replyToConvoLoading, + handleReply + ]); + + useEffect(() => { + const handleKeyDown = (event: KeyboardEvent) => { + if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') { + event.preventDefault(); + void handleSendMessage(); + } + }; + + document.addEventListener('keydown', handleKeyDown); + return () => { + document.removeEventListener('keydown', handleKeyDown); + }; + }, [handleSendMessage]); + return (
{ - e.preventDefault(); - return handleReply('message'); - }}> + onClick={handleSendMessage}> {isMobile ? : Send}
diff --git a/apps/web/src/app/[orgShortcode]/convo/_components/create-convo-form.tsx b/apps/web/src/app/[orgShortcode]/convo/_components/create-convo-form.tsx index 54d11e3a..6a8d8c93 100644 --- a/apps/web/src/app/[orgShortcode]/convo/_components/create-convo-form.tsx +++ b/apps/web/src/app/[orgShortcode]/convo/_components/create-convo-form.tsx @@ -548,6 +548,26 @@ export default function CreateConvoForm({ } }); + const handleSendMessage = useCallback(() => { + if (isFormValid && !isCreating) { + createConvo('message'); + } + }, [isFormValid, isCreating, createConvo]); + + useEffect(() => { + const handleKeyDown = (event: KeyboardEvent) => { + if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') { + event.preventDefault(); + handleSendMessage(); + } + }; + + document.addEventListener('keydown', handleKeyDown); + return () => { + document.removeEventListener('keydown', handleKeyDown); + }; + }, [handleSendMessage]); + return (
@@ -685,7 +705,7 @@ export default function CreateConvoForm({ size={'sm'} loading={isCreating && messageType === 'message'} disabled={!isFormValid || isCreating} - onClick={() => createConvo('message')}> + onClick={handleSendMessage}> {isMobile ? : Send}