@ai-matrx/meet 0.11.43 → 0.11.45
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-BNJNgXFB.d.cts → collab-D1TX5uf9.d.cts} +7 -0
- package/dist/{collab-BNJNgXFB.d.ts → collab-D1TX5uf9.d.ts} +7 -0
- package/dist/{components-BDtGzHf4.d.ts → components-CNwBgmYU.d.ts} +1 -1
- package/dist/{components-BxK993iW.d.cts → components-CkyDos0f.d.cts} +1 -1
- package/dist/core.cjs +206 -20
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +121 -4
- package/dist/core.d.ts +121 -4
- package/dist/core.js +206 -20
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +206 -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 +206 -20
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +554 -50
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +4 -4
- package/dist/react.d.ts +4 -4
- package/dist/react.js +960 -456
- package/dist/react.js.map +1 -1
- package/dist/skins/meet.cjs +374 -39
- package/dist/skins/meet.cjs.map +1 -1
- package/dist/skins/meet.d.cts +4 -3
- package/dist/skins/meet.d.ts +4 -3
- package/dist/skins/meet.js +721 -386
- package/dist/skins/meet.js.map +1 -1
- package/dist/styles.css +17 -1
- package/package.json +5 -5
package/dist/skins/meet.cjs
CHANGED
|
@@ -48,6 +48,7 @@ __export(meet_exports, {
|
|
|
48
48
|
ChatIcon: () => ChatIcon,
|
|
49
49
|
ChatPanel: () => ChatPanel,
|
|
50
50
|
CheckIcon: () => CheckIcon,
|
|
51
|
+
ChevronUpIcon: () => ChevronUpIcon,
|
|
51
52
|
CloseIcon: () => CloseIcon,
|
|
52
53
|
ConnectionDot: () => ConnectionDot,
|
|
53
54
|
ConsentNotice: () => ConsentNotice,
|
|
@@ -114,6 +115,33 @@ var import_react10 = require("react");
|
|
|
114
115
|
// src/react/facade.ts
|
|
115
116
|
var import_react8 = require("react");
|
|
116
117
|
|
|
118
|
+
// src/core/phase.ts
|
|
119
|
+
var LIVE_PHASES = /* @__PURE__ */ new Set(["joining", "in_call"]);
|
|
120
|
+
var WAITING_PHASES = /* @__PURE__ */ new Set([
|
|
121
|
+
"waiting:host_not_started",
|
|
122
|
+
"waiting:knocking",
|
|
123
|
+
"denied",
|
|
124
|
+
"knock_expired"
|
|
125
|
+
]);
|
|
126
|
+
var RETRYABLE_REFUSALS = /* @__PURE__ */ new Set([
|
|
127
|
+
"refused:locked",
|
|
128
|
+
"refused:too_early",
|
|
129
|
+
"refused:meeting_full",
|
|
130
|
+
"refused:platform_capacity"
|
|
131
|
+
]);
|
|
132
|
+
function isLivePhase(phase) {
|
|
133
|
+
return LIVE_PHASES.has(phase);
|
|
134
|
+
}
|
|
135
|
+
function isWaitingPhase(phase) {
|
|
136
|
+
return WAITING_PHASES.has(phase);
|
|
137
|
+
}
|
|
138
|
+
function isRefusedPhase(phase) {
|
|
139
|
+
return phase.startsWith("refused:");
|
|
140
|
+
}
|
|
141
|
+
function reasonOf(phase) {
|
|
142
|
+
return isRefusedPhase(phase) ? phase.slice("refused:".length) : null;
|
|
143
|
+
}
|
|
144
|
+
|
|
117
145
|
// src/core/names.ts
|
|
118
146
|
var CONNECTION_SEPARATOR = "~";
|
|
119
147
|
function personOf(identity) {
|
|
@@ -390,11 +418,13 @@ function minutesLeft(policy, startedAtMs, nowMs) {
|
|
|
390
418
|
}
|
|
391
419
|
|
|
392
420
|
// src/core/attendance.ts
|
|
421
|
+
var IDLE_PREVIEW = { state: "idle", unavailable: null };
|
|
393
422
|
var IDLE_MEDIA_REGION = {
|
|
394
423
|
permission: { microphone: "unknown", camera: "unknown" },
|
|
395
424
|
audioOutput: "ok",
|
|
396
425
|
notice: null,
|
|
397
|
-
offer: null
|
|
426
|
+
offer: null,
|
|
427
|
+
preview: { microphone: IDLE_PREVIEW, camera: IDLE_PREVIEW }
|
|
398
428
|
};
|
|
399
429
|
var IDLE_TRACK = { enabled: false, unavailable: null };
|
|
400
430
|
var IDLE_MEDIA = {
|
|
@@ -427,27 +457,6 @@ function isObserverRole(role) {
|
|
|
427
457
|
return role === "viewer";
|
|
428
458
|
}
|
|
429
459
|
|
|
430
|
-
// src/core/phase.ts
|
|
431
|
-
var LIVE_PHASES = /* @__PURE__ */ new Set(["joining", "in_call"]);
|
|
432
|
-
var WAITING_PHASES = /* @__PURE__ */ new Set([
|
|
433
|
-
"waiting:host_not_started",
|
|
434
|
-
"waiting:knocking",
|
|
435
|
-
"denied",
|
|
436
|
-
"knock_expired"
|
|
437
|
-
]);
|
|
438
|
-
function isLivePhase(phase) {
|
|
439
|
-
return LIVE_PHASES.has(phase);
|
|
440
|
-
}
|
|
441
|
-
function isWaitingPhase(phase) {
|
|
442
|
-
return WAITING_PHASES.has(phase);
|
|
443
|
-
}
|
|
444
|
-
function isRefusedPhase(phase) {
|
|
445
|
-
return phase.startsWith("refused:");
|
|
446
|
-
}
|
|
447
|
-
function reasonOf(phase) {
|
|
448
|
-
return isRefusedPhase(phase) ? phase.slice("refused:".length) : null;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
460
|
// src/core/app-panel.ts
|
|
452
461
|
function appPanelOf(meeting) {
|
|
453
462
|
const raw = meeting?.metadata?.["app_panel"];
|
|
@@ -615,7 +624,7 @@ function deviceFixSteps(kind, reason, where) {
|
|
|
615
624
|
case "safari":
|
|
616
625
|
return where.os === "ios" ? [`Settings \u203A Apps \u203A Safari \u203A ${device}`, "Choose Allow", "Return here and press Try again"] : ["Safari \u203A Settings for This Website", `Set ${device} to Allow`, "Press Try again"];
|
|
617
626
|
default:
|
|
618
|
-
return ["Click the icon
|
|
627
|
+
return ["Click the site settings icon in the address bar", `Set ${device} to Allow`, "Press Try again"];
|
|
619
628
|
}
|
|
620
629
|
case "blocked_os": {
|
|
621
630
|
const app = where.browser === "chrome" ? "Google Chrome" : where.browser === "edge" ? "Microsoft Edge" : where.browser === "firefox" ? "Firefox" : "your browser";
|
|
@@ -709,6 +718,21 @@ function observedDevice(track, permission = "unknown", held = null) {
|
|
|
709
718
|
const reason = deviceUnavailableReason(track, permission);
|
|
710
719
|
return reason === null ? "muted_by_me" : `unavailable:${reason}`;
|
|
711
720
|
}
|
|
721
|
+
function observedPreview(preview, permission = "unknown") {
|
|
722
|
+
if (permission === "denied") return "unavailable:denied";
|
|
723
|
+
switch (preview.state) {
|
|
724
|
+
case "on":
|
|
725
|
+
return "on";
|
|
726
|
+
case "off":
|
|
727
|
+
return "muted_by_me";
|
|
728
|
+
case "prompting":
|
|
729
|
+
return "prompting";
|
|
730
|
+
case "unavailable":
|
|
731
|
+
return `unavailable:${preview.unavailable?.reason ?? "failed"}`;
|
|
732
|
+
default:
|
|
733
|
+
return null;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
712
736
|
function endedRecord(snapshot, given) {
|
|
713
737
|
const record = snapshot?.meeting ?? given;
|
|
714
738
|
return record !== null && record.endedAt !== null;
|
|
@@ -732,18 +756,22 @@ function observe(input) {
|
|
|
732
756
|
const canAdmit = actual !== "none" && ADMITTER_ROLES.has(actual);
|
|
733
757
|
const role = actual === "none" || snapshot === null ? "none" : observedRole(actual, effectiveStanding(snapshot));
|
|
734
758
|
const deviceInPlay = phase === "joining" || inCall;
|
|
759
|
+
const region = snapshot?.mediaRegion;
|
|
760
|
+
const atPrejoin = phase === "prejoin" && region !== void 0;
|
|
735
761
|
return {
|
|
736
762
|
phase,
|
|
737
763
|
reason: phase === "ended" ? snapshot?.reason ?? null : snapshot?.reason ?? reasonOf(phase),
|
|
738
764
|
role,
|
|
739
|
-
|
|
765
|
+
// `net-blip`: in the call, a drop shorter than `reconnect_banner_delay_ms` is not something the
|
|
766
|
+
// person sees — the contract says what is on screen, so it reads `stable` until the banner shows.
|
|
767
|
+
connection: snapshot === null ? "stable" : inCall && snapshot.connection !== "stable" && !snapshot.connectionBanner ? "stable" : snapshot.connection,
|
|
740
768
|
hostPresent: inCall && snapshot !== null ? snapshot.participants.some((p) => ADMITTER_ROLES.has(p.role)) : snapshot !== null && isWaitingPhase(snapshot.phase) ? snapshot.waiting?.admittersPresent ?? null : null,
|
|
741
769
|
lobbyCount: canAdmit && snapshot !== null ? snapshot.lobby.filter((entry) => entry.state === "knocking").length : 0,
|
|
742
770
|
recording: snapshot?.recording === "recording" || snapshot?.recording === "stopping",
|
|
743
771
|
// S6c: the autoplay region is known only inside the room (no remote audio exists elsewhere).
|
|
744
772
|
audioBlocked: inCall && snapshot !== null ? snapshot.mediaRegion.audioOutput === "blocked" : null,
|
|
745
|
-
camera: deviceInPlay && snapshot !== null ? observedDevice(snapshot.media.camera, snapshot.mediaRegion.permission.camera, lockedFor(snapshot, "camera", inCall)) : null,
|
|
746
|
-
microphone: deviceInPlay && snapshot !== null ? observedDevice(snapshot.media.microphone, snapshot.mediaRegion.permission.microphone, lockedFor(snapshot, "microphone", inCall)) : null
|
|
773
|
+
camera: deviceInPlay && snapshot !== null ? observedDevice(snapshot.media.camera, snapshot.mediaRegion.permission.camera, lockedFor(snapshot, "camera", inCall)) : atPrejoin ? observedPreview(region.preview.camera, region.permission.camera) : null,
|
|
774
|
+
microphone: deviceInPlay && snapshot !== null ? observedDevice(snapshot.media.microphone, snapshot.mediaRegion.permission.microphone, lockedFor(snapshot, "microphone", inCall)) : atPrejoin ? observedPreview(region.preview.microphone, region.permission.microphone) : null
|
|
747
775
|
};
|
|
748
776
|
}
|
|
749
777
|
function lockedFor(snapshot, kind, inCall) {
|
|
@@ -1267,7 +1295,8 @@ function useRoom() {
|
|
|
1267
1295
|
failure: snapshot?.failure ?? null,
|
|
1268
1296
|
secondDevice: snapshot?.secondDevice ?? null,
|
|
1269
1297
|
local,
|
|
1270
|
-
|
|
1298
|
+
// An observer (role `viewer`) publishes nothing: no Share screen to press and be refused (MD-16).
|
|
1299
|
+
canShareScreen: (engine?.supportsScreenShare() ?? true) && !isObserverRole(snapshot?.grants.role ?? null),
|
|
1271
1300
|
join: (0, import_react5.useCallback)(
|
|
1272
1301
|
async (request) => requireEngine(engine, "join").join(request),
|
|
1273
1302
|
[engine]
|
|
@@ -1693,6 +1722,12 @@ function useMediaSupervisor() {
|
|
|
1693
1722
|
retry: (0, import_react5.useCallback)(
|
|
1694
1723
|
async (kind) => kind === "microphone" ? engine?.setMicrophoneEnabled(true) : engine?.setCameraEnabled(true),
|
|
1695
1724
|
[engine]
|
|
1725
|
+
),
|
|
1726
|
+
reportPreview: (0, import_react5.useCallback)(
|
|
1727
|
+
(kind, outcome) => {
|
|
1728
|
+
void engine?.previewDevice(kind, outcome);
|
|
1729
|
+
},
|
|
1730
|
+
[engine]
|
|
1696
1731
|
)
|
|
1697
1732
|
};
|
|
1698
1733
|
}
|
|
@@ -2013,6 +2048,39 @@ function useCollabPanelRequest(onRequest) {
|
|
|
2013
2048
|
// src/core/reactions.ts
|
|
2014
2049
|
var MEET_REACTIONS = ["\u{1F496}", "\u{1F44D}", "\u{1F389}", "\u{1F44F}", "\u{1F602}", "\u{1F62E}", "\u{1F622}", "\u{1F914}", "\u{1F44E}"];
|
|
2015
2050
|
|
|
2051
|
+
// src/core/speaking-detector.ts
|
|
2052
|
+
function createSpeakingWhileMutedDetector(options = {}) {
|
|
2053
|
+
const threshold = options.threshold ?? 0.06;
|
|
2054
|
+
const holdMs = options.holdMs ?? 600;
|
|
2055
|
+
const gapMs = options.gapMs ?? 400;
|
|
2056
|
+
const cooldownMs = options.cooldownMs ?? 2e4;
|
|
2057
|
+
let speechStart = null;
|
|
2058
|
+
let lastLoud = null;
|
|
2059
|
+
let firedAt = null;
|
|
2060
|
+
return {
|
|
2061
|
+
feed(level, at) {
|
|
2062
|
+
if (level >= threshold) {
|
|
2063
|
+
if (speechStart === null || lastLoud === null || at - lastLoud > gapMs) speechStart = at;
|
|
2064
|
+
lastLoud = at;
|
|
2065
|
+
} else if (lastLoud !== null && at - lastLoud > gapMs) {
|
|
2066
|
+
speechStart = null;
|
|
2067
|
+
lastLoud = null;
|
|
2068
|
+
}
|
|
2069
|
+
if (speechStart === null || at - speechStart < holdMs) return false;
|
|
2070
|
+
if (firedAt !== null && at - firedAt < cooldownMs) return false;
|
|
2071
|
+
firedAt = at;
|
|
2072
|
+
speechStart = null;
|
|
2073
|
+
lastLoud = null;
|
|
2074
|
+
return true;
|
|
2075
|
+
},
|
|
2076
|
+
reset() {
|
|
2077
|
+
speechStart = null;
|
|
2078
|
+
lastLoud = null;
|
|
2079
|
+
firedAt = null;
|
|
2080
|
+
}
|
|
2081
|
+
};
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2016
2084
|
// src/react/facade.ts
|
|
2017
2085
|
var NO_ACTIONS = {
|
|
2018
2086
|
join: async () => void 0,
|
|
@@ -2033,6 +2101,10 @@ function actionsFor(host) {
|
|
|
2033
2101
|
leave: () => engine.leave(),
|
|
2034
2102
|
backToPrejoin: () => engine.backToPrejoin(),
|
|
2035
2103
|
retry: () => {
|
|
2104
|
+
if (RETRYABLE_REFUSALS.has(store.snapshot().phase)) {
|
|
2105
|
+
void engine.rejoin().catch(() => void 0);
|
|
2106
|
+
return;
|
|
2107
|
+
}
|
|
2036
2108
|
store.dispatch({ type: "retry", click: true });
|
|
2037
2109
|
},
|
|
2038
2110
|
resolved: () => {
|
|
@@ -2328,6 +2400,9 @@ function UndoIcon(props) {
|
|
|
2328
2400
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5a5.5 5.5 0 0 1-5.5 5.5H11" })
|
|
2329
2401
|
] });
|
|
2330
2402
|
}
|
|
2403
|
+
function ChevronUpIcon(props) {
|
|
2404
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Icon, { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "m6 15 6-6 6 6" }) });
|
|
2405
|
+
}
|
|
2331
2406
|
|
|
2332
2407
|
// src/skins/meet/collab.tsx
|
|
2333
2408
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
@@ -3585,6 +3660,7 @@ function ControlBar({ onLeave }) {
|
|
|
3585
3660
|
) }) }) : null,
|
|
3586
3661
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BreakoutBanner, {}),
|
|
3587
3662
|
panel !== "people" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(KnockNotice, { onView: () => setPanel("people") }) : null,
|
|
3663
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SpeakingWhileMutedNudge, {}),
|
|
3588
3664
|
controls.notice !== null && panel !== "people" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Toast, { text: controls.notice.text, failed: controls.notice.failed, onDone: controls.clearNotice }) : null,
|
|
3589
3665
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mx-meet__controls", children: [
|
|
3590
3666
|
live.micLocked ? null : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
@@ -3596,6 +3672,7 @@ function ControlBar({ onLeave }) {
|
|
|
3596
3672
|
children: room.local?.micEnabled === true ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MicIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MicOffIcon, {})
|
|
3597
3673
|
}
|
|
3598
3674
|
),
|
|
3675
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AudioSettingsControl, {}),
|
|
3599
3676
|
live.cameraLocked ? null : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
3600
3677
|
ControlButton,
|
|
3601
3678
|
{
|
|
@@ -3711,6 +3788,13 @@ function ControlBar({ onLeave }) {
|
|
|
3711
3788
|
]
|
|
3712
3789
|
}
|
|
3713
3790
|
),
|
|
3791
|
+
!room.canShareScreen && room.local?.screenSharing !== true ? (
|
|
3792
|
+
// `share-unsupported-mobile`: the control is absent; the menu says where it lives.
|
|
3793
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "mx-meet__menu-note", role: "note", "data-meet-notice": "share-unsupported", children: [
|
|
3794
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ScreenShareIcon, { width: 16, height: 16 }),
|
|
3795
|
+
" Present from a computer"
|
|
3796
|
+
] })
|
|
3797
|
+
) : null,
|
|
3714
3798
|
controls.isHost ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
3715
3799
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "mx-meet__menu-sep", role: "separator" }),
|
|
3716
3800
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
@@ -3756,6 +3840,7 @@ function ControlBar({ onLeave }) {
|
|
|
3756
3840
|
]
|
|
3757
3841
|
}
|
|
3758
3842
|
),
|
|
3843
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(WaitingRoomRule, {}),
|
|
3759
3844
|
queue.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
3760
3845
|
MenuItem,
|
|
3761
3846
|
{
|
|
@@ -3975,6 +4060,58 @@ function MenuItem({
|
|
|
3975
4060
|
}
|
|
3976
4061
|
);
|
|
3977
4062
|
}
|
|
4063
|
+
var LOBBY_BYPASS_CHOICES = [
|
|
4064
|
+
{ value: "everyone", label: "Everyone" },
|
|
4065
|
+
{ value: "org_and_invited", label: "Your organization and invited people" },
|
|
4066
|
+
{ value: "org", label: "Your organization" },
|
|
4067
|
+
{ value: "invited", label: "Invited people" },
|
|
4068
|
+
{ value: "host_only", label: "Only hosts" }
|
|
4069
|
+
];
|
|
4070
|
+
function WaitingRoomRule() {
|
|
4071
|
+
const host = useRequiredMeetHost();
|
|
4072
|
+
const [open, setOpen] = (0, import_react10.useState)(false);
|
|
4073
|
+
const [value, setValue] = (0, import_react10.useState)(null);
|
|
4074
|
+
const [notice, setNotice] = (0, import_react10.useState)(null);
|
|
4075
|
+
(0, import_react10.useEffect)(() => {
|
|
4076
|
+
let live = true;
|
|
4077
|
+
void host.meetings.meetingRule("lobby_bypass").then((rule) => {
|
|
4078
|
+
if (live) setValue(typeof rule === "string" ? rule : null);
|
|
4079
|
+
}).catch(() => void 0);
|
|
4080
|
+
return () => {
|
|
4081
|
+
live = false;
|
|
4082
|
+
};
|
|
4083
|
+
}, [host]);
|
|
4084
|
+
const current = LOBBY_BYPASS_CHOICES.find((choice) => choice.value === value)?.label ?? null;
|
|
4085
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
4086
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(MenuItem, { onSelect: () => setOpen(!open), children: [
|
|
4087
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(LockIcon, { width: 16, height: 16 }),
|
|
4088
|
+
" Who can skip the waiting room",
|
|
4089
|
+
current !== null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mx-meet__menu-value", children: current }) : null
|
|
4090
|
+
] }),
|
|
4091
|
+
open ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { role: "group", "aria-label": "Who can skip the waiting room", "data-meet-rule": "lobby_bypass", children: [
|
|
4092
|
+
LOBBY_BYPASS_CHOICES.map((choice) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
4093
|
+
"button",
|
|
4094
|
+
{
|
|
4095
|
+
type: "button",
|
|
4096
|
+
role: "menuitemradio",
|
|
4097
|
+
"aria-checked": choice.value === value,
|
|
4098
|
+
className: "mx-meet__menu-item mx-meet__menu-item--sub",
|
|
4099
|
+
onClick: () => {
|
|
4100
|
+
setNotice(null);
|
|
4101
|
+
void host.meetings.setMeetingRule("lobby_bypass", choice.value).then(() => setValue(choice.value)).catch((error) => setNotice(error instanceof Error ? error.message : String(error)));
|
|
4102
|
+
},
|
|
4103
|
+
children: [
|
|
4104
|
+
choice.value === value ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CheckIcon, { width: 14, height: 14 }) : null,
|
|
4105
|
+
" ",
|
|
4106
|
+
choice.label
|
|
4107
|
+
]
|
|
4108
|
+
},
|
|
4109
|
+
choice.value
|
|
4110
|
+
)),
|
|
4111
|
+
notice !== null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mx-meet__menu-note", role: "status", children: notice }) : null
|
|
4112
|
+
] }) : null
|
|
4113
|
+
] });
|
|
4114
|
+
}
|
|
3978
4115
|
function Toast({ text, failed, onDone }) {
|
|
3979
4116
|
(0, import_react10.useEffect)(() => {
|
|
3980
4117
|
const timer = setTimeout(onDone, failed ? 7e3 : 4e3);
|
|
@@ -3985,6 +4122,94 @@ function Toast({ text, failed, onDone }) {
|
|
|
3985
4122
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "mx-meet__toast-close", "aria-label": "Dismiss", onClick: onDone, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CloseIcon, { width: 14, height: 14 }) })
|
|
3986
4123
|
] });
|
|
3987
4124
|
}
|
|
4125
|
+
function AudioSettingsControl() {
|
|
4126
|
+
const host = useMeetHost();
|
|
4127
|
+
const devices = useDeviceSnapshot();
|
|
4128
|
+
const [open, setOpen] = (0, import_react10.useState)(false);
|
|
4129
|
+
const list = (devices?.devices.audioinput ?? []).filter((device) => device.deviceId.length > 0);
|
|
4130
|
+
if (host === null || list.length < 2) return null;
|
|
4131
|
+
const current = devices?.selected.audioinput ?? host.devices.resolve("audioinput");
|
|
4132
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "mx-meet__anchor", children: [
|
|
4133
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4134
|
+
ControlButton,
|
|
4135
|
+
{
|
|
4136
|
+
label: "Audio settings",
|
|
4137
|
+
active: open,
|
|
4138
|
+
onClick: () => {
|
|
4139
|
+
if (!open) void host.devices.refresh();
|
|
4140
|
+
setOpen(!open);
|
|
4141
|
+
},
|
|
4142
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChevronUpIcon, {})
|
|
4143
|
+
}
|
|
4144
|
+
),
|
|
4145
|
+
open ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Popover, { label: "Microphone", onClose: () => setOpen(false), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "mx-meet__menu", role: "menu", "aria-label": "Microphone", children: list.map((device) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
4146
|
+
"button",
|
|
4147
|
+
{
|
|
4148
|
+
type: "button",
|
|
4149
|
+
role: "menuitemradio",
|
|
4150
|
+
"aria-checked": device.deviceId === current,
|
|
4151
|
+
className: "mx-meet__menu-item",
|
|
4152
|
+
onClick: () => {
|
|
4153
|
+
setOpen(false);
|
|
4154
|
+
host.devices.select("audioinput", device.deviceId);
|
|
4155
|
+
void host.engine.switchDevice("audioinput", device.deviceId).catch(() => void 0);
|
|
4156
|
+
},
|
|
4157
|
+
children: [
|
|
4158
|
+
device.deviceId === current ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CheckIcon, { width: 16, height: 16 }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mx-meet__menu-gap" }),
|
|
4159
|
+
device.label.length > 0 ? device.label : "Microphone"
|
|
4160
|
+
]
|
|
4161
|
+
},
|
|
4162
|
+
device.deviceId
|
|
4163
|
+
)) }) }) : null
|
|
4164
|
+
] });
|
|
4165
|
+
}
|
|
4166
|
+
function SpeakingWhileMutedNudge() {
|
|
4167
|
+
const room = useRoom();
|
|
4168
|
+
const host = useMeetHost();
|
|
4169
|
+
const devices = useDeviceSnapshot();
|
|
4170
|
+
const microphone = useLocalMedia().microphone;
|
|
4171
|
+
const mutedByMe = host !== null && room.local != null && room.local.micEnabled !== true && microphone.unavailable === null;
|
|
4172
|
+
const mic = devices?.selected.audioinput ?? host?.devices.resolve("audioinput") ?? null;
|
|
4173
|
+
const stream = useMicrophoneStream(mutedByMe ? mic : "off");
|
|
4174
|
+
const level = useAudioLevel(stream);
|
|
4175
|
+
const latest = (0, import_react10.useRef)(0);
|
|
4176
|
+
latest.current = level;
|
|
4177
|
+
const [shown, setShown] = (0, import_react10.useState)(false);
|
|
4178
|
+
const [detector] = (0, import_react10.useState)(() => createSpeakingWhileMutedDetector());
|
|
4179
|
+
(0, import_react10.useEffect)(() => {
|
|
4180
|
+
if (!mutedByMe) {
|
|
4181
|
+
detector.reset();
|
|
4182
|
+
setShown(false);
|
|
4183
|
+
return void 0;
|
|
4184
|
+
}
|
|
4185
|
+
const timer = setInterval(() => {
|
|
4186
|
+
if (detector.feed(latest.current, Date.now())) setShown(true);
|
|
4187
|
+
}, 150);
|
|
4188
|
+
return () => clearInterval(timer);
|
|
4189
|
+
}, [mutedByMe, detector]);
|
|
4190
|
+
(0, import_react10.useEffect)(() => {
|
|
4191
|
+
if (!shown) return void 0;
|
|
4192
|
+
const timer = setTimeout(() => setShown(false), 8e3);
|
|
4193
|
+
return () => clearTimeout(timer);
|
|
4194
|
+
}, [shown]);
|
|
4195
|
+
if (!shown || !mutedByMe) return null;
|
|
4196
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mx-meet__notice-bar", role: "status", "data-meet-notice": "speaking-while-muted", children: [
|
|
4197
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MicOffIcon, { width: 16, height: 16 }),
|
|
4198
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "You're muted" }),
|
|
4199
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4200
|
+
"button",
|
|
4201
|
+
{
|
|
4202
|
+
type: "button",
|
|
4203
|
+
className: "mx-meet__primary mx-meet__primary--small",
|
|
4204
|
+
onClick: () => {
|
|
4205
|
+
setShown(false);
|
|
4206
|
+
void room.toggleMicrophone();
|
|
4207
|
+
},
|
|
4208
|
+
children: "Unmute"
|
|
4209
|
+
}
|
|
4210
|
+
)
|
|
4211
|
+
] });
|
|
4212
|
+
}
|
|
3988
4213
|
function MediaNotices() {
|
|
3989
4214
|
const media = useLocalMedia();
|
|
3990
4215
|
const { region, where, enableAudio, acceptOffer, dismissOffer, dismissNotice, retry } = useMediaSupervisor();
|
|
@@ -4834,14 +5059,20 @@ async function playTestSound(sinkId) {
|
|
|
4834
5059
|
audio.pause();
|
|
4835
5060
|
await context.close();
|
|
4836
5061
|
}
|
|
4837
|
-
function useMicrophoneStream(deviceId) {
|
|
5062
|
+
function useMicrophoneStream(deviceId, report, attempt = 0) {
|
|
4838
5063
|
const [stream, setStream] = (0, import_react10.useState)(null);
|
|
5064
|
+
const reportRef = (0, import_react10.useRef)(report);
|
|
5065
|
+
reportRef.current = report;
|
|
4839
5066
|
(0, import_react10.useEffect)(() => {
|
|
4840
|
-
if (deviceId === "off")
|
|
5067
|
+
if (deviceId === "off") {
|
|
5068
|
+
reportRef.current?.("off");
|
|
5069
|
+
return void 0;
|
|
5070
|
+
}
|
|
4841
5071
|
let live = true;
|
|
4842
5072
|
let opened = null;
|
|
4843
5073
|
const media = globalThis.navigator?.mediaDevices;
|
|
4844
5074
|
if (media === void 0) return void 0;
|
|
5075
|
+
reportRef.current?.("prompting");
|
|
4845
5076
|
void media.getUserMedia({ audio: deviceId === null ? true : { deviceId: { exact: deviceId } } }).then((next) => {
|
|
4846
5077
|
if (!live) {
|
|
4847
5078
|
next.getTracks().forEach((track) => track.stop());
|
|
@@ -4849,15 +5080,49 @@ function useMicrophoneStream(deviceId) {
|
|
|
4849
5080
|
}
|
|
4850
5081
|
opened = next;
|
|
4851
5082
|
setStream(next);
|
|
4852
|
-
|
|
5083
|
+
reportRef.current?.("on");
|
|
5084
|
+
}).catch((thrown) => {
|
|
5085
|
+
setStream(null);
|
|
5086
|
+
if (live) reportRef.current?.({ error: thrown });
|
|
5087
|
+
});
|
|
4853
5088
|
return () => {
|
|
4854
5089
|
live = false;
|
|
4855
5090
|
opened?.getTracks().forEach((track) => track.stop());
|
|
4856
5091
|
setStream(null);
|
|
4857
5092
|
};
|
|
4858
|
-
}, [deviceId]);
|
|
5093
|
+
}, [deviceId, attempt]);
|
|
4859
5094
|
return stream;
|
|
4860
5095
|
}
|
|
5096
|
+
function PreviewStatus({ onRetry }) {
|
|
5097
|
+
const { region, where } = useMediaSupervisor();
|
|
5098
|
+
const [waited, setWaited] = (0, import_react10.useState)(false);
|
|
5099
|
+
const prompting = region.preview.microphone.state === "prompting" || region.preview.camera.state === "prompting";
|
|
5100
|
+
(0, import_react10.useEffect)(() => {
|
|
5101
|
+
if (!prompting) {
|
|
5102
|
+
setWaited(false);
|
|
5103
|
+
return void 0;
|
|
5104
|
+
}
|
|
5105
|
+
const timer = setTimeout(() => setWaited(true), 800);
|
|
5106
|
+
return () => clearTimeout(timer);
|
|
5107
|
+
}, [prompting]);
|
|
5108
|
+
const refused = ["microphone", "camera"].flatMap((kind) => {
|
|
5109
|
+
const preview = region.preview[kind];
|
|
5110
|
+
const reason = region.permission[kind] === "denied" ? "denied" : preview.state === "unavailable" ? preview.unavailable?.reason ?? "failed" : null;
|
|
5111
|
+
return reason === null ? [] : [{ kind, reason }];
|
|
5112
|
+
});
|
|
5113
|
+
if (refused.length === 0) {
|
|
5114
|
+
if (!prompting || !waited) return null;
|
|
5115
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mx-meet__panel-note", role: "status", "data-meet-notice": "permission-prompt", children: "Allow camera and microphone when your browser asks" });
|
|
5116
|
+
}
|
|
5117
|
+
const first = refused[0];
|
|
5118
|
+
const both = refused.length === 2 && refused[0].reason === refused[1].reason;
|
|
5119
|
+
const label = both ? deviceChipLabel("camera", first.reason).replace(/^Camera/, "Camera and mic") : refused.map((r) => deviceChipLabel(r.kind, r.reason)).join(" \xB7 ");
|
|
5120
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mx-meet__warning", role: "status", "data-meet-notice": deviceNoticeCode(first.kind, first.reason), children: [
|
|
5121
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { children: label }),
|
|
5122
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ol", { className: "mx-meet__fix-steps", children: deviceFixSteps(first.kind, first.reason, where).map((step) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("li", { children: step }, step)) }),
|
|
5123
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system.Button, { type: "button", variant: "outline", size: "sm", onClick: onRetry, children: "Try again" })
|
|
5124
|
+
] });
|
|
5125
|
+
}
|
|
4861
5126
|
function KnockNotice({ onView }) {
|
|
4862
5127
|
const { knocker, others, admit, dismiss } = useKnockNotice();
|
|
4863
5128
|
if (knocker === null) return null;
|
|
@@ -4935,7 +5200,11 @@ function WaitingList({ lobby }) {
|
|
|
4935
5200
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Avatar, { name: entry.displayName, url: entry.avatarUrl, size: 32 }),
|
|
4936
5201
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "mx-meet__person-name", children: [
|
|
4937
5202
|
entry.displayName,
|
|
4938
|
-
entry.userId === null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mx-meet__badge", children: "Guest" }) : null
|
|
5203
|
+
entry.userId === null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mx-meet__badge", children: "Guest" }) : null,
|
|
5204
|
+
entry.flag === "previously_removed" ? (
|
|
5205
|
+
// `mod-removed-bypass`: advice for the host, never a block — Admit stays.
|
|
5206
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mx-meet__badge mx-meet__badge--warn", "data-meet-notice": "knock-flagged", children: "Previously removed" })
|
|
5207
|
+
) : null
|
|
4939
5208
|
] }),
|
|
4940
5209
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ControlButton, { label: `Admit ${entry.displayName}`, onClick: () => void lobby.admit(entry), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CheckIcon, { width: 16, height: 16 }) }),
|
|
4941
5210
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
@@ -5187,8 +5456,15 @@ function PreJoinBody({
|
|
|
5187
5456
|
const [joining, setJoining] = (0, import_react10.useState)(false);
|
|
5188
5457
|
const camera = devices?.selected.videoinput ?? host.devices.resolve("videoinput");
|
|
5189
5458
|
const mic = devices?.selected.audioinput ?? host.devices.resolve("audioinput");
|
|
5190
|
-
const
|
|
5191
|
-
const
|
|
5459
|
+
const { reportPreview } = useMediaSupervisor();
|
|
5460
|
+
const [attempt, setAttempt] = (0, import_react10.useState)(0);
|
|
5461
|
+
const preview = useCameraPreview(
|
|
5462
|
+
mode === "video" ? camera : void 0,
|
|
5463
|
+
effects.effects,
|
|
5464
|
+
(outcome) => reportPreview("camera", outcome),
|
|
5465
|
+
attempt
|
|
5466
|
+
);
|
|
5467
|
+
const micStream = useMicrophoneStream(microphone ? mic : "off", (outcome) => reportPreview("microphone", outcome), attempt);
|
|
5192
5468
|
const level = useAudioLevel(micStream);
|
|
5193
5469
|
const [speakerTesting, setSpeakerTesting] = (0, import_react10.useState)(false);
|
|
5194
5470
|
(0, import_react10.useEffect)(() => {
|
|
@@ -5232,6 +5508,7 @@ function PreJoinBody({
|
|
|
5232
5508
|
] }),
|
|
5233
5509
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mx-meet__prejoin-panel", children: [
|
|
5234
5510
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h1", { className: "mx-meet__prejoin-title", children: "Ready to join?" }),
|
|
5511
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PreviewStatus, { onRetry: () => setAttempt((n) => n + 1) }),
|
|
5235
5512
|
notice?.kind === "removed" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mx-meet__warning", children: notice.text }) : null,
|
|
5236
5513
|
devices?.labelsHidden === true ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "mx-meet__panel-note", children: [
|
|
5237
5514
|
"Your browser is hiding device names until you allow camera and microphone access.",
|
|
@@ -5334,16 +5611,22 @@ function PreJoinBody({
|
|
|
5334
5611
|
] })
|
|
5335
5612
|
] });
|
|
5336
5613
|
}
|
|
5337
|
-
function useCameraPreview(deviceId, effects) {
|
|
5614
|
+
function useCameraPreview(deviceId, effects, report, attempt = 0) {
|
|
5615
|
+
const reportRef = (0, import_react10.useRef)(report);
|
|
5616
|
+
reportRef.current = report;
|
|
5338
5617
|
const [preview, setPreview] = (0, import_react10.useState)(null);
|
|
5339
5618
|
const [error, setError] = (0, import_react10.useState)(null);
|
|
5340
5619
|
const current = (0, import_react10.useRef)(null);
|
|
5341
5620
|
const initialEffect = (0, import_react10.useRef)(toVideoEffect(effects));
|
|
5342
5621
|
initialEffect.current = toVideoEffect(effects);
|
|
5343
5622
|
(0, import_react10.useEffect)(() => {
|
|
5344
|
-
if (deviceId === void 0)
|
|
5623
|
+
if (deviceId === void 0) {
|
|
5624
|
+
reportRef.current?.("off");
|
|
5625
|
+
return void 0;
|
|
5626
|
+
}
|
|
5345
5627
|
let live = true;
|
|
5346
5628
|
setError(null);
|
|
5629
|
+
reportRef.current?.("prompting");
|
|
5347
5630
|
void createCameraPreview(deviceId, initialEffect.current).then((created) => {
|
|
5348
5631
|
if (!live) {
|
|
5349
5632
|
created.stop();
|
|
@@ -5351,8 +5634,10 @@ function useCameraPreview(deviceId, effects) {
|
|
|
5351
5634
|
}
|
|
5352
5635
|
current.current = created;
|
|
5353
5636
|
setPreview(created);
|
|
5637
|
+
reportRef.current?.("on");
|
|
5354
5638
|
}).catch((thrown) => {
|
|
5355
5639
|
if (!live) return;
|
|
5640
|
+
reportRef.current?.({ error: thrown });
|
|
5356
5641
|
const failure = thrown;
|
|
5357
5642
|
const message = (failure.message ?? "").replace(/^meet\.[A-Za-z.()]+:\s*/, "");
|
|
5358
5643
|
const sentence = message.length > 0 ? message.charAt(0).toUpperCase() + message.slice(1) : "";
|
|
@@ -5364,7 +5649,7 @@ function useCameraPreview(deviceId, effects) {
|
|
|
5364
5649
|
current.current = null;
|
|
5365
5650
|
setPreview(null);
|
|
5366
5651
|
};
|
|
5367
|
-
}, [deviceId]);
|
|
5652
|
+
}, [deviceId, attempt]);
|
|
5368
5653
|
return {
|
|
5369
5654
|
preview,
|
|
5370
5655
|
error,
|
|
@@ -5504,6 +5789,14 @@ function MeetSkinRoom({
|
|
|
5504
5789
|
}
|
|
5505
5790
|
function ShareNotice() {
|
|
5506
5791
|
const { share, actions } = useMeetCore();
|
|
5792
|
+
const [soundHintClosed, setSoundHintClosed] = (0, import_react10.useState)(null);
|
|
5793
|
+
if (share.state === "presenting" && share.audio === false && soundHintClosed !== share.at) {
|
|
5794
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "share-audio-missing", children: [
|
|
5795
|
+
"Sound not shared",
|
|
5796
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mx-meet__remedy", children: "To include sound, share a tab and turn on Also share tab audio" }),
|
|
5797
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "mx-meet__link", onClick: () => setSoundHintClosed(share.at), children: "Dismiss" })
|
|
5798
|
+
] });
|
|
5799
|
+
}
|
|
5507
5800
|
if (share.state !== "refused") return null;
|
|
5508
5801
|
const text = share.reason === "unsupported" ? "You can't share your screen from this browser" : share.reason === "blocked" ? "Screen sharing is blocked" : "Couldn't start sharing your screen";
|
|
5509
5802
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "share-unavailable", children: [
|
|
@@ -5536,7 +5829,20 @@ function ExitScreen({ onLeave }) {
|
|
|
5536
5829
|
const { phase, reason, host, snapshot, actions, call, connection } = useMeetCore();
|
|
5537
5830
|
const [busy, setBusy] = (0, import_react10.useState)(false);
|
|
5538
5831
|
const failure = snapshot?.failure ?? null;
|
|
5539
|
-
const retryLeft = useCountdown(
|
|
5832
|
+
const retryLeft = useCountdown(
|
|
5833
|
+
phase === "refused:lobby_rejects" || phase === "refused:too_early" ? failure?.retryAt ?? null : null
|
|
5834
|
+
);
|
|
5835
|
+
const reaskedAtZero = (0, import_react10.useRef)(false);
|
|
5836
|
+
(0, import_react10.useEffect)(() => {
|
|
5837
|
+
if (phase !== "refused:too_early") {
|
|
5838
|
+
reaskedAtZero.current = false;
|
|
5839
|
+
return;
|
|
5840
|
+
}
|
|
5841
|
+
if (retryLeft === 0 && !reaskedAtZero.current) {
|
|
5842
|
+
reaskedAtZero.current = true;
|
|
5843
|
+
actions.retry();
|
|
5844
|
+
}
|
|
5845
|
+
}, [phase, retryLeft, actions]);
|
|
5540
5846
|
const rejoin = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
5541
5847
|
import_design_system.Button,
|
|
5542
5848
|
{
|
|
@@ -5574,11 +5880,19 @@ function ExitScreen({ onLeave }) {
|
|
|
5574
5880
|
if (phase.startsWith("refused:")) {
|
|
5575
5881
|
const code = phase.slice("refused:".length);
|
|
5576
5882
|
title = REFUSED_TITLES[code] ?? "You can't join this meeting";
|
|
5577
|
-
line = code === "too_early" || code === "registration_required" || code === "cancelled" ? failure?.remedy ?? null : null;
|
|
5883
|
+
line = code === "too_early" || code === "registration_required" || code === "cancelled" || code === "wrong_account" ? failure?.remedy ?? null : null;
|
|
5884
|
+
if (code === "too_early" && retryLeft !== null && retryLeft > 0) line = `Opens in ${clockText(retryLeft)}`;
|
|
5578
5885
|
actionsNode = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
5579
5886
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system.Button, { type: "button", variant: "outline", onClick: actions.retry, children: "Try again" }),
|
|
5580
5887
|
home
|
|
5581
5888
|
] });
|
|
5889
|
+
if (code === "sign_in_required" || code === "wrong_account") {
|
|
5890
|
+
const back = typeof window === "undefined" ? "/" : `${window.location.pathname}${window.location.search}`;
|
|
5891
|
+
actionsNode = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
5892
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system.Button, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("a", { href: `/login?redirectTo=${encodeURIComponent(back)}`, children: code === "sign_in_required" ? "Sign in" : "Switch account" }) }),
|
|
5893
|
+
home
|
|
5894
|
+
] });
|
|
5895
|
+
}
|
|
5582
5896
|
if (code === "lobby_rejects") {
|
|
5583
5897
|
if (retryLeft !== null && retryLeft > 0) {
|
|
5584
5898
|
line = `You can ask again in ${clockText(retryLeft)}`;
|
|
@@ -5616,6 +5930,27 @@ function ExitScreen({ onLeave }) {
|
|
|
5616
5930
|
] });
|
|
5617
5931
|
break;
|
|
5618
5932
|
case "disconnected":
|
|
5933
|
+
if (failure?.stage === "join" && (failure.reason ?? null) === null) {
|
|
5934
|
+
title = "Can't connect to the meeting";
|
|
5935
|
+
line = connection === "offline" ? "You're offline. Check your internet connection." : "Check your network. A VPN, firewall or proxy may be blocking the call.";
|
|
5936
|
+
notice = "cannot-connect";
|
|
5937
|
+
actionsNode = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
5938
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
5939
|
+
import_design_system.Button,
|
|
5940
|
+
{
|
|
5941
|
+
type: "button",
|
|
5942
|
+
disabled: busy,
|
|
5943
|
+
onClick: () => {
|
|
5944
|
+
setBusy(true);
|
|
5945
|
+
void actions.rejoin().catch(() => void 0).finally(() => setBusy(false));
|
|
5946
|
+
},
|
|
5947
|
+
children: "Try again"
|
|
5948
|
+
}
|
|
5949
|
+
),
|
|
5950
|
+
home
|
|
5951
|
+
] });
|
|
5952
|
+
break;
|
|
5953
|
+
}
|
|
5619
5954
|
title = "You lost your connection to the meeting";
|
|
5620
5955
|
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.";
|
|
5621
5956
|
notice = "disconnected";
|