Open message in editing mode when keyboard up is pressed (RTE) (#10079)

Move to previous message when arrow up is pressed in the main composer (RTE)
This commit is contained in:
Florian Duros 2023-02-03 17:43:02 +01:00 committed by GitHub
parent f1a08cd572
commit 2b66cfc25f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 487 additions and 365 deletions

View file

@ -489,6 +489,7 @@ export class MessageComposer extends React.Component<IProps, IState> {
e2eStatus={this.props.e2eStatus}
menuPosition={menuPosition}
placeholder={this.renderPlaceholderText()}
eventRelation={this.props.relation}
/>
);
} else {

View file

@ -15,6 +15,7 @@ limitations under the License.
*/
import { createContext, useContext } from "react";
import { IEventRelation } from "matrix-js-sdk/src/matrix";
import { SubSelection } from "./types";
import EditorStateTransfer from "../../../../utils/EditorStateTransfer";
@ -29,6 +30,7 @@ export function getDefaultContextValue(defaultValue?: Partial<ComposerContextSta
export interface ComposerContextState {
selection: SubSelection;
editorStateTransfer?: EditorStateTransfer;
eventRelation?: IEventRelation;
}
export const ComposerContext = createContext<ComposerContextState>(getDefaultContextValue());

View file

@ -15,6 +15,7 @@ limitations under the License.
*/
import React, { ForwardedRef, forwardRef, MutableRefObject, useRef } from "react";
import { IEventRelation } from "matrix-js-sdk/src/models/event";
import { useWysiwygSendActionHandler } from "./hooks/useWysiwygSendActionHandler";
import { WysiwygComposer } from "./components/WysiwygComposer";
@ -48,6 +49,7 @@ interface SendWysiwygComposerProps {
onChange: (content: string) => void;
onSend: () => void;
menuPosition: MenuProps;
eventRelation?: IEventRelation;
}
// Default needed for React.lazy
@ -55,10 +57,11 @@ export default function SendWysiwygComposer({
isRichTextEnabled,
e2eStatus,
menuPosition,
eventRelation,
...props
}: SendWysiwygComposerProps): JSX.Element {
const Composer = isRichTextEnabled ? WysiwygComposer : PlainTextComposer;
const defaultContextValue = useRef(getDefaultContextValue());
const defaultContextValue = useRef(getDefaultContextValue({ eventRelation }));
return (
<ComposerContext.Provider value={defaultContextValue.current}>

View file

@ -33,7 +33,7 @@ function getFormattedContent(editorStateTransfer: EditorStateTransfer): string {
);
}
function parseEditorStateTransfer(
export function parseEditorStateTransfer(
editorStateTransfer: EditorStateTransfer,
room: Room,
mxClient: MatrixClient,
@ -64,7 +64,7 @@ function parseEditorStateTransfer(
// this.saveStoredEditorState();
}
export function useInitialContent(editorStateTransfer: EditorStateTransfer): string {
export function useInitialContent(editorStateTransfer: EditorStateTransfer): string | undefined {
const roomContext = useRoomContext();
const mxClient = useMatrixClientContext();

View file

@ -30,7 +30,7 @@ import { ComposerContextState, useComposerContext } from "../ComposerContext";
import EditorStateTransfer from "../../../../../utils/EditorStateTransfer";
import { useMatrixClientContext } from "../../../../../contexts/MatrixClientContext";
import { isCaretAtEnd, isCaretAtStart } from "../utils/selection";
import { getEventsFromEditorStateTransfer } from "../utils/event";
import { getEventsFromEditorStateTransfer, getEventsFromRoom } from "../utils/event";
import { endEditing } from "../utils/editing";
export function useInputEventProcessor(
@ -87,7 +87,8 @@ function handleKeyboardEvent(
mxClient: MatrixClient,
): KeyboardEvent | null {
const { editorStateTransfer } = composerContext;
const isEditorModified = initialContent !== composer.content();
const isEditing = Boolean(editorStateTransfer);
const isEditorModified = isEditing ? initialContent !== composer.content() : composer.content().length !== 0;
const action = getKeyBindingsManager().getMessageComposerAction(event);
switch (action) {
@ -95,14 +96,21 @@ function handleKeyboardEvent(
send();
return null;
case KeyBindingAction.EditPrevMessage: {
// If not in edition
// Or if the caret is not at the beginning of the editor
// Or the editor is modified
if (!editorStateTransfer || !isCaretAtStart(editor) || isEditorModified) {
if (!isCaretAtStart(editor) || isEditorModified) {
break;
}
const isDispatched = dispatchEditEvent(event, false, editorStateTransfer, roomContext, mxClient);
const isDispatched = dispatchEditEvent(
event,
false,
editorStateTransfer,
composerContext,
roomContext,
mxClient,
);
if (isDispatched) {
return null;
}
@ -117,7 +125,14 @@ function handleKeyboardEvent(
break;
}
const isDispatched = dispatchEditEvent(event, true, editorStateTransfer, roomContext, mxClient);
const isDispatched = dispatchEditEvent(
event,
true,
editorStateTransfer,
composerContext,
roomContext,
mxClient,
);
if (!isDispatched) {
endEditing(roomContext);
event.preventDefault();
@ -134,11 +149,14 @@ function handleKeyboardEvent(
function dispatchEditEvent(
event: KeyboardEvent,
isForward: boolean,
editorStateTransfer: EditorStateTransfer,
editorStateTransfer: EditorStateTransfer | undefined,
composerContext: ComposerContextState,
roomContext: IRoomState,
mxClient: MatrixClient,
): boolean {
const foundEvents = getEventsFromEditorStateTransfer(editorStateTransfer, roomContext, mxClient);
const foundEvents = editorStateTransfer
? getEventsFromEditorStateTransfer(editorStateTransfer, roomContext, mxClient)
: getEventsFromRoom(composerContext, roomContext);
if (!foundEvents) {
return false;
}
@ -146,7 +164,7 @@ function dispatchEditEvent(
const newEvent = findEditableEvent({
events: foundEvents,
isForward,
fromEventId: editorStateTransfer.getEvent().getId(),
fromEventId: editorStateTransfer?.getEvent().getId(),
});
if (newEvent) {
dis.dispatch({

View file

@ -15,9 +15,11 @@ limitations under the License.
*/
import { MatrixClient, MatrixEvent } from "matrix-js-sdk/src/matrix";
import { THREAD_RELATION_TYPE } from "matrix-js-sdk/src/models/thread";
import EditorStateTransfer from "../../../../../utils/EditorStateTransfer";
import { IRoomState } from "../../../../structures/RoomView";
import { ComposerContextState } from "../ComposerContext";
// From EditMessageComposer private get events(): MatrixEvent[]
export function getEventsFromEditorStateTransfer(
@ -44,3 +46,14 @@ export function getEventsFromEditorStateTransfer(
const isInThread = Boolean(editorStateTransfer.getEvent().getThread());
return liveTimelineEvents.concat(isInThread ? [] : pendingEvents);
}
// From SendMessageComposer private onKeyDown = (event: KeyboardEvent): void
export function getEventsFromRoom(
composerContext: ComposerContextState,
roomContext: IRoomState,
): MatrixEvent[] | undefined {
const isReplyingToThread = composerContext.eventRelation?.key === THREAD_RELATION_TYPE.name;
return roomContext.liveTimeline
?.getEvents()
.concat(isReplyingToThread ? [] : roomContext.room?.getPendingEvents() || []);
}

View file

@ -44,15 +44,21 @@ export function isCaretAtStart(editor: HTMLElement): boolean {
const selection = document.getSelection();
// No selection or the caret is not at the beginning of the selected element
if (!selection || selection.anchorOffset !== 0) {
if (!selection) {
return false;
}
// When we are pressing keyboard up in an empty main composer, the selection is on the editor with an anchorOffset at O or 1 (yes, this is strange)
const isOnFirstElement = selection.anchorNode === editor && selection.anchorOffset <= 1;
if (isOnFirstElement) {
return true;
}
// In case of nested html elements (list, code blocks), we are going through all the first child
let child = editor.firstChild;
do {
if (child === selection.anchorNode) {
return true;
return selection.anchorOffset === 0;
}
} while ((child = child?.firstChild || null));