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:
parent
f1a08cd572
commit
2b66cfc25f
11 changed files with 487 additions and 365 deletions
|
@ -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 {
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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}>
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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() || []);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue