diff --git a/src/app/molecules/message/Message.jsx b/src/app/molecules/message/Message.jsx
index b17cb337..fa5d608a 100644
--- a/src/app/molecules/message/Message.jsx
+++ b/src/app/molecules/message/Message.jsx
@@ -1,5 +1,5 @@
/* eslint-disable react/prop-types */
-import React, { useState, useEffect, useRef } from 'react';
+import React, { useState, useEffect, useCallback, useRef } from 'react';
import PropTypes from 'prop-types';
import './Message.scss';
@@ -52,25 +52,35 @@ function PlaceholderMessage() {
);
}
-function MessageHeader({
- userId, name, color, time,
-}) {
+const MessageAvatar = React.memo(({
+ roomId, mEvent, userId, username,
+}) => {
+ const avatarSrc = mEvent.sender.getAvatarUrl(initMatrix.matrixClient.baseUrl, 36, 36, 'crop');
return (
-
-
- {twemojify(name)}
- {twemojify(userId)}
-
-
- {time}
-
+
+
openProfileViewer(userId, roomId)}>
+
+
);
-}
+});
+
+const MessageHeader = React.memo(({
+ userId, username, time,
+}) => (
+
+
+ {twemojify(username)}
+ {twemojify(userId)}
+
+
+ {time}
+
+
+));
MessageHeader.propTypes = {
userId: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired,
- color: PropTypes.string.isRequired,
+ username: PropTypes.string.isRequired,
time: PropTypes.string.isRequired,
};
@@ -93,7 +103,7 @@ MessageReply.propTypes = {
body: PropTypes.string.isRequired,
};
-function MessageReplyWrapper({ roomTimeline, eventId }) {
+const MessageReplyWrapper = React.memo(({ roomTimeline, eventId }) => {
const [reply, setReply] = useState(null);
const isMountedRef = useRef(true);
@@ -141,19 +151,19 @@ function MessageReplyWrapper({ roomTimeline, eventId }) {
{reply !== null &&
}
);
-}
+});
MessageReplyWrapper.propTypes = {
roomTimeline: PropTypes.shape({}).isRequired,
eventId: PropTypes.string.isRequired,
};
-function MessageBody({
+const MessageBody = React.memo(({
senderName,
body,
isCustomHTML,
isEdited,
msgType,
-}) {
+}) => {
// if body is not string it is a React element.
if (typeof body !== 'string') return {body}
;
@@ -176,7 +186,7 @@ function MessageBody({
{ isEdited && (edited) }
);
-}
+});
MessageBody.defaultProps = {
isCustomHTML: false,
isEdited: false,
@@ -379,17 +389,6 @@ MessageReactionGroup.propTypes = {
mEvent: PropTypes.shape({}).isRequired,
};
-function MessageOptions({ children }) {
- return (
-
- {children}
-
- );
-}
-MessageOptions.propTypes = {
- children: PropTypes.node.isRequired,
-};
-
function isMedia(mE) {
return (
mE.getContent()?.msgtype === 'm.file'
@@ -400,6 +399,86 @@ function isMedia(mE) {
);
}
+const MessageOptions = React.memo(({
+ roomTimeline, mEvent, edit, reply,
+}) => {
+ const { roomId, room } = roomTimeline;
+ const mx = initMatrix.matrixClient;
+ const eventId = mEvent.getId();
+ const senderId = mEvent.getSender();
+
+ const myPowerlevel = room.getMember(mx.getUserId())?.powerLevel;
+ const canIRedact = room.currentState.hasSufficientPowerLevelFor('redact', myPowerlevel);
+
+ return (
+
+ pickEmoji(e, roomId, eventId, roomTimeline)}
+ src={EmojiAddIC}
+ size="extra-small"
+ tooltip="Add reaction"
+ />
+ reply()}
+ src={ReplyArrowIC}
+ size="extra-small"
+ tooltip="Reply"
+ />
+ {(senderId === mx.getUserId() && !isMedia(mEvent)) && (
+ edit(true)}
+ src={PencilIC}
+ size="extra-small"
+ tooltip="Edit"
+ />
+ )}
+ (
+ <>
+ Options
+ openReadReceipts(roomId, roomTimeline.getEventReaders(eventId))}
+ >
+ Read receipts
+
+ {(canIRedact || senderId === mx.getUserId()) && (
+ <>
+
+ {
+ if (window.confirm('Are you sure you want to delete this event')) {
+ redactEvent(roomId, eventId);
+ }
+ }}
+ >
+ Delete
+
+ >
+ )}
+ >
+ )}
+ render={(toggleMenu) => (
+
+ )}
+ />
+
+ );
+});
+MessageOptions.propTypes = {
+ roomTimeline: PropTypes.shape({}).isRequired,
+ mEvent: PropTypes.shape({}).isRequired,
+ edit: PropTypes.func.isRequired,
+ reply: PropTypes.func.isRequired,
+};
+
function genMediaContent(mE) {
const mx = initMatrix.matrixClient;
const mContent = mE.getContent();
@@ -481,14 +560,10 @@ function getEditedBody(editedMEvent) {
}
function Message({
- mEvent, isBodyOnly, roomTimeline, focus, time
+ mEvent, isBodyOnly, roomTimeline, focus, time,
}) {
const [isEditing, setIsEditing] = useState(false);
-
- const mx = initMatrix.matrixClient;
- const {
- room, roomId, editedTimeline, reactionTimeline,
- } = roomTimeline;
+ const { roomId, editedTimeline, reactionTimeline } = roomTimeline;
const className = ['message', (isBodyOnly ? 'message--body-only' : 'message--full')];
if (focus) className.push('message--focus');
@@ -496,17 +571,12 @@ function Message({
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);
if (typeof body === 'undefined') return null;
if (msgType === 'm.emote') className.push('message--type-emote');
- const myPowerlevel = room.getMember(mx.getUserId())?.powerLevel;
- const canIRedact = room.currentState.hasSufficientPowerLevelFor('redact', myPowerlevel);
-
let isCustomHTML = content.format === 'org.matrix.custom.html';
const isEdited = editedTimeline.has(eventId);
const haveReactions = reactionTimeline.has(eventId) || !!mEvent.getServerAggregatedRelation('m.annotation');
@@ -524,18 +594,23 @@ function Message({
body = parseReply(body)?.body ?? body;
}
+ const edit = useCallback(() => {
+ setIsEditing(true);
+ }, []);
+ const reply = useCallback(() => {
+ replyTo(senderId, eventId, body);
+ }, [body]);
+
return (
-
- {!isBodyOnly && (
-
openProfileViewer(senderId, roomId)}>
-
-
- )}
-
+ {
+ isBodyOnly
+ ?
+ :
+ }
{!isBodyOnly && (
-
+
)}
{isReply && (
)}
{!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
- openReadReceipts(roomId, roomTimeline.getEventReaders(eventId))}
- >
- Read receipts
-
- {(canIRedact || senderId === mx.getUserId()) && (
- <>
-
- {
- if (window.confirm('Are you sure you want to delete this event')) {
- redactEvent(roomId, eventId);
- }
- }}
- >
- Delete
-
- >
- )}
- >
- )}
- render={(toggleMenu) => (
-
- )}
- />
-
+
)}