@@ -88,13 +107,13 @@ MessageHeader.propTypes = {
time: PropTypes.string.isRequired,
};
-function MessageReply({ name, color, content }) {
+function MessageReply({ name, color, body }) {
return (
);
@@ -103,39 +122,39 @@ function MessageReply({ name, color, content }) {
MessageReply.propTypes = {
name: PropTypes.string.isRequired,
color: PropTypes.string.isRequired,
- content: PropTypes.string.isRequired,
+ body: PropTypes.string.isRequired,
};
-function MessageContent({
+function MessageBody({
senderName,
- content,
- isMarkdown,
+ body,
+ isCustomHTML,
isEdited,
msgType,
}) {
return (
-
+
{ msgType === 'm.emote' && `* ${senderName} ` }
- { isMarkdown ? genMarkdown(content) : linkifyContent(content) }
+ { isCustomHTML ? genMarkdown(body) : linkifyContent(body) }
- { isEdited &&
(edited)}
+ { isEdited &&
(edited)}
);
}
-MessageContent.defaultProps = {
- isMarkdown: false,
+MessageBody.defaultProps = {
+ isCustomHTML: false,
isEdited: false,
};
-MessageContent.propTypes = {
+MessageBody.propTypes = {
senderName: PropTypes.string.isRequired,
- content: PropTypes.node.isRequired,
- isMarkdown: PropTypes.bool,
+ body: PropTypes.node.isRequired,
+ isCustomHTML: PropTypes.bool,
isEdited: PropTypes.bool,
msgType: PropTypes.string.isRequired,
};
-function MessageEdit({ content, onSave, onCancel }) {
+function MessageEdit({ body, onSave, onCancel }) {
const editInputRef = useRef(null);
const handleKeyDown = (e) => {
@@ -150,7 +169,7 @@ function MessageEdit({ content, onSave, onCancel }) {
Malformed event;
+
+ let mediaMXC = mContent?.url;
+ const isEncryptedFile = typeof mediaMXC === 'undefined';
+ if (isEncryptedFile) mediaMXC = mContent?.file?.url;
+
+ let thumbnailMXC = mContent?.info?.thumbnail_url;
+
+ if (typeof mediaMXC === 'undefined' || mediaMXC === '') return
Malformed event;
+
+ let msgType = mE.getContent()?.msgtype;
+ if (mE.getType() === 'm.sticker') msgType = 'm.image';
+
switch (msgType) {
- case 'm.text':
- className.push('message--type-text');
- break;
- case 'm.emote':
- className.push('message--type-emote');
- break;
- case 'm.notice':
- className.push('message--type-notice');
- break;
+ case 'm.file':
+ return (
+
+ );
+ case 'm.image':
+ return (
+
+ );
+ case 'm.audio':
+ return (
+
+ );
+ case 'm.video':
+ if (typeof thumbnailMXC === 'undefined') {
+ thumbnailMXC = mContent.info?.thumbnail_file?.url || null;
+ }
+ return (
+
+ );
default:
+ return
Malformed event;
+ }
+}
+function getMyEmojiEventId(emojiKey, eventId, roomTimeline) {
+ const mx = initMatrix.matrixClient;
+ const rEvents = roomTimeline.reactionTimeline.get(eventId);
+ let rEventId = null;
+ rEvents?.find((rE) => {
+ if (rE.getRelation() === null) return false;
+ if (rE.getRelation().key === emojiKey && rE.getSender() === mx.getUserId()) {
+ rEventId = rE.getId();
+ return true;
+ }
+ return false;
+ });
+ return rEventId;
+}
+
+function toggleEmoji(roomId, eventId, emojiKey, roomTimeline) {
+ const myAlreadyReactEventId = getMyEmojiEventId(emojiKey, eventId, roomTimeline);
+ if (typeof myAlreadyReactEventId === 'string') {
+ if (myAlreadyReactEventId.indexOf('~') === 0) return;
+ redactEvent(roomId, myAlreadyReactEventId);
+ return;
+ }
+ sendReaction(roomId, eventId, emojiKey);
+}
+
+function pickEmoji(e, roomId, eventId, roomTimeline) {
+ openEmojiBoard(getEventCords(e), (emoji) => {
+ toggleEmoji(roomId, eventId, emoji.unicode, roomTimeline);
+ e.target.click();
+ });
+}
+
+function parseReply(rawBody) {
+ if (rawBody.indexOf('>') !== 0) return null;
+ let body = rawBody.slice(rawBody.indexOf('<') + 1);
+ const user = body.slice(0, body.indexOf('>'));
+
+ body = body.slice(body.indexOf('>') + 2);
+ const replyBody = body.slice(0, body.indexOf('\n\n'));
+ body = body.slice(body.indexOf('\n\n') + 2);
+
+ if (user === '') return null;
+
+ const isUserId = user.match(/^@.+:.+/);
+
+ return {
+ userId: isUserId ? user : null,
+ displayName: isUserId ? null : user,
+ replyBody,
+ body,
+ };
+}
+function getEditedBody(eventId, editedTimeline) {
+ const editedList = editedTimeline.get(eventId);
+ const editedMEvent = editedList[editedList.length - 1];
+ const newContent = editedMEvent.getContent()['m.new_content'];
+ if (typeof newContent === 'undefined') return [null, false];
+
+ const isCustomHTML = newContent.format === 'org.matrix.custom.html';
+ const parsedContent = parseReply(newContent.body);
+ if (parsedContent === null) {
+ return [newContent.body, isCustomHTML];
+ }
+ return [parsedContent.body, isCustomHTML];
+}
+
+function Message({ mEvent, isBodyOnly, roomTimeline }) {
+ const [isEditing, setIsEditing] = useState(false);
+
+ const mx = initMatrix.matrixClient;
+ const {
+ room, roomId, editedTimeline, reactionTimeline,
+ } = roomTimeline;
+
+ const className = ['message', (isBodyOnly ? 'message--body-only' : 'message--full')];
+ const content = mEvent.getWireContent();
+ const eventId = mEvent.getId();
+ const msgType = content?.msgtype;
+ const senderId = mEvent.getSender();
+ const mxidColor = colorMXID(senderId);
+ let { body } = content;
+ const avatarSrc = mEvent.sender.getAvatarUrl(initMatrix.matrixClient.baseUrl, 36, 36, 'crop');
+ const username = getUsernameOfRoomMember(mEvent.sender);
+ const time = `${dateFormat(mEvent.getDate(), 'hh:MM TT')}`;
+
+ if (typeof body === 'undefined') return null;
+ if (msgType === 'm.emote') className.push('message--type-emote');
+
+ // TODO: these line can be moved to option menu
+ const myPowerlevel = room.getMember(mx.getUserId())?.powerLevel;
+ const canIRedact = room.currentState.hasSufficientPowerLevelFor('redact', myPowerlevel);
+
+ let [reply, reactions, isCustomHTML] = [null, null, content.format === 'org.matrix.custom.html'];
+ const [isEdited, haveReactions] = [editedTimeline.has(eventId), reactionTimeline.has(eventId)];
+ const isReply = typeof content['m.relates_to']?.['m.in_reply_to'] !== 'undefined';
+
+ if (isEdited) {
+ [body, isCustomHTML] = getEditedBody(eventId, editedTimeline);
+ if (typeof body !== 'string') return null;
+ }
+
+ if (haveReactions) {
+ reactions = [];
+ reactionTimeline.get(eventId).forEach((rEvent) => {
+ if (rEvent.getRelation() === null) return;
+ function alreadyHaveThisReaction(rE) {
+ for (let i = 0; i < reactions.length; i += 1) {
+ if (reactions[i].key === rE.getRelation().key) return true;
+ }
+ return false;
+ }
+ if (alreadyHaveThisReaction(rEvent)) {
+ for (let i = 0; i < reactions.length; i += 1) {
+ if (reactions[i].key === rEvent.getRelation().key) {
+ reactions[i].users.push(rEvent.getSender());
+ if (reactions[i].isActive !== true) {
+ const myUserId = mx.getUserId();
+ reactions[i].isActive = rEvent.getSender() === myUserId;
+ if (reactions[i].isActive) reactions[i].id = rEvent.getId();
+ }
+ break;
+ }
+ }
+ } else {
+ reactions.push({
+ id: rEvent.getId(),
+ key: rEvent.getRelation().key,
+ users: [rEvent.getSender()],
+ isActive: (rEvent.getSender() === mx.getUserId()),
+ });
+ }
+ });
+ }
+
+ if (isReply) {
+ const parsedContent = parseReply(body);
+ if (parsedContent !== null) {
+ const c = room.currentState;
+ const displayNameToUserIds = c.getUserIdsWithDisplayName(parsedContent.displayName);
+ const ID = parsedContent.userId || displayNameToUserIds[0];
+ reply = {
+ color: colorMXID(ID || parsedContent.displayName),
+ to: parsedContent.displayName || getUsername(parsedContent.userId),
+ body: parsedContent.replyBody,
+ };
+ body = parsedContent.body;
+ }
}
return (
- {avatar !== null && avatar}
+ {!isBodyOnly && (
+
+ )}
- {header !== null && header}
- {reply !== null && reply}
- {content !== null && content}
- {editContent !== null && editContent}
- {reactions !== null && reactions}
- {options !== null && options}
+ {!isBodyOnly && (
+
+ )}
+ {reply !== null && (
+
+ )}
+ {!isEditing && (
+
+ )}
+ {isEditing && (
+ {
+ if (newBody !== body) {
+ initMatrix.roomsInput.sendEditedMessage(roomId, mEvent, newBody);
+ }
+ setIsEditing(false);
+ }}
+ onCancel={() => setIsEditing(false)}
+ />
+ )}
+ {haveReactions && (
+
+ {
+ reactions.map((reaction) => (
+ {
+ toggleEmoji(roomId, eventId, reaction.key, roomTimeline);
+ }}
+ />
+ ))
+ }
+ {
+ pickEmoji(e, roomId, eventId, roomTimeline);
+ }}
+ src={EmojiAddIC}
+ size="extra-small"
+ tooltip="Add reaction"
+ />
+
+ )}
+ {!isEditing && (
+
+ pickEmoji(e, roomId, eventId, roomTimeline)}
+ src={EmojiAddIC}
+ size="extra-small"
+ tooltip="Add reaction"
+ />
+ replyTo(senderId, eventId, body)}
+ src={ReplyArrowIC}
+ size="extra-small"
+ tooltip="Reply"
+ />
+ {(senderId === mx.getUserId() && !isMedia(mEvent)) && (
+ setIsEditing(true)}
+ src={PencilIC}
+ size="extra-small"
+ tooltip="Edit"
+ />
+ )}
+ (
+ <>
+ Options
+
+ {(canIRedact || senderId === mx.getUserId()) && (
+ <>
+
+
+ >
+ )}
+ >
+ )}
+ render={(toggleMenu) => (
+
+ )}
+ />
+
+ )}
);
}
Message.defaultProps = {
- avatar: null,
- header: null,
- reply: null,
- content: null,
- editContent: null,
- reactions: null,
- options: null,
- msgType: 'm.text',
+ isBodyOnly: false,
};
Message.propTypes = {
- avatar: PropTypes.node,
- header: PropTypes.node,
- reply: PropTypes.node,
- content: PropTypes.node,
- editContent: PropTypes.node,
- reactions: PropTypes.node,
- options: PropTypes.node,
- msgType: PropTypes.string,
+ mEvent: PropTypes.shape({}).isRequired,
+ isBodyOnly: PropTypes.bool,
+ roomTimeline: PropTypes.shape({}).isRequired,
};
-export {
- Message,
- MessageHeader,
- MessageReply,
- MessageContent,
- MessageEdit,
- MessageReactionGroup,
- MessageReaction,
- MessageOptions,
- PlaceholderMessage,
-};
+export { Message, MessageReply, PlaceholderMessage };
diff --git a/src/app/molecules/message/Message.scss b/src/app/molecules/message/Message.scss
index f523ed61..3c12c923 100644
--- a/src/app/molecules/message/Message.scss
+++ b/src/app/molecules/message/Message.scss
@@ -47,7 +47,7 @@
.message {
&--full + &--full,
- &--content-only + &--full,
+ &--body-only + &--full,
& + .timeline-change,
.timeline-change + & {
margin-top: var(--sp-normal);
@@ -66,7 +66,7 @@
}
&__header,
- &__content > div {
+ &__body > div {
margin: var(--sp-ultra-tight) 0;
margin-right: var(--sp-extra-tight);
height: var(--fs-b1);
@@ -82,20 +82,20 @@
}
}
}
- &__content {
+ &__body {
display: flex;
flex-wrap: wrap;
}
- &__content > div:nth-child(1n) {
+ &__body > div:nth-child(1n) {
max-width: 10%;
}
- &__content > div:nth-child(2n) {
+ &__body > div:nth-child(2n) {
max-width: 50%;
}
}
.message__reply,
-.message__content,
+.message__body,
.message__edit,
.message__reactions {
max-width: calc(100% - 88px);
@@ -153,7 +153,7 @@
height: 14px;
}
}
-.message__content {
+.message__body {
word-break: break-word;
& > .text > * {
@@ -265,7 +265,7 @@
}
// markdown formating
-.message__content {
+.message__body {
& h1,
& h2 {
color: var(--tc-surface-high);
@@ -403,7 +403,7 @@
}
.message.message--type-emote {
- .message__content {
+ .message__body {
font-style: italic;
// Remove blockness of first `
` so that markdown emotes stay on one line.
diff --git a/src/app/molecules/message/TimelineChange.jsx b/src/app/molecules/message/TimelineChange.jsx
index 3ebc9103..2ef28c11 100644
--- a/src/app/molecules/message/TimelineChange.jsx
+++ b/src/app/molecules/message/TimelineChange.jsx
@@ -12,7 +12,9 @@ import InviteCancelArraowIC from '../../../../public/res/ic/outlined/invite-canc
import UserIC from '../../../../public/res/ic/outlined/user.svg';
import TickMarkIC from '../../../../public/res/ic/outlined/tick-mark.svg';
-function TimelineChange({ variant, content, time, onClick }) {
+function TimelineChange({
+ variant, content, time, onClick,
+}) {
let iconSrc;
switch (variant) {
@@ -47,7 +49,6 @@ function TimelineChange({ variant, content, time, onClick }) {
{content}
- {/* {content} */}
diff --git a/src/app/organisms/room/RoomViewContent.jsx b/src/app/organisms/room/RoomViewContent.jsx
index 51d69b07..74783279 100644
--- a/src/app/organisms/room/RoomViewContent.jsx
+++ b/src/app/organisms/room/RoomViewContent.jsx
@@ -7,39 +7,14 @@ import dateFormat from 'dateformat';
import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons';
-import { redactEvent, sendReaction } from '../../../client/action/roomTimeline';
-import { getUsername, getUsernameOfRoomMember } from '../../../util/matrixUtil';
-import colorMXID from '../../../util/colorMXID';
-import { diffMinutes, isNotInSameDay, getEventCords } from '../../../util/common';
-import { openEmojiBoard, openProfileViewer, openReadReceipts } from '../../../client/action/navigation';
+import { diffMinutes, isNotInSameDay } from '../../../util/common';
import Divider from '../../atoms/divider/Divider';
-import Avatar from '../../atoms/avatar/Avatar';
-import IconButton from '../../atoms/button/IconButton';
-import ContextMenu, { MenuHeader, MenuItem, MenuBorder } from '../../atoms/context-menu/ContextMenu';
-import {
- Message,
- MessageHeader,
- MessageReply,
- MessageContent,
- MessageEdit,
- MessageReactionGroup,
- MessageReaction,
- MessageOptions,
- PlaceholderMessage,
-} from '../../molecules/message/Message';
-import * as Media from '../../molecules/media/Media';
+import { Message, PlaceholderMessage } from '../../molecules/message/Message';
import RoomIntro from '../../molecules/room-intro/RoomIntro';
import TimelineChange from '../../molecules/message/TimelineChange';
-import ReplyArrowIC from '../../../../public/res/ic/outlined/reply-arrow.svg';
-import EmojiAddIC from '../../../../public/res/ic/outlined/emoji-add.svg';
-import VerticalMenuIC from '../../../../public/res/ic/outlined/vertical-menu.svg';
-import PencilIC from '../../../../public/res/ic/outlined/pencil.svg';
-import TickMarkIC from '../../../../public/res/ic/outlined/tick-mark.svg';
-import BinIC from '../../../../public/res/ic/outlined/bin.svg';
-
-import { parseReply, parseTimelineChange } from './common';
+import { parseTimelineChange } from './common';
const MAX_MSG_DIFF_MINUTES = 5;
@@ -52,84 +27,6 @@ function genPlaceholders(key) {
);
}
-function isMedia(mE) {
- return (
- mE.getContent()?.msgtype === 'm.file'
- || mE.getContent()?.msgtype === 'm.image'
- || mE.getContent()?.msgtype === 'm.audio'
- || mE.getContent()?.msgtype === 'm.video'
- || mE.getType() === 'm.sticker'
- );
-}
-
-function genMediaContent(mE) {
- const mx = initMatrix.matrixClient;
- const mContent = mE.getContent();
- if (!mContent || !mContent.body) return
Malformed event;
-
- let mediaMXC = mContent?.url;
- const isEncryptedFile = typeof mediaMXC === 'undefined';
- if (isEncryptedFile) mediaMXC = mContent?.file?.url;
-
- let thumbnailMXC = mContent?.info?.thumbnail_url;
-
- if (typeof mediaMXC === 'undefined' || mediaMXC === '') return
Malformed event;
-
- let msgType = mE.getContent()?.msgtype;
- if (mE.getType() === 'm.sticker') msgType = 'm.image';
-
- switch (msgType) {
- case 'm.file':
- return (
-
- );
- case 'm.image':
- return (
-
- );
- case 'm.audio':
- return (
-
- );
- case 'm.video':
- if (typeof thumbnailMXC === 'undefined') {
- thumbnailMXC = mContent.info?.thumbnail_file?.url || null;
- }
- return (
-
- );
- default:
- return
Malformed event;
- }
-}
-
function genRoomIntro(mEvent, roomTimeline) {
const mx = initMatrix.matrixClient;
const roomTopic = roomTimeline.room.currentState.getStateEvents('m.room.topic')[0]?.getContent().topic;
@@ -149,38 +46,6 @@ function genRoomIntro(mEvent, roomTimeline) {
);
}
-function getMyEmojiEventId(emojiKey, eventId, roomTimeline) {
- const mx = initMatrix.matrixClient;
- const rEvents = roomTimeline.reactionTimeline.get(eventId);
- let rEventId = null;
- rEvents?.find((rE) => {
- if (rE.getRelation() === null) return false;
- if (rE.getRelation().key === emojiKey && rE.getSender() === mx.getUserId()) {
- rEventId = rE.getId();
- return true;
- }
- return false;
- });
- return rEventId;
-}
-
-function toggleEmoji(roomId, eventId, emojiKey, roomTimeline) {
- const myAlreadyReactEventId = getMyEmojiEventId(emojiKey, eventId, roomTimeline);
- if (typeof myAlreadyReactEventId === 'string') {
- if (myAlreadyReactEventId.indexOf('~') === 0) return;
- redactEvent(roomId, myAlreadyReactEventId);
- return;
- }
- sendReaction(roomId, eventId, emojiKey);
-}
-
-function pickEmoji(e, roomId, eventId, roomTimeline) {
- openEmojiBoard(getEventCords(e), (emoji) => {
- toggleEmoji(roomId, eventId, emoji.unicode, roomTimeline);
- e.target.click();
- });
-}
-
const scroll = {
from: 0,
limit: 0,
@@ -194,9 +59,10 @@ function RoomViewContent({
}) {
const [isReachedTimelineEnd, setIsReachedTimelineEnd] = useState(false);
const [onStateUpdate, updateState] = useState(null);
- const [editEvent, setEditEvent] = useState(null);
+
const mx = initMatrix.matrixClient;
const noti = initMatrix.notifications;
+
if (scroll.limit === 0) {
const from = roomTimeline.timeline.size - timelineScroll.maxEvents;
scroll.from = (from < 0) ? 0 : from;
@@ -259,7 +125,7 @@ function RoomViewContent({
if (roomTimeline.ongoingDecryptionCount === 0) updateState({});
} else setIsReachedTimelineEnd(true);
};
- // force update RoomTimeline on cons.events.roomTimeline.EVENT
+ // force update RoomTimeline
const updateRT = () => {
if (timelineScroll.position === 'BOTTOM') {
trySendingReadReceipt();
@@ -323,291 +189,37 @@ function RoomViewContent({
}, [onStateUpdate]);
let prevMEvent = null;
- function genMessage(mEvent) {
- const myPowerlevel = roomTimeline.room.getMember(mx.getUserId())?.powerLevel;
- const canIRedact = roomTimeline.room.currentState.hasSufficientPowerLevelFor('redact', myPowerlevel);
-
- const isContentOnly = (
- prevMEvent !== null
- && prevMEvent.getType() !== 'm.room.member'
+ function renderMessage(mEvent) {
+ const isContentOnly = (prevMEvent !== null && prevMEvent.getType() !== 'm.room.member'
&& diffMinutes(mEvent.getDate(), prevMEvent.getDate()) <= MAX_MSG_DIFF_MINUTES
&& prevMEvent.getSender() === mEvent.getSender()
);
- let content = mEvent.getContent().body;
- if (typeof content === 'undefined') return null;
- const msgType = mEvent.getContent()?.msgtype;
- let reply = null;
- let reactions = null;
- let isMarkdown = mEvent.getContent().format === 'org.matrix.custom.html';
- const isReply = typeof mEvent.getWireContent()['m.relates_to']?.['m.in_reply_to'] !== 'undefined';
- const isEdited = roomTimeline.editedTimeline.has(mEvent.getId());
- const haveReactions = roomTimeline.reactionTimeline.has(mEvent.getId());
-
- if (isReply) {
- const parsedContent = parseReply(content);
- if (parsedContent !== null) {
- const c = roomTimeline.room.currentState;
- const displayNameToUserIds = c.getUserIdsWithDisplayName(parsedContent.displayName);
- const ID = parsedContent.userId || displayNameToUserIds[0];
- reply = {
- color: colorMXID(ID || parsedContent.displayName),
- to: parsedContent.displayName || getUsername(parsedContent.userId),
- content: parsedContent.replyContent,
- };
- content = parsedContent.content;
- }
- }
-
- if (isEdited) {
- const editedList = roomTimeline.editedTimeline.get(mEvent.getId());
- const latestEdited = editedList[editedList.length - 1];
- if (typeof latestEdited.getContent()['m.new_content'] === 'undefined') return null;
- const latestEditBody = latestEdited.getContent()['m.new_content'].body;
- const parsedEditedContent = parseReply(latestEditBody);
- isMarkdown = latestEdited.getContent()['m.new_content'].format === 'org.matrix.custom.html';
- if (parsedEditedContent === null) {
- content = latestEditBody;
- } else {
- content = parsedEditedContent.content;
- }
- }
-
- if (haveReactions) {
- reactions = [];
- roomTimeline.reactionTimeline.get(mEvent.getId()).forEach((rEvent) => {
- if (rEvent.getRelation() === null) return;
- function alreadyHaveThisReaction(rE) {
- for (let i = 0; i < reactions.length; i += 1) {
- if (reactions[i].key === rE.getRelation().key) return true;
- }
- return false;
- }
- if (alreadyHaveThisReaction(rEvent)) {
- for (let i = 0; i < reactions.length; i += 1) {
- if (reactions[i].key === rEvent.getRelation().key) {
- reactions[i].users.push(rEvent.getSender());
- if (reactions[i].isActive !== true) {
- const myUserId = initMatrix.matrixClient.getUserId();
- reactions[i].isActive = rEvent.getSender() === myUserId;
- if (reactions[i].isActive) reactions[i].id = rEvent.getId();
- }
- break;
- }
- }
- } else {
- reactions.push({
- id: rEvent.getId(),
- key: rEvent.getRelation().key,
- users: [rEvent.getSender()],
- isActive: (rEvent.getSender() === initMatrix.matrixClient.getUserId()),
- });
- }
- });
- }
-
- const senderMXIDColor = colorMXID(mEvent.sender.userId);
- const userAvatar = isContentOnly ? null : (
-
- );
- const userHeader = isContentOnly ? null : (
-
- );
- const userReply = reply === null ? null : (
-
- );
- const userContent = (
-
- );
- const userReactions = reactions === null ? null : (
-
- {
- reactions.map((reaction) => (
- {
- toggleEmoji(roomId, mEvent.getId(), reaction.key, roomTimeline);
- }}
- />
- ))
- }
- pickEmoji(e, roomId, mEvent.getId(), roomTimeline)}
- src={EmojiAddIC}
- size="extra-small"
- tooltip="Add reaction"
- />
-
- );
- const userOptions = (
-
- pickEmoji(e, roomId, mEvent.getId(), roomTimeline)}
- src={EmojiAddIC}
- size="extra-small"
- tooltip="Add reaction"
- />
- {
- viewEvent.emit('reply_to', mEvent.getSender(), mEvent.getId(), isMedia(mEvent) ? mEvent.getContent().body : content);
- }}
- src={ReplyArrowIC}
- size="extra-small"
- tooltip="Reply"
- />
- {(mEvent.getSender() === mx.getUserId() && !isMedia(mEvent)) && (
- setEditEvent(mEvent)}
- src={PencilIC}
- size="extra-small"
- tooltip="Edit"
- />
- )}
- (
- <>
- Options
-
-
- {(mEvent.getSender() === mx.getUserId() && !isMedia(mEvent)) && (
-
- )}
-
- {(canIRedact || mEvent.getSender() === mx.getUserId()) && (
- <>
-
-
- >
- )}
- >
- )}
- render={(toggleMenu) => (
-
- )}
- />
-
- );
-
- const isEditingEvent = editEvent?.getId() === mEvent.getId();
- const myMessageEl = (
-
{
- if (newBody !== content) {
- initMatrix.roomsInput.sendEditedMessage(roomId, mEvent, newBody);
- }
- setEditEvent(null);
- }}
- onCancel={() => setEditEvent(null)}
- />
- ) : null}
- reactions={userReactions}
- options={editEvent !== null && isEditingEvent ? null : userOptions}
- />
- );
- return myMessageEl;
- }
-
- function renderMessage(mEvent) {
- if (!cons.supportEventTypes.includes(mEvent.getType())) return false;
- if (mEvent.getRelation()?.rel_type === 'm.replace') return false;
- if (mEvent.isRedacted()) return false;
-
- if (mEvent.getType() === 'm.room.create') return genRoomIntro(mEvent, roomTimeline);
-
- let divider = null;
+ let DividerComp = null;
if (prevMEvent !== null && isNotInSameDay(mEvent.getDate(), prevMEvent.getDate())) {
- divider = ;
+ DividerComp = ;
}
+ prevMEvent = mEvent;
- if (mEvent.getType() !== 'm.room.member') {
- const messageComp = genMessage(mEvent);
- prevMEvent = mEvent;
+ if (mEvent.getType() === 'm.room.member') {
+ const timelineChange = parseTimelineChange(mEvent);
+ if (timelineChange === null) return false;
return (
- {divider}
- {messageComp}
+ {DividerComp}
+
);
}
-
- prevMEvent = mEvent;
- const timelineChange = parseTimelineChange(mEvent);
- if (timelineChange === null) return false;
return (
- {divider}
-
+ {DividerComp}
+
);
}
@@ -625,7 +237,8 @@ function RoomViewContent({
if (mEvent.getType() !== 'm.room.create' && !isReachedTimelineEnd) tl.push(genPlaceholders(1));
if (mEvent.getType() !== 'm.room.create' && isReachedTimelineEnd) tl.push(genRoomIntro(undefined, roomTimeline));
}
- tl.push(renderMessage(mEvent));
+ if (mEvent.getType() === 'm.room.create') tl.push(genRoomIntro(mEvent, roomTimeline));
+ else tl.push(renderMessage(mEvent));
}
i += 1;
if (i > scroll.getEndIndex()) break;
diff --git a/src/app/organisms/room/RoomViewInput.jsx b/src/app/organisms/room/RoomViewInput.jsx
index 5b18fcb9..0f1ca944 100644
--- a/src/app/organisms/room/RoomViewInput.jsx
+++ b/src/app/organisms/room/RoomViewInput.jsx
@@ -9,6 +9,7 @@ import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons';
import settings from '../../../client/state/settings';
import { openEmojiBoard } from '../../../client/action/navigation';
+import navigation from '../../../client/state/navigation';
import { bytesToSize, getEventCords } from '../../../util/common';
import { getUsername } from '../../../util/matrixUtil';
import colorMXID from '../../../util/colorMXID';
@@ -152,9 +153,9 @@ function RoomViewInput({
textAreaRef.current.focus();
}
- function setUpReply(userId, eventId, content) {
- setReplyTo({ userId, eventId, content });
- roomsInput.setReplyTo(roomId, { userId, eventId, content });
+ function setUpReply(userId, eventId, body) {
+ setReplyTo({ userId, eventId, body });
+ roomsInput.setReplyTo(roomId, { userId, eventId, body });
focusInput();
}
@@ -164,7 +165,7 @@ function RoomViewInput({
roomsInput.on(cons.events.roomsInput.FILE_UPLOADED, clearAttachment);
viewEvent.on('cmd_error', errorCmd);
viewEvent.on('cmd_fired', firedCmd);
- viewEvent.on('reply_to', setUpReply);
+ navigation.on(cons.events.navigation.REPLY_TO_CLICKED, setUpReply);
if (textAreaRef?.current !== null) {
isTyping = false;
focusInput();
@@ -178,7 +179,7 @@ function RoomViewInput({
roomsInput.removeListener(cons.events.roomsInput.FILE_UPLOADED, clearAttachment);
viewEvent.removeListener('cmd_error', errorCmd);
viewEvent.removeListener('cmd_fired', firedCmd);
- viewEvent.removeListener('reply_to', setUpReply);
+ navigation.removeListener(cons.events.navigation.REPLY_TO_CLICKED, setUpReply);
if (isCmdActivated) deactivateCmd();
if (textAreaRef?.current === null) return;
@@ -410,7 +411,7 @@ function RoomViewInput({
userId={replyTo.userId}
name={getUsername(replyTo.userId)}
color={colorMXID(replyTo.userId)}
- content={replyTo.content}
+ body={replyTo.body}
/>
);
diff --git a/src/app/organisms/room/common.jsx b/src/app/organisms/room/common.jsx
index e25eabb4..05f5d503 100644
--- a/src/app/organisms/room/common.jsx
+++ b/src/app/organisms/room/common.jsx
@@ -165,27 +165,6 @@ function getUsersActionJsx(roomId, userIds, actionStr) {
return <>{u1Jsx}, {u2Jsx}, {u3Jsx} and {othersCount} other are {actionStr}>;
}
-function parseReply(rawContent) {
- if (rawContent.indexOf('>') !== 0) return null;
- let content = rawContent.slice(rawContent.indexOf('<') + 1);
- const user = content.slice(0, content.indexOf('>'));
-
- content = content.slice(content.indexOf('>') + 2);
- const replyContent = content.slice(0, content.indexOf('\n\n'));
- content = content.slice(content.indexOf('\n\n') + 2);
-
- if (user === '') return null;
-
- const isUserId = user.match(/^@.+:.+/);
-
- return {
- userId: isUserId ? user : null,
- displayName: isUserId ? null : user,
- replyContent,
- content,
- };
-}
-
function parseTimelineChange(mEvent) {
const tJSXMsgs = getTimelineJSXMessages();
const makeReturnObj = (variant, content) => ({
@@ -237,6 +216,5 @@ function parseTimelineChange(mEvent) {
export {
getTimelineJSXMessages,
getUsersActionJsx,
- parseReply,
parseTimelineChange,
};
diff --git a/src/client/action/navigation.js b/src/client/action/navigation.js
index 66179289..0f9f4c1d 100644
--- a/src/client/action/navigation.js
+++ b/src/client/action/navigation.js
@@ -87,6 +87,15 @@ function openRoomOptions(cords, roomId) {
});
}
+function replyTo(userId, eventId, body) {
+ appDispatcher.dispatch({
+ type: cons.actions.navigation.CLICK_REPLY_TO,
+ userId,
+ eventId,
+ body,
+ });
+}
+
export {
selectTab,
selectSpace,
@@ -100,4 +109,5 @@ export {
openEmojiBoard,
openReadReceipts,
openRoomOptions,
+ replyTo,
};
diff --git a/src/client/state/RoomsInput.js b/src/client/state/RoomsInput.js
index d1100cd9..a8ee8059 100644
--- a/src/client/state/RoomsInput.js
+++ b/src/client/state/RoomsInput.js
@@ -95,13 +95,13 @@ function getFormattedBody(markdown) {
function getReplyFormattedBody(roomId, reply) {
const replyToLink = `
In reply to`;
const userLink = `
${reply.userId}`;
- const formattedReply = getFormattedBody(reply.content.replaceAll('\n', '\n> '));
+ const formattedReply = getFormattedBody(reply.body.replaceAll('\n', '\n> '));
return `
${replyToLink}${userLink}
${formattedReply}
`;
}
function bindReplyToContent(roomId, reply, content) {
const newContent = { ...content };
- newContent.body = `> <${reply.userId}> ${reply.content.replaceAll('\n', '\n> ')}`;
+ newContent.body = `> <${reply.userId}> ${reply.body.replaceAll('\n', '\n> ')}`;
newContent.body += `\n\n${content.body}`;
newContent.format = 'org.matrix.custom.html';
newContent['m.relates_to'] = content['m.relates_to'] || {};
diff --git a/src/client/state/cons.js b/src/client/state/cons.js
index 412cbb70..6443df00 100644
--- a/src/client/state/cons.js
+++ b/src/client/state/cons.js
@@ -34,6 +34,7 @@ const cons = {
OPEN_EMOJIBOARD: 'OPEN_EMOJIBOARD',
OPEN_READRECEIPTS: 'OPEN_READRECEIPTS',
OPEN_ROOMOPTIONS: 'OPEN_ROOMOPTIONS',
+ CLICK_REPLY_TO: 'CLICK_REPLY_TO',
},
room: {
JOIN: 'JOIN',
@@ -65,6 +66,7 @@ const cons = {
EMOJIBOARD_OPENED: 'EMOJIBOARD_OPENED',
READRECEIPTS_OPENED: 'READRECEIPTS_OPENED',
ROOMOPTIONS_OPENED: 'ROOMOPTIONS_OPENED',
+ REPLY_TO_CLICKED: 'REPLY_TO_CLICKED',
},
roomList: {
ROOMLIST_UPDATED: 'ROOMLIST_UPDATED',
diff --git a/src/client/state/navigation.js b/src/client/state/navigation.js
index 76cecadc..d52d96e1 100644
--- a/src/client/state/navigation.js
+++ b/src/client/state/navigation.js
@@ -90,6 +90,14 @@ class Navigation extends EventEmitter {
action.roomId,
);
},
+ [cons.actions.navigation.CLICK_REPLY_TO]: () => {
+ this.emit(
+ cons.events.navigation.REPLY_TO_CLICKED,
+ action.userId,
+ action.eventId,
+ action.body,
+ );
+ },
};
actions[action.type]?.();
}