@@ -14,22 +14,110 @@ See the License for the specific language governing permissions and
1414limitations under the License.
1515*/
1616
17- import { WysiwygEvent } from "@matrix-org/matrix-wysiwyg" ;
17+ import { Wysiwyg , WysiwygEvent } from "@matrix-org/matrix-wysiwyg" ;
1818import { useCallback } from "react" ;
19+ import { MatrixClient , MatrixEvent } from "matrix-js-sdk/src/matrix" ;
1920
2021import { useSettingValue } from "../../../../../hooks/useSettings" ;
2122import { getKeyBindingsManager } from "../../../../../KeyBindingsManager" ;
2223import { 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 { isCaretAtStart } from "../utils/selection" ;
33+
34+ export function useInputEventProcessor (
35+ onSend : ( ) => void ,
36+ initialContent ?: string ,
37+ ) : ( event : WysiwygEvent , composer : Wysiwyg , editor : HTMLElement ) => WysiwygEvent | null {
38+ const roomContext = useRoomContext ( ) ;
39+ const composerContext = useComposerContext ( ) ;
40+ const mxClient = useMatrixClientContext ( ) ;
41+ const isCtrlEnter = useSettingValue < boolean > ( "MessageComposerInput.ctrlEnterToSend" ) ;
42+
43+ return useCallback (
44+ ( event : WysiwygEvent , composer : Wysiwyg , editor : HTMLElement ) => {
45+ if ( event instanceof ClipboardEvent ) {
46+ return event ;
47+ }
48+
49+ const send = ( ) : void => {
50+ event . stopPropagation ?.( ) ;
51+ event . preventDefault ?.( ) ;
52+ onSend ( ) ;
53+ } ;
54+
55+ const isKeyboardEvent = event instanceof KeyboardEvent ;
56+ if ( isKeyboardEvent ) {
57+ return handleKeyboardEvent (
58+ event ,
59+ send ,
60+ initialContent ,
61+ composer ,
62+ editor ,
63+ roomContext ,
64+ composerContext ,
65+ mxClient ,
66+ ) ;
67+ } else {
68+ return handleInputEvent ( event , send , isCtrlEnter ) ;
69+ }
70+ } ,
71+ [ isCtrlEnter , onSend , initialContent , roomContext , composerContext , mxClient ] ,
72+ ) ;
73+ }
2374
2475type Send = ( ) => void ;
2576
26- function handleKeyboardEvent ( event : KeyboardEvent , send : Send ) : KeyboardEvent | null {
77+ function handleKeyboardEvent (
78+ event : KeyboardEvent ,
79+ send : Send ,
80+ initialContent : string ,
81+ composer : Wysiwyg ,
82+ editor : HTMLElement ,
83+ roomContext : IRoomState ,
84+ composerContext : ComposerContextState ,
85+ mxClient : MatrixClient ,
86+ ) : KeyboardEvent | null {
2787 const action = getKeyBindingsManager ( ) . getMessageComposerAction ( event ) ;
2888
2989 switch ( action ) {
3090 case KeyBindingAction . SendMessage :
3191 send ( ) ;
3292 return null ;
93+ case KeyBindingAction . EditPrevMessage : {
94+ const { editorStateTransfer } = composerContext ;
95+
96+ const isEditorModified = initialContent !== composer . content ( ) ;
97+
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 previousEvent = findEditableEvent ( {
106+ events : getEventsFromEditorStateTransfer ( editorStateTransfer , roomContext , mxClient ) ,
107+ isForward : false ,
108+ fromEventId : editorStateTransfer . getEvent ( ) . getId ( ) ,
109+ } ) ;
110+ if ( previousEvent ) {
111+ dis . dispatch ( {
112+ action : Action . EditEvent ,
113+ event : previousEvent ,
114+ timelineRenderingType : roomContext . timelineRenderingType ,
115+ } ) ;
116+ event . stopPropagation ( ) ;
117+ event . preventDefault ( ) ;
118+ }
119+ return null ;
120+ }
33121 }
34122
35123 return event ;
@@ -54,27 +142,14 @@ function handleInputEvent(event: InputEvent, send: Send, isCtrlEnter: boolean):
54142 return event ;
55143}
56144
57- export function useInputEventProcessor ( onSend : ( ) => void ) : ( event : WysiwygEvent ) => WysiwygEvent | null {
58- const isCtrlEnter = useSettingValue < boolean > ( "MessageComposerInput.ctrlEnterToSend" ) ;
59- return useCallback (
60- ( event : WysiwygEvent ) => {
61- if ( event instanceof ClipboardEvent ) {
62- return event ;
63- }
64-
65- const send = ( ) : void => {
66- event . stopPropagation ?.( ) ;
67- event . preventDefault ?.( ) ;
68- onSend ( ) ;
69- } ;
70-
71- const isKeyboardEvent = event instanceof KeyboardEvent ;
72- if ( isKeyboardEvent ) {
73- return handleKeyboardEvent ( event , send ) ;
74- } else {
75- return handleInputEvent ( event , send , isCtrlEnter ) ;
76- }
77- } ,
78- [ isCtrlEnter , onSend ] ,
79- ) ;
145+ // From EditMessageComposer private get events(): MatrixEvent[]
146+ function getEventsFromEditorStateTransfer (
147+ editorStateTransfer : EditorStateTransfer ,
148+ roomContext : IRoomState ,
149+ mxClient : MatrixClient ,
150+ ) : MatrixEvent [ ] {
151+ const liveTimelineEvents = roomContext . liveTimeline . getEvents ( ) ;
152+ const pendingEvents = mxClient . getRoom ( editorStateTransfer . getEvent ( ) . getRoomId ( ) ) . getPendingEvents ( ) ;
153+ const isInThread = Boolean ( editorStateTransfer . getEvent ( ) . getThread ( ) ) ;
154+ return liveTimelineEvents . concat ( isInThread ? [ ] : pendingEvents ) ;
80155}
0 commit comments