Space preferences for whether or not you see DMs in a Space (#7250)

This commit is contained in:
Michael Telatynski 2021-12-17 09:26:32 +00:00 committed by GitHub
parent 5ee356daaa
commit 39c4b78371
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 911 additions and 350 deletions

View file

@ -14,7 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { EventEmitter } from "events";
import { EventType } from "matrix-js-sdk/src/@types/event";
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
@ -40,11 +39,6 @@ jest.useFakeTimers();
const testUserId = "@test:user";
const getUserIdForRoomId = jest.fn();
const getDMRoomsForUserId = jest.fn();
// @ts-ignore
DMRoomMap.sharedInstance = { getUserIdForRoomId, getDMRoomsForUserId };
const fav1 = "!fav1:server";
const fav2 = "!fav2:server";
const fav3 = "!fav3:server";
@ -68,6 +62,28 @@ const space1 = "!space1:server";
const space2 = "!space2:server";
const space3 = "!space3:server";
const getUserIdForRoomId = jest.fn(roomId => {
return {
[dm1]: dm1Partner.userId,
[dm2]: dm2Partner.userId,
[dm3]: dm3Partner.userId,
}[roomId];
});
const getDMRoomsForUserId = jest.fn(userId => {
switch (userId) {
case dm1Partner.userId:
return [dm1];
case dm2Partner.userId:
return [dm2];
case dm3Partner.userId:
return [dm3];
default:
return [];
}
});
// @ts-ignore
DMRoomMap.sharedInstance = { getUserIdForRoomId, getDMRoomsForUserId };
describe("SpaceStore", () => {
stubClient();
const store = SpaceStore.instance;
@ -306,26 +322,6 @@ describe("SpaceStore", () => {
client.getRoom(roomId).getMyMembership.mockReturnValue("invite");
});
getUserIdForRoomId.mockImplementation(roomId => {
return {
[dm1]: dm1Partner.userId,
[dm2]: dm2Partner.userId,
[dm3]: dm3Partner.userId,
}[roomId];
});
getDMRoomsForUserId.mockImplementation(userId => {
switch (userId) {
case dm1Partner.userId:
return [dm1];
case dm2Partner.userId:
return [dm2];
case dm3Partner.userId:
return [dm3];
default:
return [];
}
});
// have dmPartner1 be in space1 with you
const mySpace1Member = new RoomMember(space1, testUserId);
mySpace1Member.membership = "join";
@ -388,103 +384,104 @@ describe("SpaceStore", () => {
});
it("home space contains orphaned rooms", () => {
expect(store.getSpaceFilteredRoomIds(MetaSpace.Home).has(orphan1)).toBeTruthy();
expect(store.getSpaceFilteredRoomIds(MetaSpace.Home).has(orphan2)).toBeTruthy();
expect(store.isRoomInSpace(MetaSpace.Home, orphan1)).toBeTruthy();
expect(store.isRoomInSpace(MetaSpace.Home, orphan2)).toBeTruthy();
});
it("home space does not contain all favourites", () => {
expect(store.getSpaceFilteredRoomIds(MetaSpace.Home).has(fav1)).toBeFalsy();
expect(store.getSpaceFilteredRoomIds(MetaSpace.Home).has(fav2)).toBeFalsy();
expect(store.getSpaceFilteredRoomIds(MetaSpace.Home).has(fav3)).toBeFalsy();
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.getSpaceFilteredRoomIds(MetaSpace.Home).has(dm1)).toBeTruthy();
expect(store.getSpaceFilteredRoomIds(MetaSpace.Home).has(dm2)).toBeTruthy();
expect(store.getSpaceFilteredRoomIds(MetaSpace.Home).has(dm3)).toBeTruthy();
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.getSpaceFilteredRoomIds(MetaSpace.Home).has(invite1)).toBeTruthy();
expect(store.isRoomInSpace(MetaSpace.Home, invite1)).toBeTruthy();
});
it("home space contains invites even if they are also shown in a space", () => {
expect(store.getSpaceFilteredRoomIds(MetaSpace.Home).has(invite2)).toBeTruthy();
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.getSpaceFilteredRoomIds(MetaSpace.Home).has(room1)).toBeTruthy();
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.getSpaceFilteredRoomIds(MetaSpace.Favourites).has(fav1)).toBeTruthy();
expect(store.getSpaceFilteredRoomIds(MetaSpace.Favourites).has(fav2)).toBeTruthy();
expect(store.getSpaceFilteredRoomIds(MetaSpace.Favourites).has(fav3)).toBeTruthy();
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.getSpaceFilteredRoomIds(MetaSpace.People).has(dm1)).toBeTruthy();
expect(store.getSpaceFilteredRoomIds(MetaSpace.People).has(dm2)).toBeTruthy();
expect(store.getSpaceFilteredRoomIds(MetaSpace.People).has(dm3)).toBeTruthy();
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.getSpaceFilteredRoomIds(MetaSpace.Orphans).has(orphan1)).toBeTruthy();
expect(store.getSpaceFilteredRoomIds(MetaSpace.Orphans).has(orphan2)).toBeTruthy();
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.getSpaceFilteredRoomIds(MetaSpace.Home).has(room1)).toBeFalsy();
expect(store.isRoomInSpace(MetaSpace.Home, room1)).toBeFalsy();
});
it("space contains child rooms", () => {
expect(store.getSpaceFilteredRoomIds(space1).has(fav1)).toBeTruthy();
expect(store.getSpaceFilteredRoomIds(space1).has(room1)).toBeTruthy();
expect(store.isRoomInSpace(space1, fav1)).toBeTruthy();
expect(store.isRoomInSpace(space1, room1)).toBeTruthy();
});
it("space contains child favourites", () => {
expect(store.getSpaceFilteredRoomIds(space2).has(fav1)).toBeTruthy();
expect(store.getSpaceFilteredRoomIds(space2).has(fav2)).toBeTruthy();
expect(store.getSpaceFilteredRoomIds(space2).has(fav3)).toBeTruthy();
expect(store.getSpaceFilteredRoomIds(space2).has(room1)).toBeTruthy();
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.getSpaceFilteredRoomIds(space3).has(invite2)).toBeTruthy();
expect(store.isRoomInSpace(space3, invite2)).toBeTruthy();
});
it("spaces contain dms which you have with members of that space", () => {
expect(store.getSpaceFilteredRoomIds(space1).has(dm1)).toBeTruthy();
expect(store.getSpaceFilteredRoomIds(space2).has(dm1)).toBeFalsy();
expect(store.getSpaceFilteredRoomIds(space3).has(dm1)).toBeFalsy();
expect(store.getSpaceFilteredRoomIds(space1).has(dm2)).toBeFalsy();
expect(store.getSpaceFilteredRoomIds(space2).has(dm2)).toBeTruthy();
expect(store.getSpaceFilteredRoomIds(space3).has(dm2)).toBeFalsy();
expect(store.getSpaceFilteredRoomIds(space1).has(dm3)).toBeFalsy();
expect(store.getSpaceFilteredRoomIds(space2).has(dm3)).toBeFalsy();
expect(store.getSpaceFilteredRoomIds(space3).has(dm3)).toBeFalsy();
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("dms are only added to Notification States for only the Home Space", () => {
// XXX: All rooms space is forcibly enabled, as part of a future PR test Home space better
// [dm1, dm2, dm3].forEach(d => {
// expect(store.getNotificationState(HOME_SPACE).rooms.map(r => r.roomId).includes(d)).toBeTruthy();
// });
[space1, space2, space3].forEach(s => {
it("dms are only added to Notification States for only the People Space", async () => {
[dm1, dm2, dm3].forEach(d => {
expect(store.getNotificationState(MetaSpace.People)
.rooms.map(r => r.roomId).includes(d)).toBeTruthy();
});
[space1, space2, space3, MetaSpace.Home, MetaSpace.Orphans, MetaSpace.Favourites].forEach(s => {
[dm1, dm2, dm3].forEach(d => {
expect(store.getNotificationState(s).rooms.map(r => r.roomId).includes(d)).toBeFalsy();
});
});
});
it("orphan rooms are added to Notification States for only the Home Space", () => {
// XXX: All rooms space is forcibly enabled, as part of a future PR test Home space better
// [orphan1, orphan2].forEach(d => {
// expect(store.getNotificationState(HOME_SPACE).rooms.map(r => r.roomId).includes(d)).toBeTruthy();
// });
it("orphan rooms are added to Notification States for only the Home Space", async () => {
await setShowAllRooms(false);
[orphan1, orphan2].forEach(d => {
expect(store.getNotificationState(MetaSpace.Home)
.rooms.map(r => r.roomId).includes(d)).toBeTruthy();
});
[space1, space2, space3].forEach(s => {
[orphan1, orphan2].forEach(d => {
expect(store.getNotificationState(s).rooms.map(r => r.roomId).includes(d)).toBeFalsy();
@ -517,33 +514,22 @@ describe("SpaceStore", () => {
});
it("honours m.space.parent if sender has permission in parent space", () => {
expect(store.getSpaceFilteredRoomIds(space2).has(room2)).toBeTruthy();
expect(store.isRoomInSpace(space2, room2)).toBeTruthy();
});
it("does not honour m.space.parent if sender does not have permission in parent space", () => {
expect(store.getSpaceFilteredRoomIds(space3).has(room3)).toBeFalsy();
expect(store.isRoomInSpace(space3, room3)).toBeFalsy();
});
});
});
describe("hierarchy resolution update tests", () => {
let emitter: EventEmitter;
beforeEach(async () => {
emitter = new EventEmitter();
client.on.mockImplementation(emitter.on.bind(emitter));
client.removeListener.mockImplementation(emitter.removeListener.bind(emitter));
});
afterEach(() => {
client.on.mockReset();
client.removeListener.mockReset();
});
it("updates state when spaces are joined", async () => {
await run();
expect(store.spacePanelSpaces).toStrictEqual([]);
const space = mkSpace(space1);
const prom = testUtils.emitPromise(store, UPDATE_TOP_LEVEL_SPACES);
emitter.emit("Room", space);
client.emit("Room", space);
await prom;
expect(store.spacePanelSpaces).toStrictEqual([space]);
expect(store.invitedSpaces).toStrictEqual([]);
@ -556,7 +542,7 @@ describe("SpaceStore", () => {
expect(store.spacePanelSpaces).toStrictEqual([space]);
space.getMyMembership.mockReturnValue("leave");
const prom = testUtils.emitPromise(store, UPDATE_TOP_LEVEL_SPACES);
emitter.emit("Room.myMembership", space, "leave", "join");
client.emit("Room.myMembership", space, "leave", "join");
await prom;
expect(store.spacePanelSpaces).toStrictEqual([]);
});
@ -568,7 +554,7 @@ describe("SpaceStore", () => {
const space = mkSpace(space1);
space.getMyMembership.mockReturnValue("invite");
const prom = testUtils.emitPromise(store, UPDATE_INVITED_SPACES);
emitter.emit("Room", space);
client.emit("Room", space);
await prom;
expect(store.spacePanelSpaces).toStrictEqual([]);
expect(store.invitedSpaces).toStrictEqual([space]);
@ -583,7 +569,7 @@ describe("SpaceStore", () => {
expect(store.invitedSpaces).toStrictEqual([space]);
space.getMyMembership.mockReturnValue("join");
const prom = testUtils.emitPromise(store, UPDATE_TOP_LEVEL_SPACES);
emitter.emit("Room.myMembership", space, "join", "invite");
client.emit("Room.myMembership", space, "join", "invite");
await prom;
expect(store.spacePanelSpaces).toStrictEqual([space]);
expect(store.invitedSpaces).toStrictEqual([]);
@ -598,7 +584,7 @@ describe("SpaceStore", () => {
expect(store.invitedSpaces).toStrictEqual([space]);
space.getMyMembership.mockReturnValue("leave");
const prom = testUtils.emitPromise(store, UPDATE_INVITED_SPACES);
emitter.emit("Room.myMembership", space, "leave", "invite");
client.emit("Room.myMembership", space, "leave", "invite");
await prom;
expect(store.spacePanelSpaces).toStrictEqual([]);
expect(store.invitedSpaces).toStrictEqual([]);
@ -612,21 +598,21 @@ describe("SpaceStore", () => {
expect(store.invitedSpaces).toStrictEqual([]);
expect(store.getChildSpaces(space1)).toStrictEqual([]);
expect(store.getChildRooms(space1)).toStrictEqual([]);
expect(store.getSpaceFilteredRoomIds(space1).has(invite1)).toBeFalsy();
expect(store.getSpaceFilteredRoomIds(MetaSpace.Home).has(invite1)).toBeFalsy();
expect(store.isRoomInSpace(space1, invite1)).toBeFalsy();
expect(store.isRoomInSpace(MetaSpace.Home, invite1)).toBeFalsy();
const invite = mkRoom(invite1);
invite.getMyMembership.mockReturnValue("invite");
const prom = testUtils.emitPromise(store, space1);
emitter.emit("Room", space);
client.emit("Room", space);
await prom;
expect(store.spacePanelSpaces).toStrictEqual([space]);
expect(store.invitedSpaces).toStrictEqual([]);
expect(store.getChildSpaces(space1)).toStrictEqual([]);
expect(store.getChildRooms(space1)).toStrictEqual([invite]);
expect(store.getSpaceFilteredRoomIds(space1).has(invite1)).toBeTruthy();
expect(store.getSpaceFilteredRoomIds(MetaSpace.Home).has(invite1)).toBeTruthy();
expect(store.isRoomInSpace(space1, invite1)).toBeTruthy();
expect(store.isRoomInSpace(MetaSpace.Home, invite1)).toBeTruthy();
});
});
@ -817,7 +803,7 @@ describe("SpaceStore", () => {
expect(store.activeSpace).toBe(MetaSpace.Orphans);
});
it("switch to first space when selected metaspace is disabled", async () => {
it("switch to first valid space when selected metaspace is disabled", async () => {
store.setActiveSpace(MetaSpace.People, false);
expect(store.activeSpace).toBe(MetaSpace.People);
await SettingsStore.setValue("Spaces.enabledMetaSpaces", null, SettingLevel.DEVICE, {
@ -827,7 +813,7 @@ describe("SpaceStore", () => {
[MetaSpace.Orphans]: true,
});
jest.runAllTimers();
expect(store.activeSpace).toBe(MetaSpace.Favourites);
expect(store.activeSpace).toBe(MetaSpace.Orphans);
});
it("when switching rooms in the all rooms home space don't switch to related space", async () => {
@ -889,4 +875,105 @@ describe("SpaceStore", () => {
expect(fn).toBeCalledWith("!c:server");
});
});
it("test user flow", async () => {
// init the store
await run();
await setShowAllRooms(false);
// receive invite to space
const rootSpace = mkSpace(space1, [room1, room2, space2]);
rootSpace.getMyMembership.mockReturnValue("invite");
client.emit("Room", rootSpace);
jest.runAllTimers();
expect(SpaceStore.instance.invitedSpaces).toStrictEqual([rootSpace]);
expect(SpaceStore.instance.spacePanelSpaces).toStrictEqual([]);
// accept invite to space
rootSpace.getMyMembership.mockReturnValue("join");
client.emit("Room.myMembership", rootSpace, "join", "invite");
jest.runAllTimers();
expect(SpaceStore.instance.invitedSpaces).toStrictEqual([]);
expect(SpaceStore.instance.spacePanelSpaces).toStrictEqual([rootSpace]);
// join room in space
expect(SpaceStore.instance.isRoomInSpace(space1, room1)).toBeFalsy();
const rootSpaceRoom1 = mkRoom(room1);
rootSpaceRoom1.getMyMembership.mockReturnValue("join");
client.emit("Room", rootSpaceRoom1);
jest.runAllTimers();
expect(SpaceStore.instance.invitedSpaces).toStrictEqual([]);
expect(SpaceStore.instance.spacePanelSpaces).toStrictEqual([rootSpace]);
expect(SpaceStore.instance.isRoomInSpace(space1, room1)).toBeTruthy();
expect(SpaceStore.instance.isRoomInSpace(MetaSpace.Home, room1)).toBeFalsy();
expect(SpaceStore.instance.isRoomInSpace(MetaSpace.Favourites, room1)).toBeFalsy();
expect(SpaceStore.instance.isRoomInSpace(MetaSpace.People, room1)).toBeFalsy();
expect(SpaceStore.instance.isRoomInSpace(MetaSpace.Orphans, room1)).toBeFalsy();
// receive room invite
expect(SpaceStore.instance.isRoomInSpace(space1, room2)).toBeFalsy();
const rootSpaceRoom2 = mkRoom(room2);
rootSpaceRoom2.getMyMembership.mockReturnValue("invite");
client.emit("Room", rootSpaceRoom2);
jest.runAllTimers();
expect(SpaceStore.instance.invitedSpaces).toStrictEqual([]);
expect(SpaceStore.instance.spacePanelSpaces).toStrictEqual([rootSpace]);
expect(SpaceStore.instance.isRoomInSpace(space1, room2)).toBeTruthy();
expect(SpaceStore.instance.isRoomInSpace(MetaSpace.Home, room2)).toBeTruthy();
expect(SpaceStore.instance.isRoomInSpace(MetaSpace.Favourites, room2)).toBeFalsy();
expect(SpaceStore.instance.isRoomInSpace(MetaSpace.People, room2)).toBeFalsy();
expect(SpaceStore.instance.isRoomInSpace(MetaSpace.Orphans, room2)).toBeFalsy();
// start DM in space
const myRootSpaceMember = new RoomMember(space1, testUserId);
myRootSpaceMember.membership = "join";
const rootSpaceFriend = new RoomMember(space1, dm1Partner.userId);
rootSpaceFriend.membership = "join";
rootSpace.getMembers.mockReturnValue([
myRootSpaceMember,
rootSpaceFriend,
]);
rootSpace.getMember.mockImplementation(userId => {
switch (userId) {
case testUserId:
return myRootSpaceMember;
case dm1Partner.userId:
return rootSpaceFriend;
}
});
expect(SpaceStore.instance.getSpaceFilteredUserIds(space1).has(dm1Partner.userId)).toBeFalsy();
client.emit("RoomState.members", mkEvent({
event: true,
type: EventType.RoomMember,
content: {
membership: "join",
},
skey: dm1Partner.userId,
user: dm1Partner.userId,
room: space1,
}));
jest.runAllTimers();
expect(SpaceStore.instance.getSpaceFilteredUserIds(space1).has(dm1Partner.userId)).toBeTruthy();
const dm1Room = mkRoom(dm1);
dm1Room.getMyMembership.mockReturnValue("join");
client.emit("Room", dm1Room);
jest.runAllTimers();
expect(SpaceStore.instance.invitedSpaces).toStrictEqual([]);
expect(SpaceStore.instance.spacePanelSpaces).toStrictEqual([rootSpace]);
expect(SpaceStore.instance.isRoomInSpace(space1, dm1)).toBeTruthy();
expect(SpaceStore.instance.isRoomInSpace(MetaSpace.Home, dm1)).toBeTruthy();
expect(SpaceStore.instance.isRoomInSpace(MetaSpace.Favourites, dm1)).toBeFalsy();
expect(SpaceStore.instance.isRoomInSpace(MetaSpace.People, dm1)).toBeTruthy();
expect(SpaceStore.instance.isRoomInSpace(MetaSpace.Orphans, dm1)).toBeFalsy();
// join subspace
const subspace = mkSpace(space2);
subspace.getMyMembership.mockReturnValue("join");
const prom = testUtils.emitPromise(SpaceStore.instance, space1);
client.emit("Room", subspace);
jest.runAllTimers();
expect(SpaceStore.instance.invitedSpaces).toStrictEqual([]);
expect(SpaceStore.instance.spacePanelSpaces.map(r => r.roomId)).toStrictEqual([rootSpace.roomId]);
await prom;
});
});