Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Support dynamic room predecessors in SpaceStore #10332

Merged
merged 1 commit into from
Mar 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 38 additions & 9 deletions src/stores/spaces/SpaceStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,13 +144,16 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
// The following properties are set by onReady as they live in account_data
private _allRoomsInHome = false;
private _enabledMetaSpaces: MetaSpace[] = [];
/** Whether the feature flag is set for MSC3946 */
private _msc3946ProcessDynamicPredecessor: boolean = SettingsStore.getValue("feature_dynamic_room_predecessors");

public constructor() {
super(defaultDispatcher, {});

SettingsStore.monitorSetting("Spaces.allRoomsInHome", null);
SettingsStore.monitorSetting("Spaces.enabledMetaSpaces", null);
SettingsStore.monitorSetting("Spaces.showPeopleInSpace", null);
SettingsStore.monitorSetting("feature_dynamic_room_predecessors", null);
}

public get invitedSpaces(): Room[] {
Expand Down Expand Up @@ -352,7 +355,11 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
return getChildOrder(ev.getContent().order, ev.getTs(), ev.getStateKey()!);
})
.map((ev) => {
const history = this.matrixClient.getRoomUpgradeHistory(ev.getStateKey()!, true);
const history = this.matrixClient.getRoomUpgradeHistory(
ev.getStateKey()!,
true,
this._msc3946ProcessDynamicPredecessor,
);
return history[history.length - 1];
})
.filter((room) => {
Expand Down Expand Up @@ -450,7 +457,9 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
useCache = true,
): Set<string> => {
if (space === MetaSpace.Home && this.allRoomsInHome) {
return new Set(this.matrixClient.getVisibleRooms().map((r) => r.roomId));
return new Set(
this.matrixClient.getVisibleRooms(this._msc3946ProcessDynamicPredecessor).map((r) => r.roomId),
);
}

// meta spaces never have descendants
Expand Down Expand Up @@ -539,7 +548,9 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
};

private rebuildSpaceHierarchy = (): void => {
const visibleSpaces = this.matrixClient.getVisibleRooms().filter((r) => r.isSpaceRoom());
const visibleSpaces = this.matrixClient
.getVisibleRooms(this._msc3946ProcessDynamicPredecessor)
.filter((r) => r.isSpaceRoom());
const [joinedSpaces, invitedSpaces] = visibleSpaces.reduce(
([joined, invited], s) => {
switch (getEffectiveMembership(s.getMyMembership())) {
Expand Down Expand Up @@ -573,7 +584,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
};

private rebuildParentMap = (): void => {
const joinedSpaces = this.matrixClient.getVisibleRooms().filter((r) => {
const joinedSpaces = this.matrixClient.getVisibleRooms(this._msc3946ProcessDynamicPredecessor).filter((r) => {
return r.isSpaceRoom() && r.getMyMembership() === "join";
});

Expand All @@ -595,7 +606,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
} else {
const rooms = new Set(
this.matrixClient
.getVisibleRooms()
.getVisibleRooms(this._msc3946ProcessDynamicPredecessor)
.filter(this.showInHomeSpace)
.map((r) => r.roomId),
);
Expand All @@ -609,7 +620,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {

private rebuildMetaSpaces = (): void => {
const enabledMetaSpaces = new Set(this.enabledMetaSpaces);
const visibleRooms = this.matrixClient.getVisibleRooms();
const visibleRooms = this.matrixClient.getVisibleRooms(this._msc3946ProcessDynamicPredecessor);

if (enabledMetaSpaces.has(MetaSpace.Home)) {
this.rebuildHomeSpace();
Expand Down Expand Up @@ -643,7 +654,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {

private updateNotificationStates = (spaces?: SpaceKey[]): void => {
const enabledMetaSpaces = new Set(this.enabledMetaSpaces);
const visibleRooms = this.matrixClient.getVisibleRooms();
const visibleRooms = this.matrixClient.getVisibleRooms(this._msc3946ProcessDynamicPredecessor);

let dmBadgeSpace: MetaSpace | undefined;
// only show badges on dms on the most relevant space if such exists
Expand Down Expand Up @@ -729,7 +740,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
};

private onRoomsUpdate = (): void => {
const visibleRooms = this.matrixClient.getVisibleRooms();
const visibleRooms = this.matrixClient.getVisibleRooms(this._msc3946ProcessDynamicPredecessor);

const prevRoomsBySpace = this.roomIdsBySpace;
const prevUsersBySpace = this.userIdsBySpace;
Expand Down Expand Up @@ -792,7 +803,9 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
// Expand room IDs to all known versions of the given rooms
const expandedRoomIds = new Set(
Array.from(roomIds).flatMap((roomId) => {
return this.matrixClient.getRoomUpgradeHistory(roomId, true).map((r) => r.roomId);
return this.matrixClient
.getRoomUpgradeHistory(roomId, true, this._msc3946ProcessDynamicPredecessor)
.map((r) => r.roomId);
}),
);

Expand Down Expand Up @@ -1275,6 +1288,13 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
this.updateNotificationStates([payload.roomId]);
}
break;

case "feature_dynamic_room_predecessors":
this._msc3946ProcessDynamicPredecessor = SettingsStore.getValue(
"feature_dynamic_room_predecessors",
);
this.rebuildSpaceHierarchy();
break;
}
}
}
Expand Down Expand Up @@ -1355,6 +1375,15 @@ export default class SpaceStore {
public static get instance(): SpaceStoreClass {
return SpaceStore.internalInstance;
}

/**
* @internal for test only
*/
public static testInstance(): SpaceStoreClass {
const store = new SpaceStoreClass();
store.start();
return store;
}
}

window.mxSpaceStore = SpaceStore.instance;
98 changes: 98 additions & 0 deletions test/stores/SpaceStore-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1331,4 +1331,102 @@ describe("SpaceStore", () => {
expect(metaSpaces).toEqual(store.enabledMetaSpaces);
removeListener();
});

describe("when feature_dynamic_room_predecessors is not enabled", () => {
beforeAll(() => {
jest.spyOn(SettingsStore, "getValue").mockImplementation(
(settingName) => settingName === "Spaces.allRoomsInHome",
);
// @ts-ignore calling a private function
SpaceStore.instance.onAction({
action: Action.SettingUpdated,
settingName: "feature_dynamic_room_predecessors",
roomId: null,
level: SettingLevel.ACCOUNT,
newValueAtLevel: SettingLevel.ACCOUNT,
newValue: false,
});
});

beforeEach(() => {
jest.clearAllMocks();
});

it("passes that value in calls to getVisibleRooms and getRoomUpgradeHistory during startup", async () => {
// When we create an instance, which calls onReady and rebuildSpaceHierarchy
mkSpace("!dynspace:example.com", [mkRoom("!dynroom:example.com").roomId]);
await run();

// Then we pass through the correct value of the feature flag to
// everywhere that needs it.
expect(client.getVisibleRooms).toHaveBeenCalledWith(false);
expect(client.getVisibleRooms).not.toHaveBeenCalledWith(true);
expect(client.getVisibleRooms).not.toHaveBeenCalledWith();
expect(client.getRoomUpgradeHistory).toHaveBeenCalledWith(expect.anything(), expect.anything(), false);
expect(client.getRoomUpgradeHistory).not.toHaveBeenCalledWith(expect.anything(), expect.anything(), true);
expect(client.getRoomUpgradeHistory).not.toHaveBeenCalledWith(expect.anything(), expect.anything());
});

it("passes that value in calls to getVisibleRooms during getSpaceFilteredRoomIds", () => {
// Given a store
const store = SpaceStore.testInstance();

// When we ask for filtered room ids
store.getSpaceFilteredRoomIds(MetaSpace.Home);

// Then we pass the correct feature flag
expect(client.getVisibleRooms).toHaveBeenCalledWith(false);
expect(client.getVisibleRooms).not.toHaveBeenCalledWith(true);
expect(client.getVisibleRooms).not.toHaveBeenCalledWith();
});
});

describe("when feature_dynamic_room_predecessors is enabled", () => {
beforeAll(() => {
jest.spyOn(SettingsStore, "getValue").mockImplementation(
(settingName) =>
settingName === "Spaces.allRoomsInHome" || settingName === "feature_dynamic_room_predecessors",
);
// @ts-ignore calling a private function
SpaceStore.instance.onAction({
action: Action.SettingUpdated,
settingName: "feature_dynamic_room_predecessors",
roomId: null,
level: SettingLevel.ACCOUNT,
newValueAtLevel: SettingLevel.ACCOUNT,
newValue: true,
});
});

beforeEach(() => {
jest.clearAllMocks();
});

it("passes that value in calls to getVisibleRooms and getRoomUpgradeHistory during startup", async () => {
// When we create an instance, which calls onReady and rebuildSpaceHierarchy
mkSpace("!dynspace:example.com", [mkRoom("!dynroom:example.com").roomId]);
await run();

// Then we pass through the correct value of the feature flag to
// everywhere that needs it.
expect(client.getVisibleRooms).toHaveBeenCalledWith(true);
expect(client.getVisibleRooms).not.toHaveBeenCalledWith(false);
expect(client.getVisibleRooms).not.toHaveBeenCalledWith();
expect(client.getRoomUpgradeHistory).toHaveBeenCalledWith(expect.anything(), expect.anything(), true);
expect(client.getRoomUpgradeHistory).not.toHaveBeenCalledWith(expect.anything(), expect.anything(), false);
expect(client.getRoomUpgradeHistory).not.toHaveBeenCalledWith(expect.anything(), expect.anything());
});

it("passes that value in calls to getVisibleRooms during getSpaceFilteredRoomIds", () => {
// Given a store
const store = SpaceStore.testInstance();
// When we ask for filtered room ids
store.getSpaceFilteredRoomIds(MetaSpace.Home);

// Then we pass the correct feature flag
expect(client.getVisibleRooms).toHaveBeenCalledWith(true);
expect(client.getVisibleRooms).not.toHaveBeenCalledWith(false);
expect(client.getVisibleRooms).not.toHaveBeenCalledWith();
});
});
});