polfan-server-js-client 0.4.0 → 0.4.1

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.
Files changed (33) hide show
  1. package/.idea/workspace.xml +57 -235
  2. package/build/index.cjs.js +10 -2
  3. package/build/index.cjs.js.map +1 -1
  4. package/build/index.umd.js.map +1 -1
  5. package/build/types/types/src/index.d.ts +9 -1
  6. package/build/types/types/src/schemes/Message.d.ts +5 -1
  7. package/build/types/types/src/schemes/MessagePoll.d.ts +10 -0
  8. package/build/types/types/src/schemes/Reaction.d.ts +13 -0
  9. package/build/types/types/src/schemes/commands/CreateMessage.d.ts +3 -0
  10. package/build/types/types/src/schemes/commands/GetMessages.d.ts +5 -0
  11. package/build/types/types/src/schemes/commands/GetReactionDetails.d.ts +8 -0
  12. package/build/types/types/src/schemes/commands/React.d.ts +6 -0
  13. package/build/types/types/src/schemes/commands/Unreact.d.ts +6 -0
  14. package/build/types/types/src/schemes/events/Messages.d.ts +3 -0
  15. package/build/types/types/src/schemes/events/Reacted.d.ts +9 -0
  16. package/build/types/types/src/schemes/events/ReactionDetails.d.ts +11 -0
  17. package/build/types/types/src/schemes/events/ReactionUpdated.d.ts +8 -0
  18. package/package.json +43 -43
  19. package/src/index.ts +31 -31
  20. package/src/state-tracker/UsersManager.ts +51 -51
  21. package/src/types/src/index.ts +333 -313
  22. package/src/types/src/schemes/Message.ts +5 -1
  23. package/src/types/src/schemes/MessagePoll.ts +12 -0
  24. package/src/types/src/schemes/Reaction.ts +16 -0
  25. package/src/types/src/schemes/commands/CreateMessage.ts +3 -0
  26. package/src/types/src/schemes/commands/GetMessages.ts +5 -0
  27. package/src/types/src/schemes/commands/GetReactionDetails.ts +9 -0
  28. package/src/types/src/schemes/commands/React.ts +7 -0
  29. package/src/types/src/schemes/commands/Unreact.ts +7 -0
  30. package/src/types/src/schemes/events/Messages.ts +3 -0
  31. package/src/types/src/schemes/events/Reacted.ts +10 -0
  32. package/src/types/src/schemes/events/ReactionDetails.ts +12 -0
  33. package/src/types/src/schemes/events/ReactionUpdated.ts +9 -0
@@ -145,4 +145,12 @@ import { Uninvite } from "./schemes/commands/Uninvite";
145
145
  import { GetInvited } from "./schemes/commands/GetInvited";
146
146
  import { Invited } from "./schemes/events/Invited";
147
147
  import { TopicUnfollowed } from "./schemes/events/TopicUnfollowed";
