Hook thread panel to homeserver API (#7352)

This commit is contained in:
Germain 2021-12-22 14:08:05 +00:00 committed by GitHub
parent b4755f38b9
commit cd04799cb4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -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<EventTimelineSet> {
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<string, Thread>;
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<IProps> = ({ roomId, onClose, permalinkCreator }) =>
const [filterOption, setFilterOption] = useState<ThreadFilterType>(ThreadFilterType.All);
const ref = useRef<TimelinePanel>();
const filteredTimelineSet = useFilteredThreadsTimelinePanel({
threads: room.threads,
room,
filterOption,
userId: mxClient.getUserId(),
updateTimeline: () => ref.current?.refreshTimeline(),
});
const [timelineSet, setTimelineSet] = useState<EventTimelineSet | null>(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 (
<RoomContext.Provider value={{
...roomContext,
timelineRenderingType: TimelineRenderingType.ThreadsList,
liveTimeline: filteredTimelineSet.getLiveTimeline(),
showHiddenEventsInTimeline: true,
}}>
<BaseCard
@ -206,29 +235,31 @@ const ThreadPanel: React.FC<IProps> = ({ roomId, onClose, permalinkCreator }) =>
onClose={onClose}
withoutScrollContainer={true}
>
<TimelinePanel
ref={ref}
showReadReceipts={false} // No RR support in thread's MVP
manageReadReceipts={false} // No RR support in thread's MVP
manageReadMarkers={false} // No RM support in thread's MVP
sendReadReceiptOnLoad={false} // No RR support in thread's MVP
timelineSet={filteredTimelineSet}
showUrlPreview={true}
empty={<EmptyThread
filterOption={filterOption}
showAllThreadsCallback={() => 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 && (
<TimelinePanel
ref={ref}
showReadReceipts={false} // No RR support in thread's MVP
manageReadReceipts={false} // No RR support in thread's MVP
manageReadMarkers={false} // No RM support in thread's MVP
sendReadReceiptOnLoad={false} // No RR support in thread's MVP
timelineSet={timelineSet}
showUrlPreview={true}
empty={<EmptyThread
filterOption={filterOption}
showAllThreadsCallback={() => 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}
/>
) }
</BaseCard>
</RoomContext.Provider>
);