mirror of https://github.com/vector-im/riot-web
First attempt to make the edition works in the WysiwygComposer
parent
a61076b4fb
commit
460f60e99d
|
@ -48,6 +48,7 @@ import RoomContext from "../../../contexts/RoomContext";
|
|||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
import { options as linkifyOpts } from "../../../linkify-matrix";
|
||||
import { getParentEventId } from '../../../utils/Reply';
|
||||
import { EditWysiwygComposer } from '../rooms/wysiwyg_composer';
|
||||
|
||||
const MAX_HIGHLIGHT_LENGTH = 4096;
|
||||
|
||||
|
@ -562,7 +563,10 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
|
|||
|
||||
render() {
|
||||
if (this.props.editState) {
|
||||
return <EditMessageComposer editState={this.props.editState} className="mx_EventTile_content" />;
|
||||
const isWysiwygComposerEnabled = SettingsStore.getValue("feature_wysiwyg_composer");
|
||||
return isWysiwygComposerEnabled ?
|
||||
<EditWysiwygComposer editorStateTransfer={this.props.editState} /> :
|
||||
<EditMessageComposer editState={this.props.editState} className="mx_EventTile_content" />;
|
||||
}
|
||||
const mxEvent = this.props.mxEvent;
|
||||
const content = mxEvent.getContent();
|
||||
|
|
|
@ -833,6 +833,8 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
|||
}
|
||||
|
||||
public insertPlaintext(text: string): void {
|
||||
console.log('insertPlaintext', text);
|
||||
debugger;
|
||||
this.modifiedFlag = true;
|
||||
const { model } = this.props;
|
||||
const { partCreator } = model;
|
||||
|
|
|
@ -350,6 +350,7 @@ class EditMessageComposer extends React.Component<IEditMessageComposerProps, ISt
|
|||
const event = this.props.editState.getEvent();
|
||||
const threadId = event.threadRootId || null;
|
||||
|
||||
console.log('editContent', editContent);
|
||||
this.props.mxClient.sendMessage(roomId, threadId, editContent);
|
||||
dis.dispatch({ action: "message_sent" });
|
||||
}
|
||||
|
|
|
@ -58,7 +58,8 @@ import {
|
|||
startNewVoiceBroadcastRecording,
|
||||
VoiceBroadcastRecordingsStore,
|
||||
} from '../../../voice-broadcast';
|
||||
import { WysiwygComposer } from './wysiwyg_composer/WysiwygComposer';
|
||||
import { SendWysiwygComposer, sendMessage } from './wysiwyg_composer/';
|
||||
import { MatrixClientProps, withMatrixClientHOC } from '../../../contexts/MatrixClientContext';
|
||||
|
||||
let instanceCount = 0;
|
||||
|
||||
|
@ -77,7 +78,7 @@ function SendButton(props: ISendButtonProps) {
|
|||
);
|
||||
}
|
||||
|
||||
interface IProps {
|
||||
interface IProps extends MatrixClientProps {
|
||||
room: Room;
|
||||
resizeNotifier: ResizeNotifier;
|
||||
permalinkCreator: RoomPermalinkCreator;
|
||||
|
@ -89,6 +90,7 @@ interface IProps {
|
|||
}
|
||||
|
||||
interface IState {
|
||||
composerContent: string;
|
||||
isComposerEmpty: boolean;
|
||||
haveRecording: boolean;
|
||||
recordingTimeLeftSeconds?: number;
|
||||
|
@ -100,13 +102,12 @@ interface IState {
|
|||
showVoiceBroadcastButton: boolean;
|
||||
}
|
||||
|
||||
export default class MessageComposer extends React.Component<IProps, IState> {
|
||||
class MessageComposer extends React.Component<IProps, IState> {
|
||||
private dispatcherRef?: string;
|
||||
private messageComposerInput = createRef<SendMessageComposerClass>();
|
||||
private voiceRecordingButton = createRef<VoiceRecordComposerTile>();
|
||||
private ref: React.RefObject<HTMLDivElement> = createRef();
|
||||
private instanceId: number;
|
||||
private composerSendMessage?: () => void;
|
||||
|
||||
private _voiceRecording: Optional<VoiceMessageRecording>;
|
||||
|
||||
|
@ -124,6 +125,7 @@ export default class MessageComposer extends React.Component<IProps, IState> {
|
|||
|
||||
this.state = {
|
||||
isComposerEmpty: true,
|
||||
composerContent: '',
|
||||
haveRecording: false,
|
||||
recordingTimeLeftSeconds: undefined, // when set to a number, shows a toast
|
||||
isMenuOpen: false,
|
||||
|
@ -315,7 +317,15 @@ export default class MessageComposer extends React.Component<IProps, IState> {
|
|||
}
|
||||
|
||||
this.messageComposerInput.current?.sendMessage();
|
||||
this.composerSendMessage?.();
|
||||
// this.composerSendMessage?.();
|
||||
const isWysiwygComposerEnabled = SettingsStore.getValue("feature_wysiwyg_composer");
|
||||
|
||||
if (isWysiwygComposerEnabled) {
|
||||
const { permalinkCreator, relation, replyToEvent } = this.props;
|
||||
sendMessage(this.state.composerContent,
|
||||
{ mxClient: this.props.mxClient, roomContext: this.context, permalinkCreator, relation, replyToEvent });
|
||||
dis.dispatch({ action: Action.ClearAndFocusSendMessageComposer });
|
||||
}
|
||||
};
|
||||
|
||||
private onChange = (model: EditorModel) => {
|
||||
|
@ -326,6 +336,7 @@ export default class MessageComposer extends React.Component<IProps, IState> {
|
|||
|
||||
private onWysiwygChange = (content: string) => {
|
||||
this.setState({
|
||||
composerContent: content,
|
||||
isComposerEmpty: content?.length === 0,
|
||||
});
|
||||
};
|
||||
|
@ -406,16 +417,10 @@ export default class MessageComposer extends React.Component<IProps, IState> {
|
|||
if (canSendMessages) {
|
||||
if (isWysiwygComposerEnabled) {
|
||||
controls.push(
|
||||
<WysiwygComposer key="controls_input"
|
||||
<SendWysiwygComposer key="controls_input"
|
||||
disabled={this.state.haveRecording}
|
||||
onChange={this.onWysiwygChange}
|
||||
permalinkCreator={this.props.permalinkCreator}
|
||||
relation={this.props.relation}
|
||||
replyToEvent={this.props.replyToEvent}>
|
||||
{ (sendMessage) => {
|
||||
this.composerSendMessage = sendMessage;
|
||||
} }
|
||||
</WysiwygComposer>,
|
||||
/>,
|
||||
);
|
||||
} else {
|
||||
controls.push(
|
||||
|
@ -555,3 +560,6 @@ export default class MessageComposer extends React.Component<IProps, IState> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
const MessageComposerWithMatrixClient = withMatrixClientHOC(MessageComposer);
|
||||
export default MessageComposerWithMatrixClient;
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { forwardRef, RefObject, useMemo } from 'react';
|
||||
import { MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { useRoomContext } from '../../../../contexts/RoomContext';
|
||||
import { useMatrixClientContext } from '../../../../contexts/MatrixClientContext';
|
||||
import EditorStateTransfer from '../../../../utils/EditorStateTransfer';
|
||||
import { CommandPartCreator, Part } from '../../../../editor/parts';
|
||||
import { IRoomState } from '../../../structures/RoomView';
|
||||
import SettingsStore from '../../../../settings/SettingsStore';
|
||||
import { parseEvent } from '../../../../editor/deserialize';
|
||||
import { WysiwygComposer } from './components/WysiwygComposer';
|
||||
import { EditionButtons } from './components/EditionButtons';
|
||||
import { useWysiwygEditActionHandler } from './hooks/useWysiwygEditActionHandler';
|
||||
import { endEditing } from './utils/editing';
|
||||
import { editMessage } from './utils/message';
|
||||
|
||||
function parseEditorStateTransfer(
|
||||
editorStateTransfer: EditorStateTransfer,
|
||||
roomContext: IRoomState,
|
||||
mxClient: MatrixClient,
|
||||
) {
|
||||
if (!roomContext.room) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { room } = roomContext;
|
||||
|
||||
const partCreator = new CommandPartCreator(room, mxClient);
|
||||
|
||||
let parts: Part[];
|
||||
if (editorStateTransfer.hasEditorState()) {
|
||||
// if restoring state from a previous editor,
|
||||
// restore serialized parts from the state
|
||||
parts = editorStateTransfer.getSerializedParts().map(p => partCreator.deserializePart(p));
|
||||
} else {
|
||||
// otherwise, either restore serialized parts from localStorage or parse the body of the event
|
||||
// TODO local storage
|
||||
// const restoredParts = this.restoreStoredEditorState(partCreator);
|
||||
|
||||
if (editorStateTransfer.getEvent().getContent().format === 'org.matrix.custom.html') {
|
||||
return editorStateTransfer.getEvent().getContent().formatted_body || "";
|
||||
}
|
||||
|
||||
parts = parseEvent(editorStateTransfer.getEvent(), partCreator, {
|
||||
shouldEscape: SettingsStore.getValue("MessageComposerInput.useMarkdown"),
|
||||
});
|
||||
}
|
||||
|
||||
return parts.reduce((content, part) => content + part.text, '');
|
||||
// Todo local storage
|
||||
// this.saveStoredEditorState();
|
||||
}
|
||||
|
||||
interface ContentProps {
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
const Content = forwardRef<HTMLElement, ContentProps>(
|
||||
function Content({ disabled }: ContentProps, forwardRef: RefObject<HTMLElement>) {
|
||||
useWysiwygEditActionHandler(disabled, forwardRef);
|
||||
return null;
|
||||
},
|
||||
);
|
||||
|
||||
interface EditWysiwygComposerProps {
|
||||
disabled?: boolean;
|
||||
onChange?: (content: string) => void;
|
||||
editorStateTransfer?: EditorStateTransfer;
|
||||
}
|
||||
|
||||
export function EditWysiwygComposer({ editorStateTransfer, ...props }: EditWysiwygComposerProps) {
|
||||
const roomContext = useRoomContext();
|
||||
const mxClient = useMatrixClientContext();
|
||||
|
||||
const initialContent = useMemo(() => {
|
||||
if (editorStateTransfer) {
|
||||
return parseEditorStateTransfer(editorStateTransfer, roomContext, mxClient);
|
||||
}
|
||||
}, [editorStateTransfer, roomContext, mxClient]);
|
||||
const isReady = !editorStateTransfer || Boolean(initialContent);
|
||||
|
||||
return isReady && <WysiwygComposer initialContent={initialContent} {...props}>{ (ref, wysiwyg, content) => (
|
||||
<>
|
||||
<Content disabled={props.disabled} ref={ref} />
|
||||
<EditionButtons onCancelClick={() => endEditing(roomContext)} onSaveClick={() => editMessage(content, { roomContext, mxClient, editorStateTransfer })} />
|
||||
</>)
|
||||
}
|
||||
</WysiwygComposer>;
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { forwardRef, RefObject } from 'react';
|
||||
|
||||
import { useWysiwygSendActionHandler } from './hooks/useWysiwygSendActionHandler';
|
||||
import { WysiwygComposer } from './components/WysiwygComposer';
|
||||
import { Wysiwyg } from './types';
|
||||
|
||||
interface SendWysiwygComposerProps {
|
||||
disabled?: boolean;
|
||||
onChange?: (content: string) => void;
|
||||
}
|
||||
|
||||
export function SendWysiwygComposer(props: SendWysiwygComposerProps) {
|
||||
return (
|
||||
<WysiwygComposer {...props}>{ (ref, wysiwyg) => (
|
||||
<Content disabled={props.disabled} ref={ref} wysiwyg={wysiwyg} />
|
||||
) }
|
||||
</WysiwygComposer>);
|
||||
}
|
||||
|
||||
interface ContentProps {
|
||||
disabled: boolean;
|
||||
wysiwyg: Wysiwyg;
|
||||
}
|
||||
|
||||
const Content = forwardRef<HTMLElement, ContentProps>(
|
||||
function Content({ disabled, wysiwyg }: ContentProps, forwardRef: RefObject<HTMLElement>) {
|
||||
useWysiwygSendActionHandler(disabled, forwardRef, wysiwyg);
|
||||
return null;
|
||||
},
|
||||
);
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { IEventRelation, MatrixEvent } from 'matrix-js-sdk/src/models/event';
|
||||
import { useWysiwyg } from "@matrix-org/matrix-wysiwyg";
|
||||
|
||||
import { Editor } from './Editor';
|
||||
import { FormattingButtons } from './FormattingButtons';
|
||||
import { RoomPermalinkCreator } from '../../../../utils/permalinks/Permalinks';
|
||||
import { sendMessage } from './message';
|
||||
import { useMatrixClientContext } from '../../../../contexts/MatrixClientContext';
|
||||
import { useRoomContext } from '../../../../contexts/RoomContext';
|
||||
import { useWysiwygActionHandler } from './useWysiwygActionHandler';
|
||||
|
||||
interface WysiwygProps {
|
||||
disabled?: boolean;
|
||||
onChange: (content: string) => void;
|
||||
relation?: IEventRelation;
|
||||
replyToEvent?: MatrixEvent;
|
||||
permalinkCreator: RoomPermalinkCreator;
|
||||
includeReplyLegacyFallback?: boolean;
|
||||
children?: (sendMessage: () => void) => void;
|
||||
}
|
||||
|
||||
export function WysiwygComposer(
|
||||
{ disabled = false, onChange, children, ...props }: WysiwygProps,
|
||||
) {
|
||||
const roomContext = useRoomContext();
|
||||
const mxClient = useMatrixClientContext();
|
||||
|
||||
const { ref, isWysiwygReady, content, formattingStates, wysiwyg } = useWysiwyg();
|
||||
|
||||
useEffect(() => {
|
||||
if (!disabled && content !== null) {
|
||||
onChange(content);
|
||||
}
|
||||
}, [onChange, content, disabled]);
|
||||
|
||||
const memoizedSendMessage = useCallback(() => {
|
||||
sendMessage(content, { mxClient, roomContext, ...props });
|
||||
wysiwyg.clear();
|
||||
ref.current?.focus();
|
||||
}, [content, mxClient, roomContext, wysiwyg, props, ref]);
|
||||
|
||||
useWysiwygActionHandler(disabled, ref);
|
||||
|
||||
return (
|
||||
<div className="mx_WysiwygComposer">
|
||||
<FormattingButtons composer={wysiwyg} formattingStates={formattingStates} />
|
||||
<Editor ref={ref} disabled={!isWysiwygReady || disabled} />
|
||||
{ children?.(memoizedSendMessage) }
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { MouseEventHandler } from 'react';
|
||||
|
||||
import { _t } from '../../../../../languageHandler';
|
||||
import AccessibleButton from '../../../elements/AccessibleButton';
|
||||
|
||||
interface EditionButtonsProps {
|
||||
onCancelClick: MouseEventHandler<HTMLButtonElement>;
|
||||
onSaveClick: MouseEventHandler<HTMLButtonElement>;
|
||||
}
|
||||
|
||||
export function EditionButtons({ onCancelClick, onSaveClick }: EditionButtonsProps) {
|
||||
return <div className="mx_EditMessageComposer_buttons">
|
||||
<AccessibleButton kind="secondary" onClick={onCancelClick}>
|
||||
{ _t("Cancel") }
|
||||
</AccessibleButton>
|
||||
<AccessibleButton kind="primary" disabled={false} onClick={onSaveClick}>
|
||||
{ _t("Save") }
|
||||
</AccessibleButton>
|
||||
</div>;
|
||||
}
|
|
@ -18,11 +18,12 @@ import React, { MouseEventHandler } from "react";
|
|||
import { useWysiwyg } from "@matrix-org/matrix-wysiwyg";
|
||||
import classNames from "classnames";
|
||||
|
||||
import AccessibleTooltipButton from "../../elements/AccessibleTooltipButton";
|
||||
import { Alignment } from "../../elements/Tooltip";
|
||||
import { KeyboardShortcut } from "../../settings/KeyboardShortcut";
|
||||
import { KeyCombo } from "../../../../KeyBindingsManager";
|
||||
import { _td } from "../../../../languageHandler";
|
||||
import AccessibleTooltipButton from "../../../elements/AccessibleTooltipButton";
|
||||
import { Alignment } from "../../../elements/Tooltip";
|
||||
import { KeyboardShortcut } from "../../../settings/KeyboardShortcut";
|
||||
import { KeyCombo } from "../../../../../KeyBindingsManager";
|
||||
import { _td } from "../../../../../languageHandler";
|
||||
import { Wysiwyg } from "../types";
|
||||
|
||||
interface TooltipProps {
|
||||
label: string;
|
||||
|
@ -55,7 +56,7 @@ function Button({ label, keyCombo, onClick, isActive, className }: ButtonProps)
|
|||
}
|
||||
|
||||
interface FormattingButtonsProps {
|
||||
composer: ReturnType<typeof useWysiwyg>['wysiwyg'];
|
||||
composer: Wysiwyg;
|
||||
formattingStates: ReturnType<typeof useWysiwyg>['formattingStates'];
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { MutableRefObject, ReactNode, useEffect } from 'react';
|
||||
import { useWysiwyg } from "@matrix-org/matrix-wysiwyg";
|
||||
|
||||
import { FormattingButtons } from './FormattingButtons';
|
||||
import { Editor } from './Editor';
|
||||
import { Wysiwyg } from '../types';
|
||||
|
||||
interface WysiwygComposerProps {
|
||||
disabled?: boolean;
|
||||
onChange?: (content: string) => void;
|
||||
initialContent?: string;
|
||||
children?: (ref: MutableRefObject<HTMLDivElement | null>, wysiwyg: Wysiwyg, content: string) => ReactNode;
|
||||
}
|
||||
|
||||
export function WysiwygComposer(
|
||||
{ disabled = false, onChange, initialContent, children }: WysiwygComposerProps,
|
||||
) {
|
||||
const { ref, isWysiwygReady, content, formattingStates, wysiwyg } = useWysiwyg({ initialContent });
|
||||
|
||||
useEffect(() => {
|
||||
if (!disabled && content !== null) {
|
||||
onChange?.(content);
|
||||
}
|
||||
}, [onChange, content, disabled]);
|
||||
|
||||
return (
|
||||
<div className="mx_WysiwygComposer">
|
||||
<FormattingButtons composer={wysiwyg} formattingStates={formattingStates} />
|
||||
<Editor ref={ref} disabled={!isWysiwygReady || disabled} />
|
||||
{ children?.(ref, wysiwyg, content) }
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { RefObject, useCallback, useRef } from "react";
|
||||
|
||||
import defaultDispatcher from "../../../../../dispatcher/dispatcher";
|
||||
import { Action } from "../../../../../dispatcher/actions";
|
||||
import { ActionPayload } from "../../../../../dispatcher/payloads";
|
||||
import { TimelineRenderingType, useRoomContext } from "../../../../../contexts/RoomContext";
|
||||
import { useDispatcher } from "../../../../../hooks/useDispatcher";
|
||||
import { focusComposer } from "./utils";
|
||||
|
||||
export function useWysiwygEditActionHandler(
|
||||
disabled: boolean,
|
||||
composerElement: RefObject<HTMLElement>,
|
||||
) {
|
||||
const roomContext = useRoomContext();
|
||||
const timeoutId = useRef<number>();
|
||||
|
||||
const handler = useCallback((payload: ActionPayload) => {
|
||||
// don't let the user into the composer if it is disabled - all of these branches lead
|
||||
// to the cursor being in the composer
|
||||
if (disabled || !composerElement.current) return;
|
||||
|
||||
const context = payload.context ?? TimelineRenderingType.Room;
|
||||
|
||||
switch (payload.action) {
|
||||
case Action.FocusSendMessageComposer:
|
||||
focusComposer(composerElement, context, roomContext, timeoutId);
|
||||
break;
|
||||
}
|
||||
}, [disabled, composerElement, timeoutId, roomContext]);
|
||||
|
||||
useDispatcher(defaultDispatcher, handler);
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { RefObject, useCallback, useRef } from "react";
|
||||
|
||||
import defaultDispatcher from "../../../../../dispatcher/dispatcher";
|
||||
import { Action } from "../../../../../dispatcher/actions";
|
||||
import { ActionPayload } from "../../../../../dispatcher/payloads";
|
||||
import { TimelineRenderingType, useRoomContext } from "../../../../../contexts/RoomContext";
|
||||
import { useDispatcher } from "../../../../../hooks/useDispatcher";
|
||||
import { Wysiwyg } from "../types";
|
||||
import { focusComposer } from "./utils";
|
||||
|
||||
export function useWysiwygSendActionHandler(
|
||||
disabled: boolean,
|
||||
composerElement: RefObject<HTMLElement>,
|
||||
wysiwyg: Wysiwyg,
|
||||
) {
|
||||
const roomContext = useRoomContext();
|
||||
const timeoutId = useRef<number>();
|
||||
|
||||
const handler = useCallback((payload: ActionPayload) => {
|
||||
// don't let the user into the composer if it is disabled - all of these branches lead
|
||||
// to the cursor being in the composer
|
||||
if (disabled || !composerElement.current) return;
|
||||
|
||||
const context = payload.context ?? TimelineRenderingType.Room;
|
||||
|
||||
switch (payload.action) {
|
||||
case "reply_to_event":
|
||||
case Action.FocusSendMessageComposer:
|
||||
focusComposer(composerElement, context, roomContext, timeoutId);
|
||||
break;
|
||||
case Action.ClearAndFocusSendMessageComposer:
|
||||
wysiwyg.clear();
|
||||
focusComposer(composerElement, context, roomContext, timeoutId);
|
||||
break;
|
||||
// TODO: case Action.ComposerInsert: - see SendMessageComposer
|
||||
}
|
||||
}, [disabled, composerElement, wysiwyg, timeoutId, roomContext]);
|
||||
|
||||
useDispatcher(defaultDispatcher, handler);
|
||||
}
|
|
@ -14,40 +14,10 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { useRef } from "react";
|
||||
import { TimelineRenderingType } from "../../../../../contexts/RoomContext";
|
||||
import { IRoomState } from "../../../../structures/RoomView";
|
||||
|
||||
import defaultDispatcher from "../../../../dispatcher/dispatcher";
|
||||
import { Action } from "../../../../dispatcher/actions";
|
||||
import { ActionPayload } from "../../../../dispatcher/payloads";
|
||||
import { IRoomState } from "../../../structures/RoomView";
|
||||
import { TimelineRenderingType, useRoomContext } from "../../../../contexts/RoomContext";
|
||||
import { useDispatcher } from "../../../../hooks/useDispatcher";
|
||||
|
||||
export function useWysiwygActionHandler(
|
||||
disabled: boolean,
|
||||
composerElement: React.MutableRefObject<HTMLElement>,
|
||||
) {
|
||||
const roomContext = useRoomContext();
|
||||
const timeoutId = useRef<number>();
|
||||
|
||||
useDispatcher(defaultDispatcher, (payload: ActionPayload) => {
|
||||
// don't let the user into the composer if it is disabled - all of these branches lead
|
||||
// to the cursor being in the composer
|
||||
if (disabled) return;
|
||||
|
||||
const context = payload.context ?? TimelineRenderingType.Room;
|
||||
|
||||
switch (payload.action) {
|
||||
case "reply_to_event":
|
||||
case Action.FocusSendMessageComposer:
|
||||
focusComposer(composerElement, context, roomContext, timeoutId);
|
||||
break;
|
||||
// TODO: case Action.ComposerInsert: - see SendMessageComposer
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function focusComposer(
|
||||
export function focusComposer(
|
||||
composerElement: React.MutableRefObject<HTMLElement>,
|
||||
renderingType: TimelineRenderingType,
|
||||
roomContext: IRoomState,
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
export { SendWysiwygComposer } from './SendWysiwygComposer';
|
||||
export { EditWysiwygComposer } from './EditWysiwygComposer';
|
||||
export { sendMessage } from './utils/message';
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { useWysiwyg } from "@matrix-org/matrix-wysiwyg";
|
||||
|
||||
// TODO
|
||||
// Change when the matrix-wysiwyg typescript definition will be refined
|
||||
export type Wysiwyg = ReturnType<typeof useWysiwyg>['wysiwyg'];
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { IContent, IEventRelation, MatrixEvent, MsgType } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { RoomPermalinkCreator } from "../../../../../utils/permalinks/Permalinks";
|
||||
import { addReplyToMessageContent } from "../../../../../utils/Reply";
|
||||
|
||||
// Merges favouring the given relation
|
||||
function attachRelation(content: IContent, relation?: IEventRelation): void {
|
||||
if (relation) {
|
||||
content['m.relates_to'] = {
|
||||
...(content['m.relates_to'] || {}),
|
||||
...relation,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getHtmlReplyFallback(mxEvent: MatrixEvent): string {
|
||||
const html = mxEvent.getContent().formatted_body;
|
||||
if (!html) {
|
||||
return "";
|
||||
}
|
||||
const rootNode = new DOMParser().parseFromString(html, "text/html").body;
|
||||
const mxReply = rootNode.querySelector("mx-reply");
|
||||
return (mxReply && mxReply.outerHTML) || "";
|
||||
}
|
||||
|
||||
interface CreateMessageContentParams {
|
||||
relation?: IEventRelation;
|
||||
replyToEvent?: MatrixEvent;
|
||||
permalinkCreator?: RoomPermalinkCreator;
|
||||
includeReplyLegacyFallback?: boolean;
|
||||
editedEvent?: MatrixEvent;
|
||||
}
|
||||
|
||||
export function createMessageContent(
|
||||
message: string,
|
||||
{ relation, replyToEvent, permalinkCreator, includeReplyLegacyFallback = true, editedEvent }:
|
||||
CreateMessageContentParams,
|
||||
): IContent {
|
||||
// TODO emote ?
|
||||
|
||||
const isReply = Boolean(replyToEvent?.replyEventId);
|
||||
const isEditing = Boolean(editedEvent);
|
||||
|
||||
/*const isEmote = containsEmote(model);
|
||||
if (isEmote) {
|
||||
model = stripEmoteCommand(model);
|
||||
}
|
||||
if (startsWith(model, "//")) {
|
||||
model = stripPrefix(model, "/");
|
||||
}
|
||||
model = unescapeMessage(model);*/
|
||||
|
||||
// const body = textSerialize(model);
|
||||
const body = message;
|
||||
|
||||
const content: IContent = {
|
||||
// TODO emote
|
||||
// msgtype: isEmote ? "m.emote" : "m.text",
|
||||
msgtype: MsgType.Text,
|
||||
body: body,
|
||||
};
|
||||
|
||||
// TODO markdown support
|
||||
|
||||
/*const formattedBody = htmlSerializeIfNeeded(model, {
|
||||
forceHTML: !!replyToEvent,
|
||||
useMarkdown: SettingsStore.getValue("MessageComposerInput.useMarkdown"),
|
||||
});*/
|
||||
const formattedBody = message;
|
||||
|
||||
if (formattedBody) {
|
||||
content.format = "org.matrix.custom.html";
|
||||
|
||||
const htmlPrefix = isReply ? getHtmlReplyFallback(editedEvent) : '';
|
||||
content.formatted_body = isEditing ? `${htmlPrefix} * ${formattedBody}` : formattedBody;
|
||||
|
||||
if (isEditing) {
|
||||
content['m.new_content'] = {
|
||||
"msgtype": content.msgtype,
|
||||
"body": body,
|
||||
"format": "org.matrix.custom.html",
|
||||
'formatted_body': formattedBody,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const newRelation = isEditing ?
|
||||
{ ...relation, 'rel_type': 'm.replace', 'event_id': editedEvent.getId() }
|
||||
: relation;
|
||||
|
||||
attachRelation(content, newRelation);
|
||||
|
||||
if (!isEditing && replyToEvent && permalinkCreator) {
|
||||
addReplyToMessageContent(content, replyToEvent, {
|
||||
permalinkCreator,
|
||||
includeLegacyFallback: includeReplyLegacyFallback,
|
||||
});
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { EventStatus, MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { IRoomState } from "../../../../structures/RoomView";
|
||||
import dis from '../../../../../dispatcher/dispatcher';
|
||||
import { Action } from "../../../../../dispatcher/actions";
|
||||
import EditorStateTransfer from "../../../../../utils/EditorStateTransfer";
|
||||
|
||||
export function endEditing(roomContext: IRoomState) {
|
||||
// todo local storage
|
||||
// localStorage.removeItem(this.editorRoomKey);
|
||||
// localStorage.removeItem(this.editorStateKey);
|
||||
|
||||
// close the event editing and focus composer
|
||||
dis.dispatch({
|
||||
action: Action.EditEvent,
|
||||
event: null,
|
||||
timelineRenderingType: roomContext.timelineRenderingType,
|
||||
});
|
||||
dis.dispatch({
|
||||
action: Action.FocusSendMessageComposer,
|
||||
context: roomContext.timelineRenderingType,
|
||||
});
|
||||
}
|
||||
|
||||
export function cancelPreviousPendingEdit(mxClient: MatrixClient, editorStateTransfer: EditorStateTransfer) {
|
||||
const originalEvent = editorStateTransfer.getEvent();
|
||||
const previousEdit = originalEvent.replacingEvent();
|
||||
if (previousEdit && (
|
||||
previousEdit.status === EventStatus.QUEUED ||
|
||||
previousEdit.status === EventStatus.NOT_SENT
|
||||
)) {
|
||||
mxClient.cancelPendingEvent(previousEdit);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { IContent } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import EditorStateTransfer from "../../../../../utils/EditorStateTransfer";
|
||||
|
||||
export function isContentModified(newContent: IContent, editorStateTransfer: EditorStateTransfer): boolean {
|
||||
// if nothing has changed then bail
|
||||
const oldContent = editorStateTransfer.getEvent().getContent();
|
||||
if (oldContent["msgtype"] === newContent["msgtype"] && oldContent["body"] === newContent["body"] &&
|
||||
oldContent["format"] === newContent["format"] &&
|
||||
oldContent["formatted_body"] === newContent["formatted_body"]) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
|
@ -19,90 +19,32 @@ import { IContent, IEventRelation, MatrixEvent } from 'matrix-js-sdk/src/models/
|
|||
import { MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import { THREAD_RELATION_TYPE } from "matrix-js-sdk/src/models/thread";
|
||||
|
||||
import { PosthogAnalytics } from "../../../../PosthogAnalytics";
|
||||
import SettingsStore from "../../../../settings/SettingsStore";
|
||||
import { decorateStartSendingTime, sendRoundTripMetric } from "../../../../sendTimePerformanceMetrics";
|
||||
import { RoomPermalinkCreator } from "../../../../utils/permalinks/Permalinks";
|
||||
import { doMaybeLocalRoomAction } from "../../../../utils/local-room";
|
||||
import { CHAT_EFFECTS } from "../../../../effects";
|
||||
import { containsEmoji } from "../../../../effects/utils";
|
||||
import { IRoomState } from "../../../structures/RoomView";
|
||||
import dis from '../../../../dispatcher/dispatcher';
|
||||
import { addReplyToMessageContent } from "../../../../utils/Reply";
|
||||
|
||||
// Merges favouring the given relation
|
||||
function attachRelation(content: IContent, relation?: IEventRelation): void {
|
||||
if (relation) {
|
||||
content['m.relates_to'] = {
|
||||
...(content['m.relates_to'] || {}),
|
||||
...relation,
|
||||
};
|
||||
}
|
||||
}
|
||||
import { PosthogAnalytics } from "../../../../../PosthogAnalytics";
|
||||
import SettingsStore from "../../../../../settings/SettingsStore";
|
||||
import { decorateStartSendingTime, sendRoundTripMetric } from "../../../../../sendTimePerformanceMetrics";
|
||||
import { RoomPermalinkCreator } from "../../../../../utils/permalinks/Permalinks";
|
||||
import { doMaybeLocalRoomAction } from "../../../../../utils/local-room";
|
||||
import { CHAT_EFFECTS } from "../../../../../effects";
|
||||
import { containsEmoji } from "../../../../../effects/utils";
|
||||
import { IRoomState } from "../../../../structures/RoomView";
|
||||
import dis from '../../../../../dispatcher/dispatcher';
|
||||
import { createRedactEventDialog } from "../../../dialogs/ConfirmRedactDialog";
|
||||
import { endEditing, cancelPreviousPendingEdit } from "./editing";
|
||||
import EditorStateTransfer from "../../../../../utils/EditorStateTransfer";
|
||||
import { createMessageContent } from "./createMessageContent";
|
||||
import { isContentModified } from "./isContentModified";
|
||||
|
||||
interface SendMessageParams {
|
||||
mxClient: MatrixClient;
|
||||
relation?: IEventRelation;
|
||||
replyToEvent?: MatrixEvent;
|
||||
roomContext: IRoomState;
|
||||
permalinkCreator: RoomPermalinkCreator;
|
||||
permalinkCreator?: RoomPermalinkCreator;
|
||||
includeReplyLegacyFallback?: boolean;
|
||||
}
|
||||
|
||||
// exported for tests
|
||||
export function createMessageContent(
|
||||
message: string,
|
||||
{ relation, replyToEvent, permalinkCreator, includeReplyLegacyFallback = true }:
|
||||
Omit<SendMessageParams, 'roomContext' | 'mxClient'>,
|
||||
): IContent {
|
||||
// TODO emote ?
|
||||
|
||||
/*const isEmote = containsEmote(model);
|
||||
if (isEmote) {
|
||||
model = stripEmoteCommand(model);
|
||||
}
|
||||
if (startsWith(model, "//")) {
|
||||
model = stripPrefix(model, "/");
|
||||
}
|
||||
model = unescapeMessage(model);*/
|
||||
|
||||
// const body = textSerialize(model);
|
||||
const body = message;
|
||||
|
||||
const content: IContent = {
|
||||
// TODO emote
|
||||
// msgtype: isEmote ? "m.emote" : "m.text",
|
||||
msgtype: "m.text",
|
||||
body: body,
|
||||
};
|
||||
|
||||
// TODO markdown support
|
||||
|
||||
/*const formattedBody = htmlSerializeIfNeeded(model, {
|
||||
forceHTML: !!replyToEvent,
|
||||
useMarkdown: SettingsStore.getValue("MessageComposerInput.useMarkdown"),
|
||||
});*/
|
||||
const formattedBody = message;
|
||||
|
||||
if (formattedBody) {
|
||||
content.format = "org.matrix.custom.html";
|
||||
content.formatted_body = formattedBody;
|
||||
}
|
||||
|
||||
attachRelation(content, relation);
|
||||
|
||||
if (replyToEvent) {
|
||||
addReplyToMessageContent(content, replyToEvent, {
|
||||
permalinkCreator,
|
||||
includeLegacyFallback: includeReplyLegacyFallback,
|
||||
});
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
export function sendMessage(
|
||||
message: string,
|
||||
html: string,
|
||||
{ roomContext, mxClient, ...params }: SendMessageParams,
|
||||
) {
|
||||
const { relation, replyToEvent } = params;
|
||||
|
@ -113,6 +55,7 @@ export function sendMessage(
|
|||
eventName: "Composer",
|
||||
isEditing: false,
|
||||
isReply: Boolean(replyToEvent),
|
||||
// TODO thread
|
||||
inThread: relation?.rel_type === THREAD_RELATION_TYPE.name,
|
||||
};
|
||||
|
||||
|
@ -133,7 +76,7 @@ export function sendMessage(
|
|||
|
||||
if (!content) {
|
||||
content = createMessageContent(
|
||||
message,
|
||||
html,
|
||||
params,
|
||||
);
|
||||
}
|
||||
|
@ -197,3 +140,65 @@ export function sendMessage(
|
|||
|
||||
return prom;
|
||||
}
|
||||
|
||||
interface EditMessageParams {
|
||||
mxClient: MatrixClient;
|
||||
roomContext: IRoomState;
|
||||
editorStateTransfer: EditorStateTransfer;
|
||||
}
|
||||
|
||||
export function editMessage(
|
||||
html: string,
|
||||
{ roomContext, mxClient, editorStateTransfer }: EditMessageParams,
|
||||
) {
|
||||
const editedEvent = editorStateTransfer.getEvent();
|
||||
|
||||
PosthogAnalytics.instance.trackEvent<ComposerEvent>({
|
||||
eventName: "Composer",
|
||||
isEditing: true,
|
||||
inThread: Boolean(editedEvent?.getThread()),
|
||||
isReply: Boolean(editedEvent.replyEventId),
|
||||
});
|
||||
|
||||
// Replace emoticon at the end of the message
|
||||
/* if (SettingsStore.getValue('MessageComposerInput.autoReplaceEmoji')) {
|
||||
const caret = this.editorRef.current?.getCaret();
|
||||
const position = this.model.positionForOffset(caret.offset, caret.atNodeEnd);
|
||||
this.editorRef.current?.replaceEmoticon(position, REGEX_EMOTICON);
|
||||
}*/
|
||||
const editContent = createMessageContent(html, { editedEvent });
|
||||
const newContent = editContent["m.new_content"];
|
||||
|
||||
const shouldSend = true;
|
||||
|
||||
if (newContent?.body === '') {
|
||||
cancelPreviousPendingEdit(mxClient, editorStateTransfer);
|
||||
createRedactEventDialog({
|
||||
mxEvent: editedEvent,
|
||||
onCloseDialog: () => {
|
||||
endEditing(roomContext);
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// If content is modified then send an updated event into the room
|
||||
if (isContentModified(newContent, editorStateTransfer)) {
|
||||
const roomId = editedEvent.getRoomId();
|
||||
|
||||
// TODO Slash Commands
|
||||
|
||||
if (shouldSend) {
|
||||
cancelPreviousPendingEdit(mxClient, editorStateTransfer);
|
||||
|
||||
const event = editorStateTransfer.getEvent();
|
||||
const threadId = event.threadRootId || null;
|
||||
|
||||
console.log('editContent', editContent);
|
||||
mxClient.sendMessage(roomId, threadId, editContent);
|
||||
dis.dispatch({ action: "message_sent" });
|
||||
}
|
||||
}
|
||||
|
||||
endEditing(roomContext);
|
||||
}
|
|
@ -75,6 +75,11 @@ export enum Action {
|
|||
*/
|
||||
FocusSendMessageComposer = "focus_send_message_composer",
|
||||
|
||||
/**
|
||||
* Clear the to the send message composer. Should be used with a FocusComposerPayload.
|
||||
*/
|
||||
ClearAndFocusSendMessageComposer = "clear_focus_send_message_composer",
|
||||
|
||||
/**
|
||||
* Focuses the user's cursor to the edit message composer. Should be used with a FocusComposerPayload.
|
||||
*/
|
||||
|
|
|
@ -39,7 +39,7 @@ import { SendMessageComposer } from "../../../../src/components/views/rooms/Send
|
|||
import { E2EStatus } from "../../../../src/utils/ShieldUtils";
|
||||
import { addTextToComposer } from "../../../test-utils/composer";
|
||||
import UIStore, { UI_EVENTS } from "../../../../src/stores/UIStore";
|
||||
import { WysiwygComposer } from "../../../../src/components/views/rooms/wysiwyg_composer/WysiwygComposer";
|
||||
import { WysiwygComposer } from "../../../../src/components/views/rooms/wysiwyg_composer/components/WysiwygComposer";
|
||||
|
||||
// The wysiwyg fetch wasm bytes and a specific workaround is needed to make it works in a node (jest) environnement
|
||||
// See https://github.com/matrix-org/matrix-wysiwyg/blob/main/platforms/web/test.setup.ts
|
||||
|
|
|
@ -18,7 +18,7 @@ import React from 'react';
|
|||
import { render, screen } from "@testing-library/react";
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import { FormattingButtons } from "../../../../../src/components/views/rooms/wysiwyg_composer/FormattingButtons";
|
||||
import { FormattingButtons } from "../../../../../src/components/views/rooms/wysiwyg_composer/components/FormattingButtons";
|
||||
|
||||
describe('FormattingButtons', () => {
|
||||
const wysiwyg = {
|
||||
|
|
|
@ -24,7 +24,7 @@ import defaultDispatcher from "../../../../../src/dispatcher/dispatcher";
|
|||
import { Action } from "../../../../../src/dispatcher/actions";
|
||||
import { IRoomState } from "../../../../../src/components/structures/RoomView";
|
||||
import { Layout } from "../../../../../src/settings/enums/Layout";
|
||||
import { WysiwygComposer } from "../../../../../src/components/views/rooms/wysiwyg_composer/WysiwygComposer";
|
||||
import { WysiwygComposer } from "../../../../../src/components/views/rooms/wysiwyg_composer/components/WysiwygComposer";
|
||||
import { createTestClient, mkEvent, mkStubRoom } from "../../../../test-utils";
|
||||
|
||||
// The wysiwyg fetch wasm bytes and a specific workaround is needed to make it works in a node (jest) environnement
|
||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import { IRoomState } from "../../../../../src/components/structures/RoomView";
|
||||
import { createMessageContent, sendMessage } from "../../../../../src/components/views/rooms/wysiwyg_composer/message";
|
||||
import { createMessageContent, sendMessage } from "../../../../../src/components/views/rooms/wysiwyg_composer/utils/message";
|
||||
import { TimelineRenderingType } from "../../../../../src/contexts/RoomContext";
|
||||
import { Layout } from "../../../../../src/settings/enums/Layout";
|
||||
import { createTestClient, mkEvent, mkStubRoom } from "../../../../test-utils";
|
||||
|
|
Loading…
Reference in New Issue