From cd04799cb49689fd6c94f6775e4f399c95579961 Mon Sep 17 00:00:00 2001 From: Germain Date: Wed, 22 Dec 2021 14:08:05 +0000 Subject: [PATCH] Hook thread panel to homeserver API (#7352) --- src/components/structures/ThreadPanel.tsx | 175 +++++++++++++--------- 1 file changed, 103 insertions(+), 72 deletions(-) diff --git a/src/components/structures/ThreadPanel.tsx b/src/components/structures/ThreadPanel.tsx index cdc6c63abb..c17b5eb621 100644 --- a/src/components/structures/ThreadPanel.tsx +++ b/src/components/structures/ThreadPanel.tsx @@ -14,10 +14,17 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; -import { Thread, ThreadEvent } from 'matrix-js-sdk/src/models/thread'; +import React, { useContext, useEffect, useMemo, useRef, useState } from 'react'; import { EventTimelineSet } from 'matrix-js-sdk/src/models/event-timeline-set'; import { Room } from 'matrix-js-sdk/src/models/room'; +import { RelationType } from 'matrix-js-sdk/src/@types/event'; +import { MatrixClient } from 'matrix-js-sdk/src/client'; +import { + Filter, + IFilterDefinition, + UNSTABLE_FILTER_RELATION_SENDERS, + UNSTABLE_FILTER_RELATION_TYPES, +} from 'matrix-js-sdk/src/filter'; import BaseCard from "../views/right_panel/BaseCard"; import ResizeNotifier from '../../utils/ResizeNotifier'; @@ -28,10 +35,65 @@ import ContextMenu, { ChevronFace, MenuItemRadio, useContextMenu } from './Conte import RoomContext, { TimelineRenderingType } from '../../contexts/RoomContext'; import TimelinePanel from './TimelinePanel'; import { Layout } from '../../settings/enums/Layout'; -import { useEventEmitter } from '../../hooks/useEventEmitter'; import { TileShape } from '../views/rooms/EventTile'; import { RoomPermalinkCreator } from '../../utils/permalinks/Permalinks'; +async function getThreadTimelineSet( + client: MatrixClient, + room: Room, + filterType = ThreadFilterType.All, +): Promise { + const myUserId = client.getUserId(); + const filter = new Filter(myUserId); + + const definition: IFilterDefinition = { + "room": { + "timeline": { + [UNSTABLE_FILTER_RELATION_TYPES.name]: [RelationType.Thread], + }, + }, + }; + + if (filterType === ThreadFilterType.My) { + definition.room.timeline[UNSTABLE_FILTER_RELATION_SENDERS.name] = [myUserId]; + } + + filter.setDefinition(definition); + + let timelineSet; + + try { + const filterId = await client.getOrCreateFilter( + `THREAD_PANEL_${room.roomId}_${filterType}`, + filter, + ); + filter.filterId = filterId; + timelineSet = room.getOrCreateFilteredTimelineSet( + filter, + { prepopulateTimeline: false }, + ); + + timelineSet.resetLiveTimeline(); + await client.paginateEventTimeline( + timelineSet.getLiveTimeline(), + { backwards: true, limit: 20 }, + ); + } catch (e) { + // Filter creation fails if HomeServer does not support the new relation + // filter fields. We fallback to the threads that have been discovered in + // the main timeline + timelineSet = new EventTimelineSet(room, {}); + for (const [, thread] of room.threads) { + const isOwnEvent = thread.rootEvent.getSender() === client.getUserId(); + if (filterType !== ThreadFilterType.My || isOwnEvent) { + timelineSet.getLiveTimeline().addEvent(thread.rootEvent); + } + } + } + + return timelineSet; +} + interface IProps { roomId: string; onClose: () => void; @@ -50,44 +112,6 @@ type ThreadPanelHeaderOption = { key: ThreadFilterType; }; -const useFilteredThreadsTimelinePanel = ({ - threads, - room, - filterOption, - userId, - updateTimeline, -}: { - threads: Map; - room: Room; - userId: string; - filterOption: ThreadFilterType; - updateTimeline: () => void; -}) => { - const timelineSet = useMemo(() => new EventTimelineSet(null, { - timelineSupport: true, - unstableClientRelationAggregation: true, - pendingEvents: false, - }), []); - - const buildThreadList = useCallback(function(timelineSet: EventTimelineSet) { - timelineSet.resetLiveTimeline(""); - Array.from(threads) - .forEach(([, thread]) => { - if (filterOption !== ThreadFilterType.My || thread.hasCurrentUserParticipated) { - timelineSet.addLiveEvent(thread.rootEvent); - } - }); - updateTimeline(); - }, [filterOption, threads, updateTimeline]); - - useEffect(() => { buildThreadList(timelineSet); }, [timelineSet, buildThreadList]); - - useEventEmitter(room, ThreadEvent.Update, () => { buildThreadList(timelineSet); }); - useEventEmitter(room, ThreadEvent.New, () => { buildThreadList(timelineSet); }); - - return timelineSet; -}; - export const ThreadPanelHeaderFilterOptionItem = ({ label, description, @@ -185,19 +209,24 @@ const ThreadPanel: React.FC = ({ roomId, onClose, permalinkCreator }) => const [filterOption, setFilterOption] = useState(ThreadFilterType.All); const ref = useRef(); - const filteredTimelineSet = useFilteredThreadsTimelinePanel({ - threads: room.threads, - room, - filterOption, - userId: mxClient.getUserId(), - updateTimeline: () => ref.current?.refreshTimeline(), - }); + const [timelineSet, setTimelineSet] = useState(null); + const timelineSetPromise = useMemo( + async () => { + const timelineSet = getThreadTimelineSet(mxClient, room, filterOption); + return timelineSet; + }, + [mxClient, room, filterOption], + ); + useEffect(() => { + timelineSetPromise + .then(timelineSet => { setTimelineSet(timelineSet); }) + .catch(() => setTimelineSet(null)); + }, [timelineSetPromise]); return ( = ({ roomId, onClose, permalinkCreator }) => onClose={onClose} withoutScrollContainer={true} > - setFilterOption(ThreadFilterType.All)} - />} - alwaysShowTimestamps={true} - layout={Layout.Group} - hideThreadedMessages={false} - hidden={false} - showReactions={true} - className="mx_RoomView_messagePanel mx_GroupLayout" - membersLoaded={true} - permalinkCreator={permalinkCreator} - tileShape={TileShape.ThreadPanel} - disableGrouping={true} - /> + { timelineSet && ( + setFilterOption(ThreadFilterType.All)} + />} + alwaysShowTimestamps={true} + layout={Layout.Group} + hideThreadedMessages={false} + hidden={false} + showReactions={false} + className="mx_RoomView_messagePanel mx_GroupLayout" + membersLoaded={true} + permalinkCreator={permalinkCreator} + tileShape={TileShape.ThreadPanel} + disableGrouping={true} + /> + ) } );