move message editor to new sdk
This commit is contained in:
parent
ba35672cba
commit
6652e55584
5 changed files with 291 additions and 175 deletions
|
|
@ -74,6 +74,7 @@ import { OutboxView } from './OutboxView';
|
|||
import { useUserReadReceipt } from '../../hooks/useReadReceipt';
|
||||
import { useMessageParserOptions } from '../../hooks/useMessageParserOptions';
|
||||
import { MessageContent } from '../../components/MessageContent';
|
||||
import { MessageEditor } from './message/MessageEditor';
|
||||
|
||||
type RoomTimelineProps = {
|
||||
eventId?: string;
|
||||
|
|
@ -433,20 +434,30 @@ export function RoomTimeline({ eventId, roomInputRef, editor, timeline }: RoomTi
|
|||
roomId={roomId}
|
||||
decryptionManager={ct.decryption}
|
||||
>
|
||||
{(contentEvent) => (
|
||||
<RenderMessageContent
|
||||
displayName={membersAPI.getName(event.sender)}
|
||||
eventType={contentEvent.type}
|
||||
ts={event.origin_server_ts}
|
||||
edited={!!editedEvent}
|
||||
content={rContent.newContent(contentEvent.content) ?? contentEvent.content}
|
||||
mediaAutoLoad={mediaAutoLoad}
|
||||
urlPreview={showUrlPreview}
|
||||
htmlReactParserOptions={htmlReactParserOptions}
|
||||
linkifyOpts={linkifyOpts}
|
||||
outlineAttachment={messageLayout === MessageLayout.Bubble}
|
||||
/>
|
||||
)}
|
||||
{(contentEvent) =>
|
||||
editId === event.event_id ? (
|
||||
<MessageEditor
|
||||
style={{ maxWidth: '100%', width: '100vw' }}
|
||||
roomId={roomId}
|
||||
event={contentEvent}
|
||||
imagePackRooms={imagePackRooms}
|
||||
onCancel={handleEdit}
|
||||
/>
|
||||
) : (
|
||||
<RenderMessageContent
|
||||
displayName={membersAPI.getName(event.sender)}
|
||||
eventType={contentEvent.type}
|
||||
ts={event.origin_server_ts}
|
||||
edited={!!editedEvent}
|
||||
content={rContent.newContent(contentEvent.content) ?? contentEvent.content}
|
||||
mediaAutoLoad={mediaAutoLoad}
|
||||
urlPreview={showUrlPreview}
|
||||
htmlReactParserOptions={htmlReactParserOptions}
|
||||
linkifyOpts={linkifyOpts}
|
||||
outlineAttachment={messageLayout === MessageLayout.Bubble}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</MessageContent>
|
||||
)}
|
||||
{reactionJSX}
|
||||
|
|
|
|||
178
src/app/features/room/hooks/useSendEditedMessage.ts
Normal file
178
src/app/features/room/hooks/useSendEditedMessage.ts
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
import { useCallback, useState } from 'react';
|
||||
import { Editor } from 'slate';
|
||||
import { MessageEventWithoutRoomId } from 'neurone/lib/api/types';
|
||||
import { useClient } from '../../../hooks/useClient';
|
||||
import { useRoom } from '../../../hooks/useRoom';
|
||||
import { useEnsureRoomEncryptionSession } from './useEnsureRoomEncryptionSession';
|
||||
import {
|
||||
EventSenderIdle,
|
||||
LocalEvent,
|
||||
LocalEventSender,
|
||||
LocalMessage,
|
||||
} from '../../../../client/room/local-event';
|
||||
import { MessageEvent } from '../../../../types/matrix/room';
|
||||
import {
|
||||
MentionsContent,
|
||||
MessageContent,
|
||||
MsgType,
|
||||
RelationType,
|
||||
} from '../../../../types/matrix/common';
|
||||
import { rContent } from '../../../../client/reader';
|
||||
import {
|
||||
customHtmlEqualsPlainText,
|
||||
getMentions,
|
||||
toMatrixCustomHTML,
|
||||
toPlainText,
|
||||
trimCustomHtml,
|
||||
} from '../../../components/editor';
|
||||
import { useSetting } from '../../../state/hooks/settings';
|
||||
import { getMentionContent, trimReplyFromFormattedBody } from '../../../utils/room';
|
||||
import { useAsyncCallback } from '../../../hooks/useAsyncCallback';
|
||||
|
||||
const useSendLocalMessage = () => {
|
||||
const ct = useClient();
|
||||
const roomId = useRoom();
|
||||
const prepareEncryptionSession = useEnsureRoomEncryptionSession();
|
||||
|
||||
const [localEventSender, setLocalEventSender] = useState<LocalEventSender>();
|
||||
|
||||
const sendLocalMessage = useCallback(
|
||||
async (localMessage: LocalMessage) => {
|
||||
let wireMessage = localMessage.clone();
|
||||
|
||||
if (ct.encryptedRooms.read.has(roomId)) {
|
||||
await prepareEncryptionSession();
|
||||
const encStr = await ct.machine.roomEncryption.encryptMessageEvent(
|
||||
roomId,
|
||||
localMessage.type,
|
||||
localMessage.content
|
||||
);
|
||||
const encContent = JSON.parse(encStr);
|
||||
|
||||
wireMessage = new LocalMessage(roomId, MessageEvent.RoomMessageEncrypted, encContent);
|
||||
}
|
||||
|
||||
const txnId = ct.getTxnId();
|
||||
|
||||
const localEvent = new LocalEvent(localMessage, wireMessage, txnId, Date.now());
|
||||
|
||||
const sender = new LocalEventSender(localEvent, ct.requestAuthJSON);
|
||||
|
||||
if (sender.state.read instanceof EventSenderIdle) {
|
||||
sender.state.read.send();
|
||||
}
|
||||
setLocalEventSender(sender);
|
||||
},
|
||||
[ct, roomId, prepareEncryptionSession]
|
||||
);
|
||||
|
||||
return {
|
||||
localEventSender,
|
||||
sendLocalMessage,
|
||||
};
|
||||
};
|
||||
|
||||
export const useSendEditedMessage = (editor: Editor, event: MessageEventWithoutRoomId) => {
|
||||
const ct = useClient();
|
||||
const roomId = useRoom();
|
||||
const [isMarkdown] = useSetting('isMarkdown');
|
||||
const { localEventSender, sendLocalMessage } = useSendLocalMessage();
|
||||
|
||||
const getPrevContent = useCallback((): {
|
||||
body?: string;
|
||||
formattedBody?: string;
|
||||
mentions?: MentionsContent;
|
||||
} => {
|
||||
const content = rContent.newContent(event.content) ?? event.content;
|
||||
const body = rContent.body(content);
|
||||
const formattedBody = rContent.formattedBody(content);
|
||||
|
||||
const mentions: MentionsContent | undefined = rContent.mentions(content);
|
||||
|
||||
return {
|
||||
body,
|
||||
formattedBody,
|
||||
mentions,
|
||||
};
|
||||
}, [event]);
|
||||
|
||||
const [saveState, save] = useAsyncCallback(
|
||||
useCallback(
|
||||
async (onCancel: () => void) => {
|
||||
const plainText = toPlainText(editor.children, isMarkdown).trim();
|
||||
const customHtml = trimCustomHtml(
|
||||
toMatrixCustomHTML(editor.children, {
|
||||
allowTextFormatting: true,
|
||||
allowBlockMarkdown: isMarkdown,
|
||||
allowInlineMarkdown: isMarkdown,
|
||||
})
|
||||
);
|
||||
|
||||
const prevContent = getPrevContent();
|
||||
|
||||
if (plainText === '') {
|
||||
onCancel();
|
||||
return undefined;
|
||||
}
|
||||
if (prevContent.body) {
|
||||
if (
|
||||
prevContent.formattedBody &&
|
||||
trimReplyFromFormattedBody(prevContent.formattedBody) === customHtml
|
||||
) {
|
||||
onCancel();
|
||||
return undefined;
|
||||
}
|
||||
if (
|
||||
!prevContent.formattedBody &&
|
||||
prevContent.body === plainText &&
|
||||
customHtmlEqualsPlainText(customHtml, plainText)
|
||||
) {
|
||||
onCancel();
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
const newContent: MessageContent = {
|
||||
msgtype: (rContent.msgtype(event.content) as MsgType) ?? MsgType.Text,
|
||||
body: plainText,
|
||||
};
|
||||
|
||||
const mentionData = getMentions(ct, editor);
|
||||
|
||||
prevContent.mentions?.user_ids?.forEach((prevMentionId) => {
|
||||
mentionData.users.add(prevMentionId);
|
||||
});
|
||||
|
||||
const mMentions = getMentionContent(Array.from(mentionData.users), mentionData.room);
|
||||
newContent['m.mentions'] = mMentions;
|
||||
|
||||
if (!customHtmlEqualsPlainText(customHtml, plainText)) {
|
||||
newContent.format = 'org.matrix.custom.html';
|
||||
newContent.formatted_body = customHtml;
|
||||
}
|
||||
|
||||
const content: MessageContent & {
|
||||
'm.new_content': MessageContent;
|
||||
} = {
|
||||
...newContent,
|
||||
body: `* ${plainText}`,
|
||||
'm.new_content': newContent,
|
||||
'm.relates_to': {
|
||||
event_id: event.event_id,
|
||||
rel_type: RelationType.Replace,
|
||||
},
|
||||
};
|
||||
|
||||
const messageEvent = new LocalMessage(roomId, event.type, content);
|
||||
return sendLocalMessage(messageEvent);
|
||||
},
|
||||
[ct, roomId, editor, event, isMarkdown, getPrevContent, sendLocalMessage]
|
||||
)
|
||||
);
|
||||
|
||||
return {
|
||||
saveState,
|
||||
localEventSender,
|
||||
save,
|
||||
};
|
||||
};
|
||||
|
|
@ -61,7 +61,6 @@ import { TextViewer } from '../../../components/text-viewer';
|
|||
import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
|
||||
import { EmojiBoard } from '../../../components/emoji-board';
|
||||
import { ReactionViewer } from '../reaction-viewer';
|
||||
import { MessageEditor } from './MessageEditor';
|
||||
import { UserAvatar } from '../../../components/user-avatar';
|
||||
import { copyToClipboard } from '../../../utils/dom';
|
||||
import { stopPropagation } from '../../../utils/keyboard';
|
||||
|
|
@ -890,7 +889,6 @@ export const Message = as<'div', MessageProps>(
|
|||
ref
|
||||
) => {
|
||||
const ct = useClient();
|
||||
const roomId = useRoom();
|
||||
const senderId = event.sender;
|
||||
|
||||
const [hover, setHover] = useState(false);
|
||||
|
|
@ -902,25 +900,6 @@ export const Message = as<'div', MessageProps>(
|
|||
const senderDisplayName = membersAPI.getName(senderId);
|
||||
const senderAvatarMxc = membersAPI.getAvatar(senderId);
|
||||
|
||||
const msgContentJSX = (
|
||||
<Box direction="Column" alignSelf="Start" style={{ maxWidth: '100%' }}>
|
||||
{edit && onEditId ? (
|
||||
<MessageEditor
|
||||
style={{
|
||||
maxWidth: '100%',
|
||||
width: '100vw',
|
||||
}}
|
||||
roomId={roomId}
|
||||
event={event}
|
||||
imagePackRooms={imagePackRooms}
|
||||
onCancel={() => onEditId()}
|
||||
/>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
|
||||
const handleContextMenu: MouseEventHandler<HTMLDivElement> = (evt) => {
|
||||
if (evt.altKey || !window.getSelection()?.isCollapsed || edit) return;
|
||||
const tag = (evt.target as any).tagName;
|
||||
|
|
@ -1241,7 +1220,9 @@ export const Message = as<'div', MessageProps>(
|
|||
messageLayout={messageLayout}
|
||||
onContextMenu={handleContextMenu}
|
||||
>
|
||||
{msgContentJSX}
|
||||
<Box direction="Column" alignSelf="Start" style={{ maxWidth: '100%' }}>
|
||||
{children}
|
||||
</Box>
|
||||
</RenderMessageLayout>
|
||||
</MessageBase>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import React, {
|
|||
MouseEventHandler,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react';
|
||||
import {
|
||||
|
|
@ -17,9 +18,9 @@ import {
|
|||
Spinner,
|
||||
Text,
|
||||
as,
|
||||
color,
|
||||
config,
|
||||
} from 'folds';
|
||||
import { Editor, Transforms } from 'slate';
|
||||
import { ReactEditor } from 'slate-react';
|
||||
import { MessageEventWithoutRoomId } from 'neurone/lib/api/types';
|
||||
import { isKeyHotkey } from 'is-hotkey';
|
||||
|
|
@ -33,27 +34,27 @@ import {
|
|||
Toolbar,
|
||||
UserMentionAutocomplete,
|
||||
createEmoticonElement,
|
||||
customHtmlEqualsPlainText,
|
||||
getAutocompleteQuery,
|
||||
getPrevWorldRange,
|
||||
htmlToEditorInput,
|
||||
moveCursor,
|
||||
plainToEditorInput,
|
||||
toMatrixCustomHTML,
|
||||
toPlainText,
|
||||
trimCustomHtml,
|
||||
useEditor,
|
||||
getMentions,
|
||||
} from '../../../components/editor';
|
||||
import { useSetting } from '../../../state/hooks/settings';
|
||||
import { UseStateProvider } from '../../../components/UseStateProvider';
|
||||
import { EmojiBoard } from '../../../components/emoji-board';
|
||||
import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
|
||||
import { getEditedEvent, getMentionContent, trimReplyFromFormattedBody } from '../../../utils/room';
|
||||
import { AsyncStatus } from '../../../hooks/useAsyncCallback';
|
||||
import { mobileOrTablet } from '../../../utils/user-agent';
|
||||
import { useComposingCheck } from '../../../hooks/useComposingCheck';
|
||||
import { useClient } from '../../../hooks/useClient';
|
||||
import { MentionsContent, MessageContent, RelationType } from '../../../../types/matrix/common';
|
||||
import { useSendEditedMessage } from '../hooks/useSendEditedMessage';
|
||||
import { useObservableValueOptionally } from '../../../hooks/useObservableValue';
|
||||
import { rContent } from '../../../../client/reader';
|
||||
import {
|
||||
EventSenderError,
|
||||
EventSenderPending,
|
||||
EventSenderSuccess,
|
||||
} from '../../../../client/room/local-event';
|
||||
|
||||
type MessageEditorProps = {
|
||||
roomId: string;
|
||||
|
|
@ -63,7 +64,6 @@ type MessageEditorProps = {
|
|||
};
|
||||
export const MessageEditor = as<'div', MessageEditorProps>(
|
||||
({ roomId, event, imagePackRooms, onCancel, ...props }, ref) => {
|
||||
const ct = useClient();
|
||||
const editor = useEditor();
|
||||
const [enterForNewline] = useSetting('enterForNewline');
|
||||
const [globalToolbar] = useSetting('editorToolbar');
|
||||
|
|
@ -74,96 +74,17 @@ export const MessageEditor = as<'div', MessageEditorProps>(
|
|||
const [autocompleteQuery, setAutocompleteQuery] =
|
||||
useState<AutocompleteQuery<AutocompletePrefix>>();
|
||||
|
||||
const getPrevBodyAndFormattedBody = useCallback((): [
|
||||
string | undefined,
|
||||
string | undefined,
|
||||
MentionsContent | undefined
|
||||
] => {
|
||||
const evtId = event.event_id;
|
||||
const evtTimeline = room.getTimelineForEvent(evtId);
|
||||
const editedEvent =
|
||||
evtTimeline && getEditedEvent(evtId, mEvent, evtTimeline.getTimelineSet());
|
||||
const { saveState, localEventSender, save } = useSendEditedMessage(editor, event);
|
||||
|
||||
const content: IContent = editedEvent?.getContent()['m.new_content'] ?? mEvent.getContent();
|
||||
const { body, formatted_body: customHtml }: Record<string, unknown> = content;
|
||||
|
||||
const mMentions: IMentions | undefined = content['m.mentions'];
|
||||
|
||||
return [
|
||||
typeof body === 'string' ? body : undefined,
|
||||
typeof customHtml === 'string' ? customHtml : undefined,
|
||||
mMentions,
|
||||
];
|
||||
}, [roomId, event]);
|
||||
|
||||
const [saveState, save] = useAsyncCallback(
|
||||
useCallback(async () => {
|
||||
const plainText = toPlainText(editor.children, isMarkdown).trim();
|
||||
const customHtml = trimCustomHtml(
|
||||
toMatrixCustomHTML(editor.children, {
|
||||
allowTextFormatting: true,
|
||||
allowBlockMarkdown: isMarkdown,
|
||||
allowInlineMarkdown: isMarkdown,
|
||||
})
|
||||
);
|
||||
|
||||
const [prevBody, prevCustomHtml, prevMentions] = getPrevBodyAndFormattedBody();
|
||||
|
||||
if (plainText === '') return undefined;
|
||||
if (prevBody) {
|
||||
if (prevCustomHtml && trimReplyFromFormattedBody(prevCustomHtml) === customHtml) {
|
||||
return undefined;
|
||||
}
|
||||
if (
|
||||
!prevCustomHtml &&
|
||||
prevBody === plainText &&
|
||||
customHtmlEqualsPlainText(customHtml, plainText)
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
const prevContent = event.content as MessageContent;
|
||||
const newContent: MessageContent = {
|
||||
msgtype: prevContent.msgtype,
|
||||
body: plainText,
|
||||
};
|
||||
|
||||
const mentionData = getMentions(ct, editor);
|
||||
|
||||
prevMentions?.user_ids?.forEach((prevMentionId) => {
|
||||
mentionData.users.add(prevMentionId);
|
||||
});
|
||||
|
||||
const mMentions = getMentionContent(Array.from(mentionData.users), mentionData.room);
|
||||
newContent['m.mentions'] = mMentions;
|
||||
|
||||
if (!customHtmlEqualsPlainText(customHtml, plainText)) {
|
||||
newContent.format = 'org.matrix.custom.html';
|
||||
newContent.formatted_body = customHtml;
|
||||
}
|
||||
|
||||
const content: MessageContent = {
|
||||
...newContent,
|
||||
body: `* ${plainText}`,
|
||||
'm.new_content': newContent,
|
||||
'm.relates_to': {
|
||||
event_id: event.event_id,
|
||||
rel_type: RelationType.Replace,
|
||||
},
|
||||
};
|
||||
|
||||
// TODO: send message
|
||||
// return mx.sendMessage(roomId, content);
|
||||
return Promise.resolve();
|
||||
}, [ct, editor, event, isMarkdown, getPrevBodyAndFormattedBody])
|
||||
);
|
||||
const senderState = useObservableValueOptionally(localEventSender?.state);
|
||||
const saving =
|
||||
saveState.status === AsyncStatus.Loading || senderState instanceof EventSenderPending;
|
||||
|
||||
const handleSave = useCallback(() => {
|
||||
if (saveState.status !== AsyncStatus.Loading) {
|
||||
save();
|
||||
save(onCancel);
|
||||
}
|
||||
}, [saveState, save]);
|
||||
}, [saveState, save, onCancel]);
|
||||
|
||||
const handleKeyDown: KeyboardEventHandler = useCallback(
|
||||
(evt) => {
|
||||
|
|
@ -209,27 +130,27 @@ export const MessageEditor = as<'div', MessageEditorProps>(
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
const [body, customHtml] = getPrevBodyAndFormattedBody();
|
||||
if (!mobileOrTablet()) ReactEditor.focus(editor);
|
||||
}, [editor]);
|
||||
|
||||
const initialEditorValue = useMemo(() => {
|
||||
const prevContent = rContent.newContent(event.content) ?? event.content;
|
||||
const customHtml = rContent.formattedBody(prevContent);
|
||||
const body = rContent.body(prevContent);
|
||||
|
||||
const initialValue =
|
||||
typeof customHtml === 'string'
|
||||
? htmlToEditorInput(customHtml, isMarkdown)
|
||||
: plainToEditorInput(typeof body === 'string' ? body : '', isMarkdown);
|
||||
|
||||
Transforms.select(editor, {
|
||||
anchor: Editor.start(editor, []),
|
||||
focus: Editor.end(editor, []),
|
||||
});
|
||||
|
||||
editor.insertFragment(initialValue);
|
||||
if (!mobileOrTablet()) ReactEditor.focus(editor);
|
||||
}, [editor, getPrevBodyAndFormattedBody, isMarkdown]);
|
||||
return initialValue;
|
||||
}, [event, isMarkdown]);
|
||||
|
||||
useEffect(() => {
|
||||
if (saveState.status === AsyncStatus.Success) {
|
||||
if (senderState instanceof EventSenderSuccess) {
|
||||
onCancel();
|
||||
}
|
||||
}, [saveState, onCancel]);
|
||||
}, [senderState, onCancel]);
|
||||
|
||||
return (
|
||||
<div {...props} ref={ref}>
|
||||
|
|
@ -259,6 +180,7 @@ export const MessageEditor = as<'div', MessageEditorProps>(
|
|||
)}
|
||||
<CustomEditor
|
||||
editor={editor}
|
||||
initialValue={initialEditorValue}
|
||||
placeholder="Edit message..."
|
||||
onKeyDown={handleKeyDown}
|
||||
onKeyUp={handleKeyUp}
|
||||
|
|
@ -270,24 +192,37 @@ export const MessageEditor = as<'div', MessageEditorProps>(
|
|||
justifyContent="SpaceBetween"
|
||||
gap="100"
|
||||
>
|
||||
<Box gap="Inherit">
|
||||
<Chip
|
||||
onClick={handleSave}
|
||||
variant="Primary"
|
||||
radii="Pill"
|
||||
disabled={saveState.status === AsyncStatus.Loading}
|
||||
outlined
|
||||
before={
|
||||
saveState.status === AsyncStatus.Loading ? (
|
||||
<Spinner variant="Primary" fill="Soft" size="100" />
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
<Text size="B300">Save</Text>
|
||||
</Chip>
|
||||
<Chip onClick={onCancel} variant="SurfaceVariant" radii="Pill">
|
||||
<Text size="B300">Cancel</Text>
|
||||
</Chip>
|
||||
<Box direction="Column" gap="200">
|
||||
{senderState instanceof EventSenderError && (
|
||||
<Text size="T200" style={{ color: color.Critical.Main }}>
|
||||
{senderState.error?.data.error ??
|
||||
(senderState.cause instanceof Error
|
||||
? senderState.cause.message
|
||||
: 'Unknown Error')}
|
||||
</Text>
|
||||
)}
|
||||
{saveState.status === AsyncStatus.Error && (
|
||||
<Text size="T200" style={{ color: color.Critical.Main }}>
|
||||
{saveState.error instanceof Error ? saveState.error.message : 'Unknown Error'}
|
||||
</Text>
|
||||
)}
|
||||
<Box>
|
||||
<Chip
|
||||
onClick={handleSave}
|
||||
variant="Primary"
|
||||
radii="Pill"
|
||||
disabled={saving}
|
||||
outlined
|
||||
before={
|
||||
saving ? <Spinner variant="Primary" fill="Soft" size="100" /> : undefined
|
||||
}
|
||||
>
|
||||
<Text size="B300">Save</Text>
|
||||
</Chip>
|
||||
<Chip onClick={onCancel} variant="SurfaceVariant" radii="Pill">
|
||||
<Text size="B300">Cancel</Text>
|
||||
</Chip>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box gap="Inherit">
|
||||
<IconButton
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ import { OutboxView } from '../OutboxView';
|
|||
import { useUserReadReceipt } from '../../../hooks/useReadReceipt';
|
||||
import { useMessageParserOptions } from '../../../hooks/useMessageParserOptions';
|
||||
import { MessageContent } from '../../../components/MessageContent';
|
||||
import { MessageEditor } from '../message/MessageEditor';
|
||||
|
||||
type TimelineViewProps = {
|
||||
threadTimeline: ThreadTimeline;
|
||||
|
|
@ -339,20 +340,30 @@ export function TimelineView({ threadTimeline, threadInputRef }: TimelineViewPro
|
|||
roomId={roomId}
|
||||
decryptionManager={ct.decryption}
|
||||
>
|
||||
{(contentEvent) => (
|
||||
<RenderMessageContent
|
||||
displayName={membersAPI.getName(event.sender)}
|
||||
eventType={contentEvent.type}
|
||||
ts={event.origin_server_ts}
|
||||
edited={!!editedEvent}
|
||||
content={rContent.newContent(contentEvent.content) ?? contentEvent.content}
|
||||
mediaAutoLoad={mediaAutoLoad}
|
||||
urlPreview={showUrlPreview}
|
||||
htmlReactParserOptions={htmlReactParserOptions}
|
||||
linkifyOpts={linkifyOpts}
|
||||
outlineAttachment={messageLayout === MessageLayout.Bubble}
|
||||
/>
|
||||
)}
|
||||
{(contentEvent) =>
|
||||
editId === event.event_id ? (
|
||||
<MessageEditor
|
||||
style={{ maxWidth: '100%', width: '100vw' }}
|
||||
roomId={roomId}
|
||||
event={contentEvent}
|
||||
imagePackRooms={imagePackRooms}
|
||||
onCancel={handleEdit}
|
||||
/>
|
||||
) : (
|
||||
<RenderMessageContent
|
||||
displayName={membersAPI.getName(event.sender)}
|
||||
eventType={contentEvent.type}
|
||||
ts={event.origin_server_ts}
|
||||
edited={!!editedEvent}
|
||||
content={rContent.newContent(contentEvent.content) ?? contentEvent.content}
|
||||
mediaAutoLoad={mediaAutoLoad}
|
||||
urlPreview={showUrlPreview}
|
||||
htmlReactParserOptions={htmlReactParserOptions}
|
||||
linkifyOpts={linkifyOpts}
|
||||
outlineAttachment={messageLayout === MessageLayout.Bubble}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</MessageContent>
|
||||
)}
|
||||
{reactionJSX}
|
||||
|
|
|
|||
Loading…
Reference in a new issue