148
- export { Envelope, Message, MessageType, MessageAuthor, Role, Room, RoomType, RoomStream, RoomStreamType, RoomHistory, RoomHistoryMode, RoomMember, RoomSummary, RoomSummaryExtras, Space, SpaceMember, Topic, FollowedTopic, NotificationLevel, User, UserStatus, UserState, PermissionOverwritesValue, ChatLocation, SpaceSummary, SpaceDiscoverable, Emoticon, PermissionOverwritesTarget, BanObject, LeaveReason, UserRelationship, UserRelationshipType, CreateTopicInitialMessage, UserInformation, SessionPush, SessionData, SessionPlatform, UserData, PrivateMessagePolicy, Bye, Error, Messages, NewMessage, NewRole, NewRoom, NewTopic, TopicFollowed, TopicUnfollowed, FollowedTopics, FollowedTopicUpdated, ComputedPermissions, PermissionOverwrites, PermissionOverwritesUpdated, RoleDeleted, RoleUpdated, RoomDeleted, RoomUpdated, RoomJoined, RoomLeft, RoomMembersJoined, RoomMemberLeft, RoomMembers, RoomMemberUpdated, UserUpdated, Session, SpaceDeleted, SpaceUpdated, SpaceJoined, SpaceLeft, SpaceMemberJoined, SpaceMemberLeft, SpaceMembers, SpaceMemberUpdated, SpaceRooms, TopicDeleted, TopicUpdated, PermissionOverwriteTargets, Owners, Ok, DiscoverableSpaces, Emoticons, EmoticonDeleted, NewEmoticon, Bans, ClientData, SpaceSummaryEvent, RoomSummaryEvent, NewRelationship, RelationshipDeleted, Relationships, RoomSummaryUpdated, Pong, MessagesRedacted, UserInfo, Invited, AssignRole, GetMessages, CreateMessage, Ack, CreateRole, CreateRoom, CreateSpace, CreateTopic, FollowTopic, UnfollowTopic, UpdateFollowedTopic, GetFollowedTopics, DeassignRole, DeleteRole, DeleteRoom, DeleteSpace, DeleteTopic, SetPermissionOverwrites, GetPermissionOverwrites, GetComputedPermissions, GetRoomMembers, GetSession, GetSpaceMembers, GetSpaceRooms, JoinRoom, JoinSpace, LeaveRoom, LeaveSpace, UpdateRole, UpdateSpace, UpdateRoom, UpdateTopic, GetPermissionOverwriteTargets, CreateOwner, DeleteOwner, GetOwners, Topics, GetTopics, GetDiscoverableSpaces, GetEmoticons, CreateEmoticon, DeleteEmoticon, Ban, Unban, GetBans, Kick, GetClientData, SetClientData, GetSpaceSummary, GetRoomSummary, UpdateSpaceMember, CreateRelationship, DeleteRelationship, GetRelationships, UpdateRoomMember, Ping, ReportAbuse, RedactMessages, GetUserInfo, SetSessionData, GetSessionData, SetUserData, GetUserData, Invite, Uninvite, GetInvited, };
148
+ import { MessageReaction, ReactionType, ToggledReaction, UserReaction } from "./schemes/Reaction";
149
+ import { MessagePoll, MessagePollOption } from "./schemes/MessagePoll";
150
+ import { React } from "./schemes/commands/React";
151
+ import { Unreact } from "./schemes/commands/Unreact";
152
+ import { GetReactionDetails } from "./schemes/commands/GetReactionDetails";
153
+ import { ReactionUpdated } from "./schemes/events/ReactionUpdated";
154
+ import { Reacted } from "./schemes/events/Reacted";
155
+ import { ReactionDetails } from "./schemes/events/ReactionDetails";
156
+ export { Envelope, Message, MessageType, MessageAuthor, Role, Room, RoomType, RoomStream, RoomStreamType, RoomHistory, RoomHistoryMode, RoomMember, RoomSummary, RoomSummaryExtras, Space, SpaceMember, Topic, FollowedTopic, NotificationLevel, User, UserStatus, UserState, PermissionOverwritesValue, ChatLocation, SpaceSummary, SpaceDiscoverable, Emoticon, PermissionOverwritesTarget, BanObject, LeaveReason, UserRelationship, UserRelationshipType, CreateTopicInitialMessage, UserInformation, SessionPush, SessionData, SessionPlatform, UserData, PrivateMessagePolicy, ReactionType, MessageReaction, UserReaction, ToggledReaction, MessagePoll, MessagePollOption, Bye, Error, Messages, NewMessage, NewRole, NewRoom, NewTopic, TopicFollowed, TopicUnfollowed, FollowedTopics, FollowedTopicUpdated, ComputedPermissions, PermissionOverwrites, PermissionOverwritesUpdated, RoleDeleted, RoleUpdated, RoomDeleted, RoomUpdated, RoomJoined, RoomLeft, RoomMembersJoined, RoomMemberLeft, RoomMembers, RoomMemberUpdated, UserUpdated, Session, SpaceDeleted, SpaceUpdated, SpaceJoined, SpaceLeft, SpaceMemberJoined, SpaceMemberLeft, SpaceMembers, SpaceMemberUpdated, SpaceRooms, TopicDeleted, TopicUpdated, PermissionOverwriteTargets, Owners, Ok, DiscoverableSpaces, Emoticons, EmoticonDeleted, NewEmoticon, Bans, ClientData, SpaceSummaryEvent, RoomSummaryEvent, NewRelationship, RelationshipDeleted, Relationships, RoomSummaryUpdated, Pong, MessagesRedacted, UserInfo, Invited, ReactionUpdated, Reacted, ReactionDetails, AssignRole, GetMessages, CreateMessage, Ack, CreateRole, CreateRoom, CreateSpace, CreateTopic, FollowTopic, UnfollowTopic, UpdateFollowedTopic, GetFollowedTopics, DeassignRole, DeleteRole, DeleteRoom, DeleteSpace, DeleteTopic, SetPermissionOverwrites, GetPermissionOverwrites, GetComputedPermissions, GetRoomMembers, GetSession, GetSpaceMembers, GetSpaceRooms, JoinRoom, JoinSpace, LeaveRoom, LeaveSpace, UpdateRole, UpdateSpace, UpdateRoom, UpdateTopic, GetPermissionOverwriteTargets, CreateOwner, DeleteOwner, GetOwners, Topics, GetTopics, GetDiscoverableSpaces, GetEmoticons, CreateEmoticon, DeleteEmoticon, Ban, Unban, GetBans, Kick, GetClientData, SetClientData, GetSpaceSummary, GetRoomSummary, UpdateSpaceMember, CreateRelationship, DeleteRelationship, GetRelationships, UpdateRoomMember, Ping, ReportAbuse, RedactMessages, GetUserInfo, SetSessionData, GetSessionData, SetUserData, GetUserData, Invite, Uninvite, GetInvited, React, Unreact, GetReactionDetails, };
@@ -1,6 +1,8 @@
1
1
  import { User } from "./User";
