Respect tombstones in locally known rooms for Space children

This commit is contained in:
Michael Telatynski 2021-10-06 09:41:57 +01:00
parent b86d646fa7
commit fa800796c7
2 changed files with 61 additions and 13 deletions

View file

@ -15,17 +15,17 @@ limitations under the License.
*/ */
import React, { import React, {
Dispatch,
KeyboardEvent,
KeyboardEventHandler,
ReactNode, ReactNode,
SetStateAction,
useCallback, useCallback,
useContext,
useEffect, useEffect,
useMemo, useMemo,
useRef, useRef,
useState, useState,
KeyboardEvent,
KeyboardEventHandler,
useContext,
SetStateAction,
Dispatch,
} from "react"; } from "react";
import { Room } from "matrix-js-sdk/src/models/room"; import { Room } from "matrix-js-sdk/src/models/room";
import { RoomHierarchy } from "matrix-js-sdk/src/room-hierarchy"; import { RoomHierarchy } from "matrix-js-sdk/src/room-hierarchy";
@ -33,7 +33,8 @@ import { EventType, RoomType } from "matrix-js-sdk/src/@types/event";
import { IHierarchyRelation, IHierarchyRoom } from "matrix-js-sdk/src/@types/spaces"; import { IHierarchyRelation, IHierarchyRoom } from "matrix-js-sdk/src/@types/spaces";
import { MatrixClient } from "matrix-js-sdk/src/matrix"; import { MatrixClient } from "matrix-js-sdk/src/matrix";
import classNames from "classnames"; import classNames from "classnames";
import { sortBy } from "lodash"; import { sortBy, uniqBy } from "lodash";
import { GuestAccess, HistoryVisibility } from "matrix-js-sdk/src/@types/partials";
import dis from "../../dispatcher/dispatcher"; import dis from "../../dispatcher/dispatcher";
import defaultDispatcher from "../../dispatcher/dispatcher"; import defaultDispatcher from "../../dispatcher/dispatcher";
@ -48,7 +49,7 @@ import { mediaFromMxc } from "../../customisations/Media";
import InfoTooltip from "../views/elements/InfoTooltip"; import InfoTooltip from "../views/elements/InfoTooltip";
import TextWithTooltip from "../views/elements/TextWithTooltip"; import TextWithTooltip from "../views/elements/TextWithTooltip";
import { useStateToggle } from "../../hooks/useStateToggle"; import { useStateToggle } from "../../hooks/useStateToggle";
import { getChildOrder } from "../../stores/SpaceStore"; import SpaceStore, { getChildOrder } from "../../stores/SpaceStore";
import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton"; import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
import { linkifyElement } from "../../HtmlUtils"; import { linkifyElement } from "../../HtmlUtils";
import { useDispatcher } from "../../hooks/useDispatcher"; import { useDispatcher } from "../../hooks/useDispatcher";
@ -333,6 +334,29 @@ interface IHierarchyLevelProps {
onToggleClick?(parentId: string, childId: string): void; onToggleClick?(parentId: string, childId: string): void;
} }
const toLocalRoom = (cli: MatrixClient, room: IHierarchyRoom, upgradedRoomMap: Map<string, string>): IHierarchyRoom => {
const cliRoom = cli.getRoom(SpaceStore.instance.findMostUpgradedVersion(room.room_id, upgradedRoomMap));
if (cliRoom) {
return {
...room,
room_id: cliRoom.roomId,
room_type: cliRoom.getType(),
name: cliRoom.name,
topic: cliRoom.currentState.getStateEvents(EventType.RoomTopic, "")?.getContent().topic,
avatar_url: cliRoom.getMxcAvatarUrl(),
canonical_alias: cliRoom.getCanonicalAlias(),
aliases: cliRoom.getAltAliases(),
world_readable: cliRoom.currentState.getStateEvents(EventType.RoomHistoryVisibility, "")?.getContent()
.history_visibility === HistoryVisibility.WorldReadable,
guest_can_join: cliRoom.currentState.getStateEvents(EventType.RoomGuestAccess, "")?.getContent()
.guest_access === GuestAccess.CanJoin,
num_joined_members: cliRoom.getJoinedMemberCount(),
};
}
return room;
};
export const HierarchyLevel = ({ export const HierarchyLevel = ({
root, root,
roomSet, roomSet,
@ -350,10 +374,11 @@ export const HierarchyLevel = ({
return getChildOrder(ev.content.order, ev.origin_server_ts, ev.state_key); return getChildOrder(ev.content.order, ev.origin_server_ts, ev.state_key);
}); });
const upgradedRoomMap = new Map<string, string>();
const [subspaces, childRooms] = sortedChildren.reduce((result, ev: IHierarchyRelation) => { const [subspaces, childRooms] = sortedChildren.reduce((result, ev: IHierarchyRelation) => {
const room = hierarchy.roomMap.get(ev.state_key); const room = hierarchy.roomMap.get(ev.state_key);
if (room && roomSet.has(room)) { if (room && roomSet.has(room)) {
result[room.room_type === RoomType.Space ? 0 : 1].push(room); result[room.room_type === RoomType.Space ? 0 : 1].push(toLocalRoom(cli, room, upgradedRoomMap));
} }
return result; return result;
}, [[] as IHierarchyRoom[], [] as IHierarchyRoom[]]); }, [[] as IHierarchyRoom[], [] as IHierarchyRoom[]]);
@ -361,7 +386,7 @@ export const HierarchyLevel = ({
const newParents = new Set(parents).add(root.room_id); const newParents = new Set(parents).add(root.room_id);
return <React.Fragment> return <React.Fragment>
{ {
childRooms.map(room => ( uniqBy(childRooms, "room_id").map(room => (
<Tile <Tile
key={room.room_id} key={room.room_id}
room={room} room={room}

View file

@ -283,7 +283,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
const createTs = childRoom?.currentState.getStateEvents(EventType.RoomCreate, "")?.getTs(); const createTs = childRoom?.currentState.getStateEvents(EventType.RoomCreate, "")?.getTs();
return getChildOrder(ev.getContent().order, createTs, roomId); return getChildOrder(ev.getContent().order, createTs, roomId);
}).map(ev => { }).map(ev => {
return this.matrixClient.getRoom(ev.getStateKey()); return this.matrixClient.getRoom(this.findMostUpgradedVersion(ev.getStateKey()));
}).filter(room => { }).filter(room => {
return room?.getMyMembership() === "join" || room?.getMyMembership() === "invite"; return room?.getMyMembership() === "join" || room?.getMyMembership() === "invite";
}) || []; }) || [];
@ -452,6 +452,28 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
this.onRoomsUpdate(); this.onRoomsUpdate();
}; };
// Utility to walk tombstones and find the most updated variant of the given room,
// takes a Map to enable caching of the responses given the recursive nature of the function.
public findMostUpgradedVersion(
roomId: string,
upgradedRoomMap?: Map<string, string>,
seen= new Set<string>(),
): string {
if (seen.has(roomId)) return roomId;
if (upgradedRoomMap?.has(roomId)) return upgradedRoomMap.get(roomId);
const room = this.matrixClient.getRoom(roomId);
const tombstone = room?.currentState.getStateEvents(EventType.RoomTombstone, "");
const replacementRoom = tombstone?.getContent().replacement_room;
if (replacementRoom && this.matrixClient.getRoom(replacementRoom)?.getMyMembership() === "join") {
seen.add(roomId);
const result = this.findMostUpgradedVersion(replacementRoom, upgradedRoomMap);
upgradedRoomMap?.set(roomId, result);
return result;
}
upgradedRoomMap?.set(roomId, roomId);
return roomId;
}
private onRoomsUpdate = throttle(() => { private onRoomsUpdate = throttle(() => {
// TODO resolve some updates as deltas // TODO resolve some updates as deltas
const visibleRooms = this.matrixClient.getVisibleRooms(); const visibleRooms = this.matrixClient.getVisibleRooms();
@ -479,6 +501,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
}); });
}); });
const upgradedRoomMap = new Map<string, string>();
this.rootSpaces.forEach(s => { this.rootSpaces.forEach(s => {
// traverse each space tree in DFS to build up the supersets as you go up, // traverse each space tree in DFS to build up the supersets as you go up,
// reusing results from like subtrees. // reusing results from like subtrees.
@ -491,7 +514,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
} }
const [childSpaces, childRooms] = partitionSpacesAndRooms(this.getChildren(spaceId)); const [childSpaces, childRooms] = partitionSpacesAndRooms(this.getChildren(spaceId));
const roomIds = new Set(childRooms.map(r => r.roomId)); const roomIds = new Set(childRooms.map(r => this.findMostUpgradedVersion(r.roomId, upgradedRoomMap)));
const space = this.matrixClient?.getRoom(spaceId); const space = this.matrixClient?.getRoom(spaceId);
// Add relevant DMs // Add relevant DMs
@ -505,11 +528,11 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
const newPath = new Set(parentPath).add(spaceId); const newPath = new Set(parentPath).add(spaceId);
childSpaces.forEach(childSpace => { childSpaces.forEach(childSpace => {
fn(childSpace.roomId, newPath)?.forEach(roomId => { fn(childSpace.roomId, newPath)?.forEach(roomId => {
roomIds.add(roomId); roomIds.add(this.findMostUpgradedVersion(roomId, upgradedRoomMap));
}); });
}); });
hiddenChildren.get(spaceId)?.forEach(roomId => { hiddenChildren.get(spaceId)?.forEach(roomId => {
roomIds.add(roomId); roomIds.add(this.findMostUpgradedVersion(roomId, upgradedRoomMap));
}); });
this.spaceFilteredRooms.set(spaceId, roomIds); this.spaceFilteredRooms.set(spaceId, roomIds);
return roomIds; return roomIds;