@signalwire/js 3.7.1-dev.202201131750.6d234cc.0 → 3.8.0
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/dist/core/src/BaseComponent.d.ts +29 -31
- package/dist/core/src/BaseComponent.d.ts.map +1 -1
- package/dist/core/src/BaseJWTSession.d.ts +1 -0
- package/dist/core/src/BaseJWTSession.d.ts.map +1 -1
- package/dist/core/src/BaseSession.d.ts +1 -1
- package/dist/core/src/BaseSession.d.ts.map +1 -1
- package/dist/core/src/chat/BaseChat.d.ts +4 -3
- package/dist/core/src/chat/BaseChat.d.ts.map +1 -1
- package/dist/core/src/chat/ChatMember.d.ts +6 -0
- package/dist/core/src/chat/ChatMember.d.ts.map +1 -1
- package/dist/core/src/chat/ChatMessage.d.ts +9 -0
- package/dist/core/src/chat/ChatMessage.d.ts.map +1 -1
- package/dist/core/src/chat/index.d.ts +1 -0
- package/dist/core/src/chat/index.d.ts.map +1 -1
- package/dist/core/src/chat/methods.d.ts +18 -15
- package/dist/core/src/chat/methods.d.ts.map +1 -1
- package/dist/core/src/chat/workers.d.ts +2 -1
- package/dist/core/src/chat/workers.d.ts.map +1 -1
- package/dist/core/src/index.d.ts +3 -3
- package/dist/core/src/index.d.ts.map +1 -1
- package/dist/core/src/internal/BaseBackendSession.d.ts.map +1 -1
- package/dist/core/src/redux/actions.d.ts +17 -14
- package/dist/core/src/redux/actions.d.ts.map +1 -1
- package/dist/core/src/redux/features/component/componentSlice.d.ts +3952 -10
- package/dist/core/src/redux/features/component/componentSlice.d.ts.map +1 -1
- package/dist/core/src/redux/features/executeQueue/executeQueueSlice.d.ts +44 -7
- package/dist/core/src/redux/features/executeQueue/executeQueueSlice.d.ts.map +1 -1
- package/dist/core/src/redux/features/pubSub/pubSubSaga.d.ts.map +1 -1
- package/dist/core/src/redux/features/session/sessionSlice.d.ts +74 -8
- package/dist/core/src/redux/features/session/sessionSlice.d.ts.map +1 -1
- package/dist/core/src/redux/index.d.ts +9 -25
- package/dist/core/src/redux/index.d.ts.map +1 -1
- package/dist/core/src/redux/interfaces.d.ts +5 -4
- package/dist/core/src/redux/interfaces.d.ts.map +1 -1
- package/dist/core/src/redux/rootReducer.d.ts +417 -3
- package/dist/core/src/redux/rootReducer.d.ts.map +1 -1
- package/dist/core/src/redux/rootSaga.d.ts +23 -5
- package/dist/core/src/redux/rootSaga.d.ts.map +1 -1
- package/dist/core/src/redux/toolkit/configureStore.d.ts +77 -0
- package/dist/core/src/redux/toolkit/configureStore.d.ts.map +1 -0
- package/dist/core/src/redux/toolkit/createAction.d.ts +180 -0
- package/dist/core/src/redux/toolkit/createAction.d.ts.map +1 -0
- package/dist/core/src/redux/toolkit/createReducer.d.ts +42 -0
- package/dist/core/src/redux/toolkit/createReducer.d.ts.map +1 -0
- package/dist/core/src/redux/toolkit/createSlice.d.ts +142 -0
- package/dist/core/src/redux/toolkit/createSlice.d.ts.map +1 -0
- package/dist/core/src/redux/toolkit/devtoolsExtension.d.ts +185 -0
- package/dist/core/src/redux/toolkit/devtoolsExtension.d.ts.map +1 -0
- package/dist/core/src/redux/toolkit/getDefaultMiddleware.d.ts +12 -0
- package/dist/core/src/redux/toolkit/getDefaultMiddleware.d.ts.map +1 -0
- package/dist/core/src/redux/toolkit/index.d.ts +13 -0
- package/dist/core/src/redux/toolkit/index.d.ts.map +1 -0
- package/dist/core/src/redux/toolkit/isPlainObject.d.ts +12 -0
- package/dist/core/src/redux/toolkit/isPlainObject.d.ts.map +1 -0
- package/dist/core/src/redux/toolkit/mapBuilders.d.ts +38 -0
- package/dist/core/src/redux/toolkit/mapBuilders.d.ts.map +1 -0
- package/dist/core/src/redux/toolkit/tsHelpers.d.ts +51 -0
- package/dist/core/src/redux/toolkit/tsHelpers.d.ts.map +1 -0
- package/dist/core/src/redux/toolkit/utils.d.ts +11 -0
- package/dist/core/src/redux/toolkit/utils.d.ts.map +1 -0
- package/dist/core/src/redux/utils/createDestroyableSlice.d.ts +3 -2
- package/dist/core/src/redux/utils/createDestroyableSlice.d.ts.map +1 -1
- package/dist/core/src/rooms/methods.d.ts +31 -21
- package/dist/core/src/rooms/methods.d.ts.map +1 -1
- package/dist/core/src/testUtils.d.ts +8 -25
- package/dist/core/src/testUtils.d.ts.map +1 -1
- package/dist/core/src/types/chat.d.ts +12 -12
- package/dist/core/src/types/chat.d.ts.map +1 -1
- package/dist/core/src/types/utils.d.ts +7 -0
- package/dist/core/src/types/utils.d.ts.map +1 -1
- package/dist/core/src/types/videoMember.d.ts +10 -1
- package/dist/core/src/types/videoMember.d.ts.map +1 -1
- package/dist/core/src/utils/constants.d.ts +1 -0
- package/dist/core/src/utils/constants.d.ts.map +1 -1
- package/dist/core/src/utils/index.d.ts +4 -1
- package/dist/core/src/utils/index.d.ts.map +1 -1
- package/dist/core/src/utils/interfaces.d.ts +19 -6
- package/dist/core/src/utils/interfaces.d.ts.map +1 -1
- package/dist/index.esm.js +179 -49
- package/dist/index.esm.js.map +3 -3
- package/dist/index.js +223 -100
- package/dist/index.js.map +3 -3
- package/dist/index.umd.js +2 -2
- package/dist/index.umd.js.map +1 -1
- package/dist/js/src/BaseRoomSession.d.ts +7 -0
- package/dist/js/src/BaseRoomSession.d.ts.map +1 -1
- package/dist/js/src/JWTSession.d.ts.map +1 -1
- package/dist/js/src/RoomSession.d.ts.map +1 -1
- package/dist/js/src/RoomSession.docs.d.ts +9 -1
- package/dist/js/src/RoomSession.docs.d.ts.map +1 -1
- package/dist/js/src/chat/Client.d.ts +37 -3
- package/dist/js/src/chat/Client.d.ts.map +1 -1
- package/dist/js/src/chat/Client.docs.d.ts +238 -0
- package/dist/js/src/chat/Client.docs.d.ts.map +1 -0
- package/dist/js/src/chat/index.d.ts +5 -0
- package/dist/js/src/chat/index.d.ts.map +1 -1
- package/dist/js/src/features/actions.d.ts +1 -1
- package/dist/js/src/features/actions.d.ts.map +1 -1
- package/dist/js/src/index.d.ts +5 -2
- package/dist/js/src/index.d.ts.map +1 -1
- package/dist/js/src/testUtils.d.ts +16 -50
- package/dist/js/src/testUtils.d.ts.map +1 -1
- package/dist/js/src/utils/interfaces.d.ts +16 -2
- package/dist/js/src/utils/interfaces.d.ts.map +1 -1
- package/dist/js/src/video/memberListUpdatedWorker.d.ts +14 -0
- package/dist/js/src/video/memberListUpdatedWorker.d.ts.map +1 -0
- package/dist/js/src/video/workers.d.ts +2 -0
- package/dist/js/src/video/workers.d.ts.map +1 -0
- package/dist/js/tsconfig.build.tsbuildinfo +1 -1
- package/dist/webrtc/src/BaseConnection.d.ts +7 -0
- package/dist/webrtc/src/BaseConnection.d.ts.map +1 -1
- package/dist/webrtc/src/RTCPeer.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/BaseRoomSession.test.ts +2 -2
- package/src/BaseRoomSession.ts +15 -0
- package/src/RoomSession.docs.ts +10 -1
- package/src/RoomSession.ts +16 -4
- package/src/chat/Client.docs.ts +259 -0
- package/src/chat/Client.test.ts +4 -4
- package/src/chat/Client.ts +39 -3
- package/src/chat/index.ts +10 -0
- package/src/index.ts +6 -3
- package/src/utils/interfaces.ts +22 -2
- package/src/video/memberListUpdatedWorker.ts +217 -0
- package/src/video/workers.ts +1 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import type { ConsumerContract, Chat } from '@signalwire/core'
|
|
2
|
+
|
|
3
|
+
import type { ClientApiEvents, ClientFullState } from './Client'
|
|
4
|
+
|
|
5
|
+
export type PagingCursor =
|
|
6
|
+
| {
|
|
7
|
+
before: string
|
|
8
|
+
after?: never
|
|
9
|
+
}
|
|
10
|
+
| {
|
|
11
|
+
before?: never
|
|
12
|
+
after: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ClientDocs
|
|
16
|
+
extends Omit<
|
|
17
|
+
ConsumerContract<ClientApiEvents, ClientFullState>,
|
|
18
|
+
'subscribe'
|
|
19
|
+
> {
|
|
20
|
+
/**
|
|
21
|
+
* Creates a new Chat client.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
*
|
|
25
|
+
* ```js
|
|
26
|
+
* import { Chat } from '@signalwire/js'
|
|
27
|
+
*
|
|
28
|
+
* const chatClient = new Chat.Client({
|
|
29
|
+
* token: '<your_chat_token>',
|
|
30
|
+
* })
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
new (chatOptions: {
|
|
34
|
+
/** SignalWire Chat token (you can get one with the REST APIs) */
|
|
35
|
+
token: string
|
|
36
|
+
/** @ignore */
|
|
37
|
+
logLevel?: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent'
|
|
38
|
+
}): this
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Replaces the token used by the client with a new one. You can use this
|
|
42
|
+
* method to replace the token when for example it is expiring, in order to
|
|
43
|
+
* keep the session alive.
|
|
44
|
+
*
|
|
45
|
+
* The new token can contain different channels from the previous one. In that
|
|
46
|
+
* case, you will need to subscribe to the new channels if you want to receive
|
|
47
|
+
* messages for those. Channels that were in the previous token but are not in
|
|
48
|
+
* the new one will get unsubscribed automatically.
|
|
49
|
+
*
|
|
50
|
+
* @param token the new token.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```js
|
|
54
|
+
* const chatClient = new Chat.Client({
|
|
55
|
+
* token: '<your chat token>'
|
|
56
|
+
* })
|
|
57
|
+
*
|
|
58
|
+
* chatClient.on('session.expiring', async () => {
|
|
59
|
+
* const newToken = await fetchNewToken(..)
|
|
60
|
+
*
|
|
61
|
+
* await chatClient.updateToken(newToken)
|
|
62
|
+
* })
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
updateToken(token: string): Promise<void>
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* List of channels for which you want to receive messages. You can only
|
|
69
|
+
* subscribe to those channels for which your token has read permission.
|
|
70
|
+
*
|
|
71
|
+
* Note that the `subscribe` function is idempotent, and calling it again with
|
|
72
|
+
* a different set of channels _will not_ unsubscribe you from the old ones.
|
|
73
|
+
* To unsubscribe, use {@link unsubscribe}.
|
|
74
|
+
*
|
|
75
|
+
* @param channels the channels to subscribe to, either in the form of a
|
|
76
|
+
* string (for one channel) or an array of strings.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```js
|
|
80
|
+
* const chatClient = new Chat.Client({
|
|
81
|
+
* token: '<your chat token>'
|
|
82
|
+
* })
|
|
83
|
+
*
|
|
84
|
+
* chatClient.on('message', m => console.log(m))
|
|
85
|
+
*
|
|
86
|
+
* await chatClient.subscribe("my-channel")
|
|
87
|
+
* await chatClient.subscribe(["chan-2", "chan-3"])
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
subscribe(channels: string | string[]): Promise<any>
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* List of channels from which you want to unsubscribe.
|
|
94
|
+
*
|
|
95
|
+
* @param channels the channels to unsubscribe from, either in the form of a
|
|
96
|
+
* string (for one channel) or an array of strings.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```js
|
|
100
|
+
* await chatClient.unsubscribe("my-channel")
|
|
101
|
+
* await chatClient.unsubscribe(["chan-2", "chan-3"])
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
unsubscribe(channels: string | string[]): Promise<any>
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Publish a message into the specified channel.
|
|
108
|
+
*
|
|
109
|
+
* @example Publishing a message as a string:
|
|
110
|
+
* ```js
|
|
111
|
+
* await chatClient.publish({
|
|
112
|
+
* channel: 'my-channel',
|
|
113
|
+
* message: 'Hello, world.'
|
|
114
|
+
* })
|
|
115
|
+
* ```
|
|
116
|
+
*
|
|
117
|
+
* @example Publishing a message as an object:
|
|
118
|
+
* ```js
|
|
119
|
+
* await chatClient.publish({
|
|
120
|
+
* channel: 'my-channel',
|
|
121
|
+
* message: {
|
|
122
|
+
* field_one: 'value_one',
|
|
123
|
+
* field_two: 'value_two',
|
|
124
|
+
* }
|
|
125
|
+
* })
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
publish(params: {
|
|
129
|
+
/** The message to send. This can be any JSON-serializable object. */
|
|
130
|
+
content: any
|
|
131
|
+
/** Channel in which to send the message. */
|
|
132
|
+
channel: string
|
|
133
|
+
/**
|
|
134
|
+
* Metadata associated with the message. There are no requirements on the
|
|
135
|
+
* content of metadata.
|
|
136
|
+
*/
|
|
137
|
+
meta?: Record<any, any>
|
|
138
|
+
}): Promise<any>
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Returns the list of messages that were sent to the specified channel.
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```js
|
|
145
|
+
* const m = await chatClient.getMessages({ channel: 'chan1' })
|
|
146
|
+
*
|
|
147
|
+
* m.messages.length; // 23
|
|
148
|
+
* m.messages[0]; // the most recent message
|
|
149
|
+
* m.messages[0].member; // the sender
|
|
150
|
+
* m.messages[0].content; // the content
|
|
151
|
+
* m.messages[0].meta; // the metadata (if any)
|
|
152
|
+
*
|
|
153
|
+
* m.cursor.next; // if not null, there are more messages.
|
|
154
|
+
*
|
|
155
|
+
* // Get the next page using the cursor
|
|
156
|
+
* const next = await chatClient.getMessages({
|
|
157
|
+
* channel: 'chan1',
|
|
158
|
+
* cursor: {
|
|
159
|
+
* after: m.cursor.after
|
|
160
|
+
* }
|
|
161
|
+
* })
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
getMessages(params: {
|
|
165
|
+
/** Channel for which to retrieve the messages. */
|
|
166
|
+
channel: string
|
|
167
|
+
/** Cursor for pagination. */
|
|
168
|
+
cursor?: PagingCursor
|
|
169
|
+
}): Promise<any>
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Returns the list of members in the given channel.
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* ```js
|
|
176
|
+
* const m = await chatClient.getMembers({ channel: 'my-channel' })
|
|
177
|
+
*
|
|
178
|
+
* m.members.length; // 7
|
|
179
|
+
* m.members[0]; // { id: ..., channel: ..., state: ... }
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
182
|
+
getMembers(params: {
|
|
183
|
+
/** The channel for which to get the list of members. */
|
|
184
|
+
channel: string
|
|
185
|
+
}): Promise<any>
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Sets a state object for the current member, for the specified channels. The
|
|
189
|
+
* previous state object will be completely replaced.
|
|
190
|
+
*
|
|
191
|
+
* @example
|
|
192
|
+
* ```js
|
|
193
|
+
* await chatClient.setMemberState({
|
|
194
|
+
* channels: ['chan1', 'chan2'],
|
|
195
|
+
* state: {
|
|
196
|
+
* online: true,
|
|
197
|
+
* typing: false
|
|
198
|
+
* }
|
|
199
|
+
* })
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
202
|
+
setMemberState(params: {
|
|
203
|
+
/** Channels for which to set the state. */
|
|
204
|
+
channels: string | string[]
|
|
205
|
+
/**
|
|
206
|
+
* The state to set. There are no requirements on the content of the state.
|
|
207
|
+
*/
|
|
208
|
+
state: Record<any, any>
|
|
209
|
+
}): Promise<any>
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Returns the states of a member in the specified channels.
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* ```js
|
|
216
|
+
* const s = await chatClient.getMemberState({
|
|
217
|
+
* channels: ['chan1', 'chan2'],
|
|
218
|
+
* memberId: 'my-member-id'
|
|
219
|
+
* })
|
|
220
|
+
*
|
|
221
|
+
* s.channels.length; // 2
|
|
222
|
+
* s.channels.chan1.state; // the state object for chan1
|
|
223
|
+
* ```
|
|
224
|
+
*/
|
|
225
|
+
getMemberState(params: {
|
|
226
|
+
/** Channels for which to get the state. */
|
|
227
|
+
channels: string | string[]
|
|
228
|
+
/** Id of the member for which to get the state. */
|
|
229
|
+
memberId: string
|
|
230
|
+
}): Promise<any>
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export interface ClientApiEventsDocs {
|
|
234
|
+
/**
|
|
235
|
+
* The session is going to expire.
|
|
236
|
+
* Use the `updateToken` method to refresh your token.
|
|
237
|
+
*/
|
|
238
|
+
'session.expiring': () => void
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* A new message has been received.
|
|
242
|
+
*/
|
|
243
|
+
message: (message: Chat.ChatMessage) => void
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* A new member joined the chat.
|
|
247
|
+
*/
|
|
248
|
+
'member.joined': (member: Chat.ChatMember) => void
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* A member updated its state.
|
|
252
|
+
*/
|
|
253
|
+
'member.updated': (member: Chat.ChatMember) => void
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* A member left the chat.
|
|
257
|
+
*/
|
|
258
|
+
'member.left': (member: Chat.ChatMember) => void
|
|
259
|
+
}
|
package/src/chat/Client.test.ts
CHANGED
|
@@ -128,7 +128,7 @@ describe('ChatClient Object', () => {
|
|
|
128
128
|
await chat.subscribe(['test'])
|
|
129
129
|
await chat.publish({
|
|
130
130
|
channel: 'test',
|
|
131
|
-
|
|
131
|
+
content: 'test',
|
|
132
132
|
})
|
|
133
133
|
|
|
134
134
|
const connectMsg = JSON.parse(server.messages[0].toString())
|
|
@@ -147,7 +147,7 @@ describe('ChatClient Object', () => {
|
|
|
147
147
|
chat.subscribe(['test']),
|
|
148
148
|
chat.publish({
|
|
149
149
|
channel: 'test',
|
|
150
|
-
|
|
150
|
+
content: 'test',
|
|
151
151
|
}),
|
|
152
152
|
])
|
|
153
153
|
|
|
@@ -390,7 +390,7 @@ describe('ChatClient Object', () => {
|
|
|
390
390
|
|
|
391
391
|
const params = {
|
|
392
392
|
channel: 'test',
|
|
393
|
-
|
|
393
|
+
content: 'test',
|
|
394
394
|
}
|
|
395
395
|
|
|
396
396
|
await chat.publish(params)
|
|
@@ -476,7 +476,7 @@ describe('ChatClient Object', () => {
|
|
|
476
476
|
// calling unsubscribe()
|
|
477
477
|
await chat.publish({
|
|
478
478
|
channel: 'test',
|
|
479
|
-
|
|
479
|
+
content: 'test',
|
|
480
480
|
})
|
|
481
481
|
|
|
482
482
|
await expect(() => chat.unsubscribe(['test1_error'])).rejects.toBeTruthy()
|
package/src/chat/Client.ts
CHANGED
|
@@ -7,18 +7,54 @@ import type {
|
|
|
7
7
|
} from '@signalwire/core'
|
|
8
8
|
import { getLogger } from '@signalwire/core'
|
|
9
9
|
import { createClient } from '../createClient'
|
|
10
|
+
import {
|
|
11
|
+
ClientApiEventsDocs,
|
|
12
|
+
ClientDocs,
|
|
13
|
+
} from './Client.docs'
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
interface ClientApiEventsMain extends ChatNamespace.BaseChatApiEvents {}
|
|
16
|
+
export interface ClientApiEvents extends AssertSameType<ClientApiEventsMain, ClientApiEventsDocs> {}
|
|
12
17
|
|
|
18
|
+
/** @ignore */
|
|
13
19
|
export interface ClientFullState extends Client {}
|
|
14
20
|
interface ClientMain
|
|
15
21
|
extends ChatContract,
|
|
16
22
|
Omit<ConsumerContract<ClientApiEvents, ClientFullState>, 'subscribe'> {}
|
|
17
23
|
|
|
18
|
-
|
|
19
|
-
|
|
24
|
+
/**
|
|
25
|
+
* You can use the Client object to build a messaging system into the browser.
|
|
26
|
+
*
|
|
27
|
+
* Example usage:
|
|
28
|
+
*
|
|
29
|
+
* ```js
|
|
30
|
+
* import { Chat } from '@signalwire/js'
|
|
31
|
+
*
|
|
32
|
+
* const chatClient = new Chat.Client({
|
|
33
|
+
* token: '<your_chat_token>', // get this from the REST APIs
|
|
34
|
+
* })
|
|
35
|
+
*
|
|
36
|
+
* await chatClient.subscribe([ 'mychannel1', 'mychannel2' ])
|
|
37
|
+
*
|
|
38
|
+
* chatClient.on('message', (message) => {
|
|
39
|
+
* console.log("Received", message.content,
|
|
40
|
+
* "on", message.channel,
|
|
41
|
+
* "at", message.publishedAt)
|
|
42
|
+
* })
|
|
43
|
+
*
|
|
44
|
+
* await chatClient.publish({
|
|
45
|
+
* channel: 'mychannel1',
|
|
46
|
+
* message: 'hello world'
|
|
47
|
+
* })
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* ## Events
|
|
51
|
+
*
|
|
52
|
+
* Please see {@link ClientApiEvents} for the list of events emitted by a chat
|
|
53
|
+
* Client object.
|
|
54
|
+
*/
|
|
20
55
|
export interface Client extends AssertSameType<ClientMain, ClientDocs> {}
|
|
21
56
|
|
|
57
|
+
/** @ignore */
|
|
22
58
|
export interface ClientOptions extends UserOptions {}
|
|
23
59
|
|
|
24
60
|
export const Client = function (chatOptions: ClientOptions) {
|
package/src/chat/index.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -46,6 +46,12 @@ export type RoomScreenShare = VideoMemberEntity & { type: 'screen' }
|
|
|
46
46
|
/** @ignore @deprecated */
|
|
47
47
|
export type RoomDevice = VideoMemberEntity & { type: 'device' }
|
|
48
48
|
|
|
49
|
+
/**
|
|
50
|
+
* The Chat namespace contains the classes and functions that you need to
|
|
51
|
+
* create a real-time chat application.
|
|
52
|
+
*/
|
|
53
|
+
export * as Chat from './chat'
|
|
54
|
+
|
|
49
55
|
/**
|
|
50
56
|
* The Video namespace contains the classes and functions that you need to
|
|
51
57
|
* create a video conferencing application.
|
|
@@ -96,6 +102,3 @@ export type {
|
|
|
96
102
|
RoomSessionObjectEventsHandlerMap as RoomObjectEventsHandlerMap,
|
|
97
103
|
RoomSessionObjectEvents as RoomObjectEvents,
|
|
98
104
|
} from './utils/interfaces'
|
|
99
|
-
|
|
100
|
-
/** @internal */
|
|
101
|
-
export * as __sw__Chat from './chat'
|
package/src/utils/interfaces.ts
CHANGED
|
@@ -5,7 +5,6 @@ import type {
|
|
|
5
5
|
VideoLayoutEventNames,
|
|
6
6
|
VideoRoomSessionEventNames,
|
|
7
7
|
VideoRoomEventParams,
|
|
8
|
-
VideoMemberEntity,
|
|
9
8
|
InternalVideoMemberEntity,
|
|
10
9
|
VideoMemberEventNames,
|
|
11
10
|
MemberUpdated,
|
|
@@ -22,6 +21,7 @@ import type {
|
|
|
22
21
|
VideoRoomSessionContract,
|
|
23
22
|
OnlyFunctionProperties,
|
|
24
23
|
AssertSameType,
|
|
24
|
+
MemberListUpdated,
|
|
25
25
|
} from '@signalwire/core'
|
|
26
26
|
import { INTERNAL_MEMBER_UPDATABLE_PROPS } from '@signalwire/core'
|
|
27
27
|
import type { RoomSession } from '../RoomSession'
|
|
@@ -29,6 +29,18 @@ import type { RoomSessionDevice } from '../RoomSessionDevice'
|
|
|
29
29
|
import type { RoomSessionScreenShare } from '../RoomSessionScreenShare'
|
|
30
30
|
import { RoomMemberSelfMethodsInterfaceDocs } from './interfaces.docs'
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* @privateRemarks
|
|
34
|
+
* Every other package exposing a `VideoMemberEntity` is
|
|
35
|
+
* transforming the server payload into something else, with
|
|
36
|
+
* the most significant change being converting properties
|
|
37
|
+
* from snake to camel case. The `js` package, on the other
|
|
38
|
+
* hand, exposes the server payload pretty much as is (as as
|
|
39
|
+
* v3) so what we consider internal (sdk and server) in
|
|
40
|
+
* other packages is external (user facing) for `js`.
|
|
41
|
+
*/
|
|
42
|
+
type VideoMemberEntity = InternalVideoMemberEntity
|
|
43
|
+
|
|
32
44
|
const INTERNAL_MEMBER_UPDATED_EVENTS = Object.keys(
|
|
33
45
|
INTERNAL_MEMBER_UPDATABLE_PROPS
|
|
34
46
|
).map((key) => {
|
|
@@ -47,19 +59,27 @@ export type VideoMemberHandlerParams = { member: VideoMemberEntity }
|
|
|
47
59
|
export type VideoMemberUpdatedHandlerParams = {
|
|
48
60
|
member: VideoMemberEntityUpdated
|
|
49
61
|
}
|
|
62
|
+
export type VideoMemberListUpdatedParams = { members: VideoMemberEntity[] }
|
|
50
63
|
|
|
51
64
|
export type RoomSessionObjectEventsHandlerMap = Record<
|
|
52
65
|
VideoLayoutEventNames,
|
|
53
66
|
(params: { layout: VideoLayout }) => void
|
|
54
67
|
> &
|
|
55
68
|
Record<
|
|
56
|
-
Exclude<
|
|
69
|
+
Exclude<
|
|
70
|
+
VideoMemberEventNames,
|
|
71
|
+
MemberUpdated | MemberUpdatedEventNames | MemberListUpdated
|
|
72
|
+
>,
|
|
57
73
|
(params: VideoMemberHandlerParams) => void
|
|
58
74
|
> &
|
|
59
75
|
Record<
|
|
60
76
|
Extract<VideoMemberEventNames, MemberUpdated | MemberUpdatedEventNames>,
|
|
61
77
|
(params: VideoMemberUpdatedHandlerParams) => void
|
|
62
78
|
> &
|
|
79
|
+
Record<
|
|
80
|
+
Extract<VideoMemberEventNames, MemberListUpdated>,
|
|
81
|
+
(params: VideoMemberListUpdatedParams) => void
|
|
82
|
+
> &
|
|
63
83
|
Record<
|
|
64
84
|
DeprecatedMemberUpdatableProps,
|
|
65
85
|
(params: DeprecatedVideoMemberHandlerParams) => void
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import {
|
|
2
|
+
sagaEffects,
|
|
3
|
+
SagaIterator,
|
|
4
|
+
SDKWorker,
|
|
5
|
+
toSyntheticEvent,
|
|
6
|
+
validateEventsToSubscribe,
|
|
7
|
+
toInternalEventName,
|
|
8
|
+
PubSubChannel,
|
|
9
|
+
InternalVideoMemberEntity,
|
|
10
|
+
InternalVideoMemberUpdatedEvent,
|
|
11
|
+
VideoMemberJoinedEvent,
|
|
12
|
+
VideoMemberLeftEvent,
|
|
13
|
+
VideoMemberUpdatedEvent,
|
|
14
|
+
InternalVideoRoomJoinedEvent,
|
|
15
|
+
MapToPubSubShape,
|
|
16
|
+
} from '@signalwire/core'
|
|
17
|
+
import type { RoomSession } from '../RoomSession'
|
|
18
|
+
import type { VideoMemberListUpdatedParams } from '../utils/interfaces'
|
|
19
|
+
|
|
20
|
+
const noop = () => {}
|
|
21
|
+
|
|
22
|
+
const EXTERNAL_MEMBER_LIST_UPDATED_EVENT = 'video.memberList.updated'
|
|
23
|
+
|
|
24
|
+
const INTERNAL_MEMBER_LIST_UPDATED_EVENT = toInternalEventName({
|
|
25
|
+
event: EXTERNAL_MEMBER_LIST_UPDATED_EVENT,
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
const SYNTHETIC_MEMBER_LIST_UPDATED_EVENT = toSyntheticEvent(
|
|
29
|
+
INTERNAL_MEMBER_LIST_UPDATED_EVENT
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* List of action types this worker cares about.
|
|
34
|
+
*/
|
|
35
|
+
type MemberListUpdatedTargetActions = MapToPubSubShape<
|
|
36
|
+
| InternalVideoRoomJoinedEvent
|
|
37
|
+
| InternalVideoMemberUpdatedEvent
|
|
38
|
+
| VideoMemberJoinedEvent
|
|
39
|
+
| VideoMemberLeftEvent
|
|
40
|
+
| VideoMemberUpdatedEvent
|
|
41
|
+
>
|
|
42
|
+
|
|
43
|
+
const MEMBER_LIST_EVENTS: Array<MemberListUpdatedTargetActions['type']> = [
|
|
44
|
+
/** Alias to `video.room.subscribed` */
|
|
45
|
+
'video.room.joined',
|
|
46
|
+
'video.member.joined',
|
|
47
|
+
'video.member.left',
|
|
48
|
+
'video.member.updated',
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
type MemberList = Map<string, InternalVideoMemberEntity>
|
|
52
|
+
|
|
53
|
+
const isMemberListEvent = (
|
|
54
|
+
event: string
|
|
55
|
+
): event is MemberListUpdatedTargetActions['type'] => {
|
|
56
|
+
// @ts-expect-error
|
|
57
|
+
return MEMBER_LIST_EVENTS.includes(event)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const getMemberListEventsToSubscribe = (subscriptions: MemberListUpdatedTargetActions['type'][]) => {
|
|
61
|
+
return validateEventsToSubscribe(MEMBER_LIST_EVENTS).filter((event) => {
|
|
62
|
+
return !subscriptions.includes(event)
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const shouldHandleMemberList = (subscriptions: string[]) => {
|
|
67
|
+
return subscriptions.some((event) =>
|
|
68
|
+
event.includes(INTERNAL_MEMBER_LIST_UPDATED_EVENT)
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const getMembersFromAction = (action: MemberListUpdatedTargetActions) => {
|
|
73
|
+
if (action.type === 'video.room.joined') {
|
|
74
|
+
return action.payload.room_session.members
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return [action.payload.member]
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const getUpdatedMembers = ({
|
|
81
|
+
action,
|
|
82
|
+
memberList,
|
|
83
|
+
}: {
|
|
84
|
+
action: MemberListUpdatedTargetActions
|
|
85
|
+
memberList: MemberList
|
|
86
|
+
}) => {
|
|
87
|
+
const actionMembers = getMembersFromAction(action)
|
|
88
|
+
|
|
89
|
+
switch (action.type) {
|
|
90
|
+
case 'video.member.left':
|
|
91
|
+
actionMembers.forEach((member: InternalVideoMemberEntity) => {
|
|
92
|
+
memberList.delete(member.id)
|
|
93
|
+
})
|
|
94
|
+
break
|
|
95
|
+
default:
|
|
96
|
+
actionMembers.forEach((member: InternalVideoMemberEntity) => {
|
|
97
|
+
memberList.set(member.id, member)
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return Array.from(memberList.values())
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const initMemberListSubscriptions = (
|
|
105
|
+
room: RoomSession,
|
|
106
|
+
subscriptions: MemberListUpdatedTargetActions['type'][]
|
|
107
|
+
) => {
|
|
108
|
+
const events = getMemberListEventsToSubscribe(subscriptions)
|
|
109
|
+
|
|
110
|
+
events.forEach((event) => {
|
|
111
|
+
/**
|
|
112
|
+
* Params to `subscribe` come from the event handlers
|
|
113
|
+
* the user has attached so to make sure we subscribe to
|
|
114
|
+
* all the appropiate events needed for
|
|
115
|
+
* `memberList.updated` to work, we must subscribe to
|
|
116
|
+
* the required events. We don't need to act upon the
|
|
117
|
+
* event (that's why we attach a `noop`), just to
|
|
118
|
+
* register it (`subscribe` gets its values from
|
|
119
|
+
* `BaseComponent.getSubscriptions`, which gets
|
|
120
|
+
* populated by each of the event handlers the user
|
|
121
|
+
* attached).
|
|
122
|
+
*/
|
|
123
|
+
room.once(event as any, noop)
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* This handler will act as a simple bridge between
|
|
128
|
+
* synthetic events and external events.
|
|
129
|
+
*/
|
|
130
|
+
const eventBridgeHandler = ({ members }: VideoMemberListUpdatedParams) => {
|
|
131
|
+
// @ts-expect-error
|
|
132
|
+
room.emit(EXTERNAL_MEMBER_LIST_UPDATED_EVENT, { members })
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// @ts-expect-error
|
|
136
|
+
room.on(SYNTHETIC_MEMBER_LIST_UPDATED_EVENT, eventBridgeHandler)
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Any events attached by the saga should be specified
|
|
140
|
+
* here so it can be cleaned up when needed.
|
|
141
|
+
*/
|
|
142
|
+
const cleanup = () => {
|
|
143
|
+
// @ts-expect-error
|
|
144
|
+
room.off(SYNTHETIC_MEMBER_LIST_UPDATED_EVENT, eventBridgeHandler)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
cleanup,
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function* membersListUpdatedWatcher({
|
|
153
|
+
pubSubChannel,
|
|
154
|
+
}: {
|
|
155
|
+
pubSubChannel: PubSubChannel
|
|
156
|
+
}): SagaIterator {
|
|
157
|
+
const memberList: MemberList = new Map()
|
|
158
|
+
|
|
159
|
+
function* worker(pubSubAction: MemberListUpdatedTargetActions) {
|
|
160
|
+
const roomSessionId =
|
|
161
|
+
pubSubAction.type === 'video.room.joined'
|
|
162
|
+
? pubSubAction.payload.room_session.id
|
|
163
|
+
: pubSubAction.payload.room_session_id
|
|
164
|
+
|
|
165
|
+
const members = getUpdatedMembers({ action: pubSubAction, memberList })
|
|
166
|
+
const memberListPayload = {
|
|
167
|
+
/**
|
|
168
|
+
* At this point it's needed to send the
|
|
169
|
+
* `room_session_id` so the pubSubSaga can properly
|
|
170
|
+
* infer the namespace for emitting the events to the
|
|
171
|
+
* appropiate room.
|
|
172
|
+
*/
|
|
173
|
+
room_session_id: roomSessionId,
|
|
174
|
+
members,
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// TODO: add typings
|
|
178
|
+
yield sagaEffects.put(pubSubChannel, {
|
|
179
|
+
type: SYNTHETIC_MEMBER_LIST_UPDATED_EVENT as any,
|
|
180
|
+
payload: memberListPayload as any,
|
|
181
|
+
})
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
while (true) {
|
|
185
|
+
const pubSubAction: MemberListUpdatedTargetActions = yield sagaEffects.take(
|
|
186
|
+
pubSubChannel,
|
|
187
|
+
({ type }: any) => {
|
|
188
|
+
return isMemberListEvent(type)
|
|
189
|
+
}
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
yield sagaEffects.fork(worker, pubSubAction)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export const memberListUpdatedWorker: SDKWorker<RoomSession> =
|
|
197
|
+
function* membersChangedWorker({
|
|
198
|
+
channels: { pubSubChannel },
|
|
199
|
+
instance,
|
|
200
|
+
}): SagaIterator {
|
|
201
|
+
// @ts-expect-error
|
|
202
|
+
const subscriptions = instance.getSubscriptions()
|
|
203
|
+
|
|
204
|
+
if (!shouldHandleMemberList(subscriptions)) {
|
|
205
|
+
return
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const { cleanup } = initMemberListSubscriptions(instance, subscriptions)
|
|
209
|
+
|
|
210
|
+
yield sagaEffects.fork(membersListUpdatedWatcher, {
|
|
211
|
+
pubSubChannel,
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
instance.once('destroy', () => {
|
|
215
|
+
cleanup()
|
|
216
|
+
})
|
|
217
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./memberListUpdatedWorker"
|