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

645 lines
21 KiB
React
Raw Normal View History

/* eslint-disable jsx-a11y/no-static-element-interactions */
/* eslint-disable jsx-a11y/click-events-have-key-events */
2021-08-04 05:52:59 -04:00
/* eslint-disable react/prop-types */
import React, {
useState, useEffect, useLayoutEffect, useCallback, useRef,
} from 'react';
2021-08-04 05:52:59 -04:00
import PropTypes from 'prop-types';
2021-08-31 09:13:31 -04:00
import './RoomViewContent.scss';
2021-08-04 05:52:59 -04:00
import dateFormat from 'dateformat';
2022-05-03 04:35:56 -04:00
import { twemojify } from '../../../util/twemojify';
2021-08-04 05:52:59 -04:00
import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons';
import navigation from '../../../client/state/navigation';
import { openProfileViewer } from '../../../client/action/navigation';
import { diffMinutes, isInSameDay, Throttle } from '../../../util/common';
import { markAsRead } from '../../../client/action/notifications';
2021-08-04 05:52:59 -04:00
import Divider from '../../atoms/divider/Divider';
import ScrollView from '../../atoms/scroll/ScrollView';
import { Message, PlaceholderMessage } from '../../molecules/message/Message';
2021-08-31 09:13:31 -04:00
import RoomIntro from '../../molecules/room-intro/RoomIntro';
2021-08-04 05:52:59 -04:00
import TimelineChange from '../../molecules/message/TimelineChange';
import { useStore } from '../../hooks/useStore';
import { useForceUpdate } from '../../hooks/useForceUpdate';
import { parseTimelineChange } from './common';
import TimelineScroll from './TimelineScroll';
import EventLimit from './EventLimit';
Refactor state & Custom editor (#1190) * Fix eslint * Enable ts strict mode * install folds, jotai & immer * Enable immer map/set * change cross-signing alert anim to 30 iteration * Add function to access matrix client * Add new types * Add disposable util * Add room utils * Add mDirect list atom * Add invite list atom * add room list atom * add utils for jotai atoms * Add room id to parents atom * Add mute list atom * Add room to unread atom * Use hook to bind atoms with sdk * Add settings atom * Add settings hook * Extract set settings hook * Add Sidebar components * WIP * Add bind atoms hook * Fix init muted room list atom * add navigation atoms * Add custom editor * Fix hotkeys * Update folds * Add editor output function * Add matrix client context * Add tooltip to editor toolbar items * WIP - Add editor to room input * Refocus editor on toolbar item click * Add Mentions - WIP * update folds * update mention focus outline * rename emoji element type * Add auto complete menu * add autocomplete query functions * add index file for editor * fix bug in getPrevWord function * Show room mention autocomplete * Add async search function * add use async search hook * use async search in room mention autocomplete * remove folds prefer font for now * allow number array in async search * reset search with empty query * Autocomplete unknown room mention * Autocomplete first room mention on tab * fix roomAliasFromQueryText * change mention color to primary * add isAlive hook * add getMxIdLocalPart to mx utils * fix getRoomAvatarUrl size * fix types * add room members hook * fix bug in room mention * add user mention autocomplete * Fix async search giving prev result after no match * update folds * add twemoji font * add use state provider hook * add prevent scroll with arrow key util * add ts to custom-emoji and emoji files * add types * add hook for emoji group labels * add hook for emoji group icons * add emoji board with basic emoji * add emojiboard in room input * select multiple emoji with shift press * display custom emoji in emojiboard * Add emoji preview * focus element on hover * update folds * position emojiboard properly * convert recent-emoji.js to ts * add use recent emoji hook * add io.element.recent_emoji to account data evt * Render recent emoji in emoji board * show custom emoji from parent spaces * show room emoji * improve emoji sidebar * update folds * fix pack avatar and name fallback in emoji board * add stickers to emoji board * fix bug in emoji preview * Add sticker icon in room input * add debounce hook * add search in emoji board * Optimize emoji board * fix emoji board sidebar divider * sync emojiboard sidebar with scroll & update ui * Add use throttle hook * support custom emoji in editor * remove duplicate emoji selection function * fix emoji and mention spacing * add emoticon autocomplete in editor * fix string * makes emoji size relative to font size in editor * add option to render link element * add spoiler in editor * fix sticker in emoji board search using wrong type * render custom placeholder * update hotkey for block quote and block code * add terminate search function in async search * add getImageInfo to matrix utils * send stickers * add resize observer hook * move emoji board component hooks in hooks dir * prevent editor expand hides room timeline * send typing notifications * improve emoji style and performance * fix imports * add on paste param to editor * add selectFile utils * add file picker hook * add file paste handler hook * add file drop handler * update folds * Add file upload card * add bytes to size util * add blurHash util * add await to js lib * add browser-encrypt-attachment types * add list atom * convert mimetype file to ts * add matrix types * add matrix file util * add file related dom utils * add common utils * add upload atom * add room input draft atom * add upload card renderer component * add upload board component * add support for file upload in editor * send files with message / enter * fix circular deps * store editor toolbar state in local store * move msg content util to separate file * store msg draft on room switch * fix following member not updating on msg sent * add theme for folds component * fix system default theme * Add reply support in editor * prevent initMatrix to init multiple time * add state event hooks * add async callback hook * Show tombstone info for tombstone room * fix room tombstone component border * add power level hook * Add room input placeholder component * Show input placeholder for muted member
2023-06-12 07:15:23 -04:00
import { getResizeObserverEntry, useResizeObserver } from '../../hooks/useResizeObserver';
2021-08-04 05:52:59 -04:00
const PAG_LIMIT = 30;
2021-08-04 05:52:59 -04:00
const MAX_MSG_DIFF_MINUTES = 5;
const PLACEHOLDER_COUNT = 2;
const PLACEHOLDERS_HEIGHT = 96 * PLACEHOLDER_COUNT;
const SCROLL_TRIGGER_POS = PLACEHOLDERS_HEIGHT * 4;
function loadingMsgPlaceholders(key, count = 2) {
const pl = [];
const genPlaceholders = () => {
for (let i = 0; i < count; i += 1) {
pl.push(<PlaceholderMessage key={`placeholder-${i}${key}`} />);
}
return pl;
};
2021-08-04 05:52:59 -04:00
2021-08-15 04:29:09 -04:00
return (
<React.Fragment key={`placeholder-container${key}`}>
{genPlaceholders()}
</React.Fragment>
2021-08-15 04:29:09 -04:00
);
}
2022-05-03 04:35:56 -04:00
function RoomIntroContainer({ event, timeline }) {
const [, nameForceUpdate] = useForceUpdate();
2021-08-17 07:21:22 -04:00
const mx = initMatrix.matrixClient;
2022-05-03 04:35:56 -04:00
const { roomList } = initMatrix;
const { room } = timeline;
const roomTopic = room.currentState.getStateEvents('m.room.topic')[0]?.getContent().topic;
const isDM = roomList.directs.has(timeline.roomId);
let avatarSrc = room.getAvatarUrl(mx.baseUrl, 80, 80, 'crop');
avatarSrc = isDM ? room.getAvatarFallbackMember()?.getAvatarUrl(mx.baseUrl, 80, 80, 'crop') : avatarSrc;
const heading = isDM ? room.name : `Welcome to ${room.name}`;
const topic = twemojify(roomTopic || '', undefined, true);
const nameJsx = twemojify(room.name);
const desc = isDM
? (
<>
This is the beginning of your direct message history with @
<b>{nameJsx}</b>
{'. '}
{topic}
</>
)
: (
<>
{'This is the beginning of the '}
<b>{nameJsx}</b>
{' room. '}
{topic}
</>
);
useEffect(() => {
const handleUpdate = () => nameForceUpdate();
roomList.on(cons.events.roomList.ROOM_PROFILE_UPDATED, handleUpdate);
return () => {
roomList.removeListener(cons.events.roomList.ROOM_PROFILE_UPDATED, handleUpdate);
};
}, []);
2021-08-15 04:29:09 -04:00
return (
2021-08-31 09:13:31 -04:00
<RoomIntro
2022-05-03 04:35:56 -04:00
roomId={timeline.roomId}
2021-08-17 07:21:22 -04:00
avatarSrc={avatarSrc}
2022-05-03 04:35:56 -04:00
name={room.name}
heading={twemojify(heading)}
desc={desc}
time={event ? `Created at ${dateFormat(event.getDate(), 'dd mmmm yyyy, hh:MM TT')}` : null}
2021-08-15 04:29:09 -04:00
/>
);
}
function handleOnClickCapture(e) {
const { target, nativeEvent } = e;
const userId = target.getAttribute('data-mx-pill');
if (userId) {
const roomId = navigation.selectedRoomId;
openProfileViewer(userId, roomId);
}
const spoiler = nativeEvent.composedPath().find((el) => el?.hasAttribute?.('data-mx-spoiler'));
if (spoiler) {
2023-04-16 08:22:01 -04:00
if (!spoiler.classList.contains('data-mx-spoiler--visible')) e.preventDefault();
spoiler.classList.toggle('data-mx-spoiler--visible');
}
}
function renderEvent(
roomTimeline,
mEvent,
prevMEvent,
isFocus,
isEdit,
setEdit,
cancelEdit,
) {
const isBodyOnly = (prevMEvent !== null
&& prevMEvent.getSender() === mEvent.getSender()
&& prevMEvent.getType() !== 'm.room.member'
&& prevMEvent.getType() !== 'm.room.create'
&& diffMinutes(mEvent.getDate(), prevMEvent.getDate()) <= MAX_MSG_DIFF_MINUTES
);
const timestamp = mEvent.getTs();
2021-08-04 05:52:59 -04:00
if (mEvent.getType() === 'm.room.member') {
const timelineChange = parseTimelineChange(mEvent);
if (timelineChange === null) return <div key={mEvent.getId()} />;
return (
<TimelineChange
key={mEvent.getId()}
variant={timelineChange.variant}
content={timelineChange.content}
timestamp={timestamp}
/>
);
2021-08-04 05:52:59 -04:00
}
return (
<Message
key={mEvent.getId()}
mEvent={mEvent}
isBodyOnly={isBodyOnly}
roomTimeline={roomTimeline}
focus={isFocus}
fullTime={false}
isEdit={isEdit}
setEdit={setEdit}
cancelEdit={cancelEdit}
/>
);
}
function useTimeline(roomTimeline, eventId, readUptoEvtStore, eventLimitRef) {
const [timelineInfo, setTimelineInfo] = useState(null);
const setEventTimeline = async (eId) => {
if (typeof eId === 'string') {
const isLoaded = await roomTimeline.loadEventTimeline(eId);
if (isLoaded) return;
// if eventTimeline failed to load,
// we will load live timeline as fallback.
}
roomTimeline.loadLiveTimeline();
};
2021-08-04 05:52:59 -04:00
useEffect(() => {
const limit = eventLimitRef.current;
const initTimeline = (eId) => {
// NOTICE: eId can be id of readUpto, reply or specific event.
// readUpTo: when user click jump to unread message button.
// reply: when user click reply from timeline.
// specific event when user open a link of event. behave same as ^^^^
const readUpToId = roomTimeline.getReadUpToEventId();
let focusEventIndex = -1;
const isSpecificEvent = eId && eId !== readUpToId;
if (isSpecificEvent) {
focusEventIndex = roomTimeline.getEventIndex(eId);
}
if (!readUptoEvtStore.getItem() && roomTimeline.hasEventInTimeline(readUpToId)) {
// either opening live timeline or jump to unread.
readUptoEvtStore.setItem(roomTimeline.findEventByIdInTimelineSet(readUpToId));
}
if (readUptoEvtStore.getItem() && !isSpecificEvent) {
focusEventIndex = roomTimeline.getUnreadEventIndex(readUptoEvtStore.getItem().getId());
}
if (focusEventIndex > -1) {
limit.setFrom(focusEventIndex - Math.round(limit.maxEvents / 2));
} else {
limit.setFrom(roomTimeline.timeline.length - limit.maxEvents);
}
setTimelineInfo({ focusEventId: isSpecificEvent ? eId : null });
};
roomTimeline.on(cons.events.roomTimeline.READY, initTimeline);
setEventTimeline(eventId);
return () => {
roomTimeline.removeListener(cons.events.roomTimeline.READY, initTimeline);
limit.setFrom(0);
};
}, [roomTimeline, eventId]);
return timelineInfo;
}
function usePaginate(
roomTimeline,
readUptoEvtStore,
forceUpdateLimit,
timelineScrollRef,
eventLimitRef,
) {
const [info, setInfo] = useState(null);
2021-08-04 05:52:59 -04:00
useEffect(() => {
const handlePaginatedFromServer = (backwards, loaded) => {
const limit = eventLimitRef.current;
if (loaded === 0) return;
if (!readUptoEvtStore.getItem()) {
const readUpToId = roomTimeline.getReadUpToEventId();
readUptoEvtStore.setItem(roomTimeline.findEventByIdInTimelineSet(readUpToId));
}
limit.paginate(backwards, PAG_LIMIT, roomTimeline.timeline.length);
setTimeout(() => setInfo({
backwards,
loaded,
}));
};
roomTimeline.on(cons.events.roomTimeline.PAGINATED, handlePaginatedFromServer);
return () => {
2022-05-03 04:35:56 -04:00
roomTimeline.removeListener(cons.events.roomTimeline.PAGINATED, handlePaginatedFromServer);
};
}, [roomTimeline]);
const autoPaginate = useCallback(async () => {
const timelineScroll = timelineScrollRef.current;
const limit = eventLimitRef.current;
if (roomTimeline.isOngoingPagination) return;
const tLength = roomTimeline.timeline.length;
if (timelineScroll.bottom < SCROLL_TRIGGER_POS) {
if (limit.length < tLength) {
// paginate from memory
limit.paginate(false, PAG_LIMIT, tLength);
forceUpdateLimit();
} else if (roomTimeline.canPaginateForward()) {
// paginate from server.
await roomTimeline.paginateTimeline(false, PAG_LIMIT);
return;
}
}
if (timelineScroll.top < SCROLL_TRIGGER_POS) {
if (limit.from > 0) {
// paginate from memory
limit.paginate(true, PAG_LIMIT, tLength);
forceUpdateLimit();
} else if (roomTimeline.canPaginateBackward()) {
// paginate from server.
await roomTimeline.paginateTimeline(true, PAG_LIMIT);
}
}
}, [roomTimeline]);
return [info, autoPaginate];
}
2021-08-04 05:52:59 -04:00
function useHandleScroll(
roomTimeline,
autoPaginate,
readUptoEvtStore,
forceUpdateLimit,
timelineScrollRef,
eventLimitRef,
) {
const handleScroll = useCallback(() => {
const timelineScroll = timelineScrollRef.current;
const limit = eventLimitRef.current;
requestAnimationFrame(() => {
// emit event to toggle scrollToBottom button visibility
const isAtBottom = (
timelineScroll.bottom < 16 && !roomTimeline.canPaginateForward()
&& limit.length >= roomTimeline.timeline.length
);
roomTimeline.emit(cons.events.roomTimeline.AT_BOTTOM, isAtBottom);
if (isAtBottom && readUptoEvtStore.getItem()) {
requestAnimationFrame(() => markAsRead(roomTimeline.roomId));
}
});
autoPaginate();
}, [roomTimeline]);
const handleScrollToLive = useCallback(() => {
const timelineScroll = timelineScrollRef.current;
const limit = eventLimitRef.current;
if (readUptoEvtStore.getItem()) {
requestAnimationFrame(() => markAsRead(roomTimeline.roomId));
}
if (roomTimeline.isServingLiveTimeline()) {
limit.setFrom(roomTimeline.timeline.length - limit.maxEvents);
timelineScroll.scrollToBottom();
forceUpdateLimit();
return;
}
roomTimeline.loadLiveTimeline();
}, [roomTimeline]);
return [handleScroll, handleScrollToLive];
}
function useEventArrive(roomTimeline, readUptoEvtStore, timelineScrollRef, eventLimitRef) {
const myUserId = initMatrix.matrixClient.getUserId();
const [newEvent, setEvent] = useState(null);
useEffect(() => {
const timelineScroll = timelineScrollRef.current;
const limit = eventLimitRef.current;
const trySendReadReceipt = (event) => {
if (myUserId === event.getSender()) {
requestAnimationFrame(() => markAsRead(roomTimeline.roomId));
return;
}
const readUpToEvent = readUptoEvtStore.getItem();
const readUpToId = roomTimeline.getReadUpToEventId();
const isUnread = readUpToEvent ? readUpToEvent?.getId() === readUpToId : true;
if (isUnread === false) {
if (document.visibilityState === 'visible' && timelineScroll.bottom < 16) {
requestAnimationFrame(() => markAsRead(roomTimeline.roomId));
} else {
readUptoEvtStore.setItem(roomTimeline.findEventByIdInTimelineSet(readUpToId));
}
return;
}
const { timeline } = roomTimeline;
const unreadMsgIsLast = timeline[timeline.length - 2].getId() === readUpToId;
if (unreadMsgIsLast) {
requestAnimationFrame(() => markAsRead(roomTimeline.roomId));
}
};
const handleEvent = (event) => {
const tLength = roomTimeline.timeline.length;
const isViewingLive = roomTimeline.isServingLiveTimeline() && limit.length >= tLength - 1;
const isAttached = timelineScroll.bottom < SCROLL_TRIGGER_POS;
if (isViewingLive && isAttached && document.hasFocus()) {
limit.setFrom(tLength - limit.maxEvents);
trySendReadReceipt(event);
setEvent(event);
return;
}
const isRelates = (event.getType() === 'm.reaction' || event.getRelation()?.rel_type === 'm.replace');
if (isRelates) {
setEvent(event);
return;
}
if (isViewingLive) {
// This stateUpdate will help to put the
// loading msg placeholder at bottom
setEvent(event);
}
};
const handleEventRedact = (event) => setEvent(event);
roomTimeline.on(cons.events.roomTimeline.EVENT, handleEvent);
roomTimeline.on(cons.events.roomTimeline.EVENT_REDACTED, handleEventRedact);
2021-08-04 05:52:59 -04:00
return () => {
roomTimeline.removeListener(cons.events.roomTimeline.EVENT, handleEvent);
roomTimeline.removeListener(cons.events.roomTimeline.EVENT_REDACTED, handleEventRedact);
2021-08-04 05:52:59 -04:00
};
}, [roomTimeline]);
2021-08-04 05:52:59 -04:00
return newEvent;
}
let jumpToItemIndex = -1;
Refactor state & Custom editor (#1190) * Fix eslint * Enable ts strict mode * install folds, jotai & immer * Enable immer map/set * change cross-signing alert anim to 30 iteration * Add function to access matrix client * Add new types * Add disposable util * Add room utils * Add mDirect list atom * Add invite list atom * add room list atom * add utils for jotai atoms * Add room id to parents atom * Add mute list atom * Add room to unread atom * Use hook to bind atoms with sdk * Add settings atom * Add settings hook * Extract set settings hook * Add Sidebar components * WIP * Add bind atoms hook * Fix init muted room list atom * add navigation atoms * Add custom editor * Fix hotkeys * Update folds * Add editor output function * Add matrix client context * Add tooltip to editor toolbar items * WIP - Add editor to room input * Refocus editor on toolbar item click * Add Mentions - WIP * update folds * update mention focus outline * rename emoji element type * Add auto complete menu * add autocomplete query functions * add index file for editor * fix bug in getPrevWord function * Show room mention autocomplete * Add async search function * add use async search hook * use async search in room mention autocomplete * remove folds prefer font for now * allow number array in async search * reset search with empty query * Autocomplete unknown room mention * Autocomplete first room mention on tab * fix roomAliasFromQueryText * change mention color to primary * add isAlive hook * add getMxIdLocalPart to mx utils * fix getRoomAvatarUrl size * fix types * add room members hook * fix bug in room mention * add user mention autocomplete * Fix async search giving prev result after no match * update folds * add twemoji font * add use state provider hook * add prevent scroll with arrow key util * add ts to custom-emoji and emoji files * add types * add hook for emoji group labels * add hook for emoji group icons * add emoji board with basic emoji * add emojiboard in room input * select multiple emoji with shift press * display custom emoji in emojiboard * Add emoji preview * focus element on hover * update folds * position emojiboard properly * convert recent-emoji.js to ts * add use recent emoji hook * add io.element.recent_emoji to account data evt * Render recent emoji in emoji board * show custom emoji from parent spaces * show room emoji * improve emoji sidebar * update folds * fix pack avatar and name fallback in emoji board * add stickers to emoji board * fix bug in emoji preview * Add sticker icon in room input * add debounce hook * add search in emoji board * Optimize emoji board * fix emoji board sidebar divider * sync emojiboard sidebar with scroll & update ui * Add use throttle hook * support custom emoji in editor * remove duplicate emoji selection function * fix emoji and mention spacing * add emoticon autocomplete in editor * fix string * makes emoji size relative to font size in editor * add option to render link element * add spoiler in editor * fix sticker in emoji board search using wrong type * render custom placeholder * update hotkey for block quote and block code * add terminate search function in async search * add getImageInfo to matrix utils * send stickers * add resize observer hook * move emoji board component hooks in hooks dir * prevent editor expand hides room timeline * send typing notifications * improve emoji style and performance * fix imports * add on paste param to editor * add selectFile utils * add file picker hook * add file paste handler hook * add file drop handler * update folds * Add file upload card * add bytes to size util * add blurHash util * add await to js lib * add browser-encrypt-attachment types * add list atom * convert mimetype file to ts * add matrix types * add matrix file util * add file related dom utils * add common utils * add upload atom * add room input draft atom * add upload card renderer component * add upload board component * add support for file upload in editor * send files with message / enter * fix circular deps * store editor toolbar state in local store * move msg content util to separate file * store msg draft on room switch * fix following member not updating on msg sent * add theme for folds component * fix system default theme * Add reply support in editor * prevent initMatrix to init multiple time * add state event hooks * add async callback hook * Show tombstone info for tombstone room * fix room tombstone component border * add power level hook * Add room input placeholder component * Show input placeholder for muted member
2023-06-12 07:15:23 -04:00
function RoomViewContent({ roomInputRef, eventId, roomTimeline }) {
const [throttle] = useState(new Throttle());
const timelineSVRef = useRef(null);
const timelineScrollRef = useRef(null);
const eventLimitRef = useRef(null);
const [editEventId, setEditEventId] = useState(null);
const cancelEdit = () => setEditEventId(null);
const readUptoEvtStore = useStore(roomTimeline);
const [onLimitUpdate, forceUpdateLimit] = useForceUpdate();
const timelineInfo = useTimeline(roomTimeline, eventId, readUptoEvtStore, eventLimitRef);
const [paginateInfo, autoPaginate] = usePaginate(
roomTimeline,
readUptoEvtStore,
forceUpdateLimit,
timelineScrollRef,
eventLimitRef,
);
const [handleScroll, handleScrollToLive] = useHandleScroll(
roomTimeline,
autoPaginate,
readUptoEvtStore,
forceUpdateLimit,
timelineScrollRef,
eventLimitRef,
);
const newEvent = useEventArrive(roomTimeline, readUptoEvtStore, timelineScrollRef, eventLimitRef);
const { timeline } = roomTimeline;
2021-08-04 05:52:59 -04:00
useLayoutEffect(() => {
if (!roomTimeline.initialized) {
timelineScrollRef.current = new TimelineScroll(timelineSVRef.current);
eventLimitRef.current = new EventLimit();
}
});
// when active timeline changes
useEffect(() => {
if (!roomTimeline.initialized) return undefined;
const timelineScroll = timelineScrollRef.current;
if (timeline.length > 0) {
if (jumpToItemIndex === -1) {
timelineScroll.scrollToBottom();
} else {
timelineScroll.scrollToIndex(jumpToItemIndex, 80);
}
if (timelineScroll.bottom < 16 && !roomTimeline.canPaginateForward()) {
const readUpToId = roomTimeline.getReadUpToEventId();
if (readUptoEvtStore.getItem()?.getId() === readUpToId || readUpToId === null) {
requestAnimationFrame(() => markAsRead(roomTimeline.roomId));
}
}
jumpToItemIndex = -1;
}
autoPaginate();
2021-08-04 05:52:59 -04:00
roomTimeline.on(cons.events.roomTimeline.SCROLL_TO_LIVE, handleScrollToLive);
return () => {
if (timelineSVRef.current === null) return;
roomTimeline.removeListener(cons.events.roomTimeline.SCROLL_TO_LIVE, handleScrollToLive);
};
}, [timelineInfo]);
// when paginating from server
useEffect(() => {
if (!roomTimeline.initialized) return;
const timelineScroll = timelineScrollRef.current;
timelineScroll.tryRestoringScroll();
autoPaginate();
}, [paginateInfo]);
// when paginating locally
useEffect(() => {
if (!roomTimeline.initialized) return;
const timelineScroll = timelineScrollRef.current;
timelineScroll.tryRestoringScroll();
}, [onLimitUpdate]);
useEffect(() => {
const timelineScroll = timelineScrollRef.current;
if (!roomTimeline.initialized) return;
if (timelineScroll.bottom < 16 && !roomTimeline.canPaginateForward() && document.visibilityState === 'visible') {
timelineScroll.scrollToBottom();
} else {
timelineScroll.tryRestoringScroll();
}
}, [newEvent]);
Refactor state & Custom editor (#1190) * Fix eslint * Enable ts strict mode * install folds, jotai & immer * Enable immer map/set * change cross-signing alert anim to 30 iteration * Add function to access matrix client * Add new types * Add disposable util * Add room utils * Add mDirect list atom * Add invite list atom * add room list atom * add utils for jotai atoms * Add room id to parents atom * Add mute list atom * Add room to unread atom * Use hook to bind atoms with sdk * Add settings atom * Add settings hook * Extract set settings hook * Add Sidebar components * WIP * Add bind atoms hook * Fix init muted room list atom * add navigation atoms * Add custom editor * Fix hotkeys * Update folds * Add editor output function * Add matrix client context * Add tooltip to editor toolbar items * WIP - Add editor to room input * Refocus editor on toolbar item click * Add Mentions - WIP * update folds * update mention focus outline * rename emoji element type * Add auto complete menu * add autocomplete query functions * add index file for editor * fix bug in getPrevWord function * Show room mention autocomplete * Add async search function * add use async search hook * use async search in room mention autocomplete * remove folds prefer font for now * allow number array in async search * reset search with empty query * Autocomplete unknown room mention * Autocomplete first room mention on tab * fix roomAliasFromQueryText * change mention color to primary * add isAlive hook * add getMxIdLocalPart to mx utils * fix getRoomAvatarUrl size * fix types * add room members hook * fix bug in room mention * add user mention autocomplete * Fix async search giving prev result after no match * update folds * add twemoji font * add use state provider hook * add prevent scroll with arrow key util * add ts to custom-emoji and emoji files * add types * add hook for emoji group labels * add hook for emoji group icons * add emoji board with basic emoji * add emojiboard in room input * select multiple emoji with shift press * display custom emoji in emojiboard * Add emoji preview * focus element on hover * update folds * position emojiboard properly * convert recent-emoji.js to ts * add use recent emoji hook * add io.element.recent_emoji to account data evt * Render recent emoji in emoji board * show custom emoji from parent spaces * show room emoji * improve emoji sidebar * update folds * fix pack avatar and name fallback in emoji board * add stickers to emoji board * fix bug in emoji preview * Add sticker icon in room input * add debounce hook * add search in emoji board * Optimize emoji board * fix emoji board sidebar divider * sync emojiboard sidebar with scroll & update ui * Add use throttle hook * support custom emoji in editor * remove duplicate emoji selection function * fix emoji and mention spacing * add emoticon autocomplete in editor * fix string * makes emoji size relative to font size in editor * add option to render link element * add spoiler in editor * fix sticker in emoji board search using wrong type * render custom placeholder * update hotkey for block quote and block code * add terminate search function in async search * add getImageInfo to matrix utils * send stickers * add resize observer hook * move emoji board component hooks in hooks dir * prevent editor expand hides room timeline * send typing notifications * improve emoji style and performance * fix imports * add on paste param to editor * add selectFile utils * add file picker hook * add file paste handler hook * add file drop handler * update folds * Add file upload card * add bytes to size util * add blurHash util * add await to js lib * add browser-encrypt-attachment types * add list atom * convert mimetype file to ts * add matrix types * add matrix file util * add file related dom utils * add common utils * add upload atom * add room input draft atom * add upload card renderer component * add upload board component * add support for file upload in editor * send files with message / enter * fix circular deps * store editor toolbar state in local store * move msg content util to separate file * store msg draft on room switch * fix following member not updating on msg sent * add theme for folds component * fix system default theme * Add reply support in editor * prevent initMatrix to init multiple time * add state event hooks * add async callback hook * Show tombstone info for tombstone room * fix room tombstone component border * add power level hook * Add room input placeholder component * Show input placeholder for muted member
2023-06-12 07:15:23 -04:00
useResizeObserver(
roomInputRef.current,
useCallback((entries) => {
if (!roomInputRef.current) return;
const editorBaseEntry = getResizeObserverEntry(roomInputRef.current, entries);
if (!editorBaseEntry) return;
const timelineScroll = timelineScrollRef.current;
if (!roomTimeline.initialized) return;
if (timelineScroll.bottom < 40 && !roomTimeline.canPaginateForward() && document.visibilityState === 'visible') {
timelineScroll.scrollToBottom();
}
}, [roomInputRef])
);
const listenKeyboard = useCallback((event) => {
if (event.ctrlKey || event.altKey || event.metaKey) return;
if (event.key !== 'ArrowUp') return;
if (navigation.isRawModalVisible) return;
if (document.activeElement.id !== 'message-textarea') return;
if (document.activeElement.value !== '') return;
const {
timeline: tl, activeTimeline, liveTimeline, matrixClient: mx,
} = roomTimeline;
const limit = eventLimitRef.current;
if (activeTimeline !== liveTimeline) return;
if (tl.length > limit.length) return;
const mTypes = ['m.text'];
for (let i = tl.length - 1; i >= 0; i -= 1) {
const mE = tl[i];
if (
mE.getSender() === mx.getUserId()
&& mE.getType() === 'm.room.message'
&& mTypes.includes(mE.getContent()?.msgtype)
) {
setEditEventId(mE.getId());
return;
}
}
}, [roomTimeline]);
useEffect(() => {
document.body.addEventListener('keydown', listenKeyboard);
return () => {
document.body.removeEventListener('keydown', listenKeyboard);
};
}, [listenKeyboard]);
const handleTimelineScroll = (event) => {
const timelineScroll = timelineScrollRef.current;
if (!event.target) return;
throttle._(() => {
const backwards = timelineScroll?.calcScroll();
if (typeof backwards !== 'boolean') return;
handleScroll(backwards);
}, 200)();
};
const renderTimeline = () => {
const tl = [];
const limit = eventLimitRef.current;
let itemCountIndex = 0;
jumpToItemIndex = -1;
const readUptoEvent = readUptoEvtStore.getItem();
let unreadDivider = false;
if (roomTimeline.canPaginateBackward() || limit.from > 0) {
tl.push(loadingMsgPlaceholders(1, PLACEHOLDER_COUNT));
itemCountIndex += PLACEHOLDER_COUNT;
}
for (let i = limit.from; i < limit.length; i += 1) {
if (i >= timeline.length) break;
const mEvent = timeline[i];
const prevMEvent = timeline[i - 1] ?? null;
if (i === 0 && !roomTimeline.canPaginateBackward()) {
if (mEvent.getType() === 'm.room.create') {
2022-05-03 04:35:56 -04:00
tl.push(
<RoomIntroContainer key={mEvent.getId()} event={mEvent} timeline={roomTimeline} />,
);
itemCountIndex += 1;
// eslint-disable-next-line no-continue
continue;
} else {
2022-05-03 04:35:56 -04:00
tl.push(<RoomIntroContainer key="room-intro" event={null} timeline={roomTimeline} />);
itemCountIndex += 1;
}
}
let isNewEvent = false;
if (!unreadDivider) {
unreadDivider = (readUptoEvent
&& prevMEvent?.getTs() <= readUptoEvent.getTs()
&& readUptoEvent.getTs() < mEvent.getTs());
if (unreadDivider) {
isNewEvent = true;
tl.push(<Divider key={`new-${mEvent.getId()}`} variant="positive" text="New messages" />);
itemCountIndex += 1;
if (jumpToItemIndex === -1) jumpToItemIndex = itemCountIndex;
}
}
const dayDivider = prevMEvent && !isInSameDay(mEvent.getDate(), prevMEvent.getDate());
if (dayDivider) {
tl.push(<Divider key={`divider-${mEvent.getId()}`} text={`${dateFormat(mEvent.getDate(), 'mmmm dd, yyyy')}`} />);
itemCountIndex += 1;
}
const focusId = timelineInfo.focusEventId;
const isFocus = focusId === mEvent.getId();
if (isFocus) jumpToItemIndex = itemCountIndex;
tl.push(renderEvent(
roomTimeline,
mEvent,
isNewEvent ? null : prevMEvent,
isFocus,
editEventId === mEvent.getId(),
setEditEventId,
cancelEdit,
));
itemCountIndex += 1;
}
if (roomTimeline.canPaginateForward() || limit.length < timeline.length) {
tl.push(loadingMsgPlaceholders(2, PLACEHOLDER_COUNT));
}
return tl;
};
2021-08-04 05:52:59 -04:00
return (
<ScrollView onScroll={handleTimelineScroll} ref={timelineSVRef} autoHide>
<div className="room-view__content" onClick={handleOnClickCapture}>
<div className="timeline__wrapper">
{ roomTimeline.initialized ? renderTimeline() : loadingMsgPlaceholders('loading', 3) }
</div>
2021-08-04 05:52:59 -04:00
</div>
</ScrollView>
2021-08-04 05:52:59 -04:00
);
}
RoomViewContent.defaultProps = {
eventId: null,
};
2021-08-31 09:13:31 -04:00
RoomViewContent.propTypes = {
eventId: PropTypes.string,
2021-08-04 05:52:59 -04:00
roomTimeline: PropTypes.shape({}).isRequired,
Refactor state & Custom editor (#1190) * Fix eslint * Enable ts strict mode * install folds, jotai & immer * Enable immer map/set * change cross-signing alert anim to 30 iteration * Add function to access matrix client * Add new types * Add disposable util * Add room utils * Add mDirect list atom * Add invite list atom * add room list atom * add utils for jotai atoms * Add room id to parents atom * Add mute list atom * Add room to unread atom * Use hook to bind atoms with sdk * Add settings atom * Add settings hook * Extract set settings hook * Add Sidebar components * WIP * Add bind atoms hook * Fix init muted room list atom * add navigation atoms * Add custom editor * Fix hotkeys * Update folds * Add editor output function * Add matrix client context * Add tooltip to editor toolbar items * WIP - Add editor to room input * Refocus editor on toolbar item click * Add Mentions - WIP * update folds * update mention focus outline * rename emoji element type * Add auto complete menu * add autocomplete query functions * add index file for editor * fix bug in getPrevWord function * Show room mention autocomplete * Add async search function * add use async search hook * use async search in room mention autocomplete * remove folds prefer font for now * allow number array in async search * reset search with empty query * Autocomplete unknown room mention * Autocomplete first room mention on tab * fix roomAliasFromQueryText * change mention color to primary * add isAlive hook * add getMxIdLocalPart to mx utils * fix getRoomAvatarUrl size * fix types * add room members hook * fix bug in room mention * add user mention autocomplete * Fix async search giving prev result after no match * update folds * add twemoji font * add use state provider hook * add prevent scroll with arrow key util * add ts to custom-emoji and emoji files * add types * add hook for emoji group labels * add hook for emoji group icons * add emoji board with basic emoji * add emojiboard in room input * select multiple emoji with shift press * display custom emoji in emojiboard * Add emoji preview * focus element on hover * update folds * position emojiboard properly * convert recent-emoji.js to ts * add use recent emoji hook * add io.element.recent_emoji to account data evt * Render recent emoji in emoji board * show custom emoji from parent spaces * show room emoji * improve emoji sidebar * update folds * fix pack avatar and name fallback in emoji board * add stickers to emoji board * fix bug in emoji preview * Add sticker icon in room input * add debounce hook * add search in emoji board * Optimize emoji board * fix emoji board sidebar divider * sync emojiboard sidebar with scroll & update ui * Add use throttle hook * support custom emoji in editor * remove duplicate emoji selection function * fix emoji and mention spacing * add emoticon autocomplete in editor * fix string * makes emoji size relative to font size in editor * add option to render link element * add spoiler in editor * fix sticker in emoji board search using wrong type * render custom placeholder * update hotkey for block quote and block code * add terminate search function in async search * add getImageInfo to matrix utils * send stickers * add resize observer hook * move emoji board component hooks in hooks dir * prevent editor expand hides room timeline * send typing notifications * improve emoji style and performance * fix imports * add on paste param to editor * add selectFile utils * add file picker hook * add file paste handler hook * add file drop handler * update folds * Add file upload card * add bytes to size util * add blurHash util * add await to js lib * add browser-encrypt-attachment types * add list atom * convert mimetype file to ts * add matrix types * add matrix file util * add file related dom utils * add common utils * add upload atom * add room input draft atom * add upload card renderer component * add upload board component * add support for file upload in editor * send files with message / enter * fix circular deps * store editor toolbar state in local store * move msg content util to separate file * store msg draft on room switch * fix following member not updating on msg sent * add theme for folds component * fix system default theme * Add reply support in editor * prevent initMatrix to init multiple time * add state event hooks * add async callback hook * Show tombstone info for tombstone room * fix room tombstone component border * add power level hook * Add room input placeholder component * Show input placeholder for muted member
2023-06-12 07:15:23 -04:00
roomInputRef: PropTypes.shape({
current: PropTypes.shape({})
}).isRequired
2021-08-04 05:52:59 -04:00
};
2021-08-31 09:13:31 -04:00
export default RoomViewContent;