@ai-matrx/meet 0.11.27 → 0.11.29
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/CHANGELOG.md +12 -0
- package/dist/{collab-CIyXdQ5l.d.cts → collab-XY6Xj1dG.d.cts} +18 -0
- package/dist/{collab-CIyXdQ5l.d.ts → collab-XY6Xj1dG.d.ts} +18 -0
- package/dist/{components-DUvy3BQf.d.cts → components-4f9Mf8fz.d.cts} +1 -1
- package/dist/{components-DiGxfKXI.d.ts → components-Cwep2NYU.d.ts} +1 -1
- package/dist/core.cjs +515 -20
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +374 -165
- package/dist/core.d.ts +374 -165
- package/dist/core.js +515 -20
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +515 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +515 -20
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +575 -28
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +6 -4
- package/dist/react.d.ts +6 -4
- package/dist/react.js +575 -28
- package/dist/react.js.map +1 -1
- package/dist/skins/meet.cjs +18 -6
- package/dist/skins/meet.cjs.map +1 -1
- package/dist/skins/meet.d.cts +2 -2
- package/dist/skins/meet.d.ts +2 -2
- package/dist/skins/meet.js +18 -6
- package/dist/skins/meet.js.map +1 -1
- package/package.json +5 -5
package/dist/react.js
CHANGED
|
@@ -1,6 +1,352 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
"use client";
|
|
3
3
|
|
|
4
|
+
// src/core/connection-supervisor.ts
|
|
5
|
+
var DEFAULT_CONNECTION_POLICY = {
|
|
6
|
+
bannerDelayMs: 2e3,
|
|
7
|
+
autoRejoin: true,
|
|
8
|
+
autoRejoinMaxAttempts: 5,
|
|
9
|
+
reconnectGraceSeconds: 30
|
|
10
|
+
};
|
|
11
|
+
function clampInt(value, min, max, fallback) {
|
|
12
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
|
|
13
|
+
return Math.min(max, Math.max(min, Math.round(value)));
|
|
14
|
+
}
|
|
15
|
+
function connectionPolicyFrom(raw) {
|
|
16
|
+
if (typeof raw !== "object" || raw === null) return DEFAULT_CONNECTION_POLICY;
|
|
17
|
+
const row = raw;
|
|
18
|
+
const d = DEFAULT_CONNECTION_POLICY;
|
|
19
|
+
return {
|
|
20
|
+
bannerDelayMs: clampInt(row["reconnect_banner_delay_ms"], 0, 1e4, d.bannerDelayMs),
|
|
21
|
+
autoRejoin: typeof row["auto_rejoin"] === "boolean" ? row["auto_rejoin"] : d.autoRejoin,
|
|
22
|
+
autoRejoinMaxAttempts: clampInt(row["auto_rejoin_max_attempts"], 0, 20, d.autoRejoinMaxAttempts),
|
|
23
|
+
reconnectGraceSeconds: clampInt(row["reconnect_grace_seconds"], 0, 600, d.reconnectGraceSeconds)
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
var LIVEKIT_BASE_DELAYS_MS = [0, 300, 1200, 2700, 4800, 7e3];
|
|
27
|
+
function createReconnectPolicy(options = {}) {
|
|
28
|
+
const delays = options.delaysMs !== void 0 && options.delaysMs.length > 0 ? options.delaysMs : LIVEKIT_BASE_DELAYS_MS;
|
|
29
|
+
const giveUpMs = options.giveUpMs ?? 45e3;
|
|
30
|
+
const maxAttempts = options.maxAttempts ?? 12;
|
|
31
|
+
const random = options.random ?? Math.random;
|
|
32
|
+
return {
|
|
33
|
+
nextRetryDelayInMs({ retryCount, elapsedMs }) {
|
|
34
|
+
if (retryCount >= maxAttempts || elapsedMs >= giveUpMs) return null;
|
|
35
|
+
const base = delays[Math.min(retryCount, delays.length - 1)] ?? 0;
|
|
36
|
+
const spread = Math.min(2e3, Math.max(500, base / 2));
|
|
37
|
+
return Math.round(base + random() * spread);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function createWebNetworkPort() {
|
|
42
|
+
const scope = globalThis;
|
|
43
|
+
return {
|
|
44
|
+
online: () => scope.navigator?.onLine !== false,
|
|
45
|
+
subscribe(listener) {
|
|
46
|
+
if (typeof scope.addEventListener !== "function") return () => void 0;
|
|
47
|
+
const on = () => listener(true);
|
|
48
|
+
const off = () => listener(false);
|
|
49
|
+
scope.addEventListener("online", on);
|
|
50
|
+
scope.addEventListener("offline", off);
|
|
51
|
+
return () => {
|
|
52
|
+
scope.removeEventListener?.("online", on);
|
|
53
|
+
scope.removeEventListener?.("offline", off);
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function createReachabilityProbe(urls, timeoutMs = 4e3) {
|
|
59
|
+
return async () => {
|
|
60
|
+
const targets = urls().filter((url) => url.length > 0);
|
|
61
|
+
if (targets.length === 0 || typeof fetch !== "function") return true;
|
|
62
|
+
const one = async (url) => {
|
|
63
|
+
const controller = typeof AbortController === "function" ? new AbortController() : null;
|
|
64
|
+
const timer = setTimeout(() => controller?.abort(), timeoutMs);
|
|
65
|
+
try {
|
|
66
|
+
await fetch(url, {
|
|
67
|
+
method: "GET",
|
|
68
|
+
mode: "no-cors",
|
|
69
|
+
cache: "no-store",
|
|
70
|
+
credentials: "omit",
|
|
71
|
+
...controller !== null ? { signal: controller.signal } : {}
|
|
72
|
+
});
|
|
73
|
+
return true;
|
|
74
|
+
} catch {
|
|
75
|
+
return false;
|
|
76
|
+
} finally {
|
|
77
|
+
clearTimeout(timer);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
const answers = await Promise.all(targets.map(one));
|
|
81
|
+
return answers.every(Boolean);
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function httpUrlOf(serverUrl) {
|
|
85
|
+
return serverUrl.replace(/^ws(s?):\/\//i, "http$1://");
|
|
86
|
+
}
|
|
87
|
+
var RESUME_WINDOW_MS = 15 * 6e4;
|
|
88
|
+
var RESUME_KEY = "mx-meet:resume:";
|
|
89
|
+
function createWebResumeStorage() {
|
|
90
|
+
const storage = globalThis.sessionStorage;
|
|
91
|
+
if (storage === void 0) return void 0;
|
|
92
|
+
return {
|
|
93
|
+
load(roomName) {
|
|
94
|
+
try {
|
|
95
|
+
const raw = storage.getItem(RESUME_KEY + roomName);
|
|
96
|
+
if (raw === null) return null;
|
|
97
|
+
const parsed = JSON.parse(raw);
|
|
98
|
+
return typeof parsed?.at === "number" && typeof parsed.request?.roomName === "string" ? parsed : null;
|
|
99
|
+
} catch {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
save(roomName, marker) {
|
|
104
|
+
try {
|
|
105
|
+
storage.setItem(RESUME_KEY + roomName, JSON.stringify(marker));
|
|
106
|
+
} catch {
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
clear(roomName) {
|
|
110
|
+
try {
|
|
111
|
+
storage.removeItem(RESUME_KEY + roomName);
|
|
112
|
+
} catch {
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
var CLEARS_RESUME = (phase) => phase === "left" || phase === "removed" || phase === "superseded" || phase === "inactive" || phase === "ended" || phase === "record" || phase === "prejoin" || phase.startsWith("refused:") || phase.startsWith("waiting:");
|
|
118
|
+
function autoRejoinDelayMs(attempt, random = Math.random) {
|
|
119
|
+
const base = Math.min(15e3, 1e3 * 2 ** Math.max(0, attempt));
|
|
120
|
+
return Math.round(base * (0.5 + random()));
|
|
121
|
+
}
|
|
122
|
+
function createConnectionSupervisor(options) {
|
|
123
|
+
const { store, network } = options;
|
|
124
|
+
const now = options.now ?? (() => Date.now());
|
|
125
|
+
const random = options.random ?? Math.random;
|
|
126
|
+
const probeEveryMs = options.probeEveryMs ?? 3e3;
|
|
127
|
+
let driverRegion = null;
|
|
128
|
+
let probeTimer = null;
|
|
129
|
+
let probing = false;
|
|
130
|
+
let bannerTimer = null;
|
|
131
|
+
let rejoinTimer = null;
|
|
132
|
+
let rejoinAttempts = 0;
|
|
133
|
+
let lastPhase = store.snapshot().phase;
|
|
134
|
+
let lastSavedAt = 0;
|
|
135
|
+
let disposed = false;
|
|
136
|
+
const inCall = () => store.snapshot().phase === "in_call";
|
|
137
|
+
const dropped = () => store.snapshot().phase === "disconnected";
|
|
138
|
+
function setRegion(next) {
|
|
139
|
+
if (store.snapshot().connection !== next) store.patch({ connection: next });
|
|
140
|
+
}
|
|
141
|
+
function regionFor(reachable) {
|
|
142
|
+
if (!network.online()) return "offline";
|
|
143
|
+
if (reachable === false) return "server_unreachable";
|
|
144
|
+
if (dropped()) return reachable === true ? "stable" : store.snapshot().connection;
|
|
145
|
+
return driverRegion ?? "stable";
|
|
146
|
+
}
|
|
147
|
+
function needsWatch() {
|
|
148
|
+
const snap = store.snapshot();
|
|
149
|
+
if (dropped()) return true;
|
|
150
|
+
return inCall() && (driverRegion !== null || snap.connection !== "stable");
|
|
151
|
+
}
|
|
152
|
+
function stopProbe() {
|
|
153
|
+
if (probeTimer !== null) clearTimeout(probeTimer);
|
|
154
|
+
probeTimer = null;
|
|
155
|
+
}
|
|
156
|
+
function scheduleProbe(delayMs) {
|
|
157
|
+
if (disposed || probeTimer !== null) return;
|
|
158
|
+
probeTimer = setTimeout(() => {
|
|
159
|
+
probeTimer = null;
|
|
160
|
+
void runProbe();
|
|
161
|
+
}, delayMs);
|
|
162
|
+
}
|
|
163
|
+
async function runProbe() {
|
|
164
|
+
if (disposed || probing) return;
|
|
165
|
+
if (!needsWatch()) return;
|
|
166
|
+
if (!network.online()) {
|
|
167
|
+
setRegion("offline");
|
|
168
|
+
scheduleProbe(probeEveryMs);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
probing = true;
|
|
172
|
+
let reachable = false;
|
|
173
|
+
try {
|
|
174
|
+
reachable = await options.probe();
|
|
175
|
+
} catch {
|
|
176
|
+
reachable = false;
|
|
177
|
+
} finally {
|
|
178
|
+
probing = false;
|
|
179
|
+
}
|
|
180
|
+
if (disposed) return;
|
|
181
|
+
if (inCall() || dropped()) setRegion(regionFor(reachable));
|
|
182
|
+
if (dropped() && reachable) maybeRejoin();
|
|
183
|
+
if (needsWatch()) scheduleProbe(Math.round(probeEveryMs * (0.75 + random() * 0.5)));
|
|
184
|
+
}
|
|
185
|
+
function syncBanner() {
|
|
186
|
+
const snap = store.snapshot();
|
|
187
|
+
const unstable = snap.phase === "in_call" && snap.connection !== "stable";
|
|
188
|
+
if (!unstable) {
|
|
189
|
+
if (bannerTimer !== null) clearTimeout(bannerTimer);
|
|
190
|
+
bannerTimer = null;
|
|
191
|
+
if (snap.connectionBanner) store.patch({ connectionBanner: false });
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (snap.connectionBanner || bannerTimer !== null) return;
|
|
195
|
+
const delay = options.policy().bannerDelayMs;
|
|
196
|
+
if (delay <= 0) {
|
|
197
|
+
store.patch({ connectionBanner: true });
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
bannerTimer = setTimeout(() => {
|
|
201
|
+
bannerTimer = null;
|
|
202
|
+
const latest = store.snapshot();
|
|
203
|
+
if (latest.phase === "in_call" && latest.connection !== "stable" && !latest.connectionBanner) {
|
|
204
|
+
store.patch({ connectionBanner: true });
|
|
205
|
+
}
|
|
206
|
+
}, delay);
|
|
207
|
+
}
|
|
208
|
+
function stopRejoin() {
|
|
209
|
+
if (rejoinTimer !== null) clearTimeout(rejoinTimer);
|
|
210
|
+
rejoinTimer = null;
|
|
211
|
+
}
|
|
212
|
+
function maybeRejoin() {
|
|
213
|
+
if (disposed || rejoinTimer !== null || !dropped()) return;
|
|
214
|
+
const policy = options.policy();
|
|
215
|
+
if (!policy.autoRejoin) return;
|
|
216
|
+
if (rejoinAttempts >= policy.autoRejoinMaxAttempts) return;
|
|
217
|
+
if (!network.online()) return;
|
|
218
|
+
const attempt = rejoinAttempts;
|
|
219
|
+
rejoinTimer = setTimeout(() => {
|
|
220
|
+
rejoinTimer = null;
|
|
221
|
+
if (disposed || !dropped() || !network.online()) return;
|
|
222
|
+
rejoinAttempts = attempt + 1;
|
|
223
|
+
options.report?.("info", `meet: rejoining by itself (attempt ${rejoinAttempts} of ${policy.autoRejoinMaxAttempts})`);
|
|
224
|
+
void options.autoRejoin().catch(() => void 0);
|
|
225
|
+
}, autoRejoinDelayMs(attempt, random));
|
|
226
|
+
}
|
|
227
|
+
function saveMarker(force) {
|
|
228
|
+
const resume = options.resume;
|
|
229
|
+
const snap = store.snapshot();
|
|
230
|
+
if (resume === void 0 || snap.roomName === null) return;
|
|
231
|
+
const at = now();
|
|
232
|
+
if (!force && at - lastSavedAt < 1e4) return;
|
|
233
|
+
const request = options.currentRequest();
|
|
234
|
+
if (request === null) return;
|
|
235
|
+
lastSavedAt = at;
|
|
236
|
+
resume.save(snap.roomName, { request, at });
|
|
237
|
+
}
|
|
238
|
+
const unsubscribeStore = store.subscribe(() => {
|
|
239
|
+
if (disposed) return;
|
|
240
|
+
const snap = store.snapshot();
|
|
241
|
+
const phase = snap.phase;
|
|
242
|
+
const changed = phase !== lastPhase;
|
|
243
|
+
lastPhase = phase;
|
|
244
|
+
if (changed) {
|
|
245
|
+
if (phase === "in_call") {
|
|
246
|
+
rejoinAttempts = 0;
|
|
247
|
+
stopRejoin();
|
|
248
|
+
saveMarker(true);
|
|
249
|
+
} else if (phase === "disconnected") {
|
|
250
|
+
driverRegion = null;
|
|
251
|
+
saveMarker(true);
|
|
252
|
+
stopProbe();
|
|
253
|
+
scheduleProbe(0);
|
|
254
|
+
} else {
|
|
255
|
+
stopRejoin();
|
|
256
|
+
if (phase !== "joining") driverRegion = null;
|
|
257
|
+
if (CLEARS_RESUME(phase) && snap.roomName !== null) options.resume?.clear(snap.roomName);
|
|
258
|
+
}
|
|
259
|
+
} else if (phase === "in_call" || phase === "disconnected") {
|
|
260
|
+
saveMarker(false);
|
|
261
|
+
}
|
|
262
|
+
syncBanner();
|
|
263
|
+
});
|
|
264
|
+
const unsubscribeNetwork = network.subscribe((online) => {
|
|
265
|
+
if (disposed) return;
|
|
266
|
+
if (!online) {
|
|
267
|
+
if (inCall() || dropped()) setRegion("offline");
|
|
268
|
+
stopRejoin();
|
|
269
|
+
stopProbe();
|
|
270
|
+
if (needsWatch()) scheduleProbe(probeEveryMs);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
stopProbe();
|
|
274
|
+
if (needsWatch()) scheduleProbe(0);
|
|
275
|
+
});
|
|
276
|
+
return {
|
|
277
|
+
driverState(state) {
|
|
278
|
+
if (disposed) return;
|
|
279
|
+
if (state === "connected") {
|
|
280
|
+
driverRegion = null;
|
|
281
|
+
if (inCall() || store.snapshot().phase === "joining") setRegion(network.online() ? "stable" : "offline");
|
|
282
|
+
if (!needsWatch()) stopProbe();
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
if (state === "reconnecting" || state === "signal_reconnecting") {
|
|
286
|
+
driverRegion = state;
|
|
287
|
+
if (inCall()) {
|
|
288
|
+
setRegion(network.online() ? state : "offline");
|
|
289
|
+
stopProbe();
|
|
290
|
+
scheduleProbe(0);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
exitConnection() {
|
|
295
|
+
return network.online() ? "server_unreachable" : "offline";
|
|
296
|
+
},
|
|
297
|
+
resumable(roomName) {
|
|
298
|
+
const resume = options.resume;
|
|
299
|
+
if (resume === void 0) return null;
|
|
300
|
+
const marker = resume.load(roomName);
|
|
301
|
+
if (marker === null) return null;
|
|
302
|
+
if (now() - marker.at > RESUME_WINDOW_MS) {
|
|
303
|
+
resume.clear(roomName);
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
return marker;
|
|
307
|
+
},
|
|
308
|
+
dispose() {
|
|
309
|
+
disposed = true;
|
|
310
|
+
stopProbe();
|
|
311
|
+
stopRejoin();
|
|
312
|
+
if (bannerTimer !== null) clearTimeout(bannerTimer);
|
|
313
|
+
bannerTimer = null;
|
|
314
|
+
unsubscribeStore();
|
|
315
|
+
unsubscribeNetwork();
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
var DELIBERATE_DEPARTURES = /* @__PURE__ */ new Set([
|
|
320
|
+
"CLIENT_INITIATED",
|
|
321
|
+
"PARTICIPANT_REMOVED",
|
|
322
|
+
"ROOM_DELETED",
|
|
323
|
+
"ROOM_CLOSED",
|
|
324
|
+
"DUPLICATE_IDENTITY",
|
|
325
|
+
"USER_REJECTED",
|
|
326
|
+
"USER_UNAVAILABLE",
|
|
327
|
+
"SIP_TRUNK_FAILURE"
|
|
328
|
+
]);
|
|
329
|
+
function isUnannouncedDeparture(reason) {
|
|
330
|
+
return reason === null || !DELIBERATE_DEPARTURES.has(reason);
|
|
331
|
+
}
|
|
332
|
+
function mergeGhosts(live, ghosts, at) {
|
|
333
|
+
const present = new Set(live.map((p) => p.identity));
|
|
334
|
+
const kept = /* @__PURE__ */ new Map();
|
|
335
|
+
const extra = [];
|
|
336
|
+
for (const [identity, ghost] of ghosts) {
|
|
337
|
+
if (present.has(identity) || ghost.until <= at) continue;
|
|
338
|
+
kept.set(identity, ghost);
|
|
339
|
+
extra.push({
|
|
340
|
+
...ghost.participant,
|
|
341
|
+
micEnabled: false,
|
|
342
|
+
cameraEnabled: false,
|
|
343
|
+
screenSharing: false,
|
|
344
|
+
reconnecting: true
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
return { roster: [...live, ...extra], kept };
|
|
348
|
+
}
|
|
349
|
+
|
|
4
350
|
// src/react/provider.tsx
|
|
5
351
|
import { globalSlot as globalSlot2 } from "@ai-matrx/kit/global-slot";
|
|
6
352
|
import {
|
|
@@ -410,7 +756,8 @@ function parseRoomToken(body, roomName) {
|
|
|
410
756
|
admissionState: readString(payload, "admission_state") ?? void 0,
|
|
411
757
|
pollSeconds: typeof payload["poll_seconds"] === "number" ? payload["poll_seconds"] : void 0,
|
|
412
758
|
breakoutRoomId: readString(payload, "breakout_room_id") ?? null,
|
|
413
|
-
observersVisibleTo: readString(payload, "observers_visible_to") === "hosts" ? "hosts" : "everyone"
|
|
759
|
+
observersVisibleTo: readString(payload, "observers_visible_to") === "hosts" ? "hosts" : "everyone",
|
|
760
|
+
connectionPolicy: connectionPolicyFrom(payload["connection_policy"])
|
|
414
761
|
};
|
|
415
762
|
}
|
|
416
763
|
function createTokenClient(options) {
|
|
@@ -1111,8 +1458,14 @@ function nextPhase(from, event) {
|
|
|
1111
1458
|
return from === "resolving" ? "prejoin" : null;
|
|
1112
1459
|
case "unsupported":
|
|
1113
1460
|
return from === "resolving" ? "unsupported" : null;
|
|
1461
|
+
case "link_state":
|
|
1462
|
+
return from === "resolving" && event.reason !== null && isJoinRefusalReason(event.reason) ? refusalPhase(event.reason) : null;
|
|
1114
1463
|
case "join":
|
|
1115
1464
|
return JOINABLE_FROM.has(from) ? "joining" : null;
|
|
1465
|
+
case "auto_rejoin":
|
|
1466
|
+
return from === "disconnected" ? "joining" : null;
|
|
1467
|
+
case "resumed":
|
|
1468
|
+
return from === "resolving" ? "joining" : null;
|
|
1116
1469
|
case "back_to_prejoin":
|
|
1117
1470
|
return from === "left" || from === "disconnected" ? "prejoin" : null;
|
|
1118
1471
|
case "retry":
|
|
@@ -1873,6 +2226,8 @@ var RPCS = {
|
|
|
1873
2226
|
getOrCreateMeeting: "meet_get_or_create_meeting",
|
|
1874
2227
|
/** Resolve a durable public link, before/during/after its scheduled time. */
|
|
1875
2228
|
meetingBySlug: "meet_meeting_by_slug",
|
|
2229
|
+
/** The current run of a durable link (open / not started / ended + next start), no pass needed. */
|
|
2230
|
+
linkState: "meet_link_state",
|
|
1876
2231
|
/*
|
|
1877
2232
|
* ── S4 ADMISSION (CORE-DESIGN §2.4). Each returns the decided row; the caller then
|
|
1878
2233
|
* sends the payload-free `mx.state.changed` hint. Authorized by `who_may_admit`.
|
|
@@ -2181,6 +2536,14 @@ function createMeetRepository(options) {
|
|
|
2181
2536
|
if (response.error !== null) throw normalizeMeetError(response.error, operation);
|
|
2182
2537
|
}
|
|
2183
2538
|
}
|
|
2539
|
+
async function readLinkRun(slug) {
|
|
2540
|
+
try {
|
|
2541
|
+
const response = await db.rpc(RPCS.linkState, { p_slug: slug });
|
|
2542
|
+
return projectLinkRun(response.error === null ? response.data : null);
|
|
2543
|
+
} catch {
|
|
2544
|
+
return void 0;
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2184
2547
|
async function guard(operation, run) {
|
|
2185
2548
|
let response = await run();
|
|
2186
2549
|
if (response.error !== null) {
|
|
@@ -2582,11 +2945,12 @@ function createMeetRepository(options) {
|
|
|
2582
2945
|
return projectMeeting(row);
|
|
2583
2946
|
},
|
|
2584
2947
|
async meetingBySlug(slug) {
|
|
2585
|
-
const row = await
|
|
2586
|
-
"meet.meetingBySlug",
|
|
2587
|
-
(
|
|
2588
|
-
);
|
|
2589
|
-
|
|
2948
|
+
const [row, linkRun] = await Promise.all([
|
|
2949
|
+
guard("meet.meetingBySlug", () => db.rpc(RPCS.meetingBySlug, { p_slug: slug })),
|
|
2950
|
+
readLinkRun(slug)
|
|
2951
|
+
]);
|
|
2952
|
+
const record = withRunNoteTaker(projectMeeting(row));
|
|
2953
|
+
return linkRun === void 0 ? record : { ...record, linkRun };
|
|
2590
2954
|
},
|
|
2591
2955
|
async meeting(id) {
|
|
2592
2956
|
const response = await db.from(TABLES.meetings).select(MEETING_COLUMNS).eq("id", id).single();
|
|
@@ -2775,6 +3139,15 @@ function snippetRuns(snippet) {
|
|
|
2775
3139
|
if (last < snippet.length) runs.push({ text: snippet.slice(last), hit: false });
|
|
2776
3140
|
return runs;
|
|
2777
3141
|
}
|
|
3142
|
+
function projectLinkRun(value) {
|
|
3143
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
3144
|
+
const row = value;
|
|
3145
|
+
const state = row["state"];
|
|
3146
|
+
if (state !== "open" && state !== "not_started" && state !== "ended" && state !== "cancelled") return void 0;
|
|
3147
|
+
const reason = row["reason"] === "ended" || row["reason"] === "cancelled" ? row["reason"] : null;
|
|
3148
|
+
const nextStart = typeof row["next_start"] === "string" ? row["next_start"] : null;
|
|
3149
|
+
return { state, reason, nextStart };
|
|
3150
|
+
}
|
|
2778
3151
|
|
|
2779
3152
|
// src/core/app-panel.ts
|
|
2780
3153
|
function appPanelOf(meeting) {
|
|
@@ -3232,6 +3605,7 @@ var EMPTY = {
|
|
|
3232
3605
|
phaseNotice: null,
|
|
3233
3606
|
roomName: null,
|
|
3234
3607
|
connection: "stable",
|
|
3608
|
+
connectionBanner: false,
|
|
3235
3609
|
share: NO_SHARE,
|
|
3236
3610
|
call: null,
|
|
3237
3611
|
waiting: null,
|
|
@@ -4513,6 +4887,18 @@ function createRoomEngine(options) {
|
|
|
4513
4887
|
let disposed = false;
|
|
4514
4888
|
let moving = false;
|
|
4515
4889
|
const confirmingEnds = /* @__PURE__ */ new WeakSet();
|
|
4890
|
+
let lastServerUrl = null;
|
|
4891
|
+
let connectionPolicy = DEFAULT_CONNECTION_POLICY;
|
|
4892
|
+
let ghosts = /* @__PURE__ */ new Map();
|
|
4893
|
+
let ghostTimer = null;
|
|
4894
|
+
let liveRoster = [];
|
|
4895
|
+
const departures = /* @__PURE__ */ new Map();
|
|
4896
|
+
function requestAsNow() {
|
|
4897
|
+
if (lastRequest === null) return null;
|
|
4898
|
+
const media = store.snapshot().media;
|
|
4899
|
+
if (store.snapshot().phase !== "in_call") return lastRequest;
|
|
4900
|
+
return { ...lastRequest, mode: media.camera.enabled ? "video" : "audio", microphone: media.microphone.enabled };
|
|
4901
|
+
}
|
|
4516
4902
|
async function dropRoom() {
|
|
4517
4903
|
const target = driver;
|
|
4518
4904
|
driver = null;
|
|
@@ -4523,6 +4909,7 @@ function createRoomEngine(options) {
|
|
|
4523
4909
|
store.setLocalTrack("microphone", IDLE_TRACK);
|
|
4524
4910
|
store.setLocalTrack("camera", IDLE_TRACK);
|
|
4525
4911
|
store.patch({ share: NO_SHARE, connection: "stable" });
|
|
4912
|
+
clearGhosts();
|
|
4526
4913
|
await target?.disconnect();
|
|
4527
4914
|
}
|
|
4528
4915
|
async function leaveRoom() {
|
|
@@ -4590,7 +4977,7 @@ function createRoomEngine(options) {
|
|
|
4590
4977
|
participants(list) {
|
|
4591
4978
|
const joinedAt = new Date(now()).toISOString();
|
|
4592
4979
|
const before = new Set(store.snapshot().participants.map((p) => p.identity));
|
|
4593
|
-
|
|
4980
|
+
projectRoster(list, joinedAt);
|
|
4594
4981
|
const snapshot = store.snapshot();
|
|
4595
4982
|
const newcomer = snapshot.participants.some(
|
|
4596
4983
|
(participant) => !participant.isLocal && !before.has(participant.identity)
|
|
@@ -4602,6 +4989,9 @@ function createRoomEngine(options) {
|
|
|
4602
4989
|
);
|
|
4603
4990
|
}
|
|
4604
4991
|
},
|
|
4992
|
+
participantLeft(who, reason) {
|
|
4993
|
+
departures.set(who, reason);
|
|
4994
|
+
},
|
|
4605
4995
|
roomMetadata(metadata) {
|
|
4606
4996
|
let spotlight = null;
|
|
4607
4997
|
if (metadata.length > 0) {
|
|
@@ -4628,12 +5018,8 @@ function createRoomEngine(options) {
|
|
|
4628
5018
|
store.setQuality(asParticipantIdentity(who), value);
|
|
4629
5019
|
},
|
|
4630
5020
|
connectionState(state) {
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
store.dispatch({ type: "connected" });
|
|
4634
|
-
} else if (state === "reconnecting") {
|
|
4635
|
-
store.patch({ connection: "reconnecting" });
|
|
4636
|
-
}
|
|
5021
|
+
supervisor.driverState(state);
|
|
5022
|
+
if (state === "connected" && store.snapshot().phase === "joining") store.dispatch({ type: "connected" });
|
|
4637
5023
|
},
|
|
4638
5024
|
transcription(event) {
|
|
4639
5025
|
const snapshot = store.snapshot();
|
|
@@ -4766,6 +5152,7 @@ function createRoomEngine(options) {
|
|
|
4766
5152
|
};
|
|
4767
5153
|
async function resolveExitAndApply(hint) {
|
|
4768
5154
|
const connection = current?.identity ?? store.snapshot().localIdentity ?? null;
|
|
5155
|
+
lastRequest = requestAsNow();
|
|
4769
5156
|
await dropRoom();
|
|
4770
5157
|
let server;
|
|
4771
5158
|
if (options.readExit === void 0 || connection === null || store.snapshot().meeting === null) {
|
|
@@ -4779,7 +5166,7 @@ function createRoomEngine(options) {
|
|
|
4779
5166
|
}
|
|
4780
5167
|
if (disposed) return;
|
|
4781
5168
|
const resolved = resolveExit({ server, hint, frozen: options.wasFrozen?.() ?? false });
|
|
4782
|
-
store.patch({ connection: resolved.connection });
|
|
5169
|
+
store.patch({ connection: server === null ? supervisor.exitConnection() : resolved.connection });
|
|
4783
5170
|
if (resolved.to === "waiting:knocking") {
|
|
4784
5171
|
store.patch({
|
|
4785
5172
|
waiting: {
|
|
@@ -4811,6 +5198,8 @@ function createRoomEngine(options) {
|
|
|
4811
5198
|
...request.observe === true ? { observe: true } : {}
|
|
4812
5199
|
});
|
|
4813
5200
|
current = token;
|
|
5201
|
+
lastServerUrl = token.serverUrl;
|
|
5202
|
+
connectionPolicy = token.connectionPolicy ?? DEFAULT_CONNECTION_POLICY;
|
|
4814
5203
|
store.patch({
|
|
4815
5204
|
localIdentity: token.identity,
|
|
4816
5205
|
breakoutRoomId: token.breakoutRoomId ?? null,
|
|
@@ -4915,7 +5304,59 @@ function createRoomEngine(options) {
|
|
|
4915
5304
|
throw normalized;
|
|
4916
5305
|
}
|
|
4917
5306
|
}
|
|
4918
|
-
|
|
5307
|
+
function projectRoster(list, joinedAt) {
|
|
5308
|
+
const at = now();
|
|
5309
|
+
const previous = new Map(liveRoster.map((p) => [p.identity, p]));
|
|
5310
|
+
const present = new Set(list.map((p) => p.identity));
|
|
5311
|
+
const graceMs = connectionPolicy.reconnectGraceSeconds * 1e3;
|
|
5312
|
+
for (const [who, reason] of departures) {
|
|
5313
|
+
const last = previous.get(who);
|
|
5314
|
+
if (last !== void 0 && !last.isLocal && !present.has(who) && graceMs > 0 && isUnannouncedDeparture(reason)) {
|
|
5315
|
+
ghosts.set(who, { participant: last, until: at + graceMs });
|
|
5316
|
+
}
|
|
5317
|
+
}
|
|
5318
|
+
departures.clear();
|
|
5319
|
+
liveRoster = list;
|
|
5320
|
+
const merged = mergeGhosts(list, ghosts, at);
|
|
5321
|
+
ghosts = merged.kept;
|
|
5322
|
+
store.setParticipants(
|
|
5323
|
+
merged.roster.map((entry) => {
|
|
5324
|
+
const projected = projectParticipant(entry, joinedAt);
|
|
5325
|
+
return entry.reconnecting === true ? { ...projected, reconnecting: true, quality: "lost" } : projected;
|
|
5326
|
+
})
|
|
5327
|
+
);
|
|
5328
|
+
scheduleGhostExpiry();
|
|
5329
|
+
}
|
|
5330
|
+
function scheduleGhostExpiry() {
|
|
5331
|
+
if (ghostTimer !== null) clearTimeout(ghostTimer);
|
|
5332
|
+
ghostTimer = null;
|
|
5333
|
+
if (ghosts.size === 0 || disposed) return;
|
|
5334
|
+
const next = Math.min(...[...ghosts.values()].map((g) => g.until));
|
|
5335
|
+
ghostTimer = setTimeout(() => {
|
|
5336
|
+
ghostTimer = null;
|
|
5337
|
+
if (disposed || driver === null) return;
|
|
5338
|
+
projectRoster(liveRoster, new Date(now()).toISOString());
|
|
5339
|
+
}, Math.max(0, next - now()) + 50);
|
|
5340
|
+
}
|
|
5341
|
+
function clearGhosts() {
|
|
5342
|
+
ghosts = /* @__PURE__ */ new Map();
|
|
5343
|
+
departures.clear();
|
|
5344
|
+
liveRoster = [];
|
|
5345
|
+
if (ghostTimer !== null) clearTimeout(ghostTimer);
|
|
5346
|
+
ghostTimer = null;
|
|
5347
|
+
}
|
|
5348
|
+
const supervisor = createConnectionSupervisor({
|
|
5349
|
+
store,
|
|
5350
|
+
network: options.connection?.network ?? createWebNetworkPort(),
|
|
5351
|
+
probe: () => options.connection?.probe?.(current?.serverUrl ?? lastServerUrl) ?? Promise.resolve(true),
|
|
5352
|
+
policy: () => connectionPolicy,
|
|
5353
|
+
autoRejoin: () => engine.autoRejoin(),
|
|
5354
|
+
currentRequest: requestAsNow,
|
|
5355
|
+
resume: options.connection?.resume,
|
|
5356
|
+
now,
|
|
5357
|
+
report: (level, message) => report(level, message)
|
|
5358
|
+
});
|
|
5359
|
+
const engine = {
|
|
4919
5360
|
join(request) {
|
|
4920
5361
|
if (joining !== null) return joining;
|
|
4921
5362
|
if (store.snapshot().phase !== "joining" && !store.dispatch({ type: "join", click: true })) {
|
|
@@ -4960,6 +5401,35 @@ function createRoomEngine(options) {
|
|
|
4960
5401
|
});
|
|
4961
5402
|
return joining;
|
|
4962
5403
|
},
|
|
5404
|
+
async autoRejoin() {
|
|
5405
|
+
const request = lastRequest;
|
|
5406
|
+
if (request === null || joining !== null) return joining ?? void 0;
|
|
5407
|
+
if (!store.dispatch({ type: "auto_rejoin" })) return;
|
|
5408
|
+
tokens.invalidate(request.roomName);
|
|
5409
|
+
current = null;
|
|
5410
|
+
joining = connect(request).catch((error) => {
|
|
5411
|
+
throw failJoin(error, "meet.autoRejoin");
|
|
5412
|
+
}).finally(() => {
|
|
5413
|
+
joining = null;
|
|
5414
|
+
});
|
|
5415
|
+
return joining;
|
|
5416
|
+
},
|
|
5417
|
+
resume(roomName) {
|
|
5418
|
+
const marker = supervisor.resumable(roomName);
|
|
5419
|
+
if (marker === null || joining !== null) return false;
|
|
5420
|
+
if (!store.dispatch({ type: "resumed" })) return false;
|
|
5421
|
+
lastRequest = { ...marker.request, roomName };
|
|
5422
|
+
report("info", "meet: this tab was in the call before the page reloaded \u2014 rejoining");
|
|
5423
|
+
tokens.invalidate(roomName);
|
|
5424
|
+
current = null;
|
|
5425
|
+
joining = connect(lastRequest).catch((error) => {
|
|
5426
|
+
throw failJoin(error, "meet.resume");
|
|
5427
|
+
}).finally(() => {
|
|
5428
|
+
joining = null;
|
|
5429
|
+
});
|
|
5430
|
+
joining.catch(() => void 0);
|
|
5431
|
+
return true;
|
|
5432
|
+
},
|
|
4963
5433
|
async moveToBreakout(roomId) {
|
|
4964
5434
|
const request = lastRequest;
|
|
4965
5435
|
if (request === null || driver === null) {
|
|
@@ -5127,6 +5597,8 @@ function createRoomEngine(options) {
|
|
|
5127
5597
|
token: () => current,
|
|
5128
5598
|
async dispose() {
|
|
5129
5599
|
disposed = true;
|
|
5600
|
+
supervisor.dispose();
|
|
5601
|
+
clearGhosts();
|
|
5130
5602
|
if (driver !== null) {
|
|
5131
5603
|
const target = driver;
|
|
5132
5604
|
driver = null;
|
|
@@ -5135,6 +5607,7 @@ function createRoomEngine(options) {
|
|
|
5135
5607
|
store.reset();
|
|
5136
5608
|
}
|
|
5137
5609
|
};
|
|
5610
|
+
return engine;
|
|
5138
5611
|
}
|
|
5139
5612
|
function canModerate(participant) {
|
|
5140
5613
|
return participant !== void 0 && isHostRole(participant.role);
|
|
@@ -5445,8 +5918,11 @@ function mapConnectionState(state) {
|
|
|
5445
5918
|
case "connecting":
|
|
5446
5919
|
return "connecting";
|
|
5447
5920
|
case "reconnecting":
|
|
5448
|
-
case "signalReconnecting":
|
|
5449
5921
|
return "reconnecting";
|
|
5922
|
+
// Only the signal socket is down (S6b): its own region value, so the supervisor can tell a
|
|
5923
|
+
// signal blip from a full media reconnect.
|
|
5924
|
+
case "signalReconnecting":
|
|
5925
|
+
return "signal_reconnecting";
|
|
5450
5926
|
default:
|
|
5451
5927
|
return "disconnected";
|
|
5452
5928
|
}
|
|
@@ -5527,8 +6003,12 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
5527
6003
|
// R5: large meetings degrade gracefully instead of melting a laptop.
|
|
5528
6004
|
adaptiveStream: true,
|
|
5529
6005
|
dynacast: true,
|
|
6006
|
+
// S6b: OUR retry schedule — jittered from attempt 0 (the SDK's default has no jitter on its
|
|
6007
|
+
// first two attempts), give-up limits in one place (`connection-supervisor.ts`).
|
|
6008
|
+
reconnectPolicy: createReconnectPolicy(),
|
|
5530
6009
|
...options.roomOptions
|
|
5531
6010
|
});
|
|
6011
|
+
const reasonName = (reason) => reason === void 0 || reason === null ? null : module.DisconnectReason?.[reason] ?? String(reason);
|
|
5532
6012
|
const roster = () => {
|
|
5533
6013
|
const local = snapshotParticipant(created.localParticipant, true);
|
|
5534
6014
|
const remotes = [...created.remoteParticipants.values()].map(
|
|
@@ -5546,7 +6026,10 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
5546
6026
|
if (track.kind !== Track.Kind.Audio) return;
|
|
5547
6027
|
for (const element of track.detach()) element.remove();
|
|
5548
6028
|
});
|
|
5549
|
-
created.on(RoomEvent.ParticipantConnected, roster).on(RoomEvent.ParticipantDisconnected,
|
|
6029
|
+
created.on(RoomEvent.ParticipantConnected, roster).on(RoomEvent.ParticipantDisconnected, (participant, reason) => {
|
|
6030
|
+
handlers.participantLeft?.(participant.identity, reasonName(reason));
|
|
6031
|
+
roster();
|
|
6032
|
+
}).on(RoomEvent.TrackSubscribed, roster).on(RoomEvent.TrackUnsubscribed, roster).on(RoomEvent.TrackMuted, roster).on(RoomEvent.TrackUnmuted, roster).on(RoomEvent.LocalTrackPublished, roster).on(RoomEvent.LocalTrackUnpublished, roster).on(RoomEvent.ParticipantNameChanged, roster).on(RoomEvent.ParticipantAttributesChanged, roster).on(RoomEvent.ParticipantMetadataChanged, roster).on(RoomEvent.RoomMetadataChanged, (metadata) => {
|
|
5550
6033
|
handlers.roomMetadata?.(metadata ?? "");
|
|
5551
6034
|
}).on(RoomEvent.ActiveSpeakersChanged, (speakers) => {
|
|
5552
6035
|
handlers.activeSpeakers(speakers.map((speaker) => speaker.identity));
|
|
@@ -5563,8 +6046,7 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
5563
6046
|
}).on(RoomEvent.Disconnected, (reason) => {
|
|
5564
6047
|
audioContainer?.remove();
|
|
5565
6048
|
audioContainer = null;
|
|
5566
|
-
|
|
5567
|
-
handlers.disconnected(named);
|
|
6049
|
+
handlers.disconnected(reasonName(reason));
|
|
5568
6050
|
});
|
|
5569
6051
|
try {
|
|
5570
6052
|
created.registerTextStreamHandler(TRANSCRIPTION_TOPIC, (reader, participantInfo) => {
|
|
@@ -5954,7 +6436,14 @@ function MeetRuntime(props) {
|
|
|
5954
6436
|
effectsStorage,
|
|
5955
6437
|
resolveDevice: (kind) => devicesHolder.current?.resolve(kind) ?? null,
|
|
5956
6438
|
// §3.4: on every LiveKit disconnect, ask the server what happened to this connection.
|
|
5957
|
-
readExit: (connection) => meetings.readExit(connection)
|
|
6439
|
+
readExit: (connection) => meetings.readExit(connection),
|
|
6440
|
+
// S6b: the connection supervisor's ports — the device's network, whether OUR servers (the
|
|
6441
|
+
// API and the media server) answer, and the per-tab reload-after-offline marker.
|
|
6442
|
+
connection: {
|
|
6443
|
+
network: createWebNetworkPort(),
|
|
6444
|
+
resume: createWebResumeStorage(),
|
|
6445
|
+
probe: (serverUrl) => createReachabilityProbe(() => [baseUrl, ...serverUrl !== null ? [httpUrlOf(serverUrl)] : []])()
|
|
6446
|
+
}
|
|
5958
6447
|
});
|
|
5959
6448
|
const meetings = createMeetingSession({
|
|
5960
6449
|
manager,
|
|
@@ -6282,7 +6771,7 @@ function participantAttributes(p) {
|
|
|
6282
6771
|
"data-meet-participant-role": observedRole(p.role, p.standing),
|
|
6283
6772
|
"data-meet-participant-self": String(p.isLocal)
|
|
6284
6773
|
};
|
|
6285
|
-
const quality = observedQuality(p.quality);
|
|
6774
|
+
const quality = p.reconnecting === true ? "reconnecting" : observedQuality(p.quality);
|
|
6286
6775
|
if (quality !== null) attrs["data-meet-participant-connection"] = quality;
|
|
6287
6776
|
return attrs;
|
|
6288
6777
|
}
|
|
@@ -8501,6 +8990,7 @@ function useMeetCore() {
|
|
|
8501
8990
|
reason: snapshot?.reason ?? null,
|
|
8502
8991
|
phaseNotice: snapshot?.phaseNotice ?? null,
|
|
8503
8992
|
connection: snapshot?.connection ?? "stable",
|
|
8993
|
+
connectionBanner: snapshot?.connectionBanner ?? false,
|
|
8504
8994
|
share: snapshot?.share ?? NO_SHARE,
|
|
8505
8995
|
call: snapshot?.call ?? null,
|
|
8506
8996
|
actions
|
|
@@ -9767,7 +10257,8 @@ function ParticipantTile({
|
|
|
9767
10257
|
participant.displayName,
|
|
9768
10258
|
participant.isLocal ? " (you)" : "",
|
|
9769
10259
|
participant.isAgent ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__badge", children: "AI" }) : null,
|
|
9770
|
-
/* @__PURE__ */ jsx6(RoleBadge, { role: participant.role, standing: participant.standing })
|
|
10260
|
+
/* @__PURE__ */ jsx6(RoleBadge, { role: participant.role, standing: participant.standing }),
|
|
10261
|
+
participant.reconnecting === true ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__badge", children: "Reconnecting\u2026" }) : null
|
|
9771
10262
|
] }),
|
|
9772
10263
|
!participant.micEnabled ? /* @__PURE__ */ jsx6(MicOffIcon, { width: 14, height: 14 }) : null,
|
|
9773
10264
|
/* @__PURE__ */ jsx6(QualityBars, { quality: participant.quality })
|
|
@@ -9810,6 +10301,12 @@ function QualityBars({ quality }) {
|
|
|
9810
10301
|
const label = quality === "lost" ? "Connection lost" : quality === "poor" ? "Poor connection" : `Connection ${quality}`;
|
|
9811
10302
|
return /* @__PURE__ */ jsx6("span", { className: `mx-meet__signal mx-meet__signal--${quality}`, title: label, "aria-label": label, role: "img", children: /* @__PURE__ */ jsx6(SignalIcon, { bars, width: 14, height: 14 }) });
|
|
9812
10303
|
}
|
|
10304
|
+
var CONNECTION_BANNER = {
|
|
10305
|
+
signal_reconnecting: "Reconnecting\u2026",
|
|
10306
|
+
reconnecting: "Reconnecting\u2026",
|
|
10307
|
+
offline: "You're offline. Reconnecting when your internet is back\u2026",
|
|
10308
|
+
server_unreachable: "Can't reach the meeting server. Retrying\u2026"
|
|
10309
|
+
};
|
|
9813
10310
|
function Stage({ pageSize }) {
|
|
9814
10311
|
const stage = useStage(pageSize);
|
|
9815
10312
|
const columns = gridColumns(stage.tiles.length);
|
|
@@ -11517,7 +12014,7 @@ function MeetSkinRoom({
|
|
|
11517
12014
|
headerControls !== void 0 && headerControls !== null ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__header-controls", "data-meet-header-controls": "", children: headerControls }) : null,
|
|
11518
12015
|
isHost ? /* @__PURE__ */ jsx6(HostMenu, {}) : null
|
|
11519
12016
|
] }),
|
|
11520
|
-
core.connection !== "stable" ? /* @__PURE__ */ jsx6("p", { className: "mx-meet__banner", role: "status", children:
|
|
12017
|
+
core.connectionBanner && core.connection !== "stable" ? /* @__PURE__ */ jsx6("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": `connection-${core.connection}`, children: CONNECTION_BANNER[core.connection] }) : null,
|
|
11521
12018
|
/* @__PURE__ */ jsx6(ShareNotice, {}),
|
|
11522
12019
|
/* @__PURE__ */ jsx6(Stage, {}),
|
|
11523
12020
|
/* @__PURE__ */ jsx6(Captions, {}),
|
|
@@ -11551,7 +12048,7 @@ var REFUSED_TITLES = {
|
|
|
11551
12048
|
registration_required: "Register to join this meeting"
|
|
11552
12049
|
};
|
|
11553
12050
|
function ExitScreen({ onLeave }) {
|
|
11554
|
-
const { phase, reason, host, snapshot, actions, call } = useMeetCore();
|
|
12051
|
+
const { phase, reason, host, snapshot, actions, call, connection } = useMeetCore();
|
|
11555
12052
|
const [busy, setBusy] = useState4(false);
|
|
11556
12053
|
const failure = snapshot?.failure ?? null;
|
|
11557
12054
|
const rejoin = /* @__PURE__ */ jsx6(
|
|
@@ -11591,7 +12088,7 @@ function ExitScreen({ onLeave }) {
|
|
|
11591
12088
|
if (phase.startsWith("refused:")) {
|
|
11592
12089
|
const code = phase.slice("refused:".length);
|
|
11593
12090
|
title = REFUSED_TITLES[code] ?? "You can't join this meeting";
|
|
11594
|
-
line = failure?.remedy ?? null;
|
|
12091
|
+
line = code === "too_early" || code === "registration_required" || code === "cancelled" ? failure?.remedy ?? null : null;
|
|
11595
12092
|
actionsNode = /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
11596
12093
|
/* @__PURE__ */ jsx6(Button, { type: "button", variant: "outline", onClick: actions.retry, children: "Try again" }),
|
|
11597
12094
|
home
|
|
@@ -11607,7 +12104,7 @@ function ExitScreen({ onLeave }) {
|
|
|
11607
12104
|
break;
|
|
11608
12105
|
case "disconnected":
|
|
11609
12106
|
title = "You lost your connection to the meeting";
|
|
11610
|
-
line = failure?.remedy ?? "Check your internet connection, then rejoin.";
|
|
12107
|
+
line = connection === "offline" ? "You're offline. You'll rejoin automatically when your internet is back." : connection === "server_unreachable" ? "Can't reach the meeting server. You'll rejoin automatically when it answers." : failure?.remedy ?? "Check your internet connection, then rejoin.";
|
|
11611
12108
|
notice = "disconnected";
|
|
11612
12109
|
actionsNode = /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
11613
12110
|
rejoin,
|
|
@@ -11641,6 +12138,10 @@ function ExitScreen({ onLeave }) {
|
|
|
11641
12138
|
case "ended":
|
|
11642
12139
|
case "record":
|
|
11643
12140
|
title = reason === "time_limit" ? "The meeting reached its time limit" : "The meeting has ended";
|
|
12141
|
+
if (failure?.reason === "ended" && failure.remedy.length > 0 && failure.message.startsWith("This run")) {
|
|
12142
|
+
title = failure.message.replace(/\.$/, "");
|
|
12143
|
+
line = failure.remedy;
|
|
12144
|
+
}
|
|
11644
12145
|
notice = "meeting-ended";
|
|
11645
12146
|
break;
|
|
11646
12147
|
default:
|
|
@@ -12211,8 +12712,16 @@ function MeetingSkinRootBody({
|
|
|
12211
12712
|
if (now.phase !== "resolving" && now.roomName !== null && now.roomName !== roomName && now.call === null) {
|
|
12212
12713
|
host.store.dispatch({ type: "reset" });
|
|
12213
12714
|
}
|
|
12214
|
-
if (host.store.snapshot().phase
|
|
12215
|
-
|
|
12715
|
+
if (host.store.snapshot().phase !== "resolving") return;
|
|
12716
|
+
if (given?.linkRun?.reason == null && host.engine.resume(roomName)) return;
|
|
12717
|
+
const reason = given?.linkRun?.reason ?? null;
|
|
12718
|
+
if (reason !== null && given?.linkRun !== void 0) {
|
|
12719
|
+
const words = linkRunWords(given.linkRun);
|
|
12720
|
+
host.store.patch({ failure: { message: words.message, remedy: words.remedy, reason } });
|
|
12721
|
+
if (host.store.dispatch({ type: "link_state", reason })) return;
|
|
12722
|
+
}
|
|
12723
|
+
host.store.dispatch({ type: "resolved" });
|
|
12724
|
+
}, [host, roomName, phase, given]);
|
|
12216
12725
|
useEffect8(() => {
|
|
12217
12726
|
if (given === void 0) return;
|
|
12218
12727
|
if (host.store.snapshot().meeting !== null) return;
|
|
@@ -12239,6 +12748,31 @@ function MeetingSkinRootBody({
|
|
|
12239
12748
|
}
|
|
12240
12749
|
);
|
|
12241
12750
|
}
|
|
12751
|
+
function linkRunWords(run) {
|
|
12752
|
+
if (run.reason === "cancelled") {
|
|
12753
|
+
return {
|
|
12754
|
+
message: "This occurrence of the meeting was cancelled.",
|
|
12755
|
+
remedy: "The series continues; open the same link at its next time."
|
|
12756
|
+
};
|
|
12757
|
+
}
|
|
12758
|
+
if (run.nextStart === null) {
|
|
12759
|
+
return {
|
|
12760
|
+
message: "This meeting has ended.",
|
|
12761
|
+
remedy: "The link still opens its record: summary, notes and transcript. Nobody can rejoin."
|
|
12762
|
+
};
|
|
12763
|
+
}
|
|
12764
|
+
const when2 = new Date(run.nextStart).toLocaleString(void 0, {
|
|
12765
|
+
weekday: "short",
|
|
12766
|
+
month: "short",
|
|
12767
|
+
day: "numeric",
|
|
12768
|
+
hour: "numeric",
|
|
12769
|
+
minute: "2-digit"
|
|
12770
|
+
});
|
|
12771
|
+
return {
|
|
12772
|
+
message: "This run of the meeting has ended.",
|
|
12773
|
+
remedy: `The next one starts ${when2}. Open the same link then.`
|
|
12774
|
+
};
|
|
12775
|
+
}
|
|
12242
12776
|
|
|
12243
12777
|
// src/react/calls-ui.tsx
|
|
12244
12778
|
import { useEffect as useEffect9 } from "react";
|
|
@@ -12465,6 +12999,7 @@ export {
|
|
|
12465
12999
|
CAPTION_LANGUAGES,
|
|
12466
13000
|
CONSENT_NOTICE_ATTRIBUTE,
|
|
12467
13001
|
DATA_TOPICS,
|
|
13002
|
+
DEFAULT_CONNECTION_POLICY,
|
|
12468
13003
|
DEFAULT_EFFECTS,
|
|
12469
13004
|
DEFAULT_PAGE_SIZE,
|
|
12470
13005
|
EMPTY_COLLAB,
|
|
@@ -12482,6 +13017,7 @@ export {
|
|
|
12482
13017
|
IncomingCallHost,
|
|
12483
13018
|
JOIN_REFUSAL_REASONS,
|
|
12484
13019
|
LISTEN_ONLY_NOTICE,
|
|
13020
|
+
LIVEKIT_BASE_DELAYS_MS,
|
|
12485
13021
|
LIVE_PHASES,
|
|
12486
13022
|
MEETING_INVITE_KIND,
|
|
12487
13023
|
MEETING_INVITE_VERSION,
|
|
@@ -12511,6 +13047,7 @@ export {
|
|
|
12511
13047
|
PARTICIPANT_STANDING_ATTRIBUTE,
|
|
12512
13048
|
REACTION_TTL_MS,
|
|
12513
13049
|
REFUSED_ABSENCE,
|
|
13050
|
+
RESUME_WINDOW_MS,
|
|
12514
13051
|
ROLE_RANK,
|
|
12515
13052
|
ROOM_EVENTS,
|
|
12516
13053
|
ROOM_EVENT_PREFIX,
|
|
@@ -12541,6 +13078,7 @@ export {
|
|
|
12541
13078
|
attendanceTotals,
|
|
12542
13079
|
attendeeKey,
|
|
12543
13080
|
autoAssign,
|
|
13081
|
+
autoRejoinDelayMs,
|
|
12544
13082
|
availableActivities,
|
|
12545
13083
|
breakoutRemainingMs,
|
|
12546
13084
|
callInviteAction,
|
|
@@ -12555,9 +13093,11 @@ export {
|
|
|
12555
13093
|
clockPosition,
|
|
12556
13094
|
collabFromState,
|
|
12557
13095
|
computeStage,
|
|
13096
|
+
connectionPolicyFrom,
|
|
12558
13097
|
countLabel,
|
|
12559
13098
|
createCallCenter,
|
|
12560
13099
|
createCallInviteHandler,
|
|
13100
|
+
createConnectionSupervisor,
|
|
12561
13101
|
createDeviceManager,
|
|
12562
13102
|
createInRoomClient,
|
|
12563
13103
|
createLiveKitDriver,
|
|
@@ -12569,11 +13109,15 @@ export {
|
|
|
12569
13109
|
createMeetingRecordCache,
|
|
12570
13110
|
createMeetingSession,
|
|
12571
13111
|
createMemoryDeviceStorage,
|
|
13112
|
+
createReachabilityProbe,
|
|
13113
|
+
createReconnectPolicy,
|
|
12572
13114
|
createRecordingController,
|
|
12573
13115
|
createRoomEngine,
|
|
12574
13116
|
createTokenClient,
|
|
12575
13117
|
createWebDeviceStorage,
|
|
12576
13118
|
createWebEffectsStorage,
|
|
13119
|
+
createWebNetworkPort,
|
|
13120
|
+
createWebResumeStorage,
|
|
12577
13121
|
createWebRoomTokenStorage,
|
|
12578
13122
|
defaultCaptionLanguage,
|
|
12579
13123
|
displayNameFor,
|
|
@@ -12586,6 +13130,7 @@ export {
|
|
|
12586
13130
|
guestRecordAccessSetting,
|
|
12587
13131
|
hasEnded,
|
|
12588
13132
|
hostUserIds,
|
|
13133
|
+
httpUrlOf,
|
|
12589
13134
|
invalidResponse,
|
|
12590
13135
|
isGuest,
|
|
12591
13136
|
isGuestIdentity,
|
|
@@ -12599,6 +13144,7 @@ export {
|
|
|
12599
13144
|
isRealName,
|
|
12600
13145
|
isRefusedPhase,
|
|
12601
13146
|
isRoomEventTopic,
|
|
13147
|
+
isUnannouncedDeparture,
|
|
12602
13148
|
isWaitingPhase,
|
|
12603
13149
|
learnName,
|
|
12604
13150
|
learnNames,
|
|
@@ -12621,6 +13167,7 @@ export {
|
|
|
12621
13167
|
meetingTopic,
|
|
12622
13168
|
meetingWhen,
|
|
12623
13169
|
mergeCaption,
|
|
13170
|
+
mergeGhosts,
|
|
12624
13171
|
mergeRecord,
|
|
12625
13172
|
microphoneNoticeCode,
|
|
12626
13173
|
moveToRoom,
|