forked from element-hq/element-web
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmembership.ts
More file actions
133 lines (114 loc) · 4.26 KB
/
Copy pathmembership.ts
File metadata and controls
133 lines (114 loc) · 4.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/*
Copyright 2024 New Vector Ltd.
Copyright 2020-2022 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import {
type Room,
type RoomMember,
type RoomState,
RoomStateEvent,
type MatrixEvent,
type MatrixClient,
} from "matrix-js-sdk/src/matrix";
import { KnownMembership, type Membership } from "matrix-js-sdk/src/types";
import { MatrixClientPeg } from "../MatrixClientPeg";
import SettingsStore from "../settings/SettingsStore";
/**
* Approximation of a membership status for a given room.
*/
export enum EffectiveMembership {
/**
* The user is effectively joined to the room. For example, actually joined
* or knocking on the room (when that becomes possible).
*/
Join = "JOIN",
/**
* The user is effectively invited to the room. Currently this is a direct map
* to the invite membership as no other membership states are effectively
* invites.
*/
Invite = "INVITE",
/**
* The user is effectively no longer in the room. For example, kicked,
* banned, or voluntarily left.
*/
Leave = "LEAVE",
}
export type MembershipSplit = {
[state in EffectiveMembership]: Room[];
};
export function splitRoomsByMembership(rooms: Room[]): MembershipSplit {
const split: MembershipSplit = {
[EffectiveMembership.Invite]: [],
[EffectiveMembership.Join]: [],
[EffectiveMembership.Leave]: [],
};
for (const room of rooms) {
const membership = room.getMyMembership();
// Filter out falsey relationship as this will be peeked rooms
if (!!membership) {
split[getEffectiveMembershipTag(room)].push(room);
}
}
return split;
}
export function getEffectiveMembership(membership: Membership): EffectiveMembership {
if (membership === KnownMembership.Invite) {
return EffectiveMembership.Invite;
} else if (
membership === KnownMembership.Join ||
(SettingsStore.getValue("feature_ask_to_join") && membership === KnownMembership.Knock)
) {
return EffectiveMembership.Join;
} else {
// Probably a leave, kick, or ban
return EffectiveMembership.Leave;
}
}
export function isKnockDenied(room: Room): boolean | undefined {
const memberId = MatrixClientPeg.get()?.getSafeUserId();
const member = memberId ? room.getMember(memberId) : null;
const previousMembership = member?.events.member?.getPrevContent().membership;
return member?.isKicked() && previousMembership === KnownMembership.Knock;
}
export function getEffectiveMembershipTag(room: Room, membership?: string): EffectiveMembership {
return isKnockDenied(room)
? EffectiveMembership.Join
: getEffectiveMembership(membership ?? room.getMyMembership());
}
export function isJoinedOrNearlyJoined(membership: Membership): boolean {
const effective = getEffectiveMembership(membership);
return effective === EffectiveMembership.Join || effective === EffectiveMembership.Invite;
}
/**
* Try to ensure the user is in the room (invited or joined) before continuing
*/
export async function waitForMember(
client: MatrixClient,
roomId: string,
userId: string,
opts = { timeout: 1500 },
): Promise<boolean> {
const { timeout } = opts;
let handler: (event: MatrixEvent, state: RoomState, member: RoomMember) => void;
// check if the user is in the room before we start -- in which case, no need to wait.
if ((client.getRoom(roomId)?.getMember(userId) ?? null) !== null) {
return true;
}
return new Promise<boolean>((resolve) => {
// eslint-disable-next-line @typescript-eslint/naming-convention
handler = function (_, __, member: RoomMember) {
if (member.userId !== userId) return;
if (member.roomId !== roomId) return;
resolve(true);
};
client.on(RoomStateEvent.NewMember, handler);
/* We don't want to hang if this goes wrong, so we proceed and hope the other
user is already in the room */
window.setTimeout(resolve, timeout, false);
}).finally(() => {
client.removeListener(RoomStateEvent.NewMember, handler);
});
}