Skip to content

Commit

Permalink
Support dynamic room predecessors in SpaceStore (#10332)
Browse files Browse the repository at this point in the history
  • Loading branch information
andybalaam committed Mar 9, 2023
1 parent 01d7b79 commit acb7dd8
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 9 deletions.
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();
});
});
});

0 comments on commit acb7dd8

Please sign in to comment.