11/* eslint-disable complexity */
22import { isRoomFederated , isRoomNativeFederated , type IMessage , type ISubscription } from '@rocket.chat/core-typings' ;
33import { useContentBoxSize , useStableCallback , useMediaQuery , useSafeRefCallback } from '@rocket.chat/fuselage-hooks' ;
4- import {
5- MessageComposerAction ,
6- MessageComposerToolbarActions ,
7- MessageComposer ,
8- MessageComposerToolbar ,
9- MessageComposerActionsDivider ,
10- MessageComposerToolbarSubmit ,
11- MessageComposerButton ,
12- MessageComposerInputExpandable ,
13- } from '@rocket.chat/ui-composer' ;
4+ import { MessageComposerInputExpandable } from '@rocket.chat/ui-composer' ;
145import { useTranslation , useUserPreference , useLayout , useSetting } from '@rocket.chat/ui-contexts' ;
156import { useMutation } from '@tanstack/react-query' ;
167import type { MouseEvent , ClipboardEvent , ChangeEvent } from 'react' ;
178import { memo , useRef , useReducer , useCallback , useSyncExternalStore } from 'react' ;
189
19- import MessageBoxActionsToolbar from './MessageBoxActionsToolbar' ;
20- import MessageBoxFormattingToolbar from './MessageBoxFormattingToolbar' ;
21- import MessageBoxHint from './MessageBoxHint' ;
22- import MessageBoxReplies from './MessageBoxReplies' ;
10+ import MessageBoxBase from './MessageBoxBase' ;
2311import MessageComposerFiles from './MessageComposerFiles' ;
12+ import {
13+ emptySubscribe ,
14+ getEmptyFalse ,
15+ getEmptyArray ,
16+ handleFormattingShortcut ,
17+ extractImageFilesFromClipboard ,
18+ } from './messageBoxHelpers' ;
2419import { handleSelectionWrapping } from './wrapSelection' ;
2520import { emoji } from '../../../../../app/emoji/client' ;
2621import { createComposerAPI } from '../../../../../app/ui-message/client/messageBox/createComposerAPI' ;
27- import type { FormattingButton } from '../../../../../app/ui-message/client/messageBox/messageBoxFormatting' ;
2822import { formattingButtons } from '../../../../../app/ui-message/client/messageBox/messageBoxFormatting' ;
29- import { getImageExtensionFromMime } from '../../../../../lib/getImageExtensionFromMime' ;
3023import { useFormatDateAndTime } from '../../../../hooks/useFormatDateAndTime' ;
3124import { useIsFederationEnabled } from '../../../../hooks/useIsFederationEnabled' ;
32- import type { ComposerAPI } from '../../../../lib/chats/ChatAPI' ;
3325import { roomCoordinator } from '../../../../lib/rooms/roomCoordinator' ;
3426import { keyCodes } from '../../../../lib/utils/keyCodes' ;
3527import { Subscriptions } from '../../../../stores' ;
36- import AudioMessageRecorder from '../../../composer/AudioMessageRecorder' ;
37- import VideoMessageRecorder from '../../../composer/VideoMessageRecorder' ;
3828import { useFileUpload } from '../../body/hooks/useFileUpload' ;
3929import { useChat } from '../../contexts/ChatContext' ;
4030import { useComposerPopupOptions } from '../../contexts/ComposerPopupContext' ;
4131import { useRoom , useRoomSubscription } from '../../contexts/RoomContext' ;
42- import ComposerBoxPopup from '../ComposerBoxPopup' ;
43- import ComposerBoxPopupPreview from '../ComposerBoxPopupPreview' ;
44- import ComposerUserActionIndicator from '../ComposerUserActionIndicator' ;
4532import { useAutoGrow } from '../RoomComposer/hooks/useAutoGrow' ;
4633import { useComposerBoxPopup } from '../hooks/useComposerBoxPopup' ;
4734import { useEnablePopupPreview } from '../hooks/useEnablePopupPreview' ;
@@ -56,31 +43,6 @@ const reducer = (_: unknown, event: ChangeEvent<HTMLInputElement>): boolean => {
5643 return Boolean ( target . value . trim ( ) ) ;
5744} ;
5845
59- const handleFormattingShortcut = ( event : KeyboardEvent , formattingButtons : FormattingButton [ ] , composer : ComposerAPI ) => {
60- const isMacOS = navigator . platform . indexOf ( 'Mac' ) !== - 1 ;
61- const isCmdOrCtrlPressed = ( isMacOS && event . metaKey ) || ( ! isMacOS && event . ctrlKey ) ;
62-
63- if ( ! isCmdOrCtrlPressed ) {
64- return false ;
65- }
66-
67- const key = event . key . toLowerCase ( ) ;
68-
69- const formatter = formattingButtons . find ( ( formatter ) => 'command' in formatter && formatter . command === key ) ;
70-
71- if ( ! formatter || ! ( 'pattern' in formatter ) ) {
72- return false ;
73- }
74-
75- composer . wrapSelection ( formatter . pattern ) ;
76- return true ;
77- } ;
78-
79- const emptySubscribe = ( ) => ( ) => undefined ;
80- const getEmptyFalse = ( ) => false ;
81- const a : any [ ] = [ ] ;
82- const getEmptyArray = ( ) => a ;
83-
8446export type MessageBoxProps = {
8547 tmid ?: IMessage [ '_id' ] ;
8648 onSend ?: ( params : { value : string ; tshow ?: boolean ; previewUrls ?: string [ ] ; isSlashCommandAllowed ?: boolean } ) => Promise < void > ;
@@ -335,38 +297,7 @@ const MessageBox = ({
335297 } ) ;
336298
337299 const handlePaste = useStableCallback ( ( event : ClipboardEvent < HTMLTextAreaElement > ) => {
338- const { clipboardData } = event ;
339-
340- if ( ! clipboardData ) {
341- return ;
342- }
343-
344- const items = Array . from ( clipboardData . items ) ;
345-
346- if ( items . some ( ( { kind, type } ) => kind === 'string' && type === 'text/plain' ) ) {
347- return ;
348- }
349-
350- const files = items
351- . filter ( ( item ) => item . kind === 'file' && item . type . indexOf ( 'image/' ) !== - 1 )
352- . map ( ( item ) => {
353- const fileItem = item . getAsFile ( ) ;
354-
355- if ( ! fileItem ) {
356- return ;
357- }
358-
359- const imageExtension = fileItem ? getImageExtensionFromMime ( fileItem . type ) : undefined ;
360-
361- const extension = imageExtension ? `.${ imageExtension } ` : '' ;
362-
363- Object . defineProperty ( fileItem , 'name' , {
364- writable : true ,
365- value : `Clipboard - ${ format ( new Date ( ) ) } ${ extension } ` ,
366- } ) ;
367- return fileItem ;
368- } )
369- . filter ( ( file ) : file is File => ! ! file ) ;
300+ const files = extractImageFilesFromClipboard ( event , format ) ;
370301
371302 if ( files . length ) {
372303 event . preventDefault ( ) ;
@@ -418,45 +349,33 @@ const MessageBox = ({
418349 const shouldPopupPreview = useEnablePopupPreview ( popup . filter , popup . option ) ;
419350
420351 return (
421- < >
422- { chat . composer ?. quotedMessages && < MessageBoxReplies /> }
423- { shouldPopupPreview && popup . option && (
424- < ComposerBoxPopup
425- select = { popup . select }
426- items = { popup . items }
427- focused = { popup . focused }
428- title = { popup . option . title }
429- renderItem = { popup . option . renderItem }
430- />
431- ) }
432- { /*
433- SlashCommand Preview popup works in a weird way
434- There is only one trigger for all the commands: "/"
435- After that we need to the slashcommand list and check if the command exists and provide the preview
436- if not the query is `suspend` which means the slashcommand is not found or doesn't have a preview
437- */ }
438- { popup . option ?. preview && (
439- < ComposerBoxPopupPreview
440- select = { popup . select }
441- items = { popup . items as any }
442- focused = { popup . focused as any }
443- title = { popup . option . title }
444- renderItem = { popup . option . renderItem }
445- ref = { popup . commandsRef }
446- rid = { room . _id }
447- tmid = { tmid }
448- suspended = { popup . suspended }
449- />
450- ) }
451- < MessageBoxHint
452- isEditing = { isEditing }
453- e2eEnabled = { e2eEnabled }
454- unencryptedMessagesAllowed = { unencryptedMessagesAllowed }
455- isMobile = { isMobile }
456- />
457- { isRecordingVideo && < VideoMessageRecorder reference = { messageComposerRef } rid = { room . _id } tmid = { tmid } /> }
458- < MessageComposer ref = { messageComposerRef } variant = { isEditing ? 'editing' : undefined } >
459- { isRecordingAudio && < AudioMessageRecorder rid = { room . _id } isMicrophoneDenied = { isMicrophoneDenied } /> }
352+ < MessageBoxBase
353+ rid = { room . _id }
354+ tmid = { tmid }
355+ composer = { chat . composer }
356+ messageComposerRef = { messageComposerRef }
357+ popup = { popup }
358+ shouldPopupPreview = { shouldPopupPreview }
359+ isEditing = { isEditing }
360+ isRecording = { isRecording }
361+ isRecordingAudio = { isRecordingAudio }
362+ isRecordingVideo = { isRecordingVideo }
363+ isMicrophoneDenied = { isMicrophoneDenied }
364+ formatters = { formatters }
365+ canSend = { canSend }
366+ useEmojis = { useEmojis }
367+ sendEnabled = { canSend && ! isUploading && ! isProcessingUploads && ( typing || isEditing || hasUploads ) }
368+ sendActive = { typing || isEditing || hasUploads }
369+ inlineSize = { sizes . inlineSize }
370+ e2eEnabled = { e2eEnabled }
371+ unencryptedMessagesAllowed = { unencryptedMessagesAllowed }
372+ isMobile = { isMobile }
373+ joinPending = { joinMutation . isPending }
374+ onEmojiClick = { handleOpenEmojiPicker }
375+ onSend = { handleSendMessage }
376+ onJoin = { onJoin }
377+ closeEditing = { closeEditing }
378+ input = {
460379 < MessageComposerInputExpandable
461380 dimensions = { sizes }
462381 ref = { mergedRefs }
@@ -469,58 +388,9 @@ const MessageBox = ({
469388 onPaste = { handlePaste }
470389 aria-activedescendant = { popup . focused ? `popup-item-${ popup . focused . _id } ` : undefined }
471390 />
472- < MessageComposerFiles />
473- < MessageComposerToolbar >
474- < MessageComposerToolbarActions aria-label = { t ( 'Message_composer_toolbox_primary_actions' ) } >
475- < MessageComposerAction
476- icon = 'emoji'
477- disabled = { ! useEmojis || isRecording || ! canSend }
478- onClick = { handleOpenEmojiPicker }
479- title = { t ( 'Emoji' ) }
480- />
481- < MessageComposerActionsDivider />
482- { chat . composer && formatters . length > 0 && (
483- < MessageBoxFormattingToolbar
484- composer = { chat . composer }
485- variant = { sizes . inlineSize < 480 ? 'small' : 'large' }
486- items = { formatters }
487- disabled = { isRecording || ! canSend }
488- />
489- ) }
490- < MessageBoxActionsToolbar
491- canSend = { canSend }
492- isMicrophoneDenied = { isMicrophoneDenied }
493- rid = { room . _id }
494- tmid = { tmid }
495- isRecording = { isRecording }
496- variant = { sizes . inlineSize < 480 ? 'small' : 'large' }
497- isEditing = { isEditing }
498- />
499- </ MessageComposerToolbarActions >
500- < MessageComposerToolbarSubmit >
501- { ! canSend && (
502- < MessageComposerButton primary onClick = { onJoin } loading = { joinMutation . isPending } >
503- { t ( 'Join' ) }
504- </ MessageComposerButton >
505- ) }
506- { canSend && (
507- < >
508- { isEditing && < MessageComposerButton onClick = { closeEditing } > { t ( 'Cancel' ) } </ MessageComposerButton > }
509- < MessageComposerAction
510- aria-label = { t ( 'Send' ) }
511- icon = 'send'
512- disabled = { ! canSend || isUploading || isProcessingUploads || ( ! typing && ! isEditing && ! hasUploads ) }
513- onClick = { handleSendMessage }
514- secondary = { typing || isEditing || hasUploads }
515- info = { typing || isEditing || hasUploads }
516- />
517- </ >
518- ) }
519- </ MessageComposerToolbarSubmit >
520- </ MessageComposerToolbar >
521- </ MessageComposer >
522- < ComposerUserActionIndicator rid = { room . _id } tmid = { tmid } />
523- </ >
391+ }
392+ files = { < MessageComposerFiles /> }
393+ />
524394 ) ;
525395} ;
526396
0 commit comments