polfan-server-js-client 0.2.105 → 0.2.106
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.idea/workspace.xml +22 -24
- package/build/index.cjs.js +393 -94
- package/build/index.cjs.js.map +1 -1
- package/build/index.umd.js +1 -1
- package/build/index.umd.js.map +1 -1
- package/build/types/IndexedObjectCollection.d.ts +8 -0
- package/build/types/state-tracker/FollowedTopicsManager.d.ts +18 -0
- package/build/types/state-tracker/RoomMessagesHistory.d.ts +12 -0
- package/build/types/state-tracker/TopicHistoryWindow.d.ts +2 -2
- package/package.json +1 -1
- package/src/IndexedObjectCollection.ts +31 -0
- package/src/state-tracker/EmoticonsManager.ts +72 -68
- package/src/state-tracker/FollowedTopicsManager.ts +54 -6
- package/src/state-tracker/MessagesManager.ts +21 -2
- package/src/state-tracker/PermissionsManager.ts +5 -1
- package/src/state-tracker/RelationshipsManager.ts +5 -5
- package/src/state-tracker/RoomMessagesHistory.ts +35 -1
- package/src/state-tracker/RoomsManager.ts +21 -4
- package/src/state-tracker/SpacesManager.ts +340 -313
- package/src/state-tracker/TopicHistoryWindow.ts +4 -4
- package/src/state-tracker/UsersManager.ts +3 -1
- package/tests/collections.test.ts +92 -0
- package/tests/state-reconnect.test.ts +257 -0
|
@@ -1,314 +1,341 @@
|
|
|
1
|
-
import {ChatStateTracker} from "./ChatStateTracker";
|
|
2
|
-
import {IndexedCollection, ObservableIndexedObjectCollection} from "../IndexedObjectCollection";
|
|
3
|
-
import {
|
|
4
|
-
NewRole,
|
|
5
|
-
NewRoom,
|
|
6
|
-
Role,
|
|
7
|
-
RoleDeleted,
|
|
8
|
-
RoleUpdated,
|
|
9
|
-
RoomDeleted,
|
|
10
|
-
RoomSummary,
|
|
11
|
-
RoomSummaryUpdated,
|
|
12
|
-
RoomUpdated,
|
|
13
|
-
Session,
|
|
14
|
-
Space,
|
|
15
|
-
SpaceDeleted,
|
|
16
|
-
SpaceJoined,
|
|
17
|
-
SpaceLeft,
|
|
18
|
-
SpaceMember,
|
|
19
|
-
SpaceMemberJoined,
|
|
20
|
-
SpaceMemberLeft,
|
|
21
|
-
SpaceMembers,
|
|
22
|
-
SpaceMemberUpdated,
|
|
23
|
-
SpaceRooms,
|
|
24
|
-
SpaceUpdated,
|
|
25
|
-
UserUpdated
|
|
26
|
-
} from "../types/src";
|
|
27
|
-
import {DeferredTask, PromiseRegistry} from "./AsyncUtils";
|
|
28
|
-
import {reorderRolesOnPriorityUpdate} from "./functions";
|
|
29
|
-
|
|
30
|
-
export class SpacesManager {
|
|
31
|
-
private readonly list = new ObservableIndexedObjectCollection<Space>('id');
|
|
32
|
-
private readonly roles = new IndexedCollection<string, ObservableIndexedObjectCollection<Role>>();
|
|
33
|
-
private readonly rooms = new IndexedCollection<string, ObservableIndexedObjectCollection<RoomSummary>>();
|
|
34
|
-
private readonly roomIdToSpaceId = new IndexedCollection<string, string>();
|
|
35
|
-
private readonly members = new IndexedCollection<string, ObservableIndexedObjectCollection<SpaceMember>>();
|
|
36
|
-
private readonly deferredSession = new DeferredTask();
|
|
37
|
-
private readonly roomsPromises = new PromiseRegistry();
|
|
38
|
-
private readonly membersPromises = new PromiseRegistry();
|
|
39
|
-
|
|
40
|
-
public constructor(private tracker: ChatStateTracker) {
|
|
41
|
-
this.tracker.client.on('NewRoom', ev => this.handleNewRoom(ev));
|
|
42
|
-
this.tracker.client.on('RoomDeleted', ev => this.handleRoomDeleted(ev));
|
|
43
|
-
this.tracker.client.on('RoomUpdated', ev => this.handleRoomUpdated(ev));
|
|
44
|
-
this.tracker.client.on('SpaceDeleted', ev => this.handleSpaceDeleted(ev));
|
|
45
|
-
this.tracker.client.on('SpaceUpdated', ev => this.handleSpaceUpdated(ev));
|
|
46
|
-
this.tracker.client.on('SpaceJoined', ev => this.handleSpaceJoined(ev));
|
|
47
|
-
this.tracker.client.on('SpaceLeft', ev => this.handleSpaceDeleted(ev));
|
|
48
|
-
this.tracker.client.on('SpaceMemberJoined', ev => this.handleSpaceMemberJoined(ev));
|
|
49
|
-
this.tracker.client.on('SpaceMemberLeft', ev => this.handleSpaceMemberLeft(ev));
|
|
50
|
-
this.tracker.client.on('SpaceMembers', ev => this.handleSpaceMembers(ev));
|
|
51
|
-
this.tracker.client.on('SpaceRooms', ev => this.handleSpaceRooms(ev));
|
|
52
|
-
this.tracker.client.on('RoomSummaryUpdated', ev => this.handleRoomSummaryUpdated(ev));
|
|
53
|
-
this.tracker.client.on('SpaceMemberUpdated', ev => this.handleSpaceMemberUpdated(ev));
|
|
54
|
-
this.tracker.client.on('UserUpdated', ev => this.handleUserUpdated(ev));
|
|
55
|
-
this.tracker.client.on('NewRole', ev => this.handleNewRole(ev));
|
|
56
|
-
this.tracker.client.on('RoleDeleted', ev => this.handleRoleDeleted(ev));
|
|
57
|
-
this.tracker.client.on('RoleUpdated', ev => this.handleRoleUpdated(ev));
|
|
58
|
-
this.tracker.client.on('Session', ev => this.handleSession(ev));
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Get collection of all the spaces you are in.
|
|
63
|
-
*/
|
|
64
|
-
public async get(): Promise<ObservableIndexedObjectCollection<Space>> {
|
|
65
|
-
await this.deferredSession.promise;
|
|
66
|
-
return this.list;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Get collection of space roles.
|
|
71
|
-
*/
|
|
72
|
-
public async getRoles(spaceId: string): Promise<ObservableIndexedObjectCollection<Role> | undefined> {
|
|
73
|
-
await this.deferredSession.promise;
|
|
74
|
-
return this.roles.get(spaceId);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Get collection of the all available rooms inside given space.
|
|
79
|
-
*/
|
|
80
|
-
public async getRooms(spaceId: string): Promise<ObservableIndexedObjectCollection<RoomSummary> | undefined> {
|
|
81
|
-
if (this.roomsPromises.notExist(spaceId)) {
|
|
82
|
-
this.roomsPromises.registerByFunction(async () => {
|
|
83
|
-
const result = await this.tracker.client.send('GetSpaceRooms', {id: spaceId});
|
|
84
|
-
if (result.error) {
|
|
85
|
-
throw result.error;
|
|
86
|
-
}
|
|
87
|
-
this.handleSpaceRooms(result.data);
|
|
88
|
-
}, spaceId);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
await this.roomsPromises.get(spaceId);
|
|
92
|
-
return this.rooms.get(spaceId);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Get collection of space members.
|
|
97
|
-
*/
|
|
98
|
-
public async getMembers(spaceId: string): Promise<ObservableIndexedObjectCollection<SpaceMember> | undefined> {
|
|
99
|
-
if (this.membersPromises.notExist(spaceId)) {
|
|
100
|
-
this.membersPromises.registerByFunction(async () => {
|
|
101
|
-
const result = await this.tracker.client.send('GetSpaceMembers', {id: spaceId});
|
|
102
|
-
if (result.error) {
|
|
103
|
-
throw result.error;
|
|
104
|
-
}
|
|
105
|
-
this.handleSpaceMembers(result.data);
|
|
106
|
-
}, spaceId);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
await this.membersPromises.get(spaceId);
|
|
110
|
-
return this.members.get(spaceId);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Get a space member representing the current user.
|
|
115
|
-
*/
|
|
116
|
-
public async getMe(spaceId: string): Promise<SpaceMember | undefined> {
|
|
117
|
-
const userId = (await this.tracker.getMe()).id;
|
|
118
|
-
|
|
119
|
-
if (! this.list.has(spaceId)) {
|
|
120
|
-
// User is not in passed space.
|
|
121
|
-
return undefined;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const members = await this.getMembers(spaceId);
|
|
125
|
-
return members?.items.find(member => member.user.id === userId);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
private handleNewRole(ev: NewRole): void {
|
|
129
|
-
const collection = this.roles.get(ev.spaceId);
|
|
130
|
-
collection.set(ev.role);
|
|
131
|
-
this.list.get(ev.spaceId).roles = collection.items;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
private handleNewRoom(ev: NewRoom): void {
|
|
135
|
-
this.rooms.get(ev.spaceId)?.set(ev.summary);
|
|
136
|
-
this.roomIdToSpaceId.set([ev.summary.id, ev.spaceId]);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
private handleRoomUpdated(ev: RoomUpdated): void {
|
|
140
|
-
if (ev.room.spaceId && this.rooms.has(ev.room.spaceId)) {
|
|
141
|
-
const rooms = this.rooms.get(ev.room.spaceId);
|
|
142
|
-
rooms.set({
|
|
143
|
-
...rooms.get(ev.room.id),
|
|
144
|
-
name: ev.room.name,
|
|
145
|
-
description: ev.room.description,
|
|
146
|
-
} as RoomSummary);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
private async handleRoomDeleted(ev: RoomDeleted): Promise<void> {
|
|
151
|
-
const spaceId = this.roomIdToSpaceId.get(ev.id);
|
|
152
|
-
this.roomIdToSpaceId.delete(ev.id);
|
|
153
|
-
|
|
154
|
-
if (! spaceId) {
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const space = this.list.get(spaceId);
|
|
159
|
-
let spaceChanged = false;
|
|
160
|
-
|
|
161
|
-
this.rooms.get(spaceId)?.delete(ev.id);
|
|
162
|
-
|
|
163
|
-
if (space.systemRoom === ev.id) {
|
|
164
|
-
space.systemRoom = null;
|
|
165
|
-
spaceChanged = true;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
if (space.defaultRooms.includes(ev.id)) {
|
|
169
|
-
space.defaultRooms = space.defaultRooms.filter(roomId => roomId !== ev.id);
|
|
170
|
-
spaceChanged = true;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
if (spaceChanged) {
|
|
174
|
-
this.list.set(space);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
private handleRoleDeleted(ev: RoleDeleted): void {
|
|
179
|
-
const collection = this.roles.get(ev.spaceId);
|
|
180
|
-
collection.delete(ev.id);
|
|
181
|
-
this.list.get(ev.spaceId).roles = collection.items;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
private handleSpaceUpdated(ev: SpaceUpdated): void {
|
|
185
|
-
this.list.set(ev.space);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
private handleSpaceDeleted(ev: SpaceDeleted | SpaceLeft): void {
|
|
189
|
-
const roomIds = this.rooms.get(ev.id)?.items.map(item => item.id) ?? [];
|
|
190
|
-
this.roomIdToSpaceId.delete(...roomIds);
|
|
191
|
-
|
|
192
|
-
this.roles.delete(ev.id);
|
|
193
|
-
this.members.delete(ev.id);
|
|
194
|
-
this.membersPromises.forget(ev.id);
|
|
195
|
-
this.rooms.delete(ev.id);
|
|
196
|
-
this.roomsPromises.forget(ev.id);
|
|
197
|
-
this.list.delete(ev.id);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
private handleSpaceJoined(ev: SpaceJoined): void {
|
|
201
|
-
this.addJoinedSpaces(ev.space);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
private addJoinedSpaces(...spaces: Space[]): void {
|
|
205
|
-
this.roles.set(...(spaces.map(space => [
|
|
206
|
-
space.id,
|
|
207
|
-
new ObservableIndexedObjectCollection<Role>('id', space.roles)
|
|
208
|
-
]) as [string, ObservableIndexedObjectCollection<Role>][]));
|
|
209
|
-
this.list.set(...spaces);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
private handleSpaceMemberJoined(ev: SpaceMemberJoined): void {
|
|
213
|
-
if (this.members.has(ev.spaceId)) {
|
|
214
|
-
this.members.get(ev.spaceId).set(ev.member);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
private handleSpaceMemberLeft(ev: SpaceMemberLeft): void {
|
|
219
|
-
if (this.members.has(ev.spaceId)) {
|
|
220
|
-
this.members.get(ev.spaceId).delete(ev.userId);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
private handleSpaceMembers(ev: SpaceMembers): void {
|
|
225
|
-
if (! this.members.has(ev.id)) {
|
|
226
|
-
this.members.set([
|
|
227
|
-
ev.id,
|
|
228
|
-
new ObservableIndexedObjectCollection(member => member?.user.id, ev.members)
|
|
229
|
-
]);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
this.
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
this.
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
1
|
+
import {ChatStateTracker} from "./ChatStateTracker";
|
|
2
|
+
import {IndexedCollection, ObservableIndexedObjectCollection} from "../IndexedObjectCollection";
|
|
3
|
+
import {
|
|
4
|
+
NewRole,
|
|
5
|
+
NewRoom,
|
|
6
|
+
Role,
|
|
7
|
+
RoleDeleted,
|
|
8
|
+
RoleUpdated,
|
|
9
|
+
RoomDeleted,
|
|
10
|
+
RoomSummary,
|
|
11
|
+
RoomSummaryUpdated,
|
|
12
|
+
RoomUpdated,
|
|
13
|
+
Session,
|
|
14
|
+
Space,
|
|
15
|
+
SpaceDeleted,
|
|
16
|
+
SpaceJoined,
|
|
17
|
+
SpaceLeft,
|
|
18
|
+
SpaceMember,
|
|
19
|
+
SpaceMemberJoined,
|
|
20
|
+
SpaceMemberLeft,
|
|
21
|
+
SpaceMembers,
|
|
22
|
+
SpaceMemberUpdated,
|
|
23
|
+
SpaceRooms,
|
|
24
|
+
SpaceUpdated,
|
|
25
|
+
UserUpdated
|
|
26
|
+
} from "../types/src";
|
|
27
|
+
import {DeferredTask, PromiseRegistry} from "./AsyncUtils";
|
|
28
|
+
import {reorderRolesOnPriorityUpdate} from "./functions";
|
|
29
|
+
|
|
30
|
+
export class SpacesManager {
|
|
31
|
+
private readonly list = new ObservableIndexedObjectCollection<Space>('id');
|
|
32
|
+
private readonly roles = new IndexedCollection<string, ObservableIndexedObjectCollection<Role>>();
|
|
33
|
+
private readonly rooms = new IndexedCollection<string, ObservableIndexedObjectCollection<RoomSummary>>();
|
|
34
|
+
private readonly roomIdToSpaceId = new IndexedCollection<string, string>();
|
|
35
|
+
private readonly members = new IndexedCollection<string, ObservableIndexedObjectCollection<SpaceMember>>();
|
|
36
|
+
private readonly deferredSession = new DeferredTask();
|
|
37
|
+
private readonly roomsPromises = new PromiseRegistry();
|
|
38
|
+
private readonly membersPromises = new PromiseRegistry();
|
|
39
|
+
|
|
40
|
+
public constructor(private tracker: ChatStateTracker) {
|
|
41
|
+
this.tracker.client.on('NewRoom', ev => this.handleNewRoom(ev));
|
|
42
|
+
this.tracker.client.on('RoomDeleted', ev => this.handleRoomDeleted(ev));
|
|
43
|
+
this.tracker.client.on('RoomUpdated', ev => this.handleRoomUpdated(ev));
|
|
44
|
+
this.tracker.client.on('SpaceDeleted', ev => this.handleSpaceDeleted(ev));
|
|
45
|
+
this.tracker.client.on('SpaceUpdated', ev => this.handleSpaceUpdated(ev));
|
|
46
|
+
this.tracker.client.on('SpaceJoined', ev => this.handleSpaceJoined(ev));
|
|
47
|
+
this.tracker.client.on('SpaceLeft', ev => this.handleSpaceDeleted(ev));
|
|
48
|
+
this.tracker.client.on('SpaceMemberJoined', ev => this.handleSpaceMemberJoined(ev));
|
|
49
|
+
this.tracker.client.on('SpaceMemberLeft', ev => this.handleSpaceMemberLeft(ev));
|
|
50
|
+
this.tracker.client.on('SpaceMembers', ev => this.handleSpaceMembers(ev));
|
|
51
|
+
this.tracker.client.on('SpaceRooms', ev => this.handleSpaceRooms(ev));
|
|
52
|
+
this.tracker.client.on('RoomSummaryUpdated', ev => this.handleRoomSummaryUpdated(ev));
|
|
53
|
+
this.tracker.client.on('SpaceMemberUpdated', ev => this.handleSpaceMemberUpdated(ev));
|
|
54
|
+
this.tracker.client.on('UserUpdated', ev => this.handleUserUpdated(ev));
|
|
55
|
+
this.tracker.client.on('NewRole', ev => this.handleNewRole(ev));
|
|
56
|
+
this.tracker.client.on('RoleDeleted', ev => this.handleRoleDeleted(ev));
|
|
57
|
+
this.tracker.client.on('RoleUpdated', ev => this.handleRoleUpdated(ev));
|
|
58
|
+
this.tracker.client.on('Session', ev => this.handleSession(ev));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get collection of all the spaces you are in.
|
|
63
|
+
*/
|
|
64
|
+
public async get(): Promise<ObservableIndexedObjectCollection<Space>> {
|
|
65
|
+
await this.deferredSession.promise;
|
|
66
|
+
return this.list;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get collection of space roles.
|
|
71
|
+
*/
|
|
72
|
+
public async getRoles(spaceId: string): Promise<ObservableIndexedObjectCollection<Role> | undefined> {
|
|
73
|
+
await this.deferredSession.promise;
|
|
74
|
+
return this.roles.get(spaceId);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Get collection of the all available rooms inside given space.
|
|
79
|
+
*/
|
|
80
|
+
public async getRooms(spaceId: string): Promise<ObservableIndexedObjectCollection<RoomSummary> | undefined> {
|
|
81
|
+
if (this.roomsPromises.notExist(spaceId)) {
|
|
82
|
+
this.roomsPromises.registerByFunction(async () => {
|
|
83
|
+
const result = await this.tracker.client.send('GetSpaceRooms', {id: spaceId});
|
|
84
|
+
if (result.error) {
|
|
85
|
+
throw result.error;
|
|
86
|
+
}
|
|
87
|
+
this.handleSpaceRooms(result.data);
|
|
88
|
+
}, spaceId);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
await this.roomsPromises.get(spaceId);
|
|
92
|
+
return this.rooms.get(spaceId);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Get collection of space members.
|
|
97
|
+
*/
|
|
98
|
+
public async getMembers(spaceId: string): Promise<ObservableIndexedObjectCollection<SpaceMember> | undefined> {
|
|
99
|
+
if (this.membersPromises.notExist(spaceId)) {
|
|
100
|
+
this.membersPromises.registerByFunction(async () => {
|
|
101
|
+
const result = await this.tracker.client.send('GetSpaceMembers', {id: spaceId});
|
|
102
|
+
if (result.error) {
|
|
103
|
+
throw result.error;
|
|
104
|
+
}
|
|
105
|
+
this.handleSpaceMembers(result.data);
|
|
106
|
+
}, spaceId);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
await this.membersPromises.get(spaceId);
|
|
110
|
+
return this.members.get(spaceId);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Get a space member representing the current user.
|
|
115
|
+
*/
|
|
116
|
+
public async getMe(spaceId: string): Promise<SpaceMember | undefined> {
|
|
117
|
+
const userId = (await this.tracker.getMe()).id;
|
|
118
|
+
|
|
119
|
+
if (! this.list.has(spaceId)) {
|
|
120
|
+
// User is not in passed space.
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const members = await this.getMembers(spaceId);
|
|
125
|
+
return members?.items.find(member => member.user.id === userId);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private handleNewRole(ev: NewRole): void {
|
|
129
|
+
const collection = this.roles.get(ev.spaceId);
|
|
130
|
+
collection.set(ev.role);
|
|
131
|
+
this.list.get(ev.spaceId).roles = collection.items;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private handleNewRoom(ev: NewRoom): void {
|
|
135
|
+
this.rooms.get(ev.spaceId)?.set(ev.summary);
|
|
136
|
+
this.roomIdToSpaceId.set([ev.summary.id, ev.spaceId]);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private handleRoomUpdated(ev: RoomUpdated): void {
|
|
140
|
+
if (ev.room.spaceId && this.rooms.has(ev.room.spaceId)) {
|
|
141
|
+
const rooms = this.rooms.get(ev.room.spaceId);
|
|
142
|
+
rooms.set({
|
|
143
|
+
...rooms.get(ev.room.id),
|
|
144
|
+
name: ev.room.name,
|
|
145
|
+
description: ev.room.description,
|
|
146
|
+
} as RoomSummary);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private async handleRoomDeleted(ev: RoomDeleted): Promise<void> {
|
|
151
|
+
const spaceId = this.roomIdToSpaceId.get(ev.id);
|
|
152
|
+
this.roomIdToSpaceId.delete(ev.id);
|
|
153
|
+
|
|
154
|
+
if (! spaceId) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const space = this.list.get(spaceId);
|
|
159
|
+
let spaceChanged = false;
|
|
160
|
+
|
|
161
|
+
this.rooms.get(spaceId)?.delete(ev.id);
|
|
162
|
+
|
|
163
|
+
if (space.systemRoom === ev.id) {
|
|
164
|
+
space.systemRoom = null;
|
|
165
|
+
spaceChanged = true;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (space.defaultRooms.includes(ev.id)) {
|
|
169
|
+
space.defaultRooms = space.defaultRooms.filter(roomId => roomId !== ev.id);
|
|
170
|
+
spaceChanged = true;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (spaceChanged) {
|
|
174
|
+
this.list.set(space);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private handleRoleDeleted(ev: RoleDeleted): void {
|
|
179
|
+
const collection = this.roles.get(ev.spaceId);
|
|
180
|
+
collection.delete(ev.id);
|
|
181
|
+
this.list.get(ev.spaceId).roles = collection.items;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
private handleSpaceUpdated(ev: SpaceUpdated): void {
|
|
185
|
+
this.list.set(ev.space);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
private handleSpaceDeleted(ev: SpaceDeleted | SpaceLeft): void {
|
|
189
|
+
const roomIds = this.rooms.get(ev.id)?.items.map(item => item.id) ?? [];
|
|
190
|
+
this.roomIdToSpaceId.delete(...roomIds);
|
|
191
|
+
|
|
192
|
+
this.roles.delete(ev.id);
|
|
193
|
+
this.members.delete(ev.id);
|
|
194
|
+
this.membersPromises.forget(ev.id);
|
|
195
|
+
this.rooms.delete(ev.id);
|
|
196
|
+
this.roomsPromises.forget(ev.id);
|
|
197
|
+
this.list.delete(ev.id);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
private handleSpaceJoined(ev: SpaceJoined): void {
|
|
201
|
+
this.addJoinedSpaces(ev.space);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private addJoinedSpaces(...spaces: Space[]): void {
|
|
205
|
+
this.roles.set(...(spaces.map(space => [
|
|
206
|
+
space.id,
|
|
207
|
+
new ObservableIndexedObjectCollection<Role>('id', space.roles)
|
|
208
|
+
]) as [string, ObservableIndexedObjectCollection<Role>][]));
|
|
209
|
+
this.list.set(...spaces);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
private handleSpaceMemberJoined(ev: SpaceMemberJoined): void {
|
|
213
|
+
if (this.members.has(ev.spaceId)) {
|
|
214
|
+
this.members.get(ev.spaceId).set(ev.member);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private handleSpaceMemberLeft(ev: SpaceMemberLeft): void {
|
|
219
|
+
if (this.members.has(ev.spaceId)) {
|
|
220
|
+
this.members.get(ev.spaceId).delete(ev.userId);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
private handleSpaceMembers(ev: SpaceMembers): void {
|
|
225
|
+
if (! this.members.has(ev.id)) {
|
|
226
|
+
this.members.set([
|
|
227
|
+
ev.id,
|
|
228
|
+
new ObservableIndexedObjectCollection(member => member?.user.id, ev.members)
|
|
229
|
+
]);
|
|
230
|
+
} else {
|
|
231
|
+
// Reconcile into the existing (bound) collection so a reconnect
|
|
232
|
+
// refetch updates it in place instead of leaving stale members.
|
|
233
|
+
this.members.get(ev.id).reconcile(...ev.members);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
private handleSpaceRooms(ev: SpaceRooms): void {
|
|
238
|
+
if (! this.rooms.has(ev.id)) {
|
|
239
|
+
this.rooms.set([ev.id, new ObservableIndexedObjectCollection('id', ev.summaries)]);
|
|
240
|
+
} else {
|
|
241
|
+
this.rooms.get(ev.id).reconcile(...ev.summaries);
|
|
242
|
+
}
|
|
243
|
+
ev.summaries.forEach(summary => this.roomIdToSpaceId.set([summary.id, ev.id]));
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
private async handleRoomSummaryUpdated(ev: RoomSummaryUpdated): Promise<void> {
|
|
247
|
+
const spaceId = this.roomIdToSpaceId.get(ev.summary.id);
|
|
248
|
+
const summariesPromise = this.roomsPromises.get(spaceId);
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Update summary only if the list was already loaded.
|
|
252
|
+
* RoomSummaryUpdated event has a partial summary, so we need to update the existing summary by merging it.
|
|
253
|
+
*/
|
|
254
|
+
if (spaceId && summariesPromise) {
|
|
255
|
+
await summariesPromise;
|
|
256
|
+
|
|
257
|
+
const summaries = this.rooms.get(spaceId);
|
|
258
|
+
const oldSummary = summaries.get(ev.summary.id);
|
|
259
|
+
let newSummary: RoomSummary;
|
|
260
|
+
|
|
261
|
+
if (oldSummary) {
|
|
262
|
+
newSummary = {...oldSummary, ...ev.summary};
|
|
263
|
+
} else {
|
|
264
|
+
newSummary = ev.summary;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
summaries.set(newSummary);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
private handleSpaceMemberUpdated(ev: SpaceMemberUpdated): void {
|
|
272
|
+
if (this.members.has(ev.spaceId)) {
|
|
273
|
+
const members = this.members.get(ev.spaceId);
|
|
274
|
+
const member = members.get(ev.userId);
|
|
275
|
+
members.set({...ev.member, user: member.user});
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
private handleRoleUpdated(ev: RoleUpdated): void {
|
|
280
|
+
const roles = this.roles.get(ev.spaceId);
|
|
281
|
+
const oldRole = roles.get(ev.role.id);
|
|
282
|
+
const newRole = ev.role;
|
|
283
|
+
const rolesToUpdate = [newRole];
|
|
284
|
+
|
|
285
|
+
if (oldRole.priority !== newRole.priority) {
|
|
286
|
+
rolesToUpdate.push(
|
|
287
|
+
...reorderRolesOnPriorityUpdate(roles.items, oldRole, newRole)
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
this.roles.get(ev.spaceId).set(...rolesToUpdate);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
private handleSession(ev: Session): void {
|
|
295
|
+
const stateSpaceIds = new Set(ev.state.spaces.map(space => space.id));
|
|
296
|
+
|
|
297
|
+
// Remove only spaces that were left/deleted on the server during the
|
|
298
|
+
// downtime, reusing the cascade cleanup (roles, rooms, members, index).
|
|
299
|
+
const removedSpaceIds = this.list.items
|
|
300
|
+
.filter(space => ! stateSpaceIds.has(space.id))
|
|
301
|
+
.map(space => space.id);
|
|
302
|
+
for (const spaceId of removedSpaceIds) {
|
|
303
|
+
this.handleSpaceDeleted({ id: spaceId } as SpaceDeleted);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Invalidate lazy caches (rooms/members) but keep their objects so the
|
|
307
|
+
// next access refetches and reconciles them in place.
|
|
308
|
+
this.roomsPromises.forgetAll();
|
|
309
|
+
this.membersPromises.forgetAll();
|
|
310
|
+
|
|
311
|
+
// Reconcile roles in place (kept, possibly bound object) and upsert the
|
|
312
|
+
// spaces from the authoritative snapshot.
|
|
313
|
+
for (const space of ev.state.spaces) {
|
|
314
|
+
if (this.roles.has(space.id)) {
|
|
315
|
+
this.roles.get(space.id).reconcile(...space.roles);
|
|
316
|
+
} else {
|
|
317
|
+
this.roles.set([
|
|
318
|
+
space.id,
|
|
319
|
+
new ObservableIndexedObjectCollection<Role>('id', space.roles),
|
|
320
|
+
]);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
this.list.set(...ev.state.spaces);
|
|
325
|
+
|
|
326
|
+
this.deferredSession.resolve();
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
private handleUserUpdated(ev: UserUpdated): void {
|
|
330
|
+
this.members.items.forEach((members) => {
|
|
331
|
+
const member = members.get(ev.user.id);
|
|
332
|
+
|
|
333
|
+
if (! member) {
|
|
334
|
+
// Skip space; updated user is not here
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
members.set({...member, user: ev.user});
|
|
339
|
+
});
|
|
340
|
+
}
|
|
314
341
|
}
|