You've already forked matrix-react-sdk
mirror of
https://github.com/matrix-org/matrix-react-sdk.git
synced 2025-07-31 13:44:28 +03:00
Don't aggregate rooms and users in SpaceStore (#7723)
* add direct child maps * track rooms, users and space children in flat hierarchy in spacestore Signed-off-by: Kerry Archibald <kerrya@element.io> * update spacefiltercondition to use new spacestore * remove unused code Signed-off-by: Kerry Archibald <kerrya@element.io> * typos Signed-off-by: Kerry Archibald <kerrya@element.io> * only build flattened rooms set once per space when updating notifs * copyright Signed-off-by: Kerry Archibald <kerrya@element.io> * remove unnecessary currying Signed-off-by: Kerry Archibald <kerrya@element.io> * rename SpaceStore spaceFilteredRooms => roomsIdsBySpace, spaceFilteredUsers => userIdsBySpace Signed-off-by: Kerry Archibald <kerrya@element.io> * cache aggregates rooms and users by space Signed-off-by: Kerry Archibald <kerrya@element.io> * emit events recursively up parent spaces on changes Signed-off-by: Kerry Archibald <kerrya@element.io> * exclude meta spaces from aggregate cache Signed-off-by: Kerry Archibald <kerrya@element.io> * stray log * fix emit on member update Signed-off-by: Kerry Archibald <kerrya@element.io> * call order Signed-off-by: Kerry Archibald <kerrya@element.io> * extend existing getKnownParents fn Signed-off-by: Kerry Archibald <kerrya@element.io> * refine types and comments Signed-off-by: Kerry Archibald <kerrya@element.io>
This commit is contained in:
@ -18,6 +18,7 @@ import { EventType } from "matrix-js-sdk/src/@types/event";
|
||||
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||
|
||||
import "../skinned-sdk"; // Must be first for skinning to work
|
||||
|
||||
import SpaceStore from "../../src/stores/spaces/SpaceStore";
|
||||
import {
|
||||
MetaSpace,
|
||||
@ -58,9 +59,11 @@ const invite2 = "!invite2:server";
|
||||
const room1 = "!room1:server";
|
||||
const room2 = "!room2:server";
|
||||
const room3 = "!room3:server";
|
||||
const room4 = "!room4:server";
|
||||
const space1 = "!space1:server";
|
||||
const space2 = "!space2:server";
|
||||
const space3 = "!space3:server";
|
||||
const space4 = "!space4:server";
|
||||
|
||||
const getUserIdForRoomId = jest.fn(roomId => {
|
||||
return {
|
||||
@ -303,11 +306,13 @@ describe("SpaceStore", () => {
|
||||
|
||||
describe("test fixture 1", () => {
|
||||
beforeEach(async () => {
|
||||
[fav1, fav2, fav3, dm1, dm2, dm3, orphan1, orphan2, invite1, invite2, room1, room2, room3]
|
||||
[fav1, fav2, fav3, dm1, dm2, dm3, orphan1, orphan2, invite1, invite2, room1, room2, room3, room4]
|
||||
.forEach(mkRoom);
|
||||
mkSpace(space1, [fav1, room1]);
|
||||
mkSpace(space2, [fav1, fav2, fav3, room1]);
|
||||
mkSpace(space3, [invite2]);
|
||||
mkSpace(space4, [room4, fav2, space2, space3]);
|
||||
|
||||
client.getRoom.mockImplementation(roomId => rooms.find(room => room.roomId === roomId));
|
||||
|
||||
[fav1, fav2, fav3].forEach(roomId => {
|
||||
@ -383,85 +388,144 @@ describe("SpaceStore", () => {
|
||||
await run();
|
||||
});
|
||||
|
||||
it("home space contains orphaned rooms", () => {
|
||||
expect(store.isRoomInSpace(MetaSpace.Home, orphan1)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(MetaSpace.Home, orphan2)).toBeTruthy();
|
||||
});
|
||||
describe('isRoomInSpace()', () => {
|
||||
it("home space contains orphaned rooms", () => {
|
||||
expect(store.isRoomInSpace(MetaSpace.Home, orphan1)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(MetaSpace.Home, orphan2)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("home space does not contain all favourites", () => {
|
||||
expect(store.isRoomInSpace(MetaSpace.Home, fav1)).toBeFalsy();
|
||||
expect(store.isRoomInSpace(MetaSpace.Home, fav2)).toBeFalsy();
|
||||
expect(store.isRoomInSpace(MetaSpace.Home, fav3)).toBeFalsy();
|
||||
});
|
||||
it("home space does not contain all favourites", () => {
|
||||
expect(store.isRoomInSpace(MetaSpace.Home, fav1)).toBeFalsy();
|
||||
expect(store.isRoomInSpace(MetaSpace.Home, fav2)).toBeFalsy();
|
||||
expect(store.isRoomInSpace(MetaSpace.Home, fav3)).toBeFalsy();
|
||||
});
|
||||
|
||||
it("home space contains dm rooms", () => {
|
||||
expect(store.isRoomInSpace(MetaSpace.Home, dm1)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(MetaSpace.Home, dm2)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(MetaSpace.Home, dm3)).toBeTruthy();
|
||||
});
|
||||
it("home space contains dm rooms", () => {
|
||||
expect(store.isRoomInSpace(MetaSpace.Home, dm1)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(MetaSpace.Home, dm2)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(MetaSpace.Home, dm3)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("home space contains invites", () => {
|
||||
expect(store.isRoomInSpace(MetaSpace.Home, invite1)).toBeTruthy();
|
||||
});
|
||||
it("home space contains invites", () => {
|
||||
expect(store.isRoomInSpace(MetaSpace.Home, invite1)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("home space contains invites even if they are also shown in a space", () => {
|
||||
expect(store.isRoomInSpace(MetaSpace.Home, invite2)).toBeTruthy();
|
||||
});
|
||||
it("home space contains invites even if they are also shown in a space", () => {
|
||||
expect(store.isRoomInSpace(MetaSpace.Home, invite2)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("all rooms space does contain rooms/low priority even if they are also shown in a space", async () => {
|
||||
await setShowAllRooms(true);
|
||||
expect(store.isRoomInSpace(MetaSpace.Home, room1)).toBeTruthy();
|
||||
});
|
||||
it(
|
||||
"all rooms space does contain rooms/low priority even if they are also shown in a space",
|
||||
async () => {
|
||||
await setShowAllRooms(true);
|
||||
expect(store.isRoomInSpace(MetaSpace.Home, room1)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("favourites space does contain favourites even if they are also shown in a space", async () => {
|
||||
expect(store.isRoomInSpace(MetaSpace.Favourites, fav1)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(MetaSpace.Favourites, fav2)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(MetaSpace.Favourites, fav3)).toBeTruthy();
|
||||
});
|
||||
it("favourites space does contain favourites even if they are also shown in a space", async () => {
|
||||
expect(store.isRoomInSpace(MetaSpace.Favourites, fav1)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(MetaSpace.Favourites, fav2)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(MetaSpace.Favourites, fav3)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("people space does contain people even if they are also shown in a space", async () => {
|
||||
expect(store.isRoomInSpace(MetaSpace.People, dm1)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(MetaSpace.People, dm2)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(MetaSpace.People, dm3)).toBeTruthy();
|
||||
});
|
||||
it("people space does contain people even if they are also shown in a space", async () => {
|
||||
expect(store.isRoomInSpace(MetaSpace.People, dm1)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(MetaSpace.People, dm2)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(MetaSpace.People, dm3)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("orphans space does contain orphans even if they are also shown in all rooms", async () => {
|
||||
await setShowAllRooms(true);
|
||||
expect(store.isRoomInSpace(MetaSpace.Orphans, orphan1)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(MetaSpace.Orphans, orphan2)).toBeTruthy();
|
||||
});
|
||||
it("orphans space does contain orphans even if they are also shown in all rooms", async () => {
|
||||
await setShowAllRooms(true);
|
||||
expect(store.isRoomInSpace(MetaSpace.Orphans, orphan1)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(MetaSpace.Orphans, orphan2)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("home space doesn't contain rooms/low priority if they are also shown in a space", async () => {
|
||||
await setShowAllRooms(false);
|
||||
expect(store.isRoomInSpace(MetaSpace.Home, room1)).toBeFalsy();
|
||||
});
|
||||
it("home space doesn't contain rooms/low priority if they are also shown in a space", async () => {
|
||||
await setShowAllRooms(false);
|
||||
expect(store.isRoomInSpace(MetaSpace.Home, room1)).toBeFalsy();
|
||||
});
|
||||
|
||||
it("space contains child rooms", () => {
|
||||
expect(store.isRoomInSpace(space1, fav1)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(space1, room1)).toBeTruthy();
|
||||
});
|
||||
it("space contains child rooms", () => {
|
||||
expect(store.isRoomInSpace(space1, fav1)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(space1, room1)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("space contains child favourites", () => {
|
||||
expect(store.isRoomInSpace(space2, fav1)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(space2, fav2)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(space2, fav3)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(space2, room1)).toBeTruthy();
|
||||
});
|
||||
it("returns true for all sub-space child rooms when includeSubSpaceRooms is undefined", () => {
|
||||
expect(store.isRoomInSpace(space4, room4)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(space4, fav2)).toBeTruthy();
|
||||
// space2's rooms
|
||||
expect(store.isRoomInSpace(space4, fav1)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(space4, fav3)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(space4, room1)).toBeTruthy();
|
||||
// space3's rooms
|
||||
expect(store.isRoomInSpace(space4, invite2)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("space contains child invites", () => {
|
||||
expect(store.isRoomInSpace(space3, invite2)).toBeTruthy();
|
||||
});
|
||||
it("returns true for all sub-space child rooms when includeSubSpaceRooms is true", () => {
|
||||
expect(store.isRoomInSpace(space4, room4, true)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(space4, fav2, true)).toBeTruthy();
|
||||
// space2's rooms
|
||||
expect(store.isRoomInSpace(space4, fav1, true)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(space4, fav3, true)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(space4, room1, true)).toBeTruthy();
|
||||
// space3's rooms
|
||||
expect(store.isRoomInSpace(space4, invite2, true)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("spaces contain dms which you have with members of that space", () => {
|
||||
expect(store.isRoomInSpace(space1, dm1)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(space2, dm1)).toBeFalsy();
|
||||
expect(store.isRoomInSpace(space3, dm1)).toBeFalsy();
|
||||
expect(store.isRoomInSpace(space1, dm2)).toBeFalsy();
|
||||
expect(store.isRoomInSpace(space2, dm2)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(space3, dm2)).toBeFalsy();
|
||||
expect(store.isRoomInSpace(space1, dm3)).toBeFalsy();
|
||||
expect(store.isRoomInSpace(space2, dm3)).toBeFalsy();
|
||||
expect(store.isRoomInSpace(space3, dm3)).toBeFalsy();
|
||||
it("returns false for all sub-space child rooms when includeSubSpaceRooms is false", () => {
|
||||
// direct children
|
||||
expect(store.isRoomInSpace(space4, room4, false)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(space4, fav2, false)).toBeTruthy();
|
||||
// space2's rooms
|
||||
expect(store.isRoomInSpace(space4, fav1, false)).toBeFalsy();
|
||||
expect(store.isRoomInSpace(space4, fav3, false)).toBeFalsy();
|
||||
expect(store.isRoomInSpace(space4, room1, false)).toBeFalsy();
|
||||
// space3's rooms
|
||||
expect(store.isRoomInSpace(space4, invite2, false)).toBeFalsy();
|
||||
});
|
||||
|
||||
it("space contains all sub-space's child rooms", () => {
|
||||
expect(store.isRoomInSpace(space4, room4)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(space4, fav2)).toBeTruthy();
|
||||
// space2's rooms
|
||||
expect(store.isRoomInSpace(space4, fav1)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(space4, fav3)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(space4, room1)).toBeTruthy();
|
||||
// space3's rooms
|
||||
expect(store.isRoomInSpace(space4, invite2)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("space contains child favourites", () => {
|
||||
expect(store.isRoomInSpace(space2, fav1)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(space2, fav2)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(space2, fav3)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(space2, room1)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("space contains child invites", () => {
|
||||
expect(store.isRoomInSpace(space3, invite2)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("spaces contain dms which you have with members of that space", () => {
|
||||
expect(store.isRoomInSpace(space1, dm1)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(space2, dm1)).toBeFalsy();
|
||||
expect(store.isRoomInSpace(space3, dm1)).toBeFalsy();
|
||||
expect(store.isRoomInSpace(space1, dm2)).toBeFalsy();
|
||||
expect(store.isRoomInSpace(space2, dm2)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(space3, dm2)).toBeFalsy();
|
||||
expect(store.isRoomInSpace(space1, dm3)).toBeFalsy();
|
||||
expect(store.isRoomInSpace(space2, dm3)).toBeFalsy();
|
||||
expect(store.isRoomInSpace(space3, dm3)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('uses cached aggregated rooms', () => {
|
||||
const rooms = store.getSpaceFilteredRoomIds(space4, true);
|
||||
expect(store.isRoomInSpace(space4, fav1)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(space4, fav3)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(space4, room1)).toBeTruthy();
|
||||
|
||||
// isRoomInSpace calls didn't rebuild room set
|
||||
expect(rooms).toStrictEqual(store.getSpaceFilteredRoomIds(space4, true));
|
||||
});
|
||||
});
|
||||
|
||||
it("dms are only added to Notification States for only the People Space", async () => {
|
||||
@ -614,6 +678,115 @@ describe("SpaceStore", () => {
|
||||
expect(store.isRoomInSpace(space1, invite1)).toBeTruthy();
|
||||
expect(store.isRoomInSpace(MetaSpace.Home, invite1)).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('onRoomsUpdate()', () => {
|
||||
beforeEach(() => {
|
||||
[fav1, fav2, fav3, dm1, dm2, dm3, orphan1, orphan2, invite1, invite2, room1, room2, room3, room4]
|
||||
.forEach(mkRoom);
|
||||
mkSpace(space2, [fav1, fav2, fav3, room1]);
|
||||
mkSpace(space3, [invite2]);
|
||||
mkSpace(space4, [room4, fav2, space2, space3]);
|
||||
mkSpace(space1, [fav1, room1, space4]);
|
||||
});
|
||||
|
||||
const addChildRoom = (spaceId, childId) => {
|
||||
const childEvent = mkEvent({
|
||||
event: true,
|
||||
type: EventType.SpaceChild,
|
||||
room: spaceId,
|
||||
user: client.getUserId(),
|
||||
skey: childId,
|
||||
content: { via: [], canonical: true },
|
||||
ts: Date.now(),
|
||||
});
|
||||
const spaceRoom = client.getRoom(spaceId);
|
||||
spaceRoom.currentState.getStateEvents.mockImplementation(
|
||||
testUtils.mockStateEventImplementation([childEvent]),
|
||||
);
|
||||
|
||||
client.emit("RoomState.events", childEvent);
|
||||
};
|
||||
|
||||
const addMember = (spaceId, user: RoomMember) => {
|
||||
const memberEvent = mkEvent({
|
||||
event: true,
|
||||
type: EventType.RoomMember,
|
||||
room: spaceId,
|
||||
user: client.getUserId(),
|
||||
skey: user.userId,
|
||||
content: { membership: 'join' },
|
||||
ts: Date.now(),
|
||||
});
|
||||
const spaceRoom = client.getRoom(spaceId);
|
||||
spaceRoom.currentState.getStateEvents.mockImplementation(
|
||||
testUtils.mockStateEventImplementation([memberEvent]),
|
||||
);
|
||||
spaceRoom.getMember.mockReturnValue(user);
|
||||
|
||||
client.emit("RoomState.members", memberEvent);
|
||||
};
|
||||
|
||||
it('emits events for parent spaces when child room is added', async () => {
|
||||
await run();
|
||||
|
||||
const room5 = mkRoom('!room5:server');
|
||||
const emitSpy = jest.spyOn(store, 'emit').mockClear();
|
||||
// add room5 into space2
|
||||
addChildRoom(space2, room5.roomId);
|
||||
|
||||
expect(emitSpy).toHaveBeenCalledWith(space2);
|
||||
// space2 is subspace of space4
|
||||
expect(emitSpy).toHaveBeenCalledWith(space4);
|
||||
// space4 is a subspace of space1
|
||||
expect(emitSpy).toHaveBeenCalledWith(space1);
|
||||
expect(emitSpy).not.toHaveBeenCalledWith(space3);
|
||||
});
|
||||
|
||||
it('updates rooms state when a child room is added', async () => {
|
||||
await run();
|
||||
const room5 = mkRoom('!room5:server');
|
||||
|
||||
expect(store.isRoomInSpace(space2, room5.roomId)).toBeFalsy();
|
||||
expect(store.isRoomInSpace(space4, room5.roomId)).toBeFalsy();
|
||||
|
||||
// add room5 into space2
|
||||
addChildRoom(space2, room5.roomId);
|
||||
|
||||
expect(store.isRoomInSpace(space2, room5.roomId)).toBeTruthy();
|
||||
// space2 is subspace of space4
|
||||
expect(store.isRoomInSpace(space4, room5.roomId)).toBeTruthy();
|
||||
// space4 is subspace of space1
|
||||
expect(store.isRoomInSpace(space1, room5.roomId)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('emits events for parent spaces when a member is added', async () => {
|
||||
await run();
|
||||
|
||||
const emitSpy = jest.spyOn(store, 'emit').mockClear();
|
||||
// add into space2
|
||||
addMember(space2, dm1Partner);
|
||||
|
||||
expect(emitSpy).toHaveBeenCalledWith(space2);
|
||||
// space2 is subspace of space4
|
||||
expect(emitSpy).toHaveBeenCalledWith(space4);
|
||||
// space4 is a subspace of space1
|
||||
expect(emitSpy).toHaveBeenCalledWith(space1);
|
||||
expect(emitSpy).not.toHaveBeenCalledWith(space3);
|
||||
});
|
||||
|
||||
it('updates users state when a member is added', async () => {
|
||||
await run();
|
||||
|
||||
expect(store.getSpaceFilteredUserIds(space2)).toEqual(new Set([]));
|
||||
|
||||
// add into space2
|
||||
addMember(space2, dm1Partner);
|
||||
|
||||
expect(store.getSpaceFilteredUserIds(space2)).toEqual(new Set([dm1Partner.userId]));
|
||||
expect(store.getSpaceFilteredUserIds(space4)).toEqual(new Set([dm1Partner.userId]));
|
||||
expect(store.getSpaceFilteredUserIds(space1)).toEqual(new Set([dm1Partner.userId]));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("active space switching tests", () => {
|
||||
|
Reference in New Issue
Block a user