Respect space ordering field in m.tag for top level spaces

This commit is contained in:
Michael Telatynski 2021-06-02 16:43:38 +01:00
parent da13ec16f3
commit 079a5c10ad
2 changed files with 31 additions and 6 deletions

View file

@ -39,7 +39,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 {getOrder} from "../../stores/SpaceStore"; import {getChildOrder} from "../../stores/SpaceStore";
import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton"; import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
import {linkifyElement} from "../../HtmlUtils"; import {linkifyElement} from "../../HtmlUtils";
@ -286,7 +286,7 @@ export const HierarchyLevel = ({
const children = Array.from(relations.get(spaceId)?.values() || []); const children = Array.from(relations.get(spaceId)?.values() || []);
const sortedChildren = sortBy(children, ev => { const sortedChildren = sortBy(children, ev => {
// XXX: Space Summary API doesn't give the child origin_server_ts but once it does we should use it for sorting // XXX: Space Summary API doesn't give the child origin_server_ts but once it does we should use it for sorting
return getOrder(ev.content.order, null, ev.state_key); return getChildOrder(ev.content.order, null, ev.state_key);
}); });
const [subspaces, childRooms] = sortedChildren.reduce((result, ev: ISpaceSummaryEvent) => { const [subspaces, childRooms] = sortedChildren.reduce((result, ev: ISpaceSummaryEvent) => {
const roomId = ev.state_key; const roomId = ev.state_key;

View file

@ -33,6 +33,7 @@ import {EnhancedMap, mapDiff} from "../utils/maps";
import {setHasDiff} from "../utils/sets"; import {setHasDiff} from "../utils/sets";
import {ISpaceSummaryEvent, ISpaceSummaryRoom} from "../components/structures/SpaceRoomDirectory"; import {ISpaceSummaryEvent, ISpaceSummaryRoom} from "../components/structures/SpaceRoomDirectory";
import RoomViewStore from "./RoomViewStore"; import RoomViewStore from "./RoomViewStore";
import { arrayHasOrderChange } from "../utils/arrays";
interface IState {} interface IState {}
@ -60,8 +61,16 @@ const partitionSpacesAndRooms = (arr: Room[]): [Room[], Room[]] => { // [spaces,
}, [[], []]); }, [[], []]);
}; };
const SpaceTagOrderingField = "org.matrix.mscXXXX.space";
const getSpaceTagOrdering = (space: Room): number | undefined => {
return space?.getAccountData(EventType.Tag)?.getContent()?.tags?.[SpaceTagOrderingField]?.order;
};
const sortRootSpaces = (spaces: Room[]): Room[] => sortBy(spaces, [getSpaceTagOrdering, "roomId"]);
// For sorting space children using a validated `order`, `m.room.create`'s `origin_server_ts`, `room_id` // For sorting space children using a validated `order`, `m.room.create`'s `origin_server_ts`, `room_id`
export const getOrder = (order: string, creationTs: number, roomId: string): Array<Many<ListIteratee<any>>> => { export const getChildOrder = (order: string, creationTs: number, roomId: string): Array<Many<ListIteratee<any>>> => {
let validatedOrder: string = null; let validatedOrder: string = null;
if (typeof order === "string" && Array.from(order).every((c: string) => { if (typeof order === "string" && Array.from(order).every((c: string) => {
@ -214,7 +223,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
const roomId = ev.getStateKey(); const roomId = ev.getStateKey();
const childRoom = this.matrixClient?.getRoom(roomId); const childRoom = this.matrixClient?.getRoom(roomId);
const createTs = childRoom?.currentState.getStateEvents(EventType.RoomCreate, "")?.getTs(); const createTs = childRoom?.currentState.getStateEvents(EventType.RoomCreate, "")?.getTs();
return getOrder(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(ev.getStateKey());
}).filter(room => { }).filter(room => {
@ -326,7 +335,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
// rootSpaces.push(space); // rootSpaces.push(space);
// }); // });
this.rootSpaces = rootSpaces; this.rootSpaces = sortRootSpaces(rootSpaces);
this.parentMap = backrefs; this.parentMap = backrefs;
// if the currently selected space no longer exists, remove its selection // if the currently selected space no longer exists, remove its selection
@ -338,7 +347,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
this.emit(UPDATE_TOP_LEVEL_SPACES, this.spacePanelSpaces); this.emit(UPDATE_TOP_LEVEL_SPACES, this.spacePanelSpaces);
// build initial state of invited spaces as we would have missed the emitted events about the room at launch // build initial state of invited spaces as we would have missed the emitted events about the room at launch
this._invitedSpaces = new Set(invitedSpaces); this._invitedSpaces = new Set(sortRootSpaces(invitedSpaces));
this.emit(UPDATE_INVITED_SPACES, this.invitedSpaces); this.emit(UPDATE_INVITED_SPACES, this.invitedSpaces);
}, 100, {trailing: true, leading: true}); }, 100, {trailing: true, leading: true});
@ -472,6 +481,20 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
} }
}; };
private onRoomAccountData = (ev: MatrixEvent, room: Room, lastEv?: MatrixEvent) => {
if (!room.isSpaceRoom() || ev.getType() !== EventType.Tag) return;
const order = ev.getContent()?.tags?.[SpaceTagOrderingField]?.order;
const lastOrder = lastEv?.getContent()?.tags?.[SpaceTagOrderingField]?.order;
if (order !== lastOrder) {
const rootSpaces = sortRootSpaces(this.rootSpaces);
if (arrayHasOrderChange(this.rootSpaces, rootSpaces)) {
this.rootSpaces = rootSpaces;
this.emit(UPDATE_TOP_LEVEL_SPACES, this.spacePanelSpaces);
}
}
};
private onRoomState = (ev: MatrixEvent) => { private onRoomState = (ev: MatrixEvent) => {
const room = this.matrixClient.getRoom(ev.getRoomId()); const room = this.matrixClient.getRoom(ev.getRoomId());
if (!room) return; if (!room) return;
@ -516,6 +539,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
if (this.matrixClient) { if (this.matrixClient) {
this.matrixClient.removeListener("Room", this.onRoom); this.matrixClient.removeListener("Room", this.onRoom);
this.matrixClient.removeListener("Room.myMembership", this.onRoom); this.matrixClient.removeListener("Room.myMembership", this.onRoom);
this.matrixClient.removeListener("Room.accountData", this.onRoomAccountData);
this.matrixClient.removeListener("RoomState.events", this.onRoomState); this.matrixClient.removeListener("RoomState.events", this.onRoomState);
} }
await this.reset(); await this.reset();
@ -525,6 +549,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
if (!SettingsStore.getValue("feature_spaces")) return; if (!SettingsStore.getValue("feature_spaces")) return;
this.matrixClient.on("Room", this.onRoom); this.matrixClient.on("Room", this.onRoom);
this.matrixClient.on("Room.myMembership", this.onRoom); this.matrixClient.on("Room.myMembership", this.onRoom);
this.matrixClient.on("Room.accountData", this.onRoomAccountData);
this.matrixClient.on("RoomState.events", this.onRoomState); this.matrixClient.on("RoomState.events", this.onRoomState);
await this.onSpaceUpdate(); // trigger an initial update await this.onSpaceUpdate(); // trigger an initial update