Skip to content

Commit 4a556a7

Browse files
feat: allow cmd+enter to send message
1 parent 0c60f67 commit 4a556a7

File tree

2 files changed

+65
-12
lines changed

2 files changed

+65
-12
lines changed

apps/web/src/app/[orgShortcode]/convo/[convoId]/_components/reply-box.tsx

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ import { useDraft } from '@/src/stores/draft-store';
3737
import { Editor } from '@/src/components/editor';
3838
import { type TypeId } from '@u22n/utils/typeid';
3939
import { useDebounce } from '@uidotdev/usehooks';
40-
import { useAtom, useAtomValue } from 'jotai';
4140
import { platform } from '@/src/lib/trpc';
4241
import { cn } from '@/src/lib/utils';
4342
import { ms } from '@u22n/utils/ms';
43+
import { useAtom } from 'jotai';
4444
import { toast } from 'sonner';
4545

4646
type ReplyBoxProps = {
@@ -58,7 +58,8 @@ export function ReplyBox({
5858
const [editorText, setEditorText] = useState(draft.content);
5959
const orgShortcode = useOrgShortcode();
6060
const spaceShortcode = useSpaceShortcode(false);
61-
const replyTo = useAtomValue(replyToMessageAtom);
61+
const [replyTo] = useAtom(replyToMessageAtom);
62+
const [emailIdentity, setEmailIdentity] = useAtom(emailIdentityAtom);
6263
const addConvoToCache = useUpdateConvoMessageList$Cache();
6364
const updateConvoData = useUpdateConvoData$Cache();
6465
const editorRef = useRef<EditorFunctions>(null);
@@ -116,8 +117,6 @@ export function ReplyBox({
116117
}
117118
);
118119

119-
const [emailIdentity, setEmailIdentity] = useAtom(emailIdentityAtom);
120-
121120
const {
122121
attachments,
123122
openFilePicker,
@@ -148,7 +147,11 @@ export function ReplyBox({
148147

149148
const handleReply = useCallback(
150149
async (type: 'comment' | 'message') => {
151-
if (!replyTo) return;
150+
const currentReplyTo = replyTo;
151+
if (!currentReplyTo) {
152+
console.error('ReplyBox: replyTo is null');
153+
return;
154+
}
152155
if (hasExternalParticipants && emailIdentity === null) {
153156
toast.error('Please select an email identity to send the message as.');
154157
return;
@@ -158,7 +161,7 @@ export function ReplyBox({
158161
attachments: getTrpcUploadFormat(),
159162
orgShortcode,
160163
message: editorText,
161-
replyToMessagePublicId: replyTo,
164+
replyToMessagePublicId: currentReplyTo,
162165
messageType: type,
163166
sendAsEmailIdentityPublicId: emailIdentity ?? undefined
164167
});
@@ -190,6 +193,7 @@ export function ReplyBox({
190193
onReply();
191194
},
192195
[
196+
replyTo,
193197
addConvoToCache,
194198
convoId,
195199
editorText,
@@ -198,13 +202,45 @@ export function ReplyBox({
198202
hasExternalParticipants,
199203
onReply,
200204
orgShortcode,
201-
replyTo,
202205
replyToConvo,
203206
spaceShortcode,
204207
updateConvoData
205208
]
206209
);
207210

211+
const handleSendMessage = useCallback(() => {
212+
if (
213+
!replyTo ||
214+
isEditorEmpty ||
215+
(hasExternalParticipants && !emailIdentity) ||
216+
replyToConvoLoading
217+
) {
218+
return;
219+
}
220+
return handleReply('message');
221+
}, [
222+
replyTo,
223+
isEditorEmpty,
224+
hasExternalParticipants,
225+
emailIdentity,
226+
replyToConvoLoading,
227+
handleReply
228+
]);
229+
230+
useEffect(() => {
231+
const handleKeyDown = (event: KeyboardEvent) => {
232+
if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {
233+
event.preventDefault();
234+
void handleSendMessage();
235+
}
236+
};
237+
238+
document.addEventListener('keydown', handleKeyDown);
239+
return () => {
240+
document.removeEventListener('keydown', handleKeyDown);
241+
};
242+
}, [handleSendMessage]);
243+
208244
return (
209245
<div className="flex min-h-32 flex-col p-4">
210246
<div
@@ -328,10 +364,7 @@ export function ReplyBox({
328364
replyToConvoLoading
329365
}
330366
size={isMobile ? 'icon' : 'sm'}
331-
onPointerDown={(e) => {
332-
e.preventDefault();
333-
return handleReply('message');
334-
}}>
367+
onClick={handleSendMessage}>
335368
{isMobile ? <PaperPlaneTilt size={16} /> : <span>Send</span>}
336369
</Button>
337370
</div>

apps/web/src/app/[orgShortcode]/convo/_components/create-convo-form.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,26 @@ export default function CreateConvoForm({
548548
}
549549
});
550550

551+
const handleSendMessage = useCallback(() => {
552+
if (isFormValid && !isCreating) {
553+
createConvo('message');
554+
}
555+
}, [isFormValid, isCreating, createConvo]);
556+
557+
useEffect(() => {
558+
const handleKeyDown = (event: KeyboardEvent) => {
559+
if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {
560+
event.preventDefault();
561+
handleSendMessage();
562+
}
563+
};
564+
565+
document.addEventListener('keydown', handleKeyDown);
566+
return () => {
567+
document.removeEventListener('keydown', handleKeyDown);
568+
};
569+
}, [handleSendMessage]);
570+
551571
return (
552572
<div className="flex h-full w-full min-w-0 flex-col gap-3 p-3">
553573
<div className="flex w-full flex-col gap-2 text-sm">
@@ -685,7 +705,7 @@ export default function CreateConvoForm({
685705
size={'sm'}
686706
loading={isCreating && messageType === 'message'}
687707
disabled={!isFormValid || isCreating}
688-
onClick={() => createConvo('message')}>
708+
onClick={handleSendMessage}>
689709
{isMobile ? <PaperPlaneTilt size={16} /> : <span>Send</span>}
690710
</Button>
691711
</div>

0 commit comments

Comments
 (0)