cinny/src/app/organisms/room/RoomViewContent.jsx

654 lines
22 KiB
React
Raw Normal View History

2021-08-04 09:52:59 +00:00
/* eslint-disable react/prop-types */
import React, { useState, useEffect, useLayoutEffect } from 'react';
import PropTypes from 'prop-types';
2021-08-31 13:13:31 +00:00
import './RoomViewContent.scss';
2021-08-04 09:52:59 +00:00
import dateFormat from 'dateformat';
import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons';
2021-08-15 08:29:09 +00:00
import { redactEvent, sendReaction } from '../../../client/action/roomTimeline';
import { getUsername, getUsernameOfRoomMember } from '../../../util/matrixUtil';
2021-08-04 09:52:59 +00:00
import colorMXID from '../../../util/colorMXID';
import { diffMinutes, isNotInSameDay, getEventCords } from '../../../util/common';
import { openEmojiBoard, openProfileViewer, openReadReceipts } from '../../../client/action/navigation';
2021-08-04 09:52:59 +00:00
import Divider from '../../atoms/divider/Divider';
2021-08-10 11:28:44 +00:00
import Avatar from '../../atoms/avatar/Avatar';
2021-08-11 07:59:01 +00:00
import IconButton from '../../atoms/button/IconButton';
2021-08-20 13:42:57 +00:00
import ContextMenu, { MenuHeader, MenuItem, MenuBorder } from '../../atoms/context-menu/ContextMenu';
2021-08-10 11:28:44 +00:00
import {
Message,
MessageHeader,
MessageReply,
MessageContent,
2021-08-20 13:42:57 +00:00
MessageEdit,
2021-08-10 11:28:44 +00:00
MessageReactionGroup,
MessageReaction,
2021-08-11 07:59:01 +00:00
MessageOptions,
2021-08-10 11:28:44 +00:00
PlaceholderMessage,
} from '../../molecules/message/Message';
2021-08-04 09:52:59 +00:00
import * as Media from '../../molecules/media/Media';
2021-08-31 13:13:31 +00:00
import RoomIntro from '../../molecules/room-intro/RoomIntro';
2021-08-04 09:52:59 +00:00
import TimelineChange from '../../molecules/message/TimelineChange';
2021-08-11 07:59:01 +00:00
import ReplyArrowIC from '../../../../public/res/ic/outlined/reply-arrow.svg';
2021-08-15 08:29:09 +00:00
import EmojiAddIC from '../../../../public/res/ic/outlined/emoji-add.svg';
2021-08-20 13:42:57 +00:00
import VerticalMenuIC from '../../../../public/res/ic/outlined/vertical-menu.svg';
import PencilIC from '../../../../public/res/ic/outlined/pencil.svg';
2021-08-16 12:21:23 +00:00
import TickMarkIC from '../../../../public/res/ic/outlined/tick-mark.svg';
2021-08-11 07:59:01 +00:00
import BinIC from '../../../../public/res/ic/outlined/bin.svg';
2021-08-04 09:52:59 +00:00
import { parseReply, parseTimelineChange } from './common';
const MAX_MSG_DIFF_MINUTES = 5;
function genPlaceholders(key) {
2021-08-15 08:29:09 +00:00
return (
<React.Fragment key={`placeholder-container${key}`}>
<PlaceholderMessage key={`placeholder-1${key}`} />
<PlaceholderMessage key={`placeholder-2${key}`} />
</React.Fragment>
2021-08-15 08:29:09 +00:00
);
}
function isMedia(mE) {
return (
mE.getContent()?.msgtype === 'm.file'
|| mE.getContent()?.msgtype === 'm.image'
|| mE.getContent()?.msgtype === 'm.audio'
|| mE.getContent()?.msgtype === 'm.video'
2021-08-18 09:26:23 +00:00
|| mE.getType() === 'm.sticker'
2021-08-15 08:29:09 +00:00
);
}
function genMediaContent(mE) {
const mx = initMatrix.matrixClient;
const mContent = mE.getContent();
2021-08-18 08:25:44 +00:00
if (!mContent || !mContent.body) return <span style={{ color: 'var(--bg-danger)' }}>Malformed event</span>;
let mediaMXC = mContent?.url;
2021-08-15 08:29:09 +00:00
const isEncryptedFile = typeof mediaMXC === 'undefined';
2021-08-18 08:25:44 +00:00
if (isEncryptedFile) mediaMXC = mContent?.file?.url;
let thumbnailMXC = mContent?.info?.thumbnail_url;
if (typeof mediaMXC === 'undefined' || mediaMXC === '') return <span style={{ color: 'var(--bg-danger)' }}>Malformed event</span>;
2021-08-15 08:29:09 +00:00
2021-08-18 09:26:23 +00:00
let msgType = mE.getContent()?.msgtype;
if (mE.getType() === 'm.sticker') msgType = 'm.image';
switch (msgType) {
2021-08-15 08:29:09 +00:00
case 'm.file':
return (
<Media.File
name={mContent.body}
link={mx.mxcUrlToHttp(mediaMXC)}
2021-08-18 08:25:44 +00:00
type={mContent.info?.mimetype}
file={mContent.file || null}
2021-08-15 08:29:09 +00:00
/>
);
case 'm.image':
return (
<Media.Image
name={mContent.body}
2021-08-18 08:25:44 +00:00
width={typeof mContent.info?.w === 'number' ? mContent.info?.w : null}
height={typeof mContent.info?.h === 'number' ? mContent.info?.h : null}
2021-08-15 08:29:09 +00:00
link={mx.mxcUrlToHttp(mediaMXC)}
file={isEncryptedFile ? mContent.file : null}
2021-08-18 08:25:44 +00:00
type={mContent.info?.mimetype}
2021-08-15 08:29:09 +00:00
/>
);
case 'm.audio':
return (
<Media.Audio
name={mContent.body}
link={mx.mxcUrlToHttp(mediaMXC)}
2021-08-18 08:25:44 +00:00
type={mContent.info?.mimetype}
file={mContent.file || null}
2021-08-15 08:29:09 +00:00
/>
);
case 'm.video':
if (typeof thumbnailMXC === 'undefined') {
thumbnailMXC = mContent.info?.thumbnail_file?.url || null;
}
return (
<Media.Video
name={mContent.body}
link={mx.mxcUrlToHttp(mediaMXC)}
thumbnail={thumbnailMXC === null ? null : mx.mxcUrlToHttp(thumbnailMXC)}
2021-08-18 08:25:44 +00:00
thumbnailFile={isEncryptedFile ? mContent.info?.thumbnail_file : null}
thumbnailType={mContent.info?.thumbnail_info?.mimetype || null}
width={typeof mContent.info?.w === 'number' ? mContent.info?.w : null}
height={typeof mContent.info?.h === 'number' ? mContent.info?.h : null}
2021-08-15 08:29:09 +00:00
file={isEncryptedFile ? mContent.file : null}
2021-08-18 08:25:44 +00:00
type={mContent.info?.mimetype}
2021-08-15 08:29:09 +00:00
/>
);
default:
2021-08-18 08:25:44 +00:00
return <span style={{ color: 'var(--bg-danger)' }}>Malformed event</span>;
2021-08-15 08:29:09 +00:00
}
}
2021-08-31 13:13:31 +00:00
function genRoomIntro(mEvent, roomTimeline) {
2021-08-17 11:21:22 +00:00
const mx = initMatrix.matrixClient;
2021-08-15 08:29:09 +00:00
const roomTopic = roomTimeline.room.currentState.getStateEvents('m.room.topic')[0]?.getContent().topic;
2021-08-17 11:21:22 +00:00
const isDM = initMatrix.roomList.directs.has(roomTimeline.roomId);
let avatarSrc = roomTimeline.room.getAvatarUrl(mx.baseUrl, 80, 80, 'crop');
avatarSrc = isDM ? roomTimeline.room.getAvatarFallbackMember()?.getAvatarUrl(mx.baseUrl, 80, 80, 'crop') : avatarSrc;
2021-08-15 08:29:09 +00:00
return (
2021-08-31 13:13:31 +00:00
<RoomIntro
key={mEvent ? mEvent.getId() : 'room-intro'}
roomId={roomTimeline.roomId}
2021-08-17 11:21:22 +00:00
avatarSrc={avatarSrc}
2021-08-15 08:29:09 +00:00
name={roomTimeline.room.name}
heading={`Welcome to ${roomTimeline.room.name}`}
2021-08-31 13:13:31 +00:00
desc={`This is the beginning of ${roomTimeline.room.name} room.${typeof roomTopic !== 'undefined' ? (` Topic: ${roomTopic}`) : ''}`}
2021-08-15 08:29:09 +00:00
time={mEvent ? `Created at ${dateFormat(mEvent.getDate(), 'dd mmmm yyyy, hh:MM TT')}` : null}
/>
);
}
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) => {
2021-08-15 08:29:09 +00:00
toggleEmoji(roomId, eventId, emoji.unicode, roomTimeline);
e.target.click();
});
}
const scroll = {
from: 0,
limit: 0,
getEndIndex() {
return (this.from + this.limit);
},
isNewEvent: false,
};
2021-08-31 13:13:31 +00:00
function RoomViewContent({
2021-08-04 09:52:59 +00:00
roomId, roomTimeline, timelineScroll, viewEvent,
}) {
const [isReachedTimelineEnd, setIsReachedTimelineEnd] = useState(false);
const [onStateUpdate, updateState] = useState(null);
2021-08-20 13:42:57 +00:00
const [editEvent, setEditEvent] = useState(null);
2021-08-04 09:52:59 +00:00
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;
scroll.limit = timelineScroll.maxEvents;
}
2021-08-04 09:52:59 +00:00
function autoLoadTimeline() {
if (timelineScroll.isScrollable === true) return;
2021-08-04 09:52:59 +00:00
roomTimeline.paginateBack();
}
function trySendingReadReceipt() {
const { timeline } = roomTimeline.room;
2021-09-13 14:17:40 +00:00
if (
(noti.doesRoomHaveUnread(roomTimeline.room) || noti.hasNoti(roomId))
2021-09-13 14:17:40 +00:00
&& timeline.length !== 0) {
2021-08-04 09:52:59 +00:00
mx.sendReadReceipt(timeline[timeline.length - 1]);
}
}
const getNewFrom = (position) => {
let newFrom = scroll.from;
const tSize = roomTimeline.timeline.size;
const doPaginate = tSize > timelineScroll.maxEvents;
if (!doPaginate || scroll.from < 0) newFrom = 0;
const newEventCount = Math.round(timelineScroll.maxEvents / 2);
scroll.limit = timelineScroll.maxEvents;
2021-08-04 09:52:59 +00:00
if (position === 'TOP' && doPaginate) newFrom -= newEventCount;
if (position === 'BOTTOM' && doPaginate) newFrom += newEventCount;
if (newFrom >= tSize || scroll.getEndIndex() >= tSize) newFrom = tSize - scroll.limit - 1;
if (newFrom < 0) newFrom = 0;
return newFrom;
};
const handleTimelineScroll = (position) => {
const tSize = roomTimeline.timeline.size;
if (position === 'BETWEEN') return;
if (position === 'BOTTOM' && scroll.getEndIndex() + 1 === tSize) return;
if (scroll.from === 0 && position === 'TOP') {
// Fetch back history.
if (roomTimeline.isOngoingPagination || isReachedTimelineEnd) return;
roomTimeline.paginateBack();
return;
}
scroll.from = getNewFrom(position);
updateState({});
if (scroll.getEndIndex() + 1 >= tSize) {
trySendingReadReceipt();
2021-08-04 09:52:59 +00:00
}
};
const updatePAG = (canPagMore, loaded) => {
if (canPagMore) {
scroll.from += loaded;
scroll.from = getNewFrom(timelineScroll.position);
if (roomTimeline.ongoingDecryptionCount === 0) updateState({});
} else setIsReachedTimelineEnd(true);
};
2021-08-04 09:52:59 +00:00
// force update RoomTimeline on cons.events.roomTimeline.EVENT
const updateRT = () => {
if (timelineScroll.position === 'BOTTOM') {
2021-08-04 09:52:59 +00:00
trySendingReadReceipt();
scroll.from = roomTimeline.timeline.size - scroll.limit - 1;
if (scroll.from < 0) scroll.from = 0;
scroll.isNewEvent = true;
2021-08-04 09:52:59 +00:00
}
updateState({});
};
const handleScrollToLive = () => {
trySendingReadReceipt();
scroll.from = roomTimeline.timeline.size - scroll.limit - 1;
if (scroll.from < 0) scroll.from = 0;
scroll.isNewEvent = true;
updateState({});
};
2021-08-04 09:52:59 +00:00
useEffect(() => {
trySendingReadReceipt();
return () => {
setIsReachedTimelineEnd(false);
scroll.limit = 0;
};
2021-08-04 09:52:59 +00:00
}, [roomId]);
// init room setup completed.
// listen for future. setup stateUpdate listener.
useEffect(() => {
roomTimeline.on(cons.events.roomTimeline.EVENT, updateRT);
roomTimeline.on(cons.events.roomTimeline.PAGINATED, updatePAG);
viewEvent.on('timeline-scroll', handleTimelineScroll);
viewEvent.on('scroll-to-live', handleScrollToLive);
2021-08-04 09:52:59 +00:00
return () => {
roomTimeline.removeListener(cons.events.roomTimeline.EVENT, updateRT);
roomTimeline.removeListener(cons.events.roomTimeline.PAGINATED, updatePAG);
viewEvent.removeListener('timeline-scroll', handleTimelineScroll);
viewEvent.removeListener('scroll-to-live', handleScrollToLive);
2021-08-04 09:52:59 +00:00
};
}, [roomTimeline, isReachedTimelineEnd]);
2021-08-04 09:52:59 +00:00
useLayoutEffect(() => {
timelineScroll.reachBottom();
autoLoadTimeline();
trySendingReadReceipt();
2021-08-04 09:52:59 +00:00
}, [roomTimeline]);
useLayoutEffect(() => {
if (onStateUpdate === null || scroll.isNewEvent) {
scroll.isNewEvent = false;
timelineScroll.reachBottom();
return;
}
if (timelineScroll.isScrollable) {
timelineScroll.tryRestoringScroll();
} else {
timelineScroll.reachBottom();
autoLoadTimeline();
}
2021-08-04 09:52:59 +00:00
}, [onStateUpdate]);
let prevMEvent = null;
2021-08-20 13:42:57 +00:00
function genMessage(mEvent) {
const myPowerlevel = roomTimeline.room.getMember(mx.getUserId())?.powerLevel;
2021-08-20 13:42:57 +00:00
const canIRedact = roomTimeline.room.currentState.hasSufficientPowerLevelFor('redact', myPowerlevel);
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;
2021-08-20 13:42:57 +00:00
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 : (
<button type="button" onClick={() => openProfileViewer(mEvent.sender.userId, roomId)}>
<Avatar
imageSrc={mEvent.sender.getAvatarUrl(initMatrix.matrixClient.baseUrl, 36, 36, 'crop')}
text={getUsernameOfRoomMember(mEvent.sender)}
bgColor={senderMXIDColor}
size="small"
/>
</button>
2021-08-20 13:42:57 +00:00
);
const userHeader = isContentOnly ? null : (
<MessageHeader
userId={mEvent.sender.userId}
2021-08-25 08:36:13 +00:00
name={getUsernameOfRoomMember(mEvent.sender)}
2021-08-20 13:42:57 +00:00
color={senderMXIDColor}
time={`${dateFormat(mEvent.getDate(), 'hh:MM TT')}`}
/>
);
const userReply = reply === null ? null : (
<MessageReply
name={reply.to}
color={reply.color}
content={reply.content}
/>
);
const userContent = (
<MessageContent
senderName={getUsernameOfRoomMember(mEvent.sender)}
2021-08-20 13:42:57 +00:00
isMarkdown={isMarkdown}
content={isMedia(mEvent) ? genMediaContent(mEvent) : content}
msgType={msgType}
2021-08-20 13:42:57 +00:00
isEdited={isEdited}
/>
);
const userReactions = reactions === null ? null : (
<MessageReactionGroup>
{
reactions.map((reaction) => (
<MessageReaction
key={reaction.id}
reaction={reaction.key}
users={reaction.users}
isActive={reaction.isActive}
onClick={() => {
toggleEmoji(roomId, mEvent.getId(), reaction.key, roomTimeline);
}}
/>
))
}
<IconButton
onClick={(e) => pickEmoji(e, roomId, mEvent.getId(), roomTimeline)}
src={EmojiAddIC}
size="extra-small"
tooltip="Add reaction"
/>
</MessageReactionGroup>
);
const userOptions = (
<MessageOptions>
<IconButton
onClick={(e) => pickEmoji(e, roomId, mEvent.getId(), roomTimeline)}
src={EmojiAddIC}
size="extra-small"
tooltip="Add reaction"
/>
<IconButton
onClick={() => {
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)) && (
<IconButton
onClick={() => setEditEvent(mEvent)}
src={PencilIC}
size="extra-small"
tooltip="Edit"
/>
)}
<ContextMenu
content={() => (
<>
<MenuHeader>Options</MenuHeader>
<MenuItem
iconSrc={EmojiAddIC}
onClick={(e) => pickEmoji(e, roomId, mEvent.getId(), roomTimeline)}
>
2021-08-22 12:45:20 +00:00
Add reaction
2021-08-20 13:42:57 +00:00
</MenuItem>
<MenuItem
iconSrc={ReplyArrowIC}
onClick={() => {
viewEvent.emit('reply_to', mEvent.getSender(), mEvent.getId(), isMedia(mEvent) ? mEvent.getContent().body : content);
}}
>
Reply
</MenuItem>
{(mEvent.getSender() === mx.getUserId() && !isMedia(mEvent)) && (
<MenuItem iconSrc={PencilIC} onClick={() => setEditEvent(mEvent)}>Edit</MenuItem>
)}
<MenuItem
iconSrc={TickMarkIC}
onClick={() => openReadReceipts(roomId, mEvent.getId())}
>
Read receipts
</MenuItem>
{(canIRedact || mEvent.getSender() === mx.getUserId()) && (
<>
<MenuBorder />
<MenuItem
variant="danger"
iconSrc={BinIC}
onClick={() => {
if (window.confirm('Are you sure you want to delete this event')) {
redactEvent(roomId, mEvent.getId());
}
}}
>
Delete
</MenuItem>
</>
)}
</>
)}
render={(toggleMenu) => (
<IconButton
onClick={toggleMenu}
src={VerticalMenuIC}
size="extra-small"
tooltip="Options"
/>
)}
/>
</MessageOptions>
);
const isEditingEvent = editEvent?.getId() === mEvent.getId();
const myMessageEl = (
<Message
key={mEvent.getId()}
avatar={userAvatar}
header={userHeader}
reply={userReply}
content={editEvent !== null && isEditingEvent ? null : userContent}
msgType={msgType}
2021-08-20 13:42:57 +00:00
editContent={editEvent !== null && isEditingEvent ? (
<MessageEdit
content={content}
onSave={(newBody) => {
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;
}
2021-08-04 09:52:59 +00:00
function renderMessage(mEvent) {
if (!cons.supportEventTypes.includes(mEvent.getType())) return false;
2021-08-04 09:52:59 +00:00
if (mEvent.getRelation()?.rel_type === 'm.replace') return false;
if (mEvent.isRedacted()) return false;
if (mEvent.getType() === 'm.room.create') return genRoomIntro(mEvent, roomTimeline);
2021-08-04 09:52:59 +00:00
let divider = null;
if (prevMEvent !== null && isNotInSameDay(mEvent.getDate(), prevMEvent.getDate())) {
divider = <Divider key={`divider-${mEvent.getId()}`} text={`${dateFormat(mEvent.getDate(), 'mmmm dd, yyyy')}`} />;
}
if (mEvent.getType() !== 'm.room.member') {
2021-08-20 13:42:57 +00:00
const messageComp = genMessage(mEvent);
2021-08-04 09:52:59 +00:00
prevMEvent = mEvent;
2021-08-15 08:29:09 +00:00
return (
<React.Fragment key={`box-${mEvent.getId()}`}>
{divider}
{messageComp}
</React.Fragment>
);
2021-08-04 09:52:59 +00:00
}
2021-08-15 08:29:09 +00:00
2021-08-04 09:52:59 +00:00
prevMEvent = mEvent;
const timelineChange = parseTimelineChange(mEvent);
if (timelineChange === null) return false;
2021-08-04 09:52:59 +00:00
return (
<React.Fragment key={`box-${mEvent.getId()}`}>
{divider}
<TimelineChange
key={mEvent.getId()}
variant={timelineChange.variant}
content={timelineChange.content}
time={`${dateFormat(mEvent.getDate(), 'hh:MM TT')}`}
/>
</React.Fragment>
);
}
const renderTimeline = () => {
const { timeline } = roomTimeline;
const tl = [];
if (timeline.size === 0) return tl;
let i = 0;
// eslint-disable-next-line no-restricted-syntax
for (const [, mEvent] of timeline.entries()) {
if (i >= scroll.from) {
if (i === scroll.from) {
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));
}
i += 1;
if (i > scroll.getEndIndex()) break;
}
if (i < timeline.size) tl.push(genPlaceholders(2));
return tl;
};
2021-08-04 09:52:59 +00:00
return (
2021-08-31 13:13:31 +00:00
<div className="room-view__content">
2021-08-04 09:52:59 +00:00
<div className="timeline__wrapper">
{ renderTimeline() }
2021-08-04 09:52:59 +00:00
</div>
</div>
);
}
2021-08-31 13:13:31 +00:00
RoomViewContent.propTypes = {
2021-08-04 09:52:59 +00:00
roomId: PropTypes.string.isRequired,
roomTimeline: PropTypes.shape({}).isRequired,
2021-08-15 08:29:09 +00:00
timelineScroll: PropTypes.shape({}).isRequired,
2021-08-04 09:52:59 +00:00
viewEvent: PropTypes.shape({}).isRequired,
};
2021-08-31 13:13:31 +00:00
export default RoomViewContent;