@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/index.cjs
CHANGED
|
@@ -36,6 +36,7 @@ __export(src_exports, {
|
|
|
36
36
|
CAPTION_LANGUAGES: () => CAPTION_LANGUAGES,
|
|
37
37
|
CONSENT_NOTICE_ATTRIBUTE: () => CONSENT_NOTICE_ATTRIBUTE,
|
|
38
38
|
DATA_TOPICS: () => DATA_TOPICS,
|
|
39
|
+
DEFAULT_CONNECTION_POLICY: () => DEFAULT_CONNECTION_POLICY,
|
|
39
40
|
DEFAULT_EFFECTS: () => DEFAULT_EFFECTS,
|
|
40
41
|
DEFAULT_PAGE_SIZE: () => DEFAULT_PAGE_SIZE,
|
|
41
42
|
EMPTY_COLLAB: () => EMPTY_COLLAB,
|
|
@@ -50,6 +51,7 @@ __export(src_exports, {
|
|
|
50
51
|
IDLE_TRACK: () => IDLE_TRACK,
|
|
51
52
|
JOIN_REFUSAL_REASONS: () => JOIN_REFUSAL_REASONS,
|
|
52
53
|
LISTEN_ONLY_NOTICE: () => LISTEN_ONLY_NOTICE,
|
|
54
|
+
LIVEKIT_BASE_DELAYS_MS: () => LIVEKIT_BASE_DELAYS_MS,
|
|
53
55
|
LIVE_PHASES: () => LIVE_PHASES,
|
|
54
56
|
MEETING_INVITE_KIND: () => MEETING_INVITE_KIND,
|
|
55
57
|
MEETING_INVITE_VERSION: () => MEETING_INVITE_VERSION,
|
|
@@ -73,6 +75,7 @@ __export(src_exports, {
|
|
|
73
75
|
PARTICIPANT_STANDING_ATTRIBUTE: () => PARTICIPANT_STANDING_ATTRIBUTE,
|
|
74
76
|
REACTION_TTL_MS: () => REACTION_TTL_MS,
|
|
75
77
|
REFUSED_ABSENCE: () => REFUSED_ABSENCE,
|
|
78
|
+
RESUME_WINDOW_MS: () => RESUME_WINDOW_MS,
|
|
76
79
|
ROLE_RANK: () => ROLE_RANK,
|
|
77
80
|
ROOM_EVENTS: () => ROOM_EVENTS,
|
|
78
81
|
ROOM_EVENT_PREFIX: () => ROOM_EVENT_PREFIX,
|
|
@@ -102,6 +105,7 @@ __export(src_exports, {
|
|
|
102
105
|
attendanceTotals: () => attendanceTotals,
|
|
103
106
|
attendeeKey: () => attendeeKey,
|
|
104
107
|
autoAssign: () => autoAssign,
|
|
108
|
+
autoRejoinDelayMs: () => autoRejoinDelayMs,
|
|
105
109
|
availableActivities: () => availableActivities,
|
|
106
110
|
breakoutRemainingMs: () => breakoutRemainingMs,
|
|
107
111
|
callInviteAction: () => callInviteAction,
|
|
@@ -116,9 +120,11 @@ __export(src_exports, {
|
|
|
116
120
|
clockPosition: () => clockPosition,
|
|
117
121
|
collabFromState: () => collabFromState,
|
|
118
122
|
computeStage: () => computeStage,
|
|
123
|
+
connectionPolicyFrom: () => connectionPolicyFrom,
|
|
119
124
|
countLabel: () => countLabel,
|
|
120
125
|
createCallCenter: () => createCallCenter,
|
|
121
126
|
createCallInviteHandler: () => createCallInviteHandler,
|
|
127
|
+
createConnectionSupervisor: () => createConnectionSupervisor,
|
|
122
128
|
createDeviceManager: () => createDeviceManager,
|
|
123
129
|
createInRoomClient: () => createInRoomClient,
|
|
124
130
|
createLiveKitDriver: () => createLiveKitDriver,
|
|
@@ -130,11 +136,15 @@ __export(src_exports, {
|
|
|
130
136
|
createMeetingRecordCache: () => createMeetingRecordCache,
|
|
131
137
|
createMeetingSession: () => createMeetingSession,
|
|
132
138
|
createMemoryDeviceStorage: () => createMemoryDeviceStorage,
|
|
139
|
+
createReachabilityProbe: () => createReachabilityProbe,
|
|
140
|
+
createReconnectPolicy: () => createReconnectPolicy,
|
|
133
141
|
createRecordingController: () => createRecordingController,
|
|
134
142
|
createRoomEngine: () => createRoomEngine,
|
|
135
143
|
createTokenClient: () => createTokenClient,
|
|
136
144
|
createWebDeviceStorage: () => createWebDeviceStorage,
|
|
137
145
|
createWebEffectsStorage: () => createWebEffectsStorage,
|
|
146
|
+
createWebNetworkPort: () => createWebNetworkPort,
|
|
147
|
+
createWebResumeStorage: () => createWebResumeStorage,
|
|
138
148
|
createWebRoomTokenStorage: () => createWebRoomTokenStorage,
|
|
139
149
|
defaultCaptionLanguage: () => defaultCaptionLanguage,
|
|
140
150
|
displayNameFor: () => displayNameFor,
|
|
@@ -147,6 +157,7 @@ __export(src_exports, {
|
|
|
147
157
|
guestRecordAccessSetting: () => guestRecordAccessSetting,
|
|
148
158
|
hasEnded: () => hasEnded,
|
|
149
159
|
hostUserIds: () => hostUserIds,
|
|
160
|
+
httpUrlOf: () => httpUrlOf,
|
|
150
161
|
invalidResponse: () => invalidResponse,
|
|
151
162
|
isGuest: () => isGuest,
|
|
152
163
|
isGuestIdentity: () => isGuestIdentity,
|
|
@@ -160,6 +171,7 @@ __export(src_exports, {
|
|
|
160
171
|
isRealName: () => isRealName,
|
|
161
172
|
isRefusedPhase: () => isRefusedPhase,
|
|
162
173
|
isRoomEventTopic: () => isRoomEventTopic,
|
|
174
|
+
isUnannouncedDeparture: () => isUnannouncedDeparture,
|
|
163
175
|
isWaitingPhase: () => isWaitingPhase,
|
|
164
176
|
learnName: () => learnName,
|
|
165
177
|
learnNames: () => learnNames,
|
|
@@ -181,6 +193,7 @@ __export(src_exports, {
|
|
|
181
193
|
meetingTopic: () => meetingTopic,
|
|
182
194
|
meetingWhen: () => meetingWhen,
|
|
183
195
|
mergeCaption: () => mergeCaption,
|
|
196
|
+
mergeGhosts: () => mergeGhosts,
|
|
184
197
|
mergeRecord: () => mergeRecord,
|
|
185
198
|
microphoneNoticeCode: () => microphoneNoticeCode,
|
|
186
199
|
moveToRoom: () => moveToRoom,
|
|
@@ -456,6 +469,352 @@ function invalidResponse(operation, detail) {
|
|
|
456
469
|
);
|
|
457
470
|
}
|
|
458
471
|
|
|
472
|
+
// src/core/connection-supervisor.ts
|
|
473
|
+
var DEFAULT_CONNECTION_POLICY = {
|
|
474
|
+
bannerDelayMs: 2e3,
|
|
475
|
+
autoRejoin: true,
|
|
476
|
+
autoRejoinMaxAttempts: 5,
|
|
477
|
+
reconnectGraceSeconds: 30
|
|
478
|
+
};
|
|
479
|
+
function clampInt(value, min, max, fallback) {
|
|
480
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
|
|
481
|
+
return Math.min(max, Math.max(min, Math.round(value)));
|
|
482
|
+
}
|
|
483
|
+
function connectionPolicyFrom(raw) {
|
|
484
|
+
if (typeof raw !== "object" || raw === null) return DEFAULT_CONNECTION_POLICY;
|
|
485
|
+
const row = raw;
|
|
486
|
+
const d = DEFAULT_CONNECTION_POLICY;
|
|
487
|
+
return {
|
|
488
|
+
bannerDelayMs: clampInt(row["reconnect_banner_delay_ms"], 0, 1e4, d.bannerDelayMs),
|
|
489
|
+
autoRejoin: typeof row["auto_rejoin"] === "boolean" ? row["auto_rejoin"] : d.autoRejoin,
|
|
490
|
+
autoRejoinMaxAttempts: clampInt(row["auto_rejoin_max_attempts"], 0, 20, d.autoRejoinMaxAttempts),
|
|
491
|
+
reconnectGraceSeconds: clampInt(row["reconnect_grace_seconds"], 0, 600, d.reconnectGraceSeconds)
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
var LIVEKIT_BASE_DELAYS_MS = [0, 300, 1200, 2700, 4800, 7e3];
|
|
495
|
+
function createReconnectPolicy(options = {}) {
|
|
496
|
+
const delays = options.delaysMs !== void 0 && options.delaysMs.length > 0 ? options.delaysMs : LIVEKIT_BASE_DELAYS_MS;
|
|
497
|
+
const giveUpMs = options.giveUpMs ?? 45e3;
|
|
498
|
+
const maxAttempts = options.maxAttempts ?? 12;
|
|
499
|
+
const random = options.random ?? Math.random;
|
|
500
|
+
return {
|
|
501
|
+
nextRetryDelayInMs({ retryCount, elapsedMs }) {
|
|
502
|
+
if (retryCount >= maxAttempts || elapsedMs >= giveUpMs) return null;
|
|
503
|
+
const base = delays[Math.min(retryCount, delays.length - 1)] ?? 0;
|
|
504
|
+
const spread = Math.min(2e3, Math.max(500, base / 2));
|
|
505
|
+
return Math.round(base + random() * spread);
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
function createWebNetworkPort() {
|
|
510
|
+
const scope = globalThis;
|
|
511
|
+
return {
|
|
512
|
+
online: () => scope.navigator?.onLine !== false,
|
|
513
|
+
subscribe(listener) {
|
|
514
|
+
if (typeof scope.addEventListener !== "function") return () => void 0;
|
|
515
|
+
const on = () => listener(true);
|
|
516
|
+
const off = () => listener(false);
|
|
517
|
+
scope.addEventListener("online", on);
|
|
518
|
+
scope.addEventListener("offline", off);
|
|
519
|
+
return () => {
|
|
520
|
+
scope.removeEventListener?.("online", on);
|
|
521
|
+
scope.removeEventListener?.("offline", off);
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
function createReachabilityProbe(urls, timeoutMs = 4e3) {
|
|
527
|
+
return async () => {
|
|
528
|
+
const targets = urls().filter((url) => url.length > 0);
|
|
529
|
+
if (targets.length === 0 || typeof fetch !== "function") return true;
|
|
530
|
+
const one = async (url) => {
|
|
531
|
+
const controller = typeof AbortController === "function" ? new AbortController() : null;
|
|
532
|
+
const timer = setTimeout(() => controller?.abort(), timeoutMs);
|
|
533
|
+
try {
|
|
534
|
+
await fetch(url, {
|
|
535
|
+
method: "GET",
|
|
536
|
+
mode: "no-cors",
|
|
537
|
+
cache: "no-store",
|
|
538
|
+
credentials: "omit",
|
|
539
|
+
...controller !== null ? { signal: controller.signal } : {}
|
|
540
|
+
});
|
|
541
|
+
return true;
|
|
542
|
+
} catch {
|
|
543
|
+
return false;
|
|
544
|
+
} finally {
|
|
545
|
+
clearTimeout(timer);
|
|
546
|
+
}
|
|
547
|
+
};
|
|
548
|
+
const answers = await Promise.all(targets.map(one));
|
|
549
|
+
return answers.every(Boolean);
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
function httpUrlOf(serverUrl) {
|
|
553
|
+
return serverUrl.replace(/^ws(s?):\/\//i, "http$1://");
|
|
554
|
+
}
|
|
555
|
+
var RESUME_WINDOW_MS = 15 * 6e4;
|
|
556
|
+
var RESUME_KEY = "mx-meet:resume:";
|
|
557
|
+
function createWebResumeStorage() {
|
|
558
|
+
const storage = globalThis.sessionStorage;
|
|
559
|
+
if (storage === void 0) return void 0;
|
|
560
|
+
return {
|
|
561
|
+
load(roomName) {
|
|
562
|
+
try {
|
|
563
|
+
const raw = storage.getItem(RESUME_KEY + roomName);
|
|
564
|
+
if (raw === null) return null;
|
|
565
|
+
const parsed = JSON.parse(raw);
|
|
566
|
+
return typeof parsed?.at === "number" && typeof parsed.request?.roomName === "string" ? parsed : null;
|
|
567
|
+
} catch {
|
|
568
|
+
return null;
|
|
569
|
+
}
|
|
570
|
+
},
|
|
571
|
+
save(roomName, marker) {
|
|
572
|
+
try {
|
|
573
|
+
storage.setItem(RESUME_KEY + roomName, JSON.stringify(marker));
|
|
574
|
+
} catch {
|
|
575
|
+
}
|
|
576
|
+
},
|
|
577
|
+
clear(roomName) {
|
|
578
|
+
try {
|
|
579
|
+
storage.removeItem(RESUME_KEY + roomName);
|
|
580
|
+
} catch {
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
var CLEARS_RESUME = (phase) => phase === "left" || phase === "removed" || phase === "superseded" || phase === "inactive" || phase === "ended" || phase === "record" || phase === "prejoin" || phase.startsWith("refused:") || phase.startsWith("waiting:");
|
|
586
|
+
function autoRejoinDelayMs(attempt, random = Math.random) {
|
|
587
|
+
const base = Math.min(15e3, 1e3 * 2 ** Math.max(0, attempt));
|
|
588
|
+
return Math.round(base * (0.5 + random()));
|
|
589
|
+
}
|
|
590
|
+
function createConnectionSupervisor(options) {
|
|
591
|
+
const { store, network } = options;
|
|
592
|
+
const now = options.now ?? (() => Date.now());
|
|
593
|
+
const random = options.random ?? Math.random;
|
|
594
|
+
const probeEveryMs = options.probeEveryMs ?? 3e3;
|
|
595
|
+
let driverRegion = null;
|
|
596
|
+
let probeTimer = null;
|
|
597
|
+
let probing = false;
|
|
598
|
+
let bannerTimer = null;
|
|
599
|
+
let rejoinTimer = null;
|
|
600
|
+
let rejoinAttempts = 0;
|
|
601
|
+
let lastPhase = store.snapshot().phase;
|
|
602
|
+
let lastSavedAt = 0;
|
|
603
|
+
let disposed = false;
|
|
604
|
+
const inCall = () => store.snapshot().phase === "in_call";
|
|
605
|
+
const dropped = () => store.snapshot().phase === "disconnected";
|
|
606
|
+
function setRegion(next) {
|
|
607
|
+
if (store.snapshot().connection !== next) store.patch({ connection: next });
|
|
608
|
+
}
|
|
609
|
+
function regionFor(reachable) {
|
|
610
|
+
if (!network.online()) return "offline";
|
|
611
|
+
if (reachable === false) return "server_unreachable";
|
|
612
|
+
if (dropped()) return reachable === true ? "stable" : store.snapshot().connection;
|
|
613
|
+
return driverRegion ?? "stable";
|
|
614
|
+
}
|
|
615
|
+
function needsWatch() {
|
|
616
|
+
const snap = store.snapshot();
|
|
617
|
+
if (dropped()) return true;
|
|
618
|
+
return inCall() && (driverRegion !== null || snap.connection !== "stable");
|
|
619
|
+
}
|
|
620
|
+
function stopProbe() {
|
|
621
|
+
if (probeTimer !== null) clearTimeout(probeTimer);
|
|
622
|
+
probeTimer = null;
|
|
623
|
+
}
|
|
624
|
+
function scheduleProbe(delayMs) {
|
|
625
|
+
if (disposed || probeTimer !== null) return;
|
|
626
|
+
probeTimer = setTimeout(() => {
|
|
627
|
+
probeTimer = null;
|
|
628
|
+
void runProbe();
|
|
629
|
+
}, delayMs);
|
|
630
|
+
}
|
|
631
|
+
async function runProbe() {
|
|
632
|
+
if (disposed || probing) return;
|
|
633
|
+
if (!needsWatch()) return;
|
|
634
|
+
if (!network.online()) {
|
|
635
|
+
setRegion("offline");
|
|
636
|
+
scheduleProbe(probeEveryMs);
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
probing = true;
|
|
640
|
+
let reachable = false;
|
|
641
|
+
try {
|
|
642
|
+
reachable = await options.probe();
|
|
643
|
+
} catch {
|
|
644
|
+
reachable = false;
|
|
645
|
+
} finally {
|
|
646
|
+
probing = false;
|
|
647
|
+
}
|
|
648
|
+
if (disposed) return;
|
|
649
|
+
if (inCall() || dropped()) setRegion(regionFor(reachable));
|
|
650
|
+
if (dropped() && reachable) maybeRejoin();
|
|
651
|
+
if (needsWatch()) scheduleProbe(Math.round(probeEveryMs * (0.75 + random() * 0.5)));
|
|
652
|
+
}
|
|
653
|
+
function syncBanner() {
|
|
654
|
+
const snap = store.snapshot();
|
|
655
|
+
const unstable = snap.phase === "in_call" && snap.connection !== "stable";
|
|
656
|
+
if (!unstable) {
|
|
657
|
+
if (bannerTimer !== null) clearTimeout(bannerTimer);
|
|
658
|
+
bannerTimer = null;
|
|
659
|
+
if (snap.connectionBanner) store.patch({ connectionBanner: false });
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
if (snap.connectionBanner || bannerTimer !== null) return;
|
|
663
|
+
const delay = options.policy().bannerDelayMs;
|
|
664
|
+
if (delay <= 0) {
|
|
665
|
+
store.patch({ connectionBanner: true });
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
bannerTimer = setTimeout(() => {
|
|
669
|
+
bannerTimer = null;
|
|
670
|
+
const latest = store.snapshot();
|
|
671
|
+
if (latest.phase === "in_call" && latest.connection !== "stable" && !latest.connectionBanner) {
|
|
672
|
+
store.patch({ connectionBanner: true });
|
|
673
|
+
}
|
|
674
|
+
}, delay);
|
|
675
|
+
}
|
|
676
|
+
function stopRejoin() {
|
|
677
|
+
if (rejoinTimer !== null) clearTimeout(rejoinTimer);
|
|
678
|
+
rejoinTimer = null;
|
|
679
|
+
}
|
|
680
|
+
function maybeRejoin() {
|
|
681
|
+
if (disposed || rejoinTimer !== null || !dropped()) return;
|
|
682
|
+
const policy = options.policy();
|
|
683
|
+
if (!policy.autoRejoin) return;
|
|
684
|
+
if (rejoinAttempts >= policy.autoRejoinMaxAttempts) return;
|
|
685
|
+
if (!network.online()) return;
|
|
686
|
+
const attempt = rejoinAttempts;
|
|
687
|
+
rejoinTimer = setTimeout(() => {
|
|
688
|
+
rejoinTimer = null;
|
|
689
|
+
if (disposed || !dropped() || !network.online()) return;
|
|
690
|
+
rejoinAttempts = attempt + 1;
|
|
691
|
+
options.report?.("info", `meet: rejoining by itself (attempt ${rejoinAttempts} of ${policy.autoRejoinMaxAttempts})`);
|
|
692
|
+
void options.autoRejoin().catch(() => void 0);
|
|
693
|
+
}, autoRejoinDelayMs(attempt, random));
|
|
694
|
+
}
|
|
695
|
+
function saveMarker(force) {
|
|
696
|
+
const resume = options.resume;
|
|
697
|
+
const snap = store.snapshot();
|
|
698
|
+
if (resume === void 0 || snap.roomName === null) return;
|
|
699
|
+
const at = now();
|
|
700
|
+
if (!force && at - lastSavedAt < 1e4) return;
|
|
701
|
+
const request = options.currentRequest();
|
|
702
|
+
if (request === null) return;
|
|
703
|
+
lastSavedAt = at;
|
|
704
|
+
resume.save(snap.roomName, { request, at });
|
|
705
|
+
}
|
|
706
|
+
const unsubscribeStore = store.subscribe(() => {
|
|
707
|
+
if (disposed) return;
|
|
708
|
+
const snap = store.snapshot();
|
|
709
|
+
const phase = snap.phase;
|
|
710
|
+
const changed = phase !== lastPhase;
|
|
711
|
+
lastPhase = phase;
|
|
712
|
+
if (changed) {
|
|
713
|
+
if (phase === "in_call") {
|
|
714
|
+
rejoinAttempts = 0;
|
|
715
|
+
stopRejoin();
|
|
716
|
+
saveMarker(true);
|
|
717
|
+
} else if (phase === "disconnected") {
|
|
718
|
+
driverRegion = null;
|
|
719
|
+
saveMarker(true);
|
|
720
|
+
stopProbe();
|
|
721
|
+
scheduleProbe(0);
|
|
722
|
+
} else {
|
|
723
|
+
stopRejoin();
|
|
724
|
+
if (phase !== "joining") driverRegion = null;
|
|
725
|
+
if (CLEARS_RESUME(phase) && snap.roomName !== null) options.resume?.clear(snap.roomName);
|
|
726
|
+
}
|
|
727
|
+
} else if (phase === "in_call" || phase === "disconnected") {
|
|
728
|
+
saveMarker(false);
|
|
729
|
+
}
|
|
730
|
+
syncBanner();
|
|
731
|
+
});
|
|
732
|
+
const unsubscribeNetwork = network.subscribe((online) => {
|
|
733
|
+
if (disposed) return;
|
|
734
|
+
if (!online) {
|
|
735
|
+
if (inCall() || dropped()) setRegion("offline");
|
|
736
|
+
stopRejoin();
|
|
737
|
+
stopProbe();
|
|
738
|
+
if (needsWatch()) scheduleProbe(probeEveryMs);
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
stopProbe();
|
|
742
|
+
if (needsWatch()) scheduleProbe(0);
|
|
743
|
+
});
|
|
744
|
+
return {
|
|
745
|
+
driverState(state) {
|
|
746
|
+
if (disposed) return;
|
|
747
|
+
if (state === "connected") {
|
|
748
|
+
driverRegion = null;
|
|
749
|
+
if (inCall() || store.snapshot().phase === "joining") setRegion(network.online() ? "stable" : "offline");
|
|
750
|
+
if (!needsWatch()) stopProbe();
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
if (state === "reconnecting" || state === "signal_reconnecting") {
|
|
754
|
+
driverRegion = state;
|
|
755
|
+
if (inCall()) {
|
|
756
|
+
setRegion(network.online() ? state : "offline");
|
|
757
|
+
stopProbe();
|
|
758
|
+
scheduleProbe(0);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
},
|
|
762
|
+
exitConnection() {
|
|
763
|
+
return network.online() ? "server_unreachable" : "offline";
|
|
764
|
+
},
|
|
765
|
+
resumable(roomName) {
|
|
766
|
+
const resume = options.resume;
|
|
767
|
+
if (resume === void 0) return null;
|
|
768
|
+
const marker = resume.load(roomName);
|
|
769
|
+
if (marker === null) return null;
|
|
770
|
+
if (now() - marker.at > RESUME_WINDOW_MS) {
|
|
771
|
+
resume.clear(roomName);
|
|
772
|
+
return null;
|
|
773
|
+
}
|
|
774
|
+
return marker;
|
|
775
|
+
},
|
|
776
|
+
dispose() {
|
|
777
|
+
disposed = true;
|
|
778
|
+
stopProbe();
|
|
779
|
+
stopRejoin();
|
|
780
|
+
if (bannerTimer !== null) clearTimeout(bannerTimer);
|
|
781
|
+
bannerTimer = null;
|
|
782
|
+
unsubscribeStore();
|
|
783
|
+
unsubscribeNetwork();
|
|
784
|
+
}
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
var DELIBERATE_DEPARTURES = /* @__PURE__ */ new Set([
|
|
788
|
+
"CLIENT_INITIATED",
|
|
789
|
+
"PARTICIPANT_REMOVED",
|
|
790
|
+
"ROOM_DELETED",
|
|
791
|
+
"ROOM_CLOSED",
|
|
792
|
+
"DUPLICATE_IDENTITY",
|
|
793
|
+
"USER_REJECTED",
|
|
794
|
+
"USER_UNAVAILABLE",
|
|
795
|
+
"SIP_TRUNK_FAILURE"
|
|
796
|
+
]);
|
|
797
|
+
function isUnannouncedDeparture(reason) {
|
|
798
|
+
return reason === null || !DELIBERATE_DEPARTURES.has(reason);
|
|
799
|
+
}
|
|
800
|
+
function mergeGhosts(live, ghosts, at) {
|
|
801
|
+
const present = new Set(live.map((p) => p.identity));
|
|
802
|
+
const kept = /* @__PURE__ */ new Map();
|
|
803
|
+
const extra = [];
|
|
804
|
+
for (const [identity, ghost] of ghosts) {
|
|
805
|
+
if (present.has(identity) || ghost.until <= at) continue;
|
|
806
|
+
kept.set(identity, ghost);
|
|
807
|
+
extra.push({
|
|
808
|
+
...ghost.participant,
|
|
809
|
+
micEnabled: false,
|
|
810
|
+
cameraEnabled: false,
|
|
811
|
+
screenSharing: false,
|
|
812
|
+
reconnecting: true
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
return { roster: [...live, ...extra], kept };
|
|
816
|
+
}
|
|
817
|
+
|
|
459
818
|
// src/core/tokens.ts
|
|
460
819
|
function createWebRoomTokenStorage(onDiagnostic) {
|
|
461
820
|
const store = globalThis.localStorage;
|
|
@@ -641,7 +1000,8 @@ function parseRoomToken(body, roomName) {
|
|
|
641
1000
|
admissionState: readString(payload, "admission_state") ?? void 0,
|
|
642
1001
|
pollSeconds: typeof payload["poll_seconds"] === "number" ? payload["poll_seconds"] : void 0,
|
|
643
1002
|
breakoutRoomId: readString(payload, "breakout_room_id") ?? null,
|
|
644
|
-
observersVisibleTo: readString(payload, "observers_visible_to") === "hosts" ? "hosts" : "everyone"
|
|
1003
|
+
observersVisibleTo: readString(payload, "observers_visible_to") === "hosts" ? "hosts" : "everyone",
|
|
1004
|
+
connectionPolicy: connectionPolicyFrom(payload["connection_policy"])
|
|
645
1005
|
};
|
|
646
1006
|
}
|
|
647
1007
|
function createTokenClient(options) {
|
|
@@ -1367,8 +1727,14 @@ function nextPhase(from, event) {
|
|
|
1367
1727
|
return from === "resolving" ? "prejoin" : null;
|
|
1368
1728
|
case "unsupported":
|
|
1369
1729
|
return from === "resolving" ? "unsupported" : null;
|
|
1730
|
+
case "link_state":
|
|
1731
|
+
return from === "resolving" && event.reason !== null && isJoinRefusalReason(event.reason) ? refusalPhase(event.reason) : null;
|
|
1370
1732
|
case "join":
|
|
1371
1733
|
return JOINABLE_FROM.has(from) ? "joining" : null;
|
|
1734
|
+
case "auto_rejoin":
|
|
1735
|
+
return from === "disconnected" ? "joining" : null;
|
|
1736
|
+
case "resumed":
|
|
1737
|
+
return from === "resolving" ? "joining" : null;
|
|
1372
1738
|
case "back_to_prejoin":
|
|
1373
1739
|
return from === "left" || from === "disconnected" ? "prejoin" : null;
|
|
1374
1740
|
case "retry":
|
|
@@ -1775,6 +2141,8 @@ var RPCS = {
|
|
|
1775
2141
|
getOrCreateMeeting: "meet_get_or_create_meeting",
|
|
1776
2142
|
/** Resolve a durable public link, before/during/after its scheduled time. */
|
|
1777
2143
|
meetingBySlug: "meet_meeting_by_slug",
|
|
2144
|
+
/** The current run of a durable link (open / not started / ended + next start), no pass needed. */
|
|
2145
|
+
linkState: "meet_link_state",
|
|
1778
2146
|
/*
|
|
1779
2147
|
* ── S4 ADMISSION (CORE-DESIGN §2.4). Each returns the decided row; the caller then
|
|
1780
2148
|
* sends the payload-free `mx.state.changed` hint. Authorized by `who_may_admit`.
|
|
@@ -2083,6 +2451,14 @@ function createMeetRepository(options) {
|
|
|
2083
2451
|
if (response.error !== null) throw normalizeMeetError(response.error, operation);
|
|
2084
2452
|
}
|
|
2085
2453
|
}
|
|
2454
|
+
async function readLinkRun(slug) {
|
|
2455
|
+
try {
|
|
2456
|
+
const response = await db.rpc(RPCS.linkState, { p_slug: slug });
|
|
2457
|
+
return projectLinkRun(response.error === null ? response.data : null);
|
|
2458
|
+
} catch {
|
|
2459
|
+
return void 0;
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2086
2462
|
async function guard(operation, run) {
|
|
2087
2463
|
let response = await run();
|
|
2088
2464
|
if (response.error !== null) {
|
|
@@ -2484,11 +2860,12 @@ function createMeetRepository(options) {
|
|
|
2484
2860
|
return projectMeeting(row);
|
|
2485
2861
|
},
|
|
2486
2862
|
async meetingBySlug(slug) {
|
|
2487
|
-
const row = await
|
|
2488
|
-
"meet.meetingBySlug",
|
|
2489
|
-
(
|
|
2490
|
-
);
|
|
2491
|
-
|
|
2863
|
+
const [row, linkRun] = await Promise.all([
|
|
2864
|
+
guard("meet.meetingBySlug", () => db.rpc(RPCS.meetingBySlug, { p_slug: slug })),
|
|
2865
|
+
readLinkRun(slug)
|
|
2866
|
+
]);
|
|
2867
|
+
const record = withRunNoteTaker(projectMeeting(row));
|
|
2868
|
+
return linkRun === void 0 ? record : { ...record, linkRun };
|
|
2492
2869
|
},
|
|
2493
2870
|
async meeting(id) {
|
|
2494
2871
|
const response = await db.from(TABLES.meetings).select(MEETING_COLUMNS).eq("id", id).single();
|
|
@@ -2677,6 +3054,15 @@ function snippetRuns(snippet) {
|
|
|
2677
3054
|
if (last < snippet.length) runs.push({ text: snippet.slice(last), hit: false });
|
|
2678
3055
|
return runs;
|
|
2679
3056
|
}
|
|
3057
|
+
function projectLinkRun(value) {
|
|
3058
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
3059
|
+
const row = value;
|
|
3060
|
+
const state = row["state"];
|
|
3061
|
+
if (state !== "open" && state !== "not_started" && state !== "ended" && state !== "cancelled") return void 0;
|
|
3062
|
+
const reason = row["reason"] === "ended" || row["reason"] === "cancelled" ? row["reason"] : null;
|
|
3063
|
+
const nextStart = typeof row["next_start"] === "string" ? row["next_start"] : null;
|
|
3064
|
+
return { state, reason, nextStart };
|
|
3065
|
+
}
|
|
2680
3066
|
|
|
2681
3067
|
// src/core/app-panel.ts
|
|
2682
3068
|
function appPanelOf(meeting) {
|
|
@@ -3262,6 +3648,7 @@ var EMPTY = {
|
|
|
3262
3648
|
phaseNotice: null,
|
|
3263
3649
|
roomName: null,
|
|
3264
3650
|
connection: "stable",
|
|
3651
|
+
connectionBanner: false,
|
|
3265
3652
|
share: NO_SHARE,
|
|
3266
3653
|
call: null,
|
|
3267
3654
|
waiting: null,
|
|
@@ -4216,8 +4603,11 @@ function mapConnectionState(state) {
|
|
|
4216
4603
|
case "connecting":
|
|
4217
4604
|
return "connecting";
|
|
4218
4605
|
case "reconnecting":
|
|
4219
|
-
case "signalReconnecting":
|
|
4220
4606
|
return "reconnecting";
|
|
4607
|
+
// Only the signal socket is down (S6b): its own region value, so the supervisor can tell a
|
|
4608
|
+
// signal blip from a full media reconnect.
|
|
4609
|
+
case "signalReconnecting":
|
|
4610
|
+
return "signal_reconnecting";
|
|
4221
4611
|
default:
|
|
4222
4612
|
return "disconnected";
|
|
4223
4613
|
}
|
|
@@ -4298,8 +4688,12 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
4298
4688
|
// R5: large meetings degrade gracefully instead of melting a laptop.
|
|
4299
4689
|
adaptiveStream: true,
|
|
4300
4690
|
dynacast: true,
|
|
4691
|
+
// S6b: OUR retry schedule — jittered from attempt 0 (the SDK's default has no jitter on its
|
|
4692
|
+
// first two attempts), give-up limits in one place (`connection-supervisor.ts`).
|
|
4693
|
+
reconnectPolicy: createReconnectPolicy(),
|
|
4301
4694
|
...options.roomOptions
|
|
4302
4695
|
});
|
|
4696
|
+
const reasonName = (reason) => reason === void 0 || reason === null ? null : module2.DisconnectReason?.[reason] ?? String(reason);
|
|
4303
4697
|
const roster = () => {
|
|
4304
4698
|
const local = snapshotParticipant(created.localParticipant, true);
|
|
4305
4699
|
const remotes = [...created.remoteParticipants.values()].map(
|
|
@@ -4317,7 +4711,10 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
4317
4711
|
if (track.kind !== Track.Kind.Audio) return;
|
|
4318
4712
|
for (const element of track.detach()) element.remove();
|
|
4319
4713
|
});
|
|
4320
|
-
created.on(RoomEvent.ParticipantConnected, roster).on(RoomEvent.ParticipantDisconnected,
|
|
4714
|
+
created.on(RoomEvent.ParticipantConnected, roster).on(RoomEvent.ParticipantDisconnected, (participant, reason) => {
|
|
4715
|
+
handlers.participantLeft?.(participant.identity, reasonName(reason));
|
|
4716
|
+
roster();
|
|
4717
|
+
}).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) => {
|
|
4321
4718
|
handlers.roomMetadata?.(metadata ?? "");
|
|
4322
4719
|
}).on(RoomEvent.ActiveSpeakersChanged, (speakers) => {
|
|
4323
4720
|
handlers.activeSpeakers(speakers.map((speaker) => speaker.identity));
|
|
@@ -4334,8 +4731,7 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
4334
4731
|
}).on(RoomEvent.Disconnected, (reason) => {
|
|
4335
4732
|
audioContainer?.remove();
|
|
4336
4733
|
audioContainer = null;
|
|
4337
|
-
|
|
4338
|
-
handlers.disconnected(named);
|
|
4734
|
+
handlers.disconnected(reasonName(reason));
|
|
4339
4735
|
});
|
|
4340
4736
|
try {
|
|
4341
4737
|
created.registerTextStreamHandler(TRANSCRIPTION_TOPIC, (reader, participantInfo) => {
|
|
@@ -5708,6 +6104,18 @@ function createRoomEngine(options) {
|
|
|
5708
6104
|
let disposed = false;
|
|
5709
6105
|
let moving = false;
|
|
5710
6106
|
const confirmingEnds = /* @__PURE__ */ new WeakSet();
|
|
6107
|
+
let lastServerUrl = null;
|
|
6108
|
+
let connectionPolicy = DEFAULT_CONNECTION_POLICY;
|
|
6109
|
+
let ghosts = /* @__PURE__ */ new Map();
|
|
6110
|
+
let ghostTimer = null;
|
|
6111
|
+
let liveRoster = [];
|
|
6112
|
+
const departures = /* @__PURE__ */ new Map();
|
|
6113
|
+
function requestAsNow() {
|
|
6114
|
+
if (lastRequest === null) return null;
|
|
6115
|
+
const media = store.snapshot().media;
|
|
6116
|
+
if (store.snapshot().phase !== "in_call") return lastRequest;
|
|
6117
|
+
return { ...lastRequest, mode: media.camera.enabled ? "video" : "audio", microphone: media.microphone.enabled };
|
|
6118
|
+
}
|
|
5711
6119
|
async function dropRoom() {
|
|
5712
6120
|
const target = driver;
|
|
5713
6121
|
driver = null;
|
|
@@ -5718,6 +6126,7 @@ function createRoomEngine(options) {
|
|
|
5718
6126
|
store.setLocalTrack("microphone", IDLE_TRACK);
|
|
5719
6127
|
store.setLocalTrack("camera", IDLE_TRACK);
|
|
5720
6128
|
store.patch({ share: NO_SHARE, connection: "stable" });
|
|
6129
|
+
clearGhosts();
|
|
5721
6130
|
await target?.disconnect();
|
|
5722
6131
|
}
|
|
5723
6132
|
async function leaveRoom() {
|
|
@@ -5785,7 +6194,7 @@ function createRoomEngine(options) {
|
|
|
5785
6194
|
participants(list) {
|
|
5786
6195
|
const joinedAt = new Date(now()).toISOString();
|
|
5787
6196
|
const before = new Set(store.snapshot().participants.map((p) => p.identity));
|
|
5788
|
-
|
|
6197
|
+
projectRoster(list, joinedAt);
|
|
5789
6198
|
const snapshot = store.snapshot();
|
|
5790
6199
|
const newcomer = snapshot.participants.some(
|
|
5791
6200
|
(participant) => !participant.isLocal && !before.has(participant.identity)
|
|
@@ -5797,6 +6206,9 @@ function createRoomEngine(options) {
|
|
|
5797
6206
|
);
|
|
5798
6207
|
}
|
|
5799
6208
|
},
|
|
6209
|
+
participantLeft(who, reason) {
|
|
6210
|
+
departures.set(who, reason);
|
|
6211
|
+
},
|
|
5800
6212
|
roomMetadata(metadata) {
|
|
5801
6213
|
let spotlight = null;
|
|
5802
6214
|
if (metadata.length > 0) {
|
|
@@ -5823,12 +6235,8 @@ function createRoomEngine(options) {
|
|
|
5823
6235
|
store.setQuality(asParticipantIdentity(who), value);
|
|
5824
6236
|
},
|
|
5825
6237
|
connectionState(state) {
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
store.dispatch({ type: "connected" });
|
|
5829
|
-
} else if (state === "reconnecting") {
|
|
5830
|
-
store.patch({ connection: "reconnecting" });
|
|
5831
|
-
}
|
|
6238
|
+
supervisor.driverState(state);
|
|
6239
|
+
if (state === "connected" && store.snapshot().phase === "joining") store.dispatch({ type: "connected" });
|
|
5832
6240
|
},
|
|
5833
6241
|
transcription(event) {
|
|
5834
6242
|
const snapshot = store.snapshot();
|
|
@@ -5961,6 +6369,7 @@ function createRoomEngine(options) {
|
|
|
5961
6369
|
};
|
|
5962
6370
|
async function resolveExitAndApply(hint) {
|
|
5963
6371
|
const connection = current?.identity ?? store.snapshot().localIdentity ?? null;
|
|
6372
|
+
lastRequest = requestAsNow();
|
|
5964
6373
|
await dropRoom();
|
|
5965
6374
|
let server;
|
|
5966
6375
|
if (options.readExit === void 0 || connection === null || store.snapshot().meeting === null) {
|
|
@@ -5974,7 +6383,7 @@ function createRoomEngine(options) {
|
|
|
5974
6383
|
}
|
|
5975
6384
|
if (disposed) return;
|
|
5976
6385
|
const resolved = resolveExit({ server, hint, frozen: options.wasFrozen?.() ?? false });
|
|
5977
|
-
store.patch({ connection: resolved.connection });
|
|
6386
|
+
store.patch({ connection: server === null ? supervisor.exitConnection() : resolved.connection });
|
|
5978
6387
|
if (resolved.to === "waiting:knocking") {
|
|
5979
6388
|
store.patch({
|
|
5980
6389
|
waiting: {
|
|
@@ -6006,6 +6415,8 @@ function createRoomEngine(options) {
|
|
|
6006
6415
|
...request.observe === true ? { observe: true } : {}
|
|
6007
6416
|
});
|
|
6008
6417
|
current = token;
|
|
6418
|
+
lastServerUrl = token.serverUrl;
|
|
6419
|
+
connectionPolicy = token.connectionPolicy ?? DEFAULT_CONNECTION_POLICY;
|
|
6009
6420
|
store.patch({
|
|
6010
6421
|
localIdentity: token.identity,
|
|
6011
6422
|
breakoutRoomId: token.breakoutRoomId ?? null,
|
|
@@ -6110,7 +6521,59 @@ function createRoomEngine(options) {
|
|
|
6110
6521
|
throw normalized;
|
|
6111
6522
|
}
|
|
6112
6523
|
}
|
|
6113
|
-
|
|
6524
|
+
function projectRoster(list, joinedAt) {
|
|
6525
|
+
const at = now();
|
|
6526
|
+
const previous = new Map(liveRoster.map((p) => [p.identity, p]));
|
|
6527
|
+
const present = new Set(list.map((p) => p.identity));
|
|
6528
|
+
const graceMs = connectionPolicy.reconnectGraceSeconds * 1e3;
|
|
6529
|
+
for (const [who, reason] of departures) {
|
|
6530
|
+
const last = previous.get(who);
|
|
6531
|
+
if (last !== void 0 && !last.isLocal && !present.has(who) && graceMs > 0 && isUnannouncedDeparture(reason)) {
|
|
6532
|
+
ghosts.set(who, { participant: last, until: at + graceMs });
|
|
6533
|
+
}
|
|
6534
|
+
}
|
|
6535
|
+
departures.clear();
|
|
6536
|
+
liveRoster = list;
|
|
6537
|
+
const merged = mergeGhosts(list, ghosts, at);
|
|
6538
|
+
ghosts = merged.kept;
|
|
6539
|
+
store.setParticipants(
|
|
6540
|
+
merged.roster.map((entry) => {
|
|
6541
|
+
const projected = projectParticipant(entry, joinedAt);
|
|
6542
|
+
return entry.reconnecting === true ? { ...projected, reconnecting: true, quality: "lost" } : projected;
|
|
6543
|
+
})
|
|
6544
|
+
);
|
|
6545
|
+
scheduleGhostExpiry();
|
|
6546
|
+
}
|
|
6547
|
+
function scheduleGhostExpiry() {
|
|
6548
|
+
if (ghostTimer !== null) clearTimeout(ghostTimer);
|
|
6549
|
+
ghostTimer = null;
|
|
6550
|
+
if (ghosts.size === 0 || disposed) return;
|
|
6551
|
+
const next = Math.min(...[...ghosts.values()].map((g) => g.until));
|
|
6552
|
+
ghostTimer = setTimeout(() => {
|
|
6553
|
+
ghostTimer = null;
|
|
6554
|
+
if (disposed || driver === null) return;
|
|
6555
|
+
projectRoster(liveRoster, new Date(now()).toISOString());
|
|
6556
|
+
}, Math.max(0, next - now()) + 50);
|
|
6557
|
+
}
|
|
6558
|
+
function clearGhosts() {
|
|
6559
|
+
ghosts = /* @__PURE__ */ new Map();
|
|
6560
|
+
departures.clear();
|
|
6561
|
+
liveRoster = [];
|
|
6562
|
+
if (ghostTimer !== null) clearTimeout(ghostTimer);
|
|
6563
|
+
ghostTimer = null;
|
|
6564
|
+
}
|
|
6565
|
+
const supervisor = createConnectionSupervisor({
|
|
6566
|
+
store,
|
|
6567
|
+
network: options.connection?.network ?? createWebNetworkPort(),
|
|
6568
|
+
probe: () => options.connection?.probe?.(current?.serverUrl ?? lastServerUrl) ?? Promise.resolve(true),
|
|
6569
|
+
policy: () => connectionPolicy,
|
|
6570
|
+
autoRejoin: () => engine.autoRejoin(),
|
|
6571
|
+
currentRequest: requestAsNow,
|
|
6572
|
+
resume: options.connection?.resume,
|
|
6573
|
+
now,
|
|
6574
|
+
report: (level, message) => report(level, message)
|
|
6575
|
+
});
|
|
6576
|
+
const engine = {
|
|
6114
6577
|
join(request) {
|
|
6115
6578
|
if (joining !== null) return joining;
|
|
6116
6579
|
if (store.snapshot().phase !== "joining" && !store.dispatch({ type: "join", click: true })) {
|
|
@@ -6155,6 +6618,35 @@ function createRoomEngine(options) {
|
|
|
6155
6618
|
});
|
|
6156
6619
|
return joining;
|
|
6157
6620
|
},
|
|
6621
|
+
async autoRejoin() {
|
|
6622
|
+
const request = lastRequest;
|
|
6623
|
+
if (request === null || joining !== null) return joining ?? void 0;
|
|
6624
|
+
if (!store.dispatch({ type: "auto_rejoin" })) return;
|
|
6625
|
+
tokens.invalidate(request.roomName);
|
|
6626
|
+
current = null;
|
|
6627
|
+
joining = connect(request).catch((error) => {
|
|
6628
|
+
throw failJoin(error, "meet.autoRejoin");
|
|
6629
|
+
}).finally(() => {
|
|
6630
|
+
joining = null;
|
|
6631
|
+
});
|
|
6632
|
+
return joining;
|
|
6633
|
+
},
|
|
6634
|
+
resume(roomName) {
|
|
6635
|
+
const marker = supervisor.resumable(roomName);
|
|
6636
|
+
if (marker === null || joining !== null) return false;
|
|
6637
|
+
if (!store.dispatch({ type: "resumed" })) return false;
|
|
6638
|
+
lastRequest = { ...marker.request, roomName };
|
|
6639
|
+
report("info", "meet: this tab was in the call before the page reloaded \u2014 rejoining");
|
|
6640
|
+
tokens.invalidate(roomName);
|
|
6641
|
+
current = null;
|
|
6642
|
+
joining = connect(lastRequest).catch((error) => {
|
|
6643
|
+
throw failJoin(error, "meet.resume");
|
|
6644
|
+
}).finally(() => {
|
|
6645
|
+
joining = null;
|
|
6646
|
+
});
|
|
6647
|
+
joining.catch(() => void 0);
|
|
6648
|
+
return true;
|
|
6649
|
+
},
|
|
6158
6650
|
async moveToBreakout(roomId) {
|
|
6159
6651
|
const request = lastRequest;
|
|
6160
6652
|
if (request === null || driver === null) {
|
|
@@ -6322,6 +6814,8 @@ function createRoomEngine(options) {
|
|
|
6322
6814
|
token: () => current,
|
|
6323
6815
|
async dispose() {
|
|
6324
6816
|
disposed = true;
|
|
6817
|
+
supervisor.dispose();
|
|
6818
|
+
clearGhosts();
|
|
6325
6819
|
if (driver !== null) {
|
|
6326
6820
|
const target = driver;
|
|
6327
6821
|
driver = null;
|
|
@@ -6330,6 +6824,7 @@ function createRoomEngine(options) {
|
|
|
6330
6824
|
store.reset();
|
|
6331
6825
|
}
|
|
6332
6826
|
};
|
|
6827
|
+
return engine;
|
|
6333
6828
|
}
|
|
6334
6829
|
function canModerate(participant) {
|
|
6335
6830
|
return participant !== void 0 && isHostRole(participant.role);
|
|
@@ -7415,7 +7910,7 @@ function participantAttributes(p) {
|
|
|
7415
7910
|
"data-meet-participant-role": observedRole(p.role, p.standing),
|
|
7416
7911
|
"data-meet-participant-self": String(p.isLocal)
|
|
7417
7912
|
};
|
|
7418
|
-
const quality = observedQuality(p.quality);
|
|
7913
|
+
const quality = p.reconnecting === true ? "reconnecting" : observedQuality(p.quality);
|
|
7419
7914
|
if (quality !== null) attrs["data-meet-participant-connection"] = quality;
|
|
7420
7915
|
return attrs;
|
|
7421
7916
|
}
|