@rapidmx/meet-plugin 0.15.0 → 0.16.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/README.md +35 -55
- package/apps/meet/[token].tsx +341 -341
- package/apps/meet/_CallControls.tsx +508 -483
- package/apps/meet/_CallView.tsx +1060 -924
- package/apps/meet/_ChatPanel.tsx +194 -0
- package/apps/meet/_ColorWheel.tsx +136 -0
- package/apps/meet/_MeetLobby.tsx +224 -224
- package/apps/meet/_ParticipantTile.tsx +243 -242
- package/apps/meet/_Whiteboard.tsx +577 -0
- package/apps/meet/_icons.tsx +135 -90
- package/apps/meet/_meetApi.ts +213 -212
- package/apps/shared/collab/CollabSession.ts +675 -0
- package/apps/shared/collab/WhiteboardModel.ts +216 -0
- package/apps/shared/collab/chatMarkdown.tsx +375 -0
- package/apps/shared/collab/color.ts +67 -0
- package/apps/shared/collab/protocol.ts +321 -0
- package/apps/shared/collab/whiteboardRender.ts +311 -0
- package/apps/shared/media/filters/VideoFilterProcessor.ts +408 -408
- package/apps/shared/media/filters/mlModels.ts +174 -174
- package/apps/shared/relay/RelayClient.ts +2 -1
- package/apps/shared/relay/RelayTransport.ts +2 -1
- package/apps/shared/relay/relayEnv.ts +1 -1
- package/apps/shared/signaling/SignalingClient.ts +335 -0
- package/apps/shared/webrtc/MeshConnectionManager.ts +971 -971
- package/apps/shared/webrtc/types.ts +335 -316
- package/dist/apps/meet/[token].js +1 -1
- package/dist/apps/meet/_CallControls.d.ts +12 -4
- package/dist/apps/meet/_CallControls.js +5 -5
- package/dist/apps/meet/_CallView.d.ts +19 -4
- package/dist/apps/meet/_CallView.js +100 -11
- package/dist/apps/meet/_ChatPanel.d.ts +24 -0
- package/dist/apps/meet/_ChatPanel.js +116 -0
- package/dist/apps/meet/_ColorWheel.d.ts +14 -0
- package/dist/apps/meet/_ColorWheel.js +82 -0
- package/dist/apps/meet/_ParticipantTile.d.ts +2 -1
- package/dist/apps/meet/_ParticipantTile.js +1 -1
- package/dist/apps/meet/_Whiteboard.d.ts +64 -0
- package/dist/apps/meet/_Whiteboard.js +312 -0
- package/dist/apps/meet/_icons.d.ts +12 -0
- package/dist/apps/meet/_icons.js +12 -0
- package/dist/apps/meet/_meetApi.d.ts +8 -7
- package/dist/apps/meet/_meetApi.js +2 -2
- package/dist/apps/shared/collab/CollabSession.d.ts +208 -0
- package/dist/apps/shared/collab/CollabSession.js +559 -0
- package/dist/apps/shared/collab/WhiteboardModel.d.ts +54 -0
- package/dist/apps/shared/collab/WhiteboardModel.js +197 -0
- package/dist/apps/shared/collab/chatMarkdown.d.ts +65 -0
- package/dist/apps/shared/collab/chatMarkdown.js +311 -0
- package/dist/apps/shared/collab/color.d.ts +18 -0
- package/dist/apps/shared/collab/color.js +59 -0
- package/dist/apps/shared/collab/protocol.d.ts +157 -0
- package/dist/apps/shared/collab/protocol.js +222 -0
- package/dist/apps/shared/collab/whiteboardRender.d.ts +31 -0
- package/dist/apps/shared/collab/whiteboardRender.js +268 -0
- package/dist/apps/shared/relay/RelayClient.js +2 -1
- package/dist/apps/shared/relay/RelayTransport.d.ts +2 -1
- package/dist/apps/shared/relay/RelayTransport.js +2 -1
- package/dist/apps/shared/relay/relayEnv.d.ts +1 -1
- package/dist/apps/shared/signaling/SignalingClient.d.ts +95 -0
- package/dist/apps/shared/signaling/SignalingClient.js +278 -0
- package/dist/apps/shared/webrtc/MeshConnectionManager.d.ts +7 -7
- package/dist/apps/shared/webrtc/MeshConnectionManager.js +8 -8
- package/dist/apps/shared/webrtc/types.d.ts +38 -24
- package/dist/apps/shared/webrtc/types.js +3 -4
- package/dist/lib/routes/BaseVideoMeetingRoute.js +171 -69
- package/dist/lib/routes/BaseVideoMeetingRoute.js.map +1 -1
- package/dist/lib/util/SignalingHub.js +339 -0
- package/dist/lib/util/SignalingHub.js.map +1 -0
- package/dist/types/routes/BaseVideoMeetingRoute.d.ts +68 -46
- package/dist/types/util/SignalingHub.d.ts +112 -0
- package/package.json +208 -208
- package/apps/shared/push/GuestSignalingClient.ts +0 -359
- package/dist/apps/shared/push/GuestSignalingClient.d.ts +0 -98
- package/dist/apps/shared/push/GuestSignalingClient.js +0 -312
package/apps/meet/_meetApi.ts
CHANGED
|
@@ -1,212 +1,213 @@
|
|
|
1
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
-
// Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
|
|
3
|
-
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
-
/**
|
|
6
|
-
* Typed client for this plugin's one public, unauthenticated endpoint - `GET /api/mail/video-meetings/join/:token`
|
|
7
|
-
* (`BaseVideoMeetingRoute.join()`) - mirroring `booking-plugin`'s own `apps/shared/bookingApi.ts` convention of
|
|
8
|
-
* redefining the response shape locally rather than importing it from `../../src/...`: `apps/` compiles under its
|
|
9
|
-
* own `tsconfig.apps.json` (bundler resolution, DOM-facing), independent of the backend's `tsconfig.json` (NodeNext),
|
|
10
|
-
* and every other plugin app in this codebase keeps that boundary the same way.
|
|
11
|
-
*/
|
|
12
|
-
import { apiFetch } from "@rapidmx/web-client/lib/util/api.js";
|
|
13
|
-
|
|
14
|
-
export type VideoMeetingVisibility = "private" | "public";
|
|
15
|
-
export type VideoMeetingStatus = "scheduled" | "active" | "ended" | "cancelled";
|
|
16
|
-
|
|
17
|
-
/** Mirrors `BaseVideoMeetingRoute.ts`'s `IceServerConfig` - already the exact shape `RTCPeerConnection`'s
|
|
18
|
-
* `iceServers` constructor option expects, passed straight through with no reshaping. */
|
|
19
|
-
export interface IceServerConfig {
|
|
20
|
-
urls: string | string[];
|
|
21
|
-
username?: string;
|
|
22
|
-
credential?: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/** Mirrors `BaseVideoMeetingRoute.ts`'s `PublicVideoMeeting`. */
|
|
26
|
-
export interface PublicVideoMeeting {
|
|
27
|
-
uid: string;
|
|
28
|
-
title: string;
|
|
29
|
-
visibility: VideoMeetingVisibility;
|
|
30
|
-
status: VideoMeetingStatus;
|
|
31
|
-
hostDisplayName?: string;
|
|
32
|
-
/** The account uid that owns the meeting - the sole source of a "host" identity on the client. Absent when the
|
|
33
|
-
* server can't resolve one (an orphaned mailbox, or a server that predates this field), in which case no host
|
|
34
|
-
* controls are shown to anyone. */
|
|
35
|
-
hostUid?: string;
|
|
36
|
-
/** When `true`, a joining participant's microphone starts muted - see `useLocalMedia`'s `forceMuteOnJoin`
|
|
37
|
-
* option, which this is threaded into. Absent reads as `false`. */
|
|
38
|
-
forceMuteOnJoin?: boolean;
|
|
39
|
-
/** Whether this meeting currently requires a password - never the password or its hash, just whether one is
|
|
40
|
-
* set. Absent reads as `false`. */
|
|
41
|
-
hasPassword?: boolean;
|
|
42
|
-
/** Whether this meeting currently requires the host to admit each participant before they join - see
|
|
43
|
-
* `requestAdmission()`. Absent reads as `false`. */
|
|
44
|
-
waitingRoomEnabled?: boolean;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Mirrors `BaseVideoMeetingRoute.ts`'s `VideoMeetingJoinResult`. `authenticated: true` means the visiting browser
|
|
49
|
-
* already held a valid session for a real RapidMX identity when it called `join()` - `token`/`expiresAt` are then
|
|
50
|
-
* omitted entirely (there is no guest token to hand over), and the caller (`[token].tsx`) must not write any
|
|
51
|
-
* cookie of its own before
|
|
52
|
-
* authenticates it there, with zero new client-side auth handling. `authenticated: false` (the common
|
|
53
|
-
* case) is unchanged from Phase 1: `token`/`expiresAt` are present, and `
|
|
54
|
-
* cookie before connecting.
|
|
55
|
-
*/
|
|
56
|
-
export interface VideoMeetingJoinResult {
|
|
57
|
-
meeting: PublicVideoMeeting;
|
|
58
|
-
iceServers: IceServerConfig[];
|
|
59
|
-
authenticated: boolean;
|
|
60
|
-
/** The identity to identify as on the signaling channel/mesh - the caller's own real uid when `authenticated`
|
|
61
|
-
* is `true`, otherwise a freshly minted synthetic `guest:<random>` uid. Always present. */
|
|
62
|
-
selfUid: string;
|
|
63
|
-
/** A short-lived guest JWT, usable against
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
*
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}
|
|
1
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
|
|
3
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
+
/**
|
|
6
|
+
* Typed client for this plugin's one public, unauthenticated endpoint - `GET /api/mail/video-meetings/join/:token`
|
|
7
|
+
* (`BaseVideoMeetingRoute.join()`) - mirroring `booking-plugin`'s own `apps/shared/bookingApi.ts` convention of
|
|
8
|
+
* redefining the response shape locally rather than importing it from `../../src/...`: `apps/` compiles under its
|
|
9
|
+
* own `tsconfig.apps.json` (bundler resolution, DOM-facing), independent of the backend's `tsconfig.json` (NodeNext),
|
|
10
|
+
* and every other plugin app in this codebase keeps that boundary the same way.
|
|
11
|
+
*/
|
|
12
|
+
import { apiFetch } from "@rapidmx/web-client/lib/util/api.js";
|
|
13
|
+
|
|
14
|
+
export type VideoMeetingVisibility = "private" | "public";
|
|
15
|
+
export type VideoMeetingStatus = "scheduled" | "active" | "ended" | "cancelled";
|
|
16
|
+
|
|
17
|
+
/** Mirrors `BaseVideoMeetingRoute.ts`'s `IceServerConfig` - already the exact shape `RTCPeerConnection`'s
|
|
18
|
+
* `iceServers` constructor option expects, passed straight through with no reshaping. */
|
|
19
|
+
export interface IceServerConfig {
|
|
20
|
+
urls: string | string[];
|
|
21
|
+
username?: string;
|
|
22
|
+
credential?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Mirrors `BaseVideoMeetingRoute.ts`'s `PublicVideoMeeting`. */
|
|
26
|
+
export interface PublicVideoMeeting {
|
|
27
|
+
uid: string;
|
|
28
|
+
title: string;
|
|
29
|
+
visibility: VideoMeetingVisibility;
|
|
30
|
+
status: VideoMeetingStatus;
|
|
31
|
+
hostDisplayName?: string;
|
|
32
|
+
/** The account uid that owns the meeting - the sole source of a "host" identity on the client. Absent when the
|
|
33
|
+
* server can't resolve one (an orphaned mailbox, or a server that predates this field), in which case no host
|
|
34
|
+
* controls are shown to anyone. */
|
|
35
|
+
hostUid?: string;
|
|
36
|
+
/** When `true`, a joining participant's microphone starts muted - see `useLocalMedia`'s `forceMuteOnJoin`
|
|
37
|
+
* option, which this is threaded into. Absent reads as `false`. */
|
|
38
|
+
forceMuteOnJoin?: boolean;
|
|
39
|
+
/** Whether this meeting currently requires a password - never the password or its hash, just whether one is
|
|
40
|
+
* set. Absent reads as `false`. */
|
|
41
|
+
hasPassword?: boolean;
|
|
42
|
+
/** Whether this meeting currently requires the host to admit each participant before they join - see
|
|
43
|
+
* `requestAdmission()`. Absent reads as `false`. */
|
|
44
|
+
waitingRoomEnabled?: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Mirrors `BaseVideoMeetingRoute.ts`'s `VideoMeetingJoinResult`. `authenticated: true` means the visiting browser
|
|
49
|
+
* already held a valid session for a real RapidMX identity when it called `join()` - `token`/`expiresAt` are then
|
|
50
|
+
* omitted entirely (there is no guest token to hand over), and the caller (`[token].tsx`) must not write any
|
|
51
|
+
* cookie of its own before opening the signaling socket: the browser's own already-existing `jwt` session cookie
|
|
52
|
+
* already authenticates it there, with zero new client-side auth handling. `authenticated: false` (the common
|
|
53
|
+
* anonymous case) is unchanged from Phase 1: `token`/`expiresAt` are present, and `SignalingClient` applies `token` as
|
|
54
|
+
* a cookie before connecting.
|
|
55
|
+
*/
|
|
56
|
+
export interface VideoMeetingJoinResult {
|
|
57
|
+
meeting: PublicVideoMeeting;
|
|
58
|
+
iceServers: IceServerConfig[];
|
|
59
|
+
authenticated: boolean;
|
|
60
|
+
/** The identity to identify as on the signaling channel/mesh - the caller's own real uid when `authenticated`
|
|
61
|
+
* is `true`, otherwise a freshly minted synthetic `guest:<random>` uid. Always present. */
|
|
62
|
+
selfUid: string;
|
|
63
|
+
/** A short-lived guest JWT, usable against the meeting's signaling and relay sockets. Present only when
|
|
64
|
+
* `authenticated` is `false`. */
|
|
65
|
+
token?: string;
|
|
66
|
+
/** ISO 8601 instant `token` expires at. Present only when `authenticated` is `false`. */
|
|
67
|
+
expiresAt?: string;
|
|
68
|
+
/** Whether the server offers the WebSocket media relay - the last-resort path for a participant neither a direct
|
|
69
|
+
* connection nor the TURN server can reach. Absent from a server that predates it, which reads as off. */
|
|
70
|
+
relayEnabled?: boolean;
|
|
71
|
+
/** The base URL the video filters' machine-learning runtime and models are hosted under, when the administrator
|
|
72
|
+
* hosts them (`mail:videoconf:effects:assets_url`). Absent means the public CDN defaults. */
|
|
73
|
+
effectsAssetsUrl?: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Mirrors `BaseVideoMeetingRoute.ts`'s `VideoMeetingPasswordRequiredResult` - returned by `joinMeeting()` in place
|
|
77
|
+
* of a `VideoMeetingJoinResult` when the meeting requires a password that hasn't been submitted yet. Carries only
|
|
78
|
+
* enough to show a password prompt (the meeting's title/host); nothing here grants anything. Narrow on
|
|
79
|
+
* `"requiresPassword" in result && result.requiresPassword`. */
|
|
80
|
+
export interface VideoMeetingPasswordRequiredResult {
|
|
81
|
+
meeting: PublicVideoMeeting;
|
|
82
|
+
requiresPassword: true;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Mirrors `BaseVideoMeetingRoute.ts`'s `VideoMeetingAdmissionRequiredResult` - returned by `requestAdmission()`
|
|
86
|
+
* (and re-returned by `pollAdmission()`) while a `waitingRoomEnabled` meeting's admission request is still
|
|
87
|
+
* pending. Unlike `VideoMeetingPasswordRequiredResult`, this carries `selfUid` (and, for a guest, `token`) - a
|
|
88
|
+
* caller needs a stable, authenticated identity to poll with. `joinMeeting()` itself also answers this shape, with
|
|
89
|
+
* none of those fields, when a waiting room applies and no request has been filed yet. Narrow on
|
|
90
|
+
* `"requiresAdmission" in result`. */
|
|
91
|
+
export interface VideoMeetingAdmissionRequiredResult {
|
|
92
|
+
meeting: PublicVideoMeeting;
|
|
93
|
+
requiresAdmission: true;
|
|
94
|
+
authenticated?: boolean;
|
|
95
|
+
selfUid?: string;
|
|
96
|
+
token?: string;
|
|
97
|
+
expiresAt?: string;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** `joinMeeting()`'s actual return type - see `VideoMeetingPasswordRequiredResult`'s and
|
|
101
|
+
* `VideoMeetingAdmissionRequiredResult`'s own doc comments. */
|
|
102
|
+
export type VideoMeetingJoinResponse = VideoMeetingJoinResult | VideoMeetingPasswordRequiredResult | VideoMeetingAdmissionRequiredResult;
|
|
103
|
+
|
|
104
|
+
/** Resolves a join token (an invitee's own link) or a public meeting's slug to its meeting info, ICE servers, and
|
|
105
|
+
* either a short-lived guest signaling token (the common anonymous case) or confirmation that the caller's own
|
|
106
|
+
* existing session already grants them signaling access (`authenticated: true` - see
|
|
107
|
+
* `VideoMeetingJoinResult`'s doc comment) - see `BaseVideoMeetingRoute.join()`'s own doc comment. Rejects with
|
|
108
|
+
* `ApiRequestError` (status `404`) for a stale/unknown/malformed token, cancelled meeting, or the wrong
|
|
109
|
+
* private/public token shape for what it's being used against - the caller (`[token].tsx`) shows a single,
|
|
110
|
+
* friendly "this meeting link isn't valid" state for all of those rather than trying to distinguish them, matching
|
|
111
|
+
* `BaseVideoMeetingRoute.requireMeetingByToken()`'s own explicit "never leak which almost-matched" posture. */
|
|
112
|
+
export function joinMeeting(token: string): Promise<VideoMeetingJoinResponse> {
|
|
113
|
+
return apiFetch(`/mail/video-meetings/join/${encodeURIComponent(token)}`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Submits a password for a meeting `joinMeeting()` reported `requiresPassword` for. On success, returns an
|
|
117
|
+
* ordinary granted `VideoMeetingJoinResult` - the same shape and same meaning as `joinMeeting()`'s own non-password
|
|
118
|
+
* case. Rejects with `ApiRequestError` (status `403`) for a wrong or missing password. */
|
|
119
|
+
export function verifyMeetingPassword(token: string, password: string): Promise<VideoMeetingJoinResult> {
|
|
120
|
+
return apiFetch(`/mail/video-meetings/join/${encodeURIComponent(token)}/verify`, {
|
|
121
|
+
method: "POST",
|
|
122
|
+
body: JSON.stringify({ password }),
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Sets, replaces, or removes (`password: null`) the meeting's join password - host only, enforced the same way
|
|
127
|
+
* as every other owner-side video-meeting update. Hashed server-side; the plaintext is never stored or returned. */
|
|
128
|
+
export function setMeetingPassword(meetingUid: string, password: string | null): Promise<void> {
|
|
129
|
+
return apiFetch(`/mail/video-meetings/${encodeURIComponent(meetingUid)}`, {
|
|
130
|
+
method: "PUT",
|
|
131
|
+
body: JSON.stringify({ password }),
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Files an admission request for a meeting `joinMeeting()` reported `requiresAdmission` for - `password` only
|
|
136
|
+
* when `meeting.hasPassword` is also set (both are checked together on this one call, never in two steps). On
|
|
137
|
+
* success returns `VideoMeetingAdmissionRequiredResult` with a `selfUid` (and, for a guest, `token`) to poll with
|
|
138
|
+
* via `pollAdmission()` - nothing is granted yet. Rejects with `ApiRequestError` (status `403`) for a wrong
|
|
139
|
+
* password, or `400` if `meeting.hasPassword` and no password was given. */
|
|
140
|
+
export function requestAdmission(token: string, body: { name: string; password?: string }): Promise<VideoMeetingAdmissionRequiredResult> {
|
|
141
|
+
return apiFetch(`/mail/video-meetings/join/${encodeURIComponent(token)}/verify`, {
|
|
142
|
+
method: "POST",
|
|
143
|
+
body: JSON.stringify(body),
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Polls whether the host has responded to a pending admission request - authenticated as whichever identity
|
|
148
|
+
* `requestAdmission()` (or a previous poll) returned: `guestToken`, when given, is sent as an `Authorization`
|
|
149
|
+
* header (a guest has no session cookie of their own); omitted for an already-authenticated real caller, whose
|
|
150
|
+
* existing session cookie already identifies them. Resolves to a granted `VideoMeetingJoinResult` once admitted,
|
|
151
|
+
* or re-resolves to `VideoMeetingAdmissionRequiredResult` while still pending. Rejects with `ApiRequestError`
|
|
152
|
+
* (status `403`) once denied, or `404` if this identity never requested admission (or already polled past a final
|
|
153
|
+
* answer). */
|
|
154
|
+
export function pollAdmission(token: string, guestToken?: string): Promise<VideoMeetingJoinResult | VideoMeetingAdmissionRequiredResult> {
|
|
155
|
+
return apiFetch(`/mail/video-meetings/join/${encodeURIComponent(token)}/status`, {
|
|
156
|
+
headers: guestToken ? { Authorization: `jwt ${guestToken}` } : undefined,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** One pending admission request, as `listWaitingParticipants()` returns it. */
|
|
161
|
+
export interface WaitingParticipant {
|
|
162
|
+
uid: string;
|
|
163
|
+
name: string;
|
|
164
|
+
requestedAt: string;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** Lists a meeting's pending admission requests, oldest first - host only. */
|
|
168
|
+
export function listWaitingParticipants(meetingUid: string): Promise<WaitingParticipant[]> {
|
|
169
|
+
return apiFetch(`/mail/video-meetings/${encodeURIComponent(meetingUid)}/waiting`);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Admits a pending admission request - host only. The requester's own next `pollAdmission()` call completes
|
|
173
|
+
* their join; this alone does not notify them of anything. A no-op if `uid` has no pending request. */
|
|
174
|
+
export function admitParticipant(meetingUid: string, uid: string): Promise<void> {
|
|
175
|
+
return apiFetch(`/mail/video-meetings/${encodeURIComponent(meetingUid)}/admit/${encodeURIComponent(uid)}`, { method: "POST" });
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Denies a pending admission request - host only. The requester's own next `pollAdmission()` call then rejects
|
|
179
|
+
* with a 403. A no-op if `uid` has no pending request. */
|
|
180
|
+
export function denyParticipant(meetingUid: string, uid: string): Promise<void> {
|
|
181
|
+
return apiFetch(`/mail/video-meetings/${encodeURIComponent(meetingUid)}/deny/${encodeURIComponent(uid)}`, { method: "POST" });
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** Sets whether the meeting requires the host to admit each participant (`PublicVideoMeeting.waitingRoomEnabled`) -
|
|
185
|
+
* host only, enforced the same way as every other owner-side video-meeting update. Takes effect for anyone who
|
|
186
|
+
* joins (or reloads the join link and joins again) after this call resolves. */
|
|
187
|
+
export function setWaitingRoomEnabled(meetingUid: string, waitingRoomEnabled: boolean): Promise<void> {
|
|
188
|
+
return apiFetch(`/mail/video-meetings/${encodeURIComponent(meetingUid)}`, {
|
|
189
|
+
method: "PUT",
|
|
190
|
+
body: JSON.stringify({ waitingRoomEnabled }),
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Removes a participant from the meeting - host only, enforced server-side with the caller's own authenticated
|
|
195
|
+
* session (the same mailbox-ACL check every other owner-side video-meeting route uses), not by anything the
|
|
196
|
+
* client claims. Revokes `uid`'s grant on the meeting and closes every signaling socket they hold in it, so they can
|
|
197
|
+
* no longer signal or relay media even if their client ignores the cooperative "kicked" signal a host's UI sends alongside this
|
|
198
|
+
* (`MeshConnectionManager.sendKick()`). `uid` is the participant's real account uid (`MeshParticipant.uid` minus
|
|
199
|
+
* its tab-scoped suffix - see `_CallView.tsx`'s `accountUidOf()`), not their tab-scoped peer id. */
|
|
200
|
+
export function kickParticipant(meetingUid: string, uid: string): Promise<void> {
|
|
201
|
+
return apiFetch(`/mail/video-meetings/${encodeURIComponent(meetingUid)}/kick/${encodeURIComponent(uid)}`, { method: "POST" });
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** Sets whether a newly joining participant starts muted (`PublicVideoMeeting.forceMuteOnJoin`) - host only,
|
|
205
|
+
* enforced the same way as every other owner-side video-meeting update. Takes effect for anyone who joins (or
|
|
206
|
+
* reloads the join link and joins again) after this call resolves; it does not retroactively mute anyone already
|
|
207
|
+
* in the call - the host's own "Mute" button (see `kickParticipant()`) is the tool for that. */
|
|
208
|
+
export function setForceMuteOnJoin(meetingUid: string, forceMuteOnJoin: boolean): Promise<void> {
|
|
209
|
+
return apiFetch(`/mail/video-meetings/${encodeURIComponent(meetingUid)}`, {
|
|
210
|
+
method: "PUT",
|
|
211
|
+
body: JSON.stringify({ forceMuteOnJoin }),
|
|
212
|
+
});
|
|
213
|
+
}
|