@@ -14,40 +14,168 @@ See the License for the specific language governing permissions and
14
14
limitations under the License.
15
15
*/
16
16
17
- import { WysiwygEvent } from "@matrix-org/matrix-wysiwyg" ;
17
+ import { Wysiwyg , WysiwygEvent } from "@matrix-org/matrix-wysiwyg" ;
18
18
import { useCallback } from "react" ;
19
+ import { MatrixClient } from "matrix-js-sdk/src/matrix" ;
19
20
20
21
import { useSettingValue } from "../../../../../hooks/useSettings" ;
22
+ import { getKeyBindingsManager } from "../../../../../KeyBindingsManager" ;
23
+ import { KeyBindingAction } from "../../../../../accessibility/KeyboardShortcuts" ;
24
+ import { findEditableEvent } from "../../../../../utils/EventUtils" ;
25
+ import dis from "../../../../../dispatcher/dispatcher" ;
26
+ import { Action } from "../../../../../dispatcher/actions" ;
27
+ import { useRoomContext } from "../../../../../contexts/RoomContext" ;
28
+ import { IRoomState } from "../../../../structures/RoomView" ;
29
+ import { ComposerContextState , useComposerContext } from "../ComposerContext" ;
30
+ import EditorStateTransfer from "../../../../../utils/EditorStateTransfer" ;
31
+ import { useMatrixClientContext } from "../../../../../contexts/MatrixClientContext" ;
32
+ import { isCaretAtEnd , isCaretAtStart } from "../utils/selection" ;
33
+ import { getEventsFromEditorStateTransfer } from "../utils/event" ;
34
+ import { endEditing } from "../utils/editing" ;
21
35
22
- function isEnterPressed ( event : KeyboardEvent ) : boolean {
23
- // Ugly but here we need to send the message only if Enter is pressed
24
- // And we need to stop the event propagation on enter to avoid the composer to grow
25
- return event . key === "Enter" && ! event . shiftKey && ! event . ctrlKey && ! event . metaKey && ! event . altKey ;
26
- }
36
+ export function useInputEventProcessor (
37
+ onSend : ( ) => void ,
38
+ initialContent ?: string ,
39
+ ) : ( event : WysiwygEvent , composer : Wysiwyg , editor : HTMLElement ) => WysiwygEvent | null {
40
+ const roomContext = useRoomContext ( ) ;
41
+ const composerContext = useComposerContext ( ) ;
42
+ const mxClient = useMatrixClientContext ( ) ;
43
+ const isCtrlEnterToSend = useSettingValue < boolean > ( "MessageComposerInput.ctrlEnterToSend" ) ;
27
44
28
- export function useInputEventProcessor ( onSend : ( ) => void ) : ( event : WysiwygEvent ) => WysiwygEvent | null {
29
- const isCtrlEnter = useSettingValue < boolean > ( "MessageComposerInput.ctrlEnterToSend" ) ;
30
45
return useCallback (
31
- ( event : WysiwygEvent ) => {
46
+ ( event : WysiwygEvent , composer : Wysiwyg , editor : HTMLElement ) => {
32
47
if ( event instanceof ClipboardEvent ) {
33
48
return event ;
34
49
}
35
50
36
- const isKeyboardEvent = event instanceof KeyboardEvent ;
37
- const isEnterPress = ! isCtrlEnter && isKeyboardEvent && isEnterPressed ( event ) ;
38
- const isInsertParagraph = ! isCtrlEnter && ! isKeyboardEvent && event . inputType === "insertParagraph" ;
39
- // sendMessage is sent when cmd+enter is pressed
40
- const isSendMessage = isCtrlEnter && ! isKeyboardEvent && event . inputType === "sendMessage" ;
41
-
42
- if ( isEnterPress || isInsertParagraph || isSendMessage ) {
51
+ const send = ( ) : void => {
43
52
event . stopPropagation ?.( ) ;
44
53
event . preventDefault ?.( ) ;
45
54
onSend ( ) ;
46
- return null ;
47
- }
55
+ } ;
48
56
49
- return event ;
57
+ const isKeyboardEvent = event instanceof KeyboardEvent ;
58
+ if ( isKeyboardEvent ) {
59
+ return handleKeyboardEvent (
60
+ event ,
61
+ send ,
62
+ initialContent ,
63
+ composer ,
64
+ editor ,
65
+ roomContext ,
66
+ composerContext ,
67
+ mxClient ,
68
+ ) ;
69
+ } else {
70
+ return handleInputEvent ( event , send , isCtrlEnterToSend ) ;
71
+ }
50
72
} ,
51
- [ isCtrlEnter , onSend ] ,
73
+ [ isCtrlEnterToSend , onSend , initialContent , roomContext , composerContext , mxClient ] ,
52
74
) ;
53
75
}
76
+
77
+ type Send = ( ) => void ;
78
+
79
+ function handleKeyboardEvent (
80
+ event : KeyboardEvent ,
81
+ send : Send ,
82
+ initialContent : string | undefined ,
83
+ composer : Wysiwyg ,
84
+ editor : HTMLElement ,
85
+ roomContext : IRoomState ,
86
+ composerContext : ComposerContextState ,
87
+ mxClient : MatrixClient ,
88
+ ) : KeyboardEvent | null {
89
+ const { editorStateTransfer } = composerContext ;
90
+ const isEditorModified = initialContent !== composer . content ( ) ;
91
+ const action = getKeyBindingsManager ( ) . getMessageComposerAction ( event ) ;
92
+
93
+ switch ( action ) {
94
+ case KeyBindingAction . SendMessage :
95
+ send ( ) ;
96
+ return null ;
97
+ case KeyBindingAction . EditPrevMessage : {
98
+ // If not in edition
99
+ // Or if the caret is not at the beginning of the editor
100
+ // Or the editor is modified
101
+ if ( ! editorStateTransfer || ! isCaretAtStart ( editor ) || isEditorModified ) {
102
+ break ;
103
+ }
104
+
105
+ const isDispatched = dispatchEditEvent ( event , false , editorStateTransfer , roomContext , mxClient ) ;
106
+ if ( isDispatched ) {
107
+ return null ;
108
+ }
109
+
110
+ break ;
111
+ }
112
+ case KeyBindingAction . EditNextMessage : {
113
+ // If not in edition
114
+ // Or if the caret is not at the end of the editor
115
+ // Or the editor is modified
116
+ if ( ! editorStateTransfer || ! isCaretAtEnd ( editor ) || isEditorModified ) {
117
+ break ;
118
+ }
119
+
120
+ const isDispatched = dispatchEditEvent ( event , true , editorStateTransfer , roomContext , mxClient ) ;
121
+ if ( ! isDispatched ) {
122
+ endEditing ( roomContext ) ;
123
+ event . preventDefault ( ) ;
124
+ event . stopPropagation ( ) ;
125
+ }
126
+
127
+ return null ;
128
+ }
129
+ }
130
+
131
+ return event ;
132
+ }
133
+
134
+ function dispatchEditEvent (
135
+ event : KeyboardEvent ,
136
+ isForward : boolean ,
137
+ editorStateTransfer : EditorStateTransfer ,
138
+ roomContext : IRoomState ,
139
+ mxClient : MatrixClient ,
140
+ ) : boolean {
141
+ const foundEvents = getEventsFromEditorStateTransfer ( editorStateTransfer , roomContext , mxClient ) ;
142
+ if ( ! foundEvents ) {
143
+ return false ;
144
+ }
145
+
146
+ const newEvent = findEditableEvent ( {
147
+ events : foundEvents ,
148
+ isForward,
149
+ fromEventId : editorStateTransfer . getEvent ( ) . getId ( ) ,
150
+ } ) ;
151
+ if ( newEvent ) {
152
+ dis . dispatch ( {
153
+ action : Action . EditEvent ,
154
+ event : newEvent ,
155
+ timelineRenderingType : roomContext . timelineRenderingType ,
156
+ } ) ;
157
+ event . stopPropagation ( ) ;
158
+ event . preventDefault ( ) ;
159
+ return true ;
160
+ }
161
+ return false ;
162
+ }
163
+
164
+ type InputEvent = Exclude < WysiwygEvent , KeyboardEvent | ClipboardEvent > ;
165
+
166
+ function handleInputEvent ( event : InputEvent , send : Send , isCtrlEnterToSend : boolean ) : InputEvent | null {
167
+ switch ( event . inputType ) {
168
+ case "insertParagraph" :
169
+ if ( ! isCtrlEnterToSend ) {
170
+ send ( ) ;
171
+ }
172
+ return null ;
173
+ case "sendMessage" :
174
+ if ( isCtrlEnterToSend ) {
175
+ send ( ) ;
176
+ }
177
+ return null ;
178
+ }
179
+
180
+ return event ;
181
+ }
0 commit comments