1- import type { IMessage } from '@rocket.chat/core-typings' ;
2- import { Emitter } from '@rocket.chat/emitter' ;
31import type { RefObject } from 'react' ;
42
3+ import { createComposerAPICore , triggerEvent , type SetText } from './createComposerAPICore' ;
54import { limitQuoteChain } from './limitQuoteChain' ;
6- import type { FormattingButton } from './messageBoxFormatting' ;
7- import { formattingButtons } from './messageBoxFormatting' ;
85import type { ComposerAPI } from '../../../../client/lib/chats/ChatAPI' ;
9- import { createUploadsAPI } from '../../../../client/lib/chats/uploads' ;
10- import { settings } from '../../../../client/lib/settings' ;
11- import { withDebouncing } from '../../../../lib/utils/highOrderFunctions' ;
126
137export const createComposerAPI = (
148 input : HTMLTextAreaElement ,
@@ -18,47 +12,11 @@ export const createComposerAPI = (
1812 composerRef : RefObject < HTMLElement | null > ,
1913 { rid, tmid } : { rid : string ; tmid ?: string } ,
2014) : ComposerAPI => {
21- const triggerEvent = ( input : HTMLTextAreaElement , evt : string ) : void => {
22- const event = new Event ( evt , { bubbles : true } ) ;
23- // TODO: Remove this hack for react to trigger onChange
24- const tracker = ( input as any ) . _valueTracker ;
25- if ( tracker ) {
26- tracker . setValue ( new Date ( ) . toString ( ) ) ;
27- }
28- input . dispatchEvent ( event ) ;
29- } ;
30-
31- const emitter = new Emitter < {
32- quotedMessagesUpdate : void ;
33- editing : void ;
34- recording : void ;
35- recordingVideo : void ;
36- formatting : void ;
37- mircophoneDenied : void ;
38- } > ( ) ;
39-
40- let _quotedMessages : IMessage [ ] = [ ] ;
41-
42- const persist = withDebouncing ( { wait : 300 } ) ( ( ) => {
43- persistDraft ( input . value ) ;
44- } ) ;
45-
46- const notifyQuotedMessagesUpdate = ( ) : void => {
47- emitter . emit ( 'quotedMessagesUpdate' ) ;
15+ const focus = ( ) : void => {
16+ input . focus ( ) ;
4817 } ;
4918
50- const setText = (
51- text : string ,
52- {
53- selection,
54- skipFocus,
55- } : {
56- selection ?:
57- | { readonly start ?: number ; readonly end ?: number }
58- | ( ( previous : { readonly start : number ; readonly end : number } ) => { readonly start ?: number ; readonly end ?: number } ) ;
59- skipFocus ?: boolean ;
60- } = { } ,
61- ) : void => {
19+ const setText : SetText = ( text , { selection, skipFocus } = { } ) => {
6220 ! skipFocus && focus ( ) ;
6321
6422 const { selectionStart, selectionEnd } = input ;
@@ -86,141 +44,18 @@ export const createComposerAPI = (
8644 ! skipFocus && focus ( ) ;
8745 } ;
8846
89- const insertText = ( text : string ) : void => {
90- setText ( text , {
91- selection : ( { start, end } ) => ( {
92- start : start + text . length ,
93- end : end + text . length ,
94- } ) ,
95- } ) ;
96- } ;
97-
98- const clear = ( ) : void => {
99- setText ( '' ) ;
100- } ;
101-
102- const focus = ( ) : void => {
103- input . focus ( ) ;
104- } ;
105-
106- const replyWith = async ( text : string ) : Promise < void > => {
107- if ( input ) {
108- input . value = text ;
109- input . focus ( ) ;
110- }
111- } ;
112-
113- const quoteMessage = async ( message : IMessage ) : Promise < void > => {
114- _quotedMessages = [ ..._quotedMessages . filter ( ( _message ) => _message . _id !== message . _id ) , limitQuoteChain ( message , quoteChainLimit ) ] ;
115- notifyQuotedMessagesUpdate ( ) ;
116- input . focus ( ) ;
117- } ;
118-
119- const dismissQuotedMessage = async ( mid : IMessage [ '_id' ] ) : Promise < void > => {
120- _quotedMessages = _quotedMessages . filter ( ( message ) => message . _id !== mid ) ;
121- notifyQuotedMessagesUpdate ( ) ;
122- } ;
123-
124- const dismissAllQuotedMessages = async ( ) : Promise < void > => {
125- _quotedMessages = [ ] ;
126- notifyQuotedMessagesUpdate ( ) ;
127- } ;
128-
129- const quotedMessages = {
130- get : ( ) => _quotedMessages ,
131- subscribe : ( callback : ( ) => void ) => emitter . on ( 'quotedMessagesUpdate' , callback ) ,
132- } ;
133-
134- const [ editing , setEditing ] = ( ( ) => {
135- let editing = false ;
136-
137- return [
138- {
139- get : ( ) => editing ,
140- subscribe : ( callback : ( ) => void ) => emitter . on ( 'editing' , callback ) ,
141- } ,
142- ( value : boolean ) => {
143- editing = value ;
144- emitter . emit ( 'editing' ) ;
145- } ,
146- ] ;
147- } ) ( ) ;
148-
149- const [ recording , setRecordingMode ] = ( ( ) => {
150- let recording = false ;
151-
152- return [
153- {
154- get : ( ) => recording ,
155- subscribe : ( callback : ( ) => void ) => emitter . on ( 'recording' , callback ) ,
156- } ,
157- ( value : boolean ) => {
158- recording = value ;
159- emitter . emit ( 'recording' ) ;
160- } ,
161- ] ;
162- } ) ( ) ;
163-
164- const [ recordingVideo , setRecordingVideo ] = ( ( ) => {
165- let recordingVideo = false ;
166-
167- return [
168- {
169- get : ( ) => recordingVideo ,
170- subscribe : ( callback : ( ) => void ) => emitter . on ( 'recordingVideo' , callback ) ,
171- } ,
172- ( value : boolean ) => {
173- recordingVideo = value ;
174- emitter . emit ( 'recordingVideo' ) ;
175- } ,
176- ] ;
177- } ) ( ) ;
178-
179- const [ isMicrophoneDenied , setIsMicrophoneDenied ] = ( ( ) => {
180- let isMicrophoneDenied = false ;
181-
182- return [
183- {
184- get : ( ) => isMicrophoneDenied ,
185- subscribe : ( callback : ( ) => void ) => emitter . on ( 'mircophoneDenied' , callback ) ,
186- } ,
187- ( value : boolean ) => {
188- isMicrophoneDenied = value ;
189- emitter . emit ( 'mircophoneDenied' ) ;
190- } ,
191- ] ;
192- } ) ( ) ;
193-
194- const setEditingMode = ( editing : boolean ) : void => {
195- setEditing ( editing ) ;
196- } ;
197-
198- const [ formatters , stopFormatterSubscription ] = ( ( ) => {
199- let actions : FormattingButton [ ] = [ ] ;
200-
201- const recompute = ( ) : void => {
202- actions = formattingButtons . filter ( ( { condition } ) => ! condition || condition ( ) ) ;
203- emitter . emit ( 'formatting' ) ;
204- } ;
205- recompute ( ) ;
206- // Coarse-grained: fires on every setting change, but the only condition()
207- // today is Katex_Enabled and the recompute is a cheap zustand read, so the
208- // extra work per unrelated setting change is negligible.
209- const stop = settings . observe ( '*' , recompute ) ;
210-
211- return [
212- {
213- get : ( ) => actions ,
214- subscribe : ( callback : ( ) => void ) => emitter . on ( 'formatting' , callback ) ,
215- } ,
216- stop ,
217- ] ;
218- } ) ( ) ;
47+ const core = createComposerAPICore ( {
48+ input,
49+ composerRef,
50+ room : { rid, tmid } ,
51+ initialValue : initialDraft ,
52+ save : ( ) => persistDraft ( input . value ) ,
53+ setText,
54+ focus,
55+ prepareQuotedMessage : ( message ) => limitQuoteChain ( message , quoteChainLimit ) ,
56+ } ) ;
21957
220- const release = ( ) : void => {
221- input . removeEventListener ( 'input' , persist ) ;
222- stopFormatterSubscription ( ) ;
223- } ;
58+ const { insertText } = core ;
22459
22560 const wrapSelection = ( pattern : string ) : { selectionStart : number ; selectionEnd : number ; value : string } => {
22661 const { selectionEnd = input . value . length , selectionStart = 0 } = input ;
@@ -278,14 +113,6 @@ export const createComposerAPI = (
278113 } ;
279114 } ;
280115
281- const insertNewLine = ( ) : void => insertText ( '\n' ) ;
282-
283- setText ( initialDraft , {
284- skipFocus : true ,
285- } ) ;
286-
287- input . addEventListener ( 'input' , persist ) ;
288-
289116 // Gets the text that is connected to the cursor and replaces it with the given text
290117 const replaceText = ( text : string , selection : { readonly start : number ; readonly end : number } ) : void => {
291118 // Selects the text that is connected to the cursor
@@ -306,16 +133,22 @@ export const createComposerAPI = (
306133 focus ( ) ;
307134 } ;
308135
136+ const replyWith = async ( text : string ) : Promise < void > => {
137+ if ( input ) {
138+ input . value = text ;
139+ input . focus ( ) ;
140+ }
141+ } ;
142+
309143 return {
310- composerRef,
144+ ...core ,
145+ setText,
146+ wrapSelection,
311147 replaceText,
312- insertNewLine,
313- blur : ( ) => input . blur ( ) ,
314-
148+ replyWith,
315149 substring : ( start : number , end ?: number ) => {
316150 return input . value . substring ( start , end ) ;
317151 } ,
318-
319152 getCursorPosition : ( ) => {
320153 return input . selectionStart ;
321154 } ,
@@ -329,8 +162,6 @@ export const createComposerAPI = (
329162 input . selectionEnd = input . selectionStart ;
330163 focus ( ) ;
331164 } ,
332- release,
333- wrapSelection,
334165 get text ( ) : string {
335166 return input . value ;
336167 } ,
@@ -340,25 +171,5 @@ export const createComposerAPI = (
340171 end : input . selectionEnd ,
341172 } ;
342173 } ,
343-
344- editing,
345- setEditingMode,
346- recording,
347- setRecordingMode,
348- recordingVideo,
349- setRecordingVideo,
350- insertText,
351- setText,
352- clear,
353- focus,
354- replyWith,
355- quoteMessage,
356- dismissQuotedMessage,
357- dismissAllQuotedMessages,
358- quotedMessages,
359- formatters,
360- isMicrophoneDenied,
361- setIsMicrophoneDenied,
362- uploads : createUploadsAPI ( { rid, tmid } ) ,
363174 } ;
364175} ;
0 commit comments