2
2
  import { ChatLocation } from "./ChatLocation";
3
- export type MessageType = 'Text' | 'RoomJoin' | 'RoomMemberAdd' | 'RoomLeave' | 'SpaceJoin' | 'SpaceLeave' | 'TopicChange' | 'CustomNickChange' | 'Ephemeral';
3
+ import { MessageReaction } from "./Reaction";
4
+ import { MessagePoll } from "./MessagePoll";
5
+ export type MessageType = 'Text' | 'RoomJoin' | 'RoomMemberAdd' | 'RoomLeave' | 'SpaceJoin' | 'SpaceLeave' | 'TopicChange' | 'CustomNickChange' | 'Ephemeral' | 'Poll';
4
6
  export interface MessageAuthor {
5
7
  user: User;
6
8
  customNick?: string;
@@ -15,4 +17,6 @@ export interface Message {
15
17
  content?: string;
16
18
  topicRef: string | null;
17
19
  attachments: string[] | null;
20
+ reactions: MessageReaction[];
21
+ poll?: MessagePoll;
18
22
  }
@@ -0,0 +1,10 @@
1
+ import { ReactionType } from "./Reaction";
2
+ export interface MessagePollOption {
3
+ type: ReactionType;
4
+ value: string;
5
+ label: string;
6
+ }
7
+ export interface MessagePoll {
8
+ multipleChoice: boolean;
9
+ options: MessagePollOption[];
10
+ }
@@ -0,0 +1,13 @@
1
+ export type ReactionType = 'Emoji' | 'Emoticon';
2
+ export interface MessageReaction {
3
+ type: ReactionType;
4
+ value: string;
5
+ count: number;
6
+ }
7
+ export interface UserReaction {
8
+ type: ReactionType;
9
+ value: string;
10
+ }
11
+ export interface ToggledReaction extends UserReaction {
12
+ isAdded: boolean;
13
+ }
@@ -1,8 +1,11 @@
1
1
  import { ChatLocation } from "../ChatLocation";
2
+ import { MessagePoll } from "../MessagePoll";
2
3
  export interface CreateMessage {
3
4
  location: ChatLocation;
4
5
  content: string;
5
6
  attachments?: string[];
6
7
  customNick?: string;
7
8
  customColor?: string;
9
+ /** Turns the message into a poll; its content becomes the question. */
10
+ poll?: MessagePoll;
8
11
  }
@@ -5,4 +5,9 @@ export interface GetMessages {
5
5
  after?: string;
6
6
  around?: string;
7
7
  limit?: number;
8
+ /**
9
+ * Pull the reactions of the asking user along with the messages, instead of
10
+ * asking for their own state separately.
11
+ */
12
+ includeMyReactions?: boolean;
8
13
  }
@@ -0,0 +1,8 @@
1
+ import { ReactionType } from "../Reaction";
2
+ export interface GetReactionDetails {
3
+ messageId: string;
4
+ type: ReactionType;
5
+ value: string;
6
+ limit?: number;
7
+ offset?: number;
8
+ }
@@ -0,0 +1,6 @@
1
+ import { ReactionType } from "../Reaction";
2
+ export interface React {
3
+ messageId: string;
4
+ type: ReactionType;
5
+ value: string;
6
+ }
@@ -0,0 +1,6 @@
1
+ import { ReactionType } from "../Reaction";
2
+ export interface Unreact {
3
+ messageId: string;
4
+ type: ReactionType;
5
+ value: string;
6
+ }
@@ -1,6 +1,9 @@
1
1
  import { ChatLocation } from "../ChatLocation";
2
2
  import { Message } from "../Message";
3
+ import { UserReaction } from "../Reaction";
3
4
  export interface Messages {
4
5
  location: ChatLocation;
5
6
  messages: Message[];
7
+ /** Message id => reactions of the asking user; present only when requested. */
8
+ myReactions?: Record<string, UserReaction[]>;
6
9
  }
@@ -0,0 +1,9 @@
1
+ import { ToggledReaction } from "../Reaction";
2
+ /**
3
+ * Sent to every client of the voting user, so all of their devices know what they
4
+ * voted for without refetching the message history.
5
+ */
6
+ export interface Reacted {
7
+ messageId: string;
8
+ reaction: ToggledReaction;
9
+ }
@@ -0,0 +1,11 @@
1
+ import { ReactionType } from "../Reaction";
2
+ /**
3
+ * Who reacted, oldest vote first. Only the ids travel - the nicks are already
4
+ * known from the room members collection.
5
+ */
6
+ export interface ReactionDetails {
7
+ messageId: string;
8
+ type: ReactionType;
9
+ value: string;
10
+ userIds: string[];
11
+ }
@@ -0,0 +1,8 @@
1
+ import { MessageReaction } from "../Reaction";
2
+ /**
3
+ * The single source of truth of a reaction counter, broadcast to the whole room.
4
+ */
5
+ export interface ReactionUpdated {
6
+ messageId: string;
7
+ reaction: MessageReaction;
8
+ }
package/package.json CHANGED
@@ -1,43 +1,43 @@
1
- {
2
- "name": "polfan-server-js-client",
3
- "version": "0.4.0",
4
- "description": "JavaScript client library for handling communication with Polfan chat server.",
5
- "author": "Jarosław Żak",
6
- "license": "MIT",
7
- "main": "build/index.cjs.js",
8
- "browser": "build/index.umd.js",
9
- "types": "build/types/index.d.ts",
10
- "exports": {
11
- ".": {
12
- "types": "./build/types/index.d.ts",
13
- "require": "./build/index.cjs.js",
14
- "browser": "./build/index.umd.js",
15
- "default": "./build/index.cjs.js"
16
- }
17
- },
18
- "scripts": {
19
- "build": "npm run build:node && npm run build:browser && tsc",
20
- "build:node": "webpack --config webpack.config.node.js",
21
- "build:browser": "webpack --config webpack.config.browser.js",
22
- "test": "jest",
23
- "coverage": "npm run test -- --coverage",
24
- "trypublish": "npm publish || true"
25
- },
26
- "devDependencies": {
27
- "@babel/cli": "^7.28.3",
28
- "@babel/core": "^7.28.5",
29
- "@babel/preset-env": "^7.28.5",
30
- "@babel/preset-typescript": "^7.28.5",
31
- "@types/jest": "^29.5.14",
32
- "babel-loader": "^9.2.1",
33
- "file-loader": "^6.2.0",
34
- "jest": "^29.7.0",
35
- "ts-node": "^10.9.2",
36
- "typescript": "^4.9.5",
37
- "webpack": "^5.103.0",
38
- "webpack-cli": "^5.1.4"
39
- },
40
- "dependencies": {
41
- "terser-webpack-plugin": "^5.3.14"
42
- }
43
- }
1
+ {
2
+ "name": "polfan-server-js-client",
3
+ "version": "0.4.1",
4
+ "description": "JavaScript client library for handling communication with Polfan chat server.",
5
+ "author": "Jarosław Żak",
6
+ "license": "MIT",
7
+ "main": "build/index.cjs.js",
8
+ "browser": "build/index.umd.js",
9
+ "types": "build/types/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./build/types/index.d.ts",
13
+ "require": "./build/index.cjs.js",
14
+ "browser": "./build/index.umd.js",
15
+ "default": "./build/index.cjs.js"
16
+ }
17
+ },
18
+ "scripts": {
19
+ "build": "npm run build:node && npm run build:browser && tsc",
20
+ "build:node": "webpack --config webpack.config.node.js",
21
+ "build:browser": "webpack --config webpack.config.browser.js",
22
+ "test": "jest",
23
+ "coverage": "npm run test -- --coverage",
24
+ "trypublish": "npm publish || true"
25
+ },
26
+ "devDependencies": {
27
+ "@babel/cli": "^7.28.3",
28
+ "@babel/core": "^7.28.5",
29
+ "@babel/preset-env": "^7.28.5",
30
+ "@babel/preset-typescript": "^7.28.5",
31
+ "@types/jest": "^29.5.14",
32
+ "babel-loader": "^9.2.1",
33
+ "file-loader": "^6.2.0",
34
+ "jest": "^29.7.0",
35
+ "ts-node": "^10.9.2",
36
+ "typescript": "^4.9.5",
37
+ "webpack": "^5.103.0",
38
+ "webpack-cli": "^5.1.4"
39
+ },
40
+ "dependencies": {
41
+ "terser-webpack-plugin": "^5.3.14"
42
+ }
43
+ }
package/src/index.ts CHANGED
@@ -1,32 +1,32 @@
1
- import {WebSocketChatClient} from "./WebSocketChatClient";
2
- import {WebApiChatClient} from "./WebApiChatClient";
3
- import {
4
- IndexedCollection,
5
- IndexedObjectCollection,
6
- ObservableIndexedCollection,
7
- ObservableIndexedObjectCollection
8
- } from "./IndexedObjectCollection";
9
- import {FilesClient, File} from "./FilesClient";
10
- import { Permissions, PermissionDefinition, Layer } from "./Permissions";
11
- import * as ChatTypes from './types/src';
12
- import {UserStatus} from './types/src';
13
- import {extractUserFromMember} from "./state-tracker/functions";
14
- import {AbstractRestClient} from "./AbstractRestClient";
15
- import {UnreadSummary} from "./state-tracker/FollowedTopicsManager";
16
-
17
- export {
18
- IndexedCollection, ObservableIndexedCollection,
19
- IndexedObjectCollection, ObservableIndexedObjectCollection,
20
- Permissions, PermissionDefinition, Layer,
21
- WebSocketChatClient, WebApiChatClient,
22
- FilesClient,
23
- AbstractRestClient,
24
- extractUserFromMember,
25
- UserStatus,
26
- };
27
-
28
- export type {
29
- ChatTypes,
30
- File,
31
- UnreadSummary,
1
+ import {WebSocketChatClient} from "./WebSocketChatClient";
2
+ import {WebApiChatClient} from "./WebApiChatClient";
3
+ import {
4
+ IndexedCollection,
5
+ IndexedObjectCollection,
6
+ ObservableIndexedCollection,
7
+ ObservableIndexedObjectCollection
8
+ } from "./IndexedObjectCollection";
9
+ import {FilesClient, File} from "./FilesClient";
10
+ import { Permissions, PermissionDefinition, Layer } from "./Permissions";
11
+ import * as ChatTypes from './types/src';
12
+ import {UserStatus} from './types/src';
13
+ import {extractUserFromMember} from "./state-tracker/functions";
14
+ import {AbstractRestClient} from "./AbstractRestClient";
15
+ import {UnreadSummary} from "./state-tracker/FollowedTopicsManager";
16
+
17
+ export {
18
+ IndexedCollection, ObservableIndexedCollection,
19
+ IndexedObjectCollection, ObservableIndexedObjectCollection,
20
+ Permissions, PermissionDefinition, Layer,
21
+ WebSocketChatClient, WebApiChatClient,
22
+ FilesClient,
23
+ AbstractRestClient,
24
+ extractUserFromMember,
25
+ UserStatus,
26
+ };
27
+
28
+ export type {
29
+ ChatTypes,
30
+ File,
31
+ UnreadSummary,
32
32
  };
@@ -1,52 +1,52 @@
1
- import {ChatStateTracker} from "./ChatStateTracker";
2
- import {ObservableIndexedObjectCollection} from "../IndexedObjectCollection";
3
- import {RoomMember, Session, SpaceMember, User} from "../types/src";
4
- import {extractUserFromMember} from "./functions";
5
- import {EventTarget} from "../EventTarget";
6
-
7
- export class UsersManager {
8
- public readonly onlineStatus = new EventTarget<{ change: User }>();
9
-
10
- private readonly users: ObservableIndexedObjectCollection<User> = new ObservableIndexedObjectCollection('id');
11
-
12
- public constructor(private tracker: ChatStateTracker) {
13
- // RoomMemberUpdated & SpaceMemberUpdated events are not contains user object
14
- tracker.client.on('UserUpdated', event => this.handleUsers([event.user]));
15
- tracker.client.on('RoomMembersJoined', event => this.handleMembers(event.members));
16
- tracker.client.on('SpaceMemberJoined', event => this.handleMembers([event.member]));
17
- tracker.client.on('SpaceMembers', event => this.handleMembers(event.members));
18
- tracker.client.on('RoomMembers', event => this.handleMembers(event.members));
19
- tracker.client.on('Messages', event => this.handleUsers(event.messages.map(message => message.author.user)));
20
- tracker.client.on('NewMessage', event => this.handleUsers([event.message.author.user]));
21
- tracker.client.on('Session', event => this.handleSession(event));
22
- }
23
-
24
- /**
25
- * Get all available (cached) user objects at once.
26
- */
27
- public async getAvailable(): Promise<ObservableIndexedObjectCollection<User>> {
28
- return this.users;
29
- }
30
-
31
- private handleMembers(members: (RoomMember | SpaceMember)[]): void {
32
- this.handleUsers(members.map(extractUserFromMember));
33
- }
34
-
35
- private handleSession(session: Session): void {
36
- // Keep the "seen users" cache across reconnects so bound user lists do
37
- // not blank out; just ensure our own user is present/updated. Stale
38
- // entries are refreshed as member/message collections refetch.
39
- this.handleUsers([session.user]);
40
- }
41
-
42
- private handleUsers(users: User[]): void {
43
- users.forEach(newUser => {
44
- const oldUser = this.users.get(newUser.id);
45
- if (oldUser && oldUser.status !== newUser.status) {
46
- this.onlineStatus.emit('change', newUser);
47
- }
48
- });
49
-
50
- this.users.set(...users);
51
- }
1
+ import {ChatStateTracker} from "./ChatStateTracker";
2
+ import {ObservableIndexedObjectCollection} from "../IndexedObjectCollection";
3
+ import {RoomMember, Session, SpaceMember, User} from "../types/src";
4
+ import {extractUserFromMember} from "./functions";
5
+ import {EventTarget} from "../EventTarget";
6
+
7
+ export class UsersManager {
8
+ public readonly onlineStatus = new EventTarget<{ change: User }>();
9
+
10
+ private readonly users: ObservableIndexedObjectCollection<User> = new ObservableIndexedObjectCollection('id');
11
+
12
+ public constructor(private tracker: ChatStateTracker) {
13
+ // RoomMemberUpdated & SpaceMemberUpdated events are not contains user object
14
+ tracker.client.on('UserUpdated', event => this.handleUsers([event.user]));
15
+ tracker.client.on('RoomMembersJoined', event => this.handleMembers(event.members));
16
+ tracker.client.on('SpaceMemberJoined', event => this.handleMembers([event.member]));
17
+ tracker.client.on('SpaceMembers', event => this.handleMembers(event.members));
18
+ tracker.client.on('RoomMembers', event => this.handleMembers(event.members));
19
+ tracker.client.on('Messages', event => this.handleUsers(event.messages.map(message => message.author.user)));
20
+ tracker.client.on('NewMessage', event => this.handleUsers([event.message.author.user]));
21
+ tracker.client.on('Session', event => this.handleSession(event));
22
+ }
23
+
24
+ /**
25
+ * Get all available (cached) user objects at once.
26
+ */
27
+ public async getAvailable(): Promise<ObservableIndexedObjectCollection<User>> {
28
+ return this.users;
29
+ }
30
+
31
+ private handleMembers(members: (RoomMember | SpaceMember)[]): void {
32
+ this.handleUsers(members.map(extractUserFromMember));
33
+ }
34
+
35
+ private handleSession(session: Session): void {
36
+ // Keep the "seen users" cache across reconnects so bound user lists do
37
+ // not blank out; just ensure our own user is present/updated. Stale
38
+ // entries are refreshed as member/message collections refetch.
39
+ this.handleUsers([session.user]);
40
+ }
41
+
42
+ private handleUsers(users: User[]): void {
43
+ users.forEach(newUser => {
44
+ const oldUser = this.users.get(newUser.id);
45
+ if (oldUser && oldUser.status !== newUser.status) {
46
+ this.onlineStatus.emit('change', newUser);
47
+ }
48
+ });
49
+
50
+ this.users.set(...users);
51
+ }
52
52
  }