@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/react.cjs
CHANGED
|
@@ -158,6 +158,7 @@ __export(react_exports, {
|
|
|
158
158
|
createReconnectPolicy: () => createReconnectPolicy,
|
|
159
159
|
createRecordingController: () => createRecordingController,
|
|
160
160
|
createRoomEngine: () => createRoomEngine,
|
|
161
|
+
createSpeakingWhileMutedDetector: () => createSpeakingWhileMutedDetector,
|
|
161
162
|
createTokenClient: () => createTokenClient,
|
|
162
163
|
createWebDeviceStorage: () => createWebDeviceStorage,
|
|
163
164
|
createWebEffectsStorage: () => createWebEffectsStorage,
|
|
@@ -166,6 +167,7 @@ __export(react_exports, {
|
|
|
166
167
|
createWebRoomTokenStorage: () => createWebRoomTokenStorage,
|
|
167
168
|
defaultCaptionLanguage: () => defaultCaptionLanguage,
|
|
168
169
|
detectBrowser: () => detectBrowser,
|
|
170
|
+
detectCapabilities: () => detectCapabilities,
|
|
169
171
|
detectOs: () => detectOs,
|
|
170
172
|
deviceChipLabel: () => deviceChipLabel,
|
|
171
173
|
deviceFixSteps: () => deviceFixSteps,
|
|
@@ -190,6 +192,7 @@ __export(react_exports, {
|
|
|
190
192
|
isLinkFollower: () => isLinkFollower,
|
|
191
193
|
isListenOnly: () => isListenOnly,
|
|
192
194
|
isLivePhase: () => isLivePhase,
|
|
195
|
+
isMobileUserAgent: () => isMobileUserAgent,
|
|
193
196
|
isNoteTaker: () => isNoteTaker,
|
|
194
197
|
isObserverRole: () => isObserverRole,
|
|
195
198
|
isRealName: () => isRealName,
|
|
@@ -282,6 +285,7 @@ __export(react_exports, {
|
|
|
282
285
|
transcriptDocument: () => transcriptDocument,
|
|
283
286
|
unavailableFrom: () => unavailableFrom,
|
|
284
287
|
uniqueAttendees: () => uniqueAttendees,
|
|
288
|
+
unsupportedWords: () => unsupportedWords,
|
|
285
289
|
untranslatedLines: () => untranslatedLines,
|
|
286
290
|
useActivityAttention: () => useActivityAttention,
|
|
287
291
|
useAudioLevel: () => useAudioLevel,
|
|
@@ -354,7 +358,14 @@ function createTabClaim(platform = webTabPlatform()) {
|
|
|
354
358
|
if (message.kind === "tab-alive?" && mine !== null && message.tabId === mine && message.from !== self) {
|
|
355
359
|
platform.bus?.post({ kind: "tab-alive", tabId: mine });
|
|
356
360
|
} else if (message.kind === "who-is-in?" && current !== null && mine !== null && message.from !== self && message.room === current.room && message.person === current.person) {
|
|
357
|
-
platform.bus?.post({
|
|
361
|
+
platform.bus?.post({
|
|
362
|
+
kind: "in-room",
|
|
363
|
+
room: current.room,
|
|
364
|
+
person: current.person,
|
|
365
|
+
tabId: mine,
|
|
366
|
+
to: message.from,
|
|
367
|
+
rule: current.rule
|
|
368
|
+
});
|
|
358
369
|
}
|
|
359
370
|
}) ?? (() => {
|
|
360
371
|
});
|
|
@@ -384,28 +395,32 @@ function createTabClaim(platform = webTabPlatform()) {
|
|
|
384
395
|
return candidate;
|
|
385
396
|
}
|
|
386
397
|
const tabId = claim();
|
|
398
|
+
async function presence(room, person) {
|
|
399
|
+
const bus = platform.bus;
|
|
400
|
+
if (bus === null) return null;
|
|
401
|
+
let seen = null;
|
|
402
|
+
const stop = bus.subscribe((message) => {
|
|
403
|
+
if (message.kind === "in-room" && message.to === self && message.room === room && message.person === person) {
|
|
404
|
+
seen = { rule: seen?.rule ?? message.rule ?? null };
|
|
405
|
+
}
|
|
406
|
+
});
|
|
407
|
+
bus.post({ kind: "who-is-in?", room, person, from: self });
|
|
408
|
+
await wait(TAB_ANSWER_MS);
|
|
409
|
+
stop();
|
|
410
|
+
return seen;
|
|
411
|
+
}
|
|
387
412
|
return {
|
|
388
413
|
tabId,
|
|
389
|
-
inRoom(room, person) {
|
|
390
|
-
current = { room, person };
|
|
414
|
+
inRoom(room, person, rule) {
|
|
415
|
+
current = { room, person, rule: rule ?? null };
|
|
391
416
|
},
|
|
392
417
|
left() {
|
|
393
418
|
current = null;
|
|
394
419
|
},
|
|
395
420
|
async presentElsewhere(room, person) {
|
|
396
|
-
|
|
397
|
-
if (bus === null) return false;
|
|
398
|
-
let seen = false;
|
|
399
|
-
const stop = bus.subscribe((message) => {
|
|
400
|
-
if (message.kind === "in-room" && message.to === self && message.room === room && message.person === person) {
|
|
401
|
-
seen = true;
|
|
402
|
-
}
|
|
403
|
-
});
|
|
404
|
-
bus.post({ kind: "who-is-in?", room, person, from: self });
|
|
405
|
-
await wait(TAB_ANSWER_MS);
|
|
406
|
-
stop();
|
|
407
|
-
return seen;
|
|
421
|
+
return await presence(room, person) !== null;
|
|
408
422
|
},
|
|
423
|
+
presenceElsewhere: (room, person) => presence(room, person),
|
|
409
424
|
dispose() {
|
|
410
425
|
unsubscribe();
|
|
411
426
|
}
|
|
@@ -583,7 +598,8 @@ function createWebResumeStorage() {
|
|
|
583
598
|
}
|
|
584
599
|
};
|
|
585
600
|
}
|
|
586
|
-
var CLEARS_RESUME = (phase) => phase === "left" || phase === "removed" || phase === "superseded" || phase === "inactive" || phase === "ended" || phase === "record" || phase === "prejoin" || phase
|
|
601
|
+
var CLEARS_RESUME = (phase) => phase === "left" || phase === "removed" || phase === "superseded" || phase === "inactive" || phase === "ended" || phase === "record" || phase === "prejoin" || phase === "denied" || phase === "knock_expired" || phase.startsWith("refused:");
|
|
602
|
+
var WAITING_RESUMES = (phase) => phase.startsWith("waiting:");
|
|
587
603
|
function autoRejoinDelayMs(attempt, random = Math.random) {
|
|
588
604
|
const base = Math.min(15e3, 1e3 * 2 ** Math.max(0, attempt));
|
|
589
605
|
return Math.round(base * (0.5 + random()));
|
|
@@ -715,6 +731,10 @@ function createConnectionSupervisor(options) {
|
|
|
715
731
|
rejoinAttempts = 0;
|
|
716
732
|
stopRejoin();
|
|
717
733
|
saveMarker(true);
|
|
734
|
+
} else if (WAITING_RESUMES(phase)) {
|
|
735
|
+
stopRejoin();
|
|
736
|
+
driverRegion = null;
|
|
737
|
+
saveMarker(true);
|
|
718
738
|
} else if (phase === "disconnected") {
|
|
719
739
|
driverRegion = null;
|
|
720
740
|
saveMarker(true);
|
|
@@ -1235,6 +1255,7 @@ function parseRoomToken(body, roomName) {
|
|
|
1235
1255
|
connectionIdentity: readString(payload, "connection_identity") ?? personKey,
|
|
1236
1256
|
connectionKind: readString(payload, "connection_kind") === "companion" ? "companion" : "main",
|
|
1237
1257
|
secondDeviceChoice,
|
|
1258
|
+
secondDeviceRule: readString(payload, "second_device_rule"),
|
|
1238
1259
|
displayName: readString(payload, "display_name") ?? readString(payload, "displayName") ?? identity,
|
|
1239
1260
|
expiresAt,
|
|
1240
1261
|
canPublish: readBoolean(payload, "can_publish", true),
|
|
@@ -1948,6 +1969,12 @@ var JOINABLE_FROM = /* @__PURE__ */ new Set([
|
|
|
1948
1969
|
// allows the press; the server's gate decides whether it knocks or is refused again.
|
|
1949
1970
|
"refused:lobby_rejects"
|
|
1950
1971
|
]);
|
|
1972
|
+
var RETRYABLE_REFUSALS = /* @__PURE__ */ new Set([
|
|
1973
|
+
"refused:locked",
|
|
1974
|
+
"refused:too_early",
|
|
1975
|
+
"refused:meeting_full",
|
|
1976
|
+
"refused:platform_capacity"
|
|
1977
|
+
]);
|
|
1951
1978
|
var NOT_ENDABLE = /* @__PURE__ */ new Set([
|
|
1952
1979
|
"ended",
|
|
1953
1980
|
"record",
|
|
@@ -2013,7 +2040,7 @@ function nextPhase(from, event) {
|
|
|
2013
2040
|
case "link_state":
|
|
2014
2041
|
return from === "resolving" && event.reason !== null && isJoinRefusalReason(event.reason) ? refusalPhase(event.reason) : null;
|
|
2015
2042
|
case "join":
|
|
2016
|
-
return JOINABLE_FROM.has(from) ? "joining" : null;
|
|
2043
|
+
return JOINABLE_FROM.has(from) || RETRYABLE_REFUSALS.has(from) ? "joining" : null;
|
|
2017
2044
|
case "auto_rejoin":
|
|
2018
2045
|
return from === "disconnected" ? "joining" : null;
|
|
2019
2046
|
case "resumed":
|
|
@@ -2792,11 +2819,13 @@ function minutesLeft(policy, startedAtMs, nowMs) {
|
|
|
2792
2819
|
}
|
|
2793
2820
|
|
|
2794
2821
|
// src/core/attendance.ts
|
|
2822
|
+
var IDLE_PREVIEW = { state: "idle", unavailable: null };
|
|
2795
2823
|
var IDLE_MEDIA_REGION = {
|
|
2796
2824
|
permission: { microphone: "unknown", camera: "unknown" },
|
|
2797
2825
|
audioOutput: "ok",
|
|
2798
2826
|
notice: null,
|
|
2799
|
-
offer: null
|
|
2827
|
+
offer: null,
|
|
2828
|
+
preview: { microphone: IDLE_PREVIEW, camera: IDLE_PREVIEW }
|
|
2800
2829
|
};
|
|
2801
2830
|
var IDLE_TRACK = { enabled: false, unavailable: null };
|
|
2802
2831
|
var IDLE_MEDIA = {
|
|
@@ -2853,6 +2882,10 @@ var RPCS = {
|
|
|
2853
2882
|
admitAll: "meet_admit_all",
|
|
2854
2883
|
setLock: "meet_set_lock",
|
|
2855
2884
|
recordConsent: "meet_record_consent",
|
|
2885
|
+
/** One meeting rule as the gate obeys it (the knob ladder, this meeting's rung included). */
|
|
2886
|
+
meetingRule: "meet_policy",
|
|
2887
|
+
/** A host sets one rule for THIS meeting (the `meet_meeting` rung); refusals come back as `{ok:false}`. */
|
|
2888
|
+
setMeetingRule: "meet_policy_set",
|
|
2856
2889
|
/*
|
|
2857
2890
|
* ── MEET WAVE 1 — management (0.8). Every write is a SECURITY DEFINER door
|
|
2858
2891
|
* authorized by the meeting's host or a real iam grant (editor to change,
|
|
@@ -2899,7 +2932,7 @@ var INVITEE_COLUMNS = "id,meeting_id,invitee_user_id,email,display_name,role,rsv
|
|
|
2899
2932
|
var RECORDING_COLUMNS = "id,meeting_id,state,started_at,ended_at,started_by,file_id,failure_reason";
|
|
2900
2933
|
var SEGMENT_COLUMNS = "id,meeting_id,identity,speaker,text,started_at,is_final,metadata";
|
|
2901
2934
|
var NOTE_COLUMNS = "id,meeting_id,text,kind,assignee_user_id,created_at,metadata";
|
|
2902
|
-
var ATTENDEE_COLUMNS = "id,meeting_id,identity,participant_user_id,display_name,avatar_url,role,admission_state,knocked_at,knock_expires_at,is_agent,joined_at,left_at,sessions:metadata->sessions";
|
|
2935
|
+
var ATTENDEE_COLUMNS = "id,meeting_id,identity,participant_user_id,display_name,avatar_url,role,admission_state,knocked_at,knock_expires_at,is_agent,joined_at,left_at,sessions:metadata->sessions,knock_flag:metadata->>knock_flag";
|
|
2903
2936
|
function projectStays(value) {
|
|
2904
2937
|
if (!Array.isArray(value)) return [];
|
|
2905
2938
|
const stays = [];
|
|
@@ -3299,6 +3332,8 @@ function createMeetRepository(options) {
|
|
|
3299
3332
|
admissionState: states.includes(admission) ? admission : "admitted",
|
|
3300
3333
|
knockedAt: text(row, "knocked_at"),
|
|
3301
3334
|
knockExpiresAt: text(row, "knock_expires_at"),
|
|
3335
|
+
// The list read selects `metadata->>knock_flag`; a Postgres Changes row carries `metadata`.
|
|
3336
|
+
knockFlag: text(row, "knock_flag") ?? (typeof row["metadata"] === "object" && row["metadata"] !== null ? text(row["metadata"], "knock_flag") : null),
|
|
3302
3337
|
isAgent: bool(row, "is_agent", false),
|
|
3303
3338
|
joinedAt: text(row, "joined_at"),
|
|
3304
3339
|
leftAt: text(row, "left_at"),
|
|
@@ -3622,6 +3657,25 @@ function createMeetRepository(options) {
|
|
|
3622
3657
|
);
|
|
3623
3658
|
return projectMeeting(row);
|
|
3624
3659
|
},
|
|
3660
|
+
async meetingRule(meetingId, key) {
|
|
3661
|
+
const response = await db.rpc(RPCS.meetingRule, { p_meeting_id: meetingId, p_key: key });
|
|
3662
|
+
if (response.error !== null) throw normalizeMeetError(response.error, "meet.meetingRule");
|
|
3663
|
+
return response.data;
|
|
3664
|
+
},
|
|
3665
|
+
async setMeetingRule(meetingId, key, value) {
|
|
3666
|
+
const response = await db.rpc(RPCS.setMeetingRule, { p_meeting_id: meetingId, p_key: key, p_value: value });
|
|
3667
|
+
if (response.error !== null) throw normalizeMeetError(response.error, "meet.setMeetingRule");
|
|
3668
|
+
const answer = response.data ?? {};
|
|
3669
|
+
if (answer["ok"] !== true) {
|
|
3670
|
+
throw new MeetError(
|
|
3671
|
+
"forbidden",
|
|
3672
|
+
typeof answer["detail"] === "string" ? answer["detail"] : "This meeting's rule was not changed.",
|
|
3673
|
+
"Only the meeting's host or a co-host sets its rules; an organization can lock a rule for everyone.",
|
|
3674
|
+
answer
|
|
3675
|
+
);
|
|
3676
|
+
}
|
|
3677
|
+
return answer["effective_value"];
|
|
3678
|
+
},
|
|
3625
3679
|
async transcript(meetingId, limit = 1e3) {
|
|
3626
3680
|
const response = await db.from(TABLES.transcripts).select(SEGMENT_COLUMNS).eq("meeting_id", meetingId).order("started_at", { ascending: true }).order("id", { ascending: true }).limit(limit);
|
|
3627
3681
|
if (response.error !== null) throw normalizeMeetError(response.error, "meet.transcript");
|
|
@@ -4516,7 +4570,8 @@ function lobbyEntryFrom(attendee, now = Date.now()) {
|
|
|
4516
4570
|
knockedAt: attendee.knockedAt ?? attendee.joinedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
4517
4571
|
knockExpiresAt: attendee.knockExpiresAt ?? null,
|
|
4518
4572
|
decidedAt: null,
|
|
4519
|
-
decidedBy: null
|
|
4573
|
+
decidedBy: null,
|
|
4574
|
+
flag: attendee.knockFlag ?? null
|
|
4520
4575
|
};
|
|
4521
4576
|
}
|
|
4522
4577
|
function deniedEntryFrom(attendee) {
|
|
@@ -5048,6 +5103,17 @@ function createMeetingSession(options) {
|
|
|
5048
5103
|
async askAgain() {
|
|
5049
5104
|
await options.rejoin?.();
|
|
5050
5105
|
},
|
|
5106
|
+
async meetingRule(key) {
|
|
5107
|
+
const meeting = requireMeeting("meet.meetingRule");
|
|
5108
|
+
return repository.meetingRule(meeting.id, key);
|
|
5109
|
+
},
|
|
5110
|
+
async setMeetingRule(key, value) {
|
|
5111
|
+
requireHostUser("meet.setMeetingRule");
|
|
5112
|
+
const meeting = requireMeeting("meet.setMeetingRule");
|
|
5113
|
+
const effective = await repository.setMeetingRule(meeting.id, key, value);
|
|
5114
|
+
hint();
|
|
5115
|
+
return effective;
|
|
5116
|
+
},
|
|
5051
5117
|
async setLock(locked) {
|
|
5052
5118
|
const by = requireHostUser("meet.setLock");
|
|
5053
5119
|
const meeting = requireMeeting("meet.setLock");
|
|
@@ -5151,6 +5217,47 @@ function createMeetSessionIdentity(options) {
|
|
|
5151
5217
|
});
|
|
5152
5218
|
}
|
|
5153
5219
|
|
|
5220
|
+
// src/core/capabilities.ts
|
|
5221
|
+
var IN_APP = [
|
|
5222
|
+
[/Instagram/i, "Instagram"],
|
|
5223
|
+
[/FBAN|FBAV|FB_IAB/i, "Facebook"],
|
|
5224
|
+
[/LinkedInApp/i, "LinkedIn"],
|
|
5225
|
+
[/MicroMessenger/i, "WeChat"],
|
|
5226
|
+
[/Snapchat/i, "Snapchat"],
|
|
5227
|
+
[/\bLine\//i, "LINE"],
|
|
5228
|
+
[/Twitter|TwitterAndroid/i, "X"],
|
|
5229
|
+
[/TikTok|musical_ly|BytedanceWebview/i, "TikTok"]
|
|
5230
|
+
];
|
|
5231
|
+
function isMobileUserAgent(userAgent, maxTouchPoints = 0) {
|
|
5232
|
+
if (/iPhone|iPod|Android|Mobile|iPad/i.test(userAgent)) return true;
|
|
5233
|
+
return /Macintosh/.test(userAgent) && maxTouchPoints > 1;
|
|
5234
|
+
}
|
|
5235
|
+
function detectCapabilities(scope = typeof globalThis === "undefined" ? null : globalThis) {
|
|
5236
|
+
if (scope === null || scope.navigator === void 0) {
|
|
5237
|
+
return { canJoin: true, reason: null, inAppBrowser: null, mobile: false, canShareScreen: false };
|
|
5238
|
+
}
|
|
5239
|
+
const ua = scope.navigator.userAgent ?? "";
|
|
5240
|
+
const webrtc = typeof scope.RTCPeerConnection === "function" || typeof scope.webkitRTCPeerConnection === "function";
|
|
5241
|
+
const mobile = isMobileUserAgent(ua, scope.navigator.maxTouchPoints ?? 0);
|
|
5242
|
+
const inAppBrowser = IN_APP.find(([pattern]) => pattern.test(ua))?.[1] ?? null;
|
|
5243
|
+
return {
|
|
5244
|
+
canJoin: webrtc,
|
|
5245
|
+
reason: webrtc ? null : "no_webrtc",
|
|
5246
|
+
inAppBrowser,
|
|
5247
|
+
mobile,
|
|
5248
|
+
canShareScreen: !mobile && typeof scope.navigator.mediaDevices?.getDisplayMedia === "function"
|
|
5249
|
+
};
|
|
5250
|
+
}
|
|
5251
|
+
function unsupportedWords(capabilities) {
|
|
5252
|
+
if (capabilities.inAppBrowser !== null) {
|
|
5253
|
+
return {
|
|
5254
|
+
title: `${capabilities.inAppBrowser}'s browser can't join calls`,
|
|
5255
|
+
body: "Open this link in Chrome, Safari, Edge or Firefox"
|
|
5256
|
+
};
|
|
5257
|
+
}
|
|
5258
|
+
return { title: "This browser can't join calls", body: "Open this link in Chrome, Safari, Edge or Firefox" };
|
|
5259
|
+
}
|
|
5260
|
+
|
|
5154
5261
|
// src/core/media-supervisor.ts
|
|
5155
5262
|
function classifyDeviceFailure(error, permission) {
|
|
5156
5263
|
if (permission === "denied") return "denied";
|
|
@@ -5211,7 +5318,7 @@ function deviceFixSteps(kind, reason, where) {
|
|
|
5211
5318
|
case "safari":
|
|
5212
5319
|
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"];
|
|
5213
5320
|
default:
|
|
5214
|
-
return ["Click the icon
|
|
5321
|
+
return ["Click the site settings icon in the address bar", `Set ${device} to Allow`, "Press Try again"];
|
|
5215
5322
|
}
|
|
5216
5323
|
case "blocked_os": {
|
|
5217
5324
|
const app = where.browser === "chrome" ? "Google Chrome" : where.browser === "edge" ? "Microsoft Edge" : where.browser === "firefox" ? "Firefox" : "your browser";
|
|
@@ -5309,7 +5416,7 @@ function createMediaSupervisor(options) {
|
|
|
5309
5416
|
function setRegion(next) {
|
|
5310
5417
|
const current = region();
|
|
5311
5418
|
const merged = { ...current, ...next };
|
|
5312
|
-
if (merged.audioOutput === current.audioOutput && merged.notice === current.notice && merged.offer === current.offer && merged.permission.microphone === current.permission.microphone && merged.permission.camera === current.permission.camera) {
|
|
5419
|
+
if (merged.audioOutput === current.audioOutput && merged.notice === current.notice && merged.offer === current.offer && merged.preview === current.preview && merged.permission.microphone === current.permission.microphone && merged.permission.camera === current.permission.camera) {
|
|
5313
5420
|
return;
|
|
5314
5421
|
}
|
|
5315
5422
|
store.patch({ mediaRegion: merged });
|
|
@@ -5405,7 +5512,7 @@ function createMediaSupervisor(options) {
|
|
|
5405
5512
|
if (permission !== "unknown") setPermission(kind, permission);
|
|
5406
5513
|
return unavailableFrom(normalized, classifyDeviceFailure(normalized, permission));
|
|
5407
5514
|
}
|
|
5408
|
-
|
|
5515
|
+
const api = {
|
|
5409
5516
|
start() {
|
|
5410
5517
|
if (started || platform === null) return;
|
|
5411
5518
|
started = true;
|
|
@@ -5427,6 +5534,16 @@ function createMediaSupervisor(options) {
|
|
|
5427
5534
|
started = false;
|
|
5428
5535
|
},
|
|
5429
5536
|
unavailable,
|
|
5537
|
+
async preview(kind, outcome) {
|
|
5538
|
+
api.start();
|
|
5539
|
+
let next;
|
|
5540
|
+
if (typeof outcome === "string") next = { state: outcome, unavailable: null };
|
|
5541
|
+
else next = { state: "unavailable", unavailable: await unavailable(outcome.error, kind, `meet.${kind}Preview`) };
|
|
5542
|
+
const current = region().preview;
|
|
5543
|
+
const was = current[kind];
|
|
5544
|
+
if (was.state === next.state && was.unavailable?.reason === next.unavailable?.reason && was.unavailable?.message === next.unavailable?.message) return;
|
|
5545
|
+
setRegion({ preview: { ...region().preview, [kind]: next } });
|
|
5546
|
+
},
|
|
5430
5547
|
onAudioPlayback(canPlay) {
|
|
5431
5548
|
setRegion({ audioOutput: canPlay ? "ok" : "blocked" });
|
|
5432
5549
|
},
|
|
@@ -5485,6 +5602,7 @@ function createMediaSupervisor(options) {
|
|
|
5485
5602
|
setRegion({ notice: null });
|
|
5486
5603
|
}
|
|
5487
5604
|
};
|
|
5605
|
+
return api;
|
|
5488
5606
|
}
|
|
5489
5607
|
|
|
5490
5608
|
// src/core/room-events.ts
|
|
@@ -5996,7 +6114,8 @@ function createRoomEngine(options) {
|
|
|
5996
6114
|
message: normalized.message,
|
|
5997
6115
|
remedy: normalized.remedy,
|
|
5998
6116
|
reason: normalized.reason,
|
|
5999
|
-
retryAt: normalized.retryAfterSeconds === null ? null : now() + normalized.retryAfterSeconds * 1e3
|
|
6117
|
+
retryAt: normalized.retryAfterSeconds === null ? null : now() + normalized.retryAfterSeconds * 1e3,
|
|
6118
|
+
stage: "join"
|
|
6000
6119
|
}
|
|
6001
6120
|
});
|
|
6002
6121
|
store.dispatch({ type: "join_failed", reason: normalized.reason ?? null });
|
|
@@ -6260,7 +6379,7 @@ function createRoomEngine(options) {
|
|
|
6260
6379
|
const { secondDevice: _answered, ...rest } = lastRequest;
|
|
6261
6380
|
lastRequest = rest;
|
|
6262
6381
|
}
|
|
6263
|
-
options.tabClaim?.inRoom(String(request.roomName), String(token.identity));
|
|
6382
|
+
options.tabClaim?.inRoom(String(request.roomName), String(token.identity), token.secondDeviceRule ?? null);
|
|
6264
6383
|
current = token;
|
|
6265
6384
|
lastServerUrl = token.serverUrl;
|
|
6266
6385
|
connectionPolicy = token.connectionPolicy ?? DEFAULT_CONNECTION_POLICY;
|
|
@@ -6387,6 +6506,10 @@ function createRoomEngine(options) {
|
|
|
6387
6506
|
}
|
|
6388
6507
|
departures.clear();
|
|
6389
6508
|
liveRoster = list;
|
|
6509
|
+
const localNow = list.find((p) => p.isLocal);
|
|
6510
|
+
if (localNow !== void 0 && !localNow.screenSharing && store.snapshot().share.state === "presenting") {
|
|
6511
|
+
store.patch({ share: NO_SHARE });
|
|
6512
|
+
}
|
|
6390
6513
|
const merged = mergeGhosts(list, ghosts, at);
|
|
6391
6514
|
ghosts = merged.kept;
|
|
6392
6515
|
const people = /* @__PURE__ */ new Map();
|
|
@@ -6568,10 +6691,12 @@ function createRoomEngine(options) {
|
|
|
6568
6691
|
if (claim === void 0 || userId === null) return false;
|
|
6569
6692
|
const before = store.snapshot();
|
|
6570
6693
|
if (before.phase !== "prejoin" || before.secondDevice !== null) return false;
|
|
6571
|
-
|
|
6694
|
+
const person = `user:${String(userId)}`;
|
|
6695
|
+
const found = claim.presenceElsewhere !== void 0 ? await claim.presenceElsewhere(String(roomName), person) : await claim.presentElsewhere(String(roomName), person) ? { rule: null } : null;
|
|
6696
|
+
if (found === null || found.rule === "allow_both") return false;
|
|
6572
6697
|
const now2 = store.snapshot();
|
|
6573
6698
|
if (now2.phase !== "prejoin" || now2.secondDevice !== null) return false;
|
|
6574
|
-
store.patch({ secondDevice: { rule: "ask_switch_or_join" } });
|
|
6699
|
+
store.patch({ secondDevice: { rule: found.rule ?? "ask_switch_or_join" } });
|
|
6575
6700
|
report("info", "meet: this browser already has you in this meeting \u2014 offering the choice before Join");
|
|
6576
6701
|
return true;
|
|
6577
6702
|
},
|
|
@@ -6606,7 +6731,10 @@ function createRoomEngine(options) {
|
|
|
6606
6731
|
try {
|
|
6607
6732
|
await driver.setScreenShareEnabled(enabled);
|
|
6608
6733
|
ambiguousShareDenials = 0;
|
|
6609
|
-
|
|
6734
|
+
const audio = enabled ? driver?.screenShareHasAudio?.() : void 0;
|
|
6735
|
+
store.patch({
|
|
6736
|
+
share: { state: enabled ? "presenting" : "none", reason: null, remedy: null, at: now(), ...audio !== void 0 ? { audio } : {} }
|
|
6737
|
+
});
|
|
6610
6738
|
} catch (error) {
|
|
6611
6739
|
const kind = classifyScreenShareError(error);
|
|
6612
6740
|
if (kind === "ambiguous") {
|
|
@@ -6673,13 +6801,14 @@ function createRoomEngine(options) {
|
|
|
6673
6801
|
return driver?.supportsVideoEffects?.() ?? false;
|
|
6674
6802
|
},
|
|
6675
6803
|
supportsScreenShare() {
|
|
6676
|
-
return driver?.supportsScreenShare?.() ??
|
|
6804
|
+
return driver?.supportsScreenShare?.() ?? detectCapabilities().canShareScreen;
|
|
6677
6805
|
},
|
|
6678
6806
|
async switchDevice(kind, deviceId2) {
|
|
6679
6807
|
if (driver === null) return;
|
|
6680
6808
|
await driver.switchDevice(kind, deviceId2);
|
|
6681
6809
|
},
|
|
6682
6810
|
enableAudio: () => media.enableAudio(),
|
|
6811
|
+
previewDevice: (kind, outcome) => media.preview(kind, outcome),
|
|
6683
6812
|
acceptDeviceOffer: () => media.acceptOffer(),
|
|
6684
6813
|
dismissDeviceOffer: () => media.dismissOffer(),
|
|
6685
6814
|
dismissDeviceNotice: () => media.dismissNotice(),
|
|
@@ -7151,7 +7280,15 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
7151
7280
|
created.on(RoomEvent.ParticipantConnected, roster).on(RoomEvent.ParticipantDisconnected, (participant, reason) => {
|
|
7152
7281
|
handlers.participantLeft?.(participant.identity, reasonName(reason));
|
|
7153
7282
|
roster();
|
|
7154
|
-
}).on(RoomEvent.TrackSubscribed, roster).on(RoomEvent.TrackUnsubscribed, roster).on(RoomEvent.TrackMuted, roster).on(RoomEvent.TrackUnmuted, roster).on(RoomEvent.LocalTrackPublished,
|
|
7283
|
+
}).on(RoomEvent.TrackSubscribed, roster).on(RoomEvent.TrackUnsubscribed, roster).on(RoomEvent.TrackMuted, roster).on(RoomEvent.TrackUnmuted, roster).on(RoomEvent.LocalTrackPublished, (publication) => {
|
|
7284
|
+
if (publication.source === Track.Source.ScreenShare) {
|
|
7285
|
+
const ended = () => {
|
|
7286
|
+
void created.localParticipant.setScreenShareEnabled(false).catch(() => void 0);
|
|
7287
|
+
};
|
|
7288
|
+
publication.track?.mediaStreamTrack.addEventListener("ended", ended, { once: true });
|
|
7289
|
+
}
|
|
7290
|
+
roster();
|
|
7291
|
+
}).on(RoomEvent.LocalTrackUnpublished, roster).on(RoomEvent.ParticipantNameChanged, roster).on(RoomEvent.ParticipantAttributesChanged, roster).on(RoomEvent.ParticipantMetadataChanged, roster).on(RoomEvent.RoomMetadataChanged, (metadata) => {
|
|
7155
7292
|
handlers.roomMetadata?.(metadata ?? "");
|
|
7156
7293
|
}).on(RoomEvent.AudioPlaybackStatusChanged, (canPlay) => {
|
|
7157
7294
|
handlers.audioPlayback?.(canPlay);
|
|
@@ -7292,7 +7429,10 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
7292
7429
|
},
|
|
7293
7430
|
async setScreenShareEnabled(enabled) {
|
|
7294
7431
|
try {
|
|
7295
|
-
await requireRoom("setScreenShareEnabled").localParticipant.setScreenShareEnabled(
|
|
7432
|
+
await requireRoom("setScreenShareEnabled").localParticipant.setScreenShareEnabled(
|
|
7433
|
+
enabled,
|
|
7434
|
+
enabled ? { audio: true, systemAudio: "include" } : void 0
|
|
7435
|
+
);
|
|
7296
7436
|
} catch (error) {
|
|
7297
7437
|
throw normalizeScreenShareError(error, "meet.setScreenShareEnabled");
|
|
7298
7438
|
}
|
|
@@ -7331,8 +7471,13 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
7331
7471
|
supportsVideoEffects() {
|
|
7332
7472
|
return videoEffectsSupported();
|
|
7333
7473
|
},
|
|
7474
|
+
screenShareHasAudio() {
|
|
7475
|
+
const target = room;
|
|
7476
|
+
if (target === null) return false;
|
|
7477
|
+
return target.localParticipant.getTrackPublications().some((p) => String(p.source) === "screen_share_audio" && p.track !== void 0);
|
|
7478
|
+
},
|
|
7334
7479
|
supportsScreenShare() {
|
|
7335
|
-
return
|
|
7480
|
+
return detectCapabilities().canShareScreen;
|
|
7336
7481
|
},
|
|
7337
7482
|
attach(identity, kind, element) {
|
|
7338
7483
|
const target = requireRoom("attach");
|
|
@@ -7818,6 +7963,21 @@ function observedDevice(track, permission = "unknown", held = null) {
|
|
|
7818
7963
|
const reason = deviceUnavailableReason(track, permission);
|
|
7819
7964
|
return reason === null ? "muted_by_me" : `unavailable:${reason}`;
|
|
7820
7965
|
}
|
|
7966
|
+
function observedPreview(preview, permission = "unknown") {
|
|
7967
|
+
if (permission === "denied") return "unavailable:denied";
|
|
7968
|
+
switch (preview.state) {
|
|
7969
|
+
case "on":
|
|
7970
|
+
return "on";
|
|
7971
|
+
case "off":
|
|
7972
|
+
return "muted_by_me";
|
|
7973
|
+
case "prompting":
|
|
7974
|
+
return "prompting";
|
|
7975
|
+
case "unavailable":
|
|
7976
|
+
return `unavailable:${preview.unavailable?.reason ?? "failed"}`;
|
|
7977
|
+
default:
|
|
7978
|
+
return null;
|
|
7979
|
+
}
|
|
7980
|
+
}
|
|
7821
7981
|
function endedRecord(snapshot, given) {
|
|
7822
7982
|
const record = snapshot?.meeting ?? given;
|
|
7823
7983
|
return record !== null && record.endedAt !== null;
|
|
@@ -7841,18 +8001,22 @@ function observe(input) {
|
|
|
7841
8001
|
const canAdmit = actual !== "none" && ADMITTER_ROLES.has(actual);
|
|
7842
8002
|
const role = actual === "none" || snapshot === null ? "none" : observedRole(actual, effectiveStanding(snapshot));
|
|
7843
8003
|
const deviceInPlay = phase === "joining" || inCall;
|
|
8004
|
+
const region = snapshot?.mediaRegion;
|
|
8005
|
+
const atPrejoin = phase === "prejoin" && region !== void 0;
|
|
7844
8006
|
return {
|
|
7845
8007
|
phase,
|
|
7846
8008
|
reason: phase === "ended" ? snapshot?.reason ?? null : snapshot?.reason ?? reasonOf(phase),
|
|
7847
8009
|
role,
|
|
7848
|
-
|
|
8010
|
+
// `net-blip`: in the call, a drop shorter than `reconnect_banner_delay_ms` is not something the
|
|
8011
|
+
// person sees — the contract says what is on screen, so it reads `stable` until the banner shows.
|
|
8012
|
+
connection: snapshot === null ? "stable" : inCall && snapshot.connection !== "stable" && !snapshot.connectionBanner ? "stable" : snapshot.connection,
|
|
7849
8013
|
hostPresent: inCall && snapshot !== null ? snapshot.participants.some((p) => ADMITTER_ROLES.has(p.role)) : snapshot !== null && isWaitingPhase(snapshot.phase) ? snapshot.waiting?.admittersPresent ?? null : null,
|
|
7850
8014
|
lobbyCount: canAdmit && snapshot !== null ? snapshot.lobby.filter((entry) => entry.state === "knocking").length : 0,
|
|
7851
8015
|
recording: snapshot?.recording === "recording" || snapshot?.recording === "stopping",
|
|
7852
8016
|
// S6c: the autoplay region is known only inside the room (no remote audio exists elsewhere).
|
|
7853
8017
|
audioBlocked: inCall && snapshot !== null ? snapshot.mediaRegion.audioOutput === "blocked" : null,
|
|
7854
|
-
camera: deviceInPlay && snapshot !== null ? observedDevice(snapshot.media.camera, snapshot.mediaRegion.permission.camera, lockedFor(snapshot, "camera", inCall)) : null,
|
|
7855
|
-
microphone: deviceInPlay && snapshot !== null ? observedDevice(snapshot.media.microphone, snapshot.mediaRegion.permission.microphone, lockedFor(snapshot, "microphone", inCall)) : null
|
|
8018
|
+
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,
|
|
8019
|
+
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
|
|
7856
8020
|
};
|
|
7857
8021
|
}
|
|
7858
8022
|
function lockedFor(snapshot, kind, inCall) {
|
|
@@ -7952,7 +8116,13 @@ function MeetRoot({
|
|
|
7952
8116
|
...options
|
|
7953
8117
|
}) {
|
|
7954
8118
|
const snapshot = useMeetSnapshot();
|
|
7955
|
-
const
|
|
8119
|
+
const probed = useCapabilities();
|
|
8120
|
+
const base = useCoreObservation(options);
|
|
8121
|
+
const unsupported = probed !== null && !probed.canJoin;
|
|
8122
|
+
const observation = (0, import_react3.useMemo)(
|
|
8123
|
+
() => unsupported ? { ...staticObservation("unsupported", probed?.reason ?? null) } : base,
|
|
8124
|
+
[unsupported, probed, base]
|
|
8125
|
+
);
|
|
7956
8126
|
const attrs = (0, import_react3.useMemo)(() => rootAttributes(observation), [observation]);
|
|
7957
8127
|
const latest = (0, import_react3.useRef)({ observation, storePhase: snapshot?.phase ?? null });
|
|
7958
8128
|
latest.current = { observation, storePhase: snapshot?.phase ?? null };
|
|
@@ -7970,7 +8140,34 @@ function MeetRoot({
|
|
|
7970
8140
|
if (target.__matrxMeet === diagnostics) delete target.__matrxMeet;
|
|
7971
8141
|
};
|
|
7972
8142
|
}, []);
|
|
7973
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ...attrs, style: { display: "contents" }, children });
|
|
8143
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ...attrs, style: { display: "contents" }, children: unsupported && probed !== null ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(UnsupportedBrowser, { capabilities: probed }) : children });
|
|
8144
|
+
}
|
|
8145
|
+
function useCapabilities() {
|
|
8146
|
+
const [found, setFound] = (0, import_react3.useState)(null);
|
|
8147
|
+
(0, import_react3.useEffect)(() => {
|
|
8148
|
+
setFound(detectCapabilities());
|
|
8149
|
+
}, []);
|
|
8150
|
+
return found;
|
|
8151
|
+
}
|
|
8152
|
+
function UnsupportedBrowser({ capabilities }) {
|
|
8153
|
+
const words = unsupportedWords(capabilities);
|
|
8154
|
+
const [copied, setCopied] = (0, import_react3.useState)(false);
|
|
8155
|
+
const href = typeof window === "undefined" ? "" : window.location.href;
|
|
8156
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "mx-meet", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mx-meet__waiting", "data-meet-exit": "unsupported", children: [
|
|
8157
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h1", { className: "mx-meet__prejoin-title", children: words.title }),
|
|
8158
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "mx-meet__hint", role: "status", "data-meet-notice": "browser-unsupported", children: words.body }),
|
|
8159
|
+
href.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
8160
|
+
"button",
|
|
8161
|
+
{
|
|
8162
|
+
type: "button",
|
|
8163
|
+
className: "mx-meet__primary",
|
|
8164
|
+
onClick: () => {
|
|
8165
|
+
void navigator.clipboard?.writeText(href).then(() => setCopied(true), () => void 0);
|
|
8166
|
+
},
|
|
8167
|
+
children: copied ? "Link copied" : "Copy link"
|
|
8168
|
+
}
|
|
8169
|
+
) : null
|
|
8170
|
+
] }) });
|
|
7974
8171
|
}
|
|
7975
8172
|
|
|
7976
8173
|
// src/react/hooks.ts
|
|
@@ -8670,7 +8867,8 @@ function useRoom() {
|
|
|
8670
8867
|
failure: snapshot?.failure ?? null,
|
|
8671
8868
|
secondDevice: snapshot?.secondDevice ?? null,
|
|
8672
8869
|
local,
|
|
8673
|
-
|
|
8870
|
+
// An observer (role `viewer`) publishes nothing: no Share screen to press and be refused (MD-16).
|
|
8871
|
+
canShareScreen: (engine?.supportsScreenShare() ?? true) && !isObserverRole(snapshot?.grants.role ?? null),
|
|
8674
8872
|
join: (0, import_react4.useCallback)(
|
|
8675
8873
|
async (request) => requireEngine(engine, "join").join(request),
|
|
8676
8874
|
[engine]
|
|
@@ -9149,6 +9347,12 @@ function useMediaSupervisor() {
|
|
|
9149
9347
|
retry: (0, import_react4.useCallback)(
|
|
9150
9348
|
async (kind) => kind === "microphone" ? engine?.setMicrophoneEnabled(true) : engine?.setCameraEnabled(true),
|
|
9151
9349
|
[engine]
|
|
9350
|
+
),
|
|
9351
|
+
reportPreview: (0, import_react4.useCallback)(
|
|
9352
|
+
(kind, outcome) => {
|
|
9353
|
+
void engine?.previewDevice(kind, outcome);
|
|
9354
|
+
},
|
|
9355
|
+
[engine]
|
|
9152
9356
|
)
|
|
9153
9357
|
};
|
|
9154
9358
|
}
|
|
@@ -10244,6 +10448,39 @@ function useCollabPanelRequest(onRequest) {
|
|
|
10244
10448
|
// src/core/reactions.ts
|
|
10245
10449
|
var MEET_REACTIONS = ["\u{1F496}", "\u{1F44D}", "\u{1F389}", "\u{1F44F}", "\u{1F602}", "\u{1F62E}", "\u{1F622}", "\u{1F914}", "\u{1F44E}"];
|
|
10246
10450
|
|
|
10451
|
+
// src/core/speaking-detector.ts
|
|
10452
|
+
function createSpeakingWhileMutedDetector(options = {}) {
|
|
10453
|
+
const threshold = options.threshold ?? 0.06;
|
|
10454
|
+
const holdMs = options.holdMs ?? 600;
|
|
10455
|
+
const gapMs = options.gapMs ?? 400;
|
|
10456
|
+
const cooldownMs = options.cooldownMs ?? 2e4;
|
|
10457
|
+
let speechStart = null;
|
|
10458
|
+
let lastLoud = null;
|
|
10459
|
+
let firedAt = null;
|
|
10460
|
+
return {
|
|
10461
|
+
feed(level, at) {
|
|
10462
|
+
if (level >= threshold) {
|
|
10463
|
+
if (speechStart === null || lastLoud === null || at - lastLoud > gapMs) speechStart = at;
|
|
10464
|
+
lastLoud = at;
|
|
10465
|
+
} else if (lastLoud !== null && at - lastLoud > gapMs) {
|
|
10466
|
+
speechStart = null;
|
|
10467
|
+
lastLoud = null;
|
|
10468
|
+
}
|
|
10469
|
+
if (speechStart === null || at - speechStart < holdMs) return false;
|
|
10470
|
+
if (firedAt !== null && at - firedAt < cooldownMs) return false;
|
|
10471
|
+
firedAt = at;
|
|
10472
|
+
speechStart = null;
|
|
10473
|
+
lastLoud = null;
|
|
10474
|
+
return true;
|
|
10475
|
+
},
|
|
10476
|
+
reset() {
|
|
10477
|
+
speechStart = null;
|
|
10478
|
+
lastLoud = null;
|
|
10479
|
+
firedAt = null;
|
|
10480
|
+
}
|
|
10481
|
+
};
|
|
10482
|
+
}
|
|
10483
|
+
|
|
10247
10484
|
// src/react/facade.ts
|
|
10248
10485
|
var NO_ACTIONS = {
|
|
10249
10486
|
join: async () => void 0,
|
|
@@ -10264,6 +10501,10 @@ function actionsFor(host) {
|
|
|
10264
10501
|
leave: () => engine.leave(),
|
|
10265
10502
|
backToPrejoin: () => engine.backToPrejoin(),
|
|
10266
10503
|
retry: () => {
|
|
10504
|
+
if (RETRYABLE_REFUSALS.has(store.snapshot().phase)) {
|
|
10505
|
+
void engine.rejoin().catch(() => void 0);
|
|
10506
|
+
return;
|
|
10507
|
+
}
|
|
10267
10508
|
store.dispatch({ type: "retry", click: true });
|
|
10268
10509
|
},
|
|
10269
10510
|
resolved: () => {
|
|
@@ -10547,6 +10788,9 @@ function UndoIcon(props) {
|
|
|
10547
10788
|
/* @__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" })
|
|
10548
10789
|
] });
|
|
10549
10790
|
}
|
|
10791
|
+
function ChevronUpIcon(props) {
|
|
10792
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Icon, { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "m6 15 6-6 6 6" }) });
|
|
10793
|
+
}
|
|
10550
10794
|
|
|
10551
10795
|
// src/skins/meet/collab.tsx
|
|
10552
10796
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
@@ -11800,6 +12044,7 @@ function ControlBar({ onLeave }) {
|
|
|
11800
12044
|
) }) }) : null,
|
|
11801
12045
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BreakoutBanner, {}),
|
|
11802
12046
|
panel !== "people" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(KnockNotice, { onView: () => setPanel("people") }) : null,
|
|
12047
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SpeakingWhileMutedNudge, {}),
|
|
11803
12048
|
controls.notice !== null && panel !== "people" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Toast, { text: controls.notice.text, failed: controls.notice.failed, onDone: controls.clearNotice }) : null,
|
|
11804
12049
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mx-meet__controls", children: [
|
|
11805
12050
|
live.micLocked ? null : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
@@ -11811,6 +12056,7 @@ function ControlBar({ onLeave }) {
|
|
|
11811
12056
|
children: room.local?.micEnabled === true ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MicIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MicOffIcon, {})
|
|
11812
12057
|
}
|
|
11813
12058
|
),
|
|
12059
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AudioSettingsControl, {}),
|
|
11814
12060
|
live.cameraLocked ? null : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
11815
12061
|
ControlButton,
|
|
11816
12062
|
{
|
|
@@ -11926,6 +12172,13 @@ function ControlBar({ onLeave }) {
|
|
|
11926
12172
|
]
|
|
11927
12173
|
}
|
|
11928
12174
|
),
|
|
12175
|
+
!room.canShareScreen && room.local?.screenSharing !== true ? (
|
|
12176
|
+
// `share-unsupported-mobile`: the control is absent; the menu says where it lives.
|
|
12177
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "mx-meet__menu-note", role: "note", "data-meet-notice": "share-unsupported", children: [
|
|
12178
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ScreenShareIcon, { width: 16, height: 16 }),
|
|
12179
|
+
" Present from a computer"
|
|
12180
|
+
] })
|
|
12181
|
+
) : null,
|
|
11929
12182
|
controls.isHost ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
11930
12183
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "mx-meet__menu-sep", role: "separator" }),
|
|
11931
12184
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
@@ -11971,6 +12224,7 @@ function ControlBar({ onLeave }) {
|
|
|
11971
12224
|
]
|
|
11972
12225
|
}
|
|
11973
12226
|
),
|
|
12227
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(WaitingRoomRule, {}),
|
|
11974
12228
|
queue.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
11975
12229
|
MenuItem,
|
|
11976
12230
|
{
|
|
@@ -12190,6 +12444,58 @@ function MenuItem({
|
|
|
12190
12444
|
}
|
|
12191
12445
|
);
|
|
12192
12446
|
}
|
|
12447
|
+
var LOBBY_BYPASS_CHOICES = [
|
|
12448
|
+
{ value: "everyone", label: "Everyone" },
|
|
12449
|
+
{ value: "org_and_invited", label: "Your organization and invited people" },
|
|
12450
|
+
{ value: "org", label: "Your organization" },
|
|
12451
|
+
{ value: "invited", label: "Invited people" },
|
|
12452
|
+
{ value: "host_only", label: "Only hosts" }
|
|
12453
|
+
];
|
|
12454
|
+
function WaitingRoomRule() {
|
|
12455
|
+
const host = useRequiredMeetHost();
|
|
12456
|
+
const [open, setOpen] = (0, import_react10.useState)(false);
|
|
12457
|
+
const [value, setValue] = (0, import_react10.useState)(null);
|
|
12458
|
+
const [notice, setNotice] = (0, import_react10.useState)(null);
|
|
12459
|
+
(0, import_react10.useEffect)(() => {
|
|
12460
|
+
let live = true;
|
|
12461
|
+
void host.meetings.meetingRule("lobby_bypass").then((rule) => {
|
|
12462
|
+
if (live) setValue(typeof rule === "string" ? rule : null);
|
|
12463
|
+
}).catch(() => void 0);
|
|
12464
|
+
return () => {
|
|
12465
|
+
live = false;
|
|
12466
|
+
};
|
|
12467
|
+
}, [host]);
|
|
12468
|
+
const current = LOBBY_BYPASS_CHOICES.find((choice) => choice.value === value)?.label ?? null;
|
|
12469
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
12470
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(MenuItem, { onSelect: () => setOpen(!open), children: [
|
|
12471
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(LockIcon, { width: 16, height: 16 }),
|
|
12472
|
+
" Who can skip the waiting room",
|
|
12473
|
+
current !== null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mx-meet__menu-value", children: current }) : null
|
|
12474
|
+
] }),
|
|
12475
|
+
open ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { role: "group", "aria-label": "Who can skip the waiting room", "data-meet-rule": "lobby_bypass", children: [
|
|
12476
|
+
LOBBY_BYPASS_CHOICES.map((choice) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
12477
|
+
"button",
|
|
12478
|
+
{
|
|
12479
|
+
type: "button",
|
|
12480
|
+
role: "menuitemradio",
|
|
12481
|
+
"aria-checked": choice.value === value,
|
|
12482
|
+
className: "mx-meet__menu-item mx-meet__menu-item--sub",
|
|
12483
|
+
onClick: () => {
|
|
12484
|
+
setNotice(null);
|
|
12485
|
+
void host.meetings.setMeetingRule("lobby_bypass", choice.value).then(() => setValue(choice.value)).catch((error) => setNotice(error instanceof Error ? error.message : String(error)));
|
|
12486
|
+
},
|
|
12487
|
+
children: [
|
|
12488
|
+
choice.value === value ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CheckIcon, { width: 14, height: 14 }) : null,
|
|
12489
|
+
" ",
|
|
12490
|
+
choice.label
|
|
12491
|
+
]
|
|
12492
|
+
},
|
|
12493
|
+
choice.value
|
|
12494
|
+
)),
|
|
12495
|
+
notice !== null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mx-meet__menu-note", role: "status", children: notice }) : null
|
|
12496
|
+
] }) : null
|
|
12497
|
+
] });
|
|
12498
|
+
}
|
|
12193
12499
|
function Toast({ text: text3, failed, onDone }) {
|
|
12194
12500
|
(0, import_react10.useEffect)(() => {
|
|
12195
12501
|
const timer = setTimeout(onDone, failed ? 7e3 : 4e3);
|
|
@@ -12200,6 +12506,94 @@ function Toast({ text: text3, failed, onDone }) {
|
|
|
12200
12506
|
/* @__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 }) })
|
|
12201
12507
|
] });
|
|
12202
12508
|
}
|
|
12509
|
+
function AudioSettingsControl() {
|
|
12510
|
+
const host = useMeetHost();
|
|
12511
|
+
const devices = useDeviceSnapshot();
|
|
12512
|
+
const [open, setOpen] = (0, import_react10.useState)(false);
|
|
12513
|
+
const list = (devices?.devices.audioinput ?? []).filter((device) => device.deviceId.length > 0);
|
|
12514
|
+
if (host === null || list.length < 2) return null;
|
|
12515
|
+
const current = devices?.selected.audioinput ?? host.devices.resolve("audioinput");
|
|
12516
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "mx-meet__anchor", children: [
|
|
12517
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
12518
|
+
ControlButton,
|
|
12519
|
+
{
|
|
12520
|
+
label: "Audio settings",
|
|
12521
|
+
active: open,
|
|
12522
|
+
onClick: () => {
|
|
12523
|
+
if (!open) void host.devices.refresh();
|
|
12524
|
+
setOpen(!open);
|
|
12525
|
+
},
|
|
12526
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChevronUpIcon, {})
|
|
12527
|
+
}
|
|
12528
|
+
),
|
|
12529
|
+
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)(
|
|
12530
|
+
"button",
|
|
12531
|
+
{
|
|
12532
|
+
type: "button",
|
|
12533
|
+
role: "menuitemradio",
|
|
12534
|
+
"aria-checked": device.deviceId === current,
|
|
12535
|
+
className: "mx-meet__menu-item",
|
|
12536
|
+
onClick: () => {
|
|
12537
|
+
setOpen(false);
|
|
12538
|
+
host.devices.select("audioinput", device.deviceId);
|
|
12539
|
+
void host.engine.switchDevice("audioinput", device.deviceId).catch(() => void 0);
|
|
12540
|
+
},
|
|
12541
|
+
children: [
|
|
12542
|
+
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" }),
|
|
12543
|
+
device.label.length > 0 ? device.label : "Microphone"
|
|
12544
|
+
]
|
|
12545
|
+
},
|
|
12546
|
+
device.deviceId
|
|
12547
|
+
)) }) }) : null
|
|
12548
|
+
] });
|
|
12549
|
+
}
|
|
12550
|
+
function SpeakingWhileMutedNudge() {
|
|
12551
|
+
const room = useRoom();
|
|
12552
|
+
const host = useMeetHost();
|
|
12553
|
+
const devices = useDeviceSnapshot();
|
|
12554
|
+
const microphone = useLocalMedia().microphone;
|
|
12555
|
+
const mutedByMe = host !== null && room.local != null && room.local.micEnabled !== true && microphone.unavailable === null;
|
|
12556
|
+
const mic = devices?.selected.audioinput ?? host?.devices.resolve("audioinput") ?? null;
|
|
12557
|
+
const stream = useMicrophoneStream(mutedByMe ? mic : "off");
|
|
12558
|
+
const level = useAudioLevel(stream);
|
|
12559
|
+
const latest = (0, import_react10.useRef)(0);
|
|
12560
|
+
latest.current = level;
|
|
12561
|
+
const [shown, setShown] = (0, import_react10.useState)(false);
|
|
12562
|
+
const [detector] = (0, import_react10.useState)(() => createSpeakingWhileMutedDetector());
|
|
12563
|
+
(0, import_react10.useEffect)(() => {
|
|
12564
|
+
if (!mutedByMe) {
|
|
12565
|
+
detector.reset();
|
|
12566
|
+
setShown(false);
|
|
12567
|
+
return void 0;
|
|
12568
|
+
}
|
|
12569
|
+
const timer = setInterval(() => {
|
|
12570
|
+
if (detector.feed(latest.current, Date.now())) setShown(true);
|
|
12571
|
+
}, 150);
|
|
12572
|
+
return () => clearInterval(timer);
|
|
12573
|
+
}, [mutedByMe, detector]);
|
|
12574
|
+
(0, import_react10.useEffect)(() => {
|
|
12575
|
+
if (!shown) return void 0;
|
|
12576
|
+
const timer = setTimeout(() => setShown(false), 8e3);
|
|
12577
|
+
return () => clearTimeout(timer);
|
|
12578
|
+
}, [shown]);
|
|
12579
|
+
if (!shown || !mutedByMe) return null;
|
|
12580
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mx-meet__notice-bar", role: "status", "data-meet-notice": "speaking-while-muted", children: [
|
|
12581
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MicOffIcon, { width: 16, height: 16 }),
|
|
12582
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "You're muted" }),
|
|
12583
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
12584
|
+
"button",
|
|
12585
|
+
{
|
|
12586
|
+
type: "button",
|
|
12587
|
+
className: "mx-meet__primary mx-meet__primary--small",
|
|
12588
|
+
onClick: () => {
|
|
12589
|
+
setShown(false);
|
|
12590
|
+
void room.toggleMicrophone();
|
|
12591
|
+
},
|
|
12592
|
+
children: "Unmute"
|
|
12593
|
+
}
|
|
12594
|
+
)
|
|
12595
|
+
] });
|
|
12596
|
+
}
|
|
12203
12597
|
function MediaNotices() {
|
|
12204
12598
|
const media = useLocalMedia();
|
|
12205
12599
|
const { region, where, enableAudio, acceptOffer, dismissOffer, dismissNotice, retry } = useMediaSupervisor();
|
|
@@ -13049,14 +13443,20 @@ async function playTestSound(sinkId) {
|
|
|
13049
13443
|
audio.pause();
|
|
13050
13444
|
await context.close();
|
|
13051
13445
|
}
|
|
13052
|
-
function useMicrophoneStream(deviceId2) {
|
|
13446
|
+
function useMicrophoneStream(deviceId2, report, attempt = 0) {
|
|
13053
13447
|
const [stream, setStream] = (0, import_react10.useState)(null);
|
|
13448
|
+
const reportRef = (0, import_react10.useRef)(report);
|
|
13449
|
+
reportRef.current = report;
|
|
13054
13450
|
(0, import_react10.useEffect)(() => {
|
|
13055
|
-
if (deviceId2 === "off")
|
|
13451
|
+
if (deviceId2 === "off") {
|
|
13452
|
+
reportRef.current?.("off");
|
|
13453
|
+
return void 0;
|
|
13454
|
+
}
|
|
13056
13455
|
let live = true;
|
|
13057
13456
|
let opened = null;
|
|
13058
13457
|
const media = globalThis.navigator?.mediaDevices;
|
|
13059
13458
|
if (media === void 0) return void 0;
|
|
13459
|
+
reportRef.current?.("prompting");
|
|
13060
13460
|
void media.getUserMedia({ audio: deviceId2 === null ? true : { deviceId: { exact: deviceId2 } } }).then((next) => {
|
|
13061
13461
|
if (!live) {
|
|
13062
13462
|
next.getTracks().forEach((track) => track.stop());
|
|
@@ -13064,15 +13464,49 @@ function useMicrophoneStream(deviceId2) {
|
|
|
13064
13464
|
}
|
|
13065
13465
|
opened = next;
|
|
13066
13466
|
setStream(next);
|
|
13067
|
-
|
|
13467
|
+
reportRef.current?.("on");
|
|
13468
|
+
}).catch((thrown) => {
|
|
13469
|
+
setStream(null);
|
|
13470
|
+
if (live) reportRef.current?.({ error: thrown });
|
|
13471
|
+
});
|
|
13068
13472
|
return () => {
|
|
13069
13473
|
live = false;
|
|
13070
13474
|
opened?.getTracks().forEach((track) => track.stop());
|
|
13071
13475
|
setStream(null);
|
|
13072
13476
|
};
|
|
13073
|
-
}, [deviceId2]);
|
|
13477
|
+
}, [deviceId2, attempt]);
|
|
13074
13478
|
return stream;
|
|
13075
13479
|
}
|
|
13480
|
+
function PreviewStatus({ onRetry }) {
|
|
13481
|
+
const { region, where } = useMediaSupervisor();
|
|
13482
|
+
const [waited, setWaited] = (0, import_react10.useState)(false);
|
|
13483
|
+
const prompting = region.preview.microphone.state === "prompting" || region.preview.camera.state === "prompting";
|
|
13484
|
+
(0, import_react10.useEffect)(() => {
|
|
13485
|
+
if (!prompting) {
|
|
13486
|
+
setWaited(false);
|
|
13487
|
+
return void 0;
|
|
13488
|
+
}
|
|
13489
|
+
const timer = setTimeout(() => setWaited(true), 800);
|
|
13490
|
+
return () => clearTimeout(timer);
|
|
13491
|
+
}, [prompting]);
|
|
13492
|
+
const refused = ["microphone", "camera"].flatMap((kind) => {
|
|
13493
|
+
const preview = region.preview[kind];
|
|
13494
|
+
const reason = region.permission[kind] === "denied" ? "denied" : preview.state === "unavailable" ? preview.unavailable?.reason ?? "failed" : null;
|
|
13495
|
+
return reason === null ? [] : [{ kind, reason }];
|
|
13496
|
+
});
|
|
13497
|
+
if (refused.length === 0) {
|
|
13498
|
+
if (!prompting || !waited) return null;
|
|
13499
|
+
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" });
|
|
13500
|
+
}
|
|
13501
|
+
const first = refused[0];
|
|
13502
|
+
const both = refused.length === 2 && refused[0].reason === refused[1].reason;
|
|
13503
|
+
const label = both ? deviceChipLabel("camera", first.reason).replace(/^Camera/, "Camera and mic") : refused.map((r) => deviceChipLabel(r.kind, r.reason)).join(" \xB7 ");
|
|
13504
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mx-meet__warning", role: "status", "data-meet-notice": deviceNoticeCode(first.kind, first.reason), children: [
|
|
13505
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { children: label }),
|
|
13506
|
+
/* @__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)) }),
|
|
13507
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system.Button, { type: "button", variant: "outline", size: "sm", onClick: onRetry, children: "Try again" })
|
|
13508
|
+
] });
|
|
13509
|
+
}
|
|
13076
13510
|
function KnockNotice({ onView }) {
|
|
13077
13511
|
const { knocker, others, admit, dismiss } = useKnockNotice();
|
|
13078
13512
|
if (knocker === null) return null;
|
|
@@ -13150,7 +13584,11 @@ function WaitingList({ lobby }) {
|
|
|
13150
13584
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Avatar, { name: entry.displayName, url: entry.avatarUrl, size: 32 }),
|
|
13151
13585
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "mx-meet__person-name", children: [
|
|
13152
13586
|
entry.displayName,
|
|
13153
|
-
entry.userId === null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mx-meet__badge", children: "Guest" }) : null
|
|
13587
|
+
entry.userId === null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mx-meet__badge", children: "Guest" }) : null,
|
|
13588
|
+
entry.flag === "previously_removed" ? (
|
|
13589
|
+
// `mod-removed-bypass`: advice for the host, never a block — Admit stays.
|
|
13590
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mx-meet__badge mx-meet__badge--warn", "data-meet-notice": "knock-flagged", children: "Previously removed" })
|
|
13591
|
+
) : null
|
|
13154
13592
|
] }),
|
|
13155
13593
|
/* @__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 }) }),
|
|
13156
13594
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
@@ -13402,8 +13840,15 @@ function PreJoinBody({
|
|
|
13402
13840
|
const [joining, setJoining] = (0, import_react10.useState)(false);
|
|
13403
13841
|
const camera = devices?.selected.videoinput ?? host.devices.resolve("videoinput");
|
|
13404
13842
|
const mic = devices?.selected.audioinput ?? host.devices.resolve("audioinput");
|
|
13405
|
-
const
|
|
13406
|
-
const
|
|
13843
|
+
const { reportPreview } = useMediaSupervisor();
|
|
13844
|
+
const [attempt, setAttempt] = (0, import_react10.useState)(0);
|
|
13845
|
+
const preview = useCameraPreview(
|
|
13846
|
+
mode === "video" ? camera : void 0,
|
|
13847
|
+
effects.effects,
|
|
13848
|
+
(outcome) => reportPreview("camera", outcome),
|
|
13849
|
+
attempt
|
|
13850
|
+
);
|
|
13851
|
+
const micStream = useMicrophoneStream(microphone ? mic : "off", (outcome) => reportPreview("microphone", outcome), attempt);
|
|
13407
13852
|
const level = useAudioLevel(micStream);
|
|
13408
13853
|
const [speakerTesting, setSpeakerTesting] = (0, import_react10.useState)(false);
|
|
13409
13854
|
(0, import_react10.useEffect)(() => {
|
|
@@ -13447,6 +13892,7 @@ function PreJoinBody({
|
|
|
13447
13892
|
] }),
|
|
13448
13893
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mx-meet__prejoin-panel", children: [
|
|
13449
13894
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h1", { className: "mx-meet__prejoin-title", children: "Ready to join?" }),
|
|
13895
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PreviewStatus, { onRetry: () => setAttempt((n) => n + 1) }),
|
|
13450
13896
|
notice?.kind === "removed" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mx-meet__warning", children: notice.text }) : null,
|
|
13451
13897
|
devices?.labelsHidden === true ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "mx-meet__panel-note", children: [
|
|
13452
13898
|
"Your browser is hiding device names until you allow camera and microphone access.",
|
|
@@ -13549,16 +13995,22 @@ function PreJoinBody({
|
|
|
13549
13995
|
] })
|
|
13550
13996
|
] });
|
|
13551
13997
|
}
|
|
13552
|
-
function useCameraPreview(deviceId2, effects) {
|
|
13998
|
+
function useCameraPreview(deviceId2, effects, report, attempt = 0) {
|
|
13999
|
+
const reportRef = (0, import_react10.useRef)(report);
|
|
14000
|
+
reportRef.current = report;
|
|
13553
14001
|
const [preview, setPreview] = (0, import_react10.useState)(null);
|
|
13554
14002
|
const [error, setError] = (0, import_react10.useState)(null);
|
|
13555
14003
|
const current = (0, import_react10.useRef)(null);
|
|
13556
14004
|
const initialEffect = (0, import_react10.useRef)(toVideoEffect(effects));
|
|
13557
14005
|
initialEffect.current = toVideoEffect(effects);
|
|
13558
14006
|
(0, import_react10.useEffect)(() => {
|
|
13559
|
-
if (deviceId2 === void 0)
|
|
14007
|
+
if (deviceId2 === void 0) {
|
|
14008
|
+
reportRef.current?.("off");
|
|
14009
|
+
return void 0;
|
|
14010
|
+
}
|
|
13560
14011
|
let live = true;
|
|
13561
14012
|
setError(null);
|
|
14013
|
+
reportRef.current?.("prompting");
|
|
13562
14014
|
void createCameraPreview(deviceId2, initialEffect.current).then((created) => {
|
|
13563
14015
|
if (!live) {
|
|
13564
14016
|
created.stop();
|
|
@@ -13566,8 +14018,10 @@ function useCameraPreview(deviceId2, effects) {
|
|
|
13566
14018
|
}
|
|
13567
14019
|
current.current = created;
|
|
13568
14020
|
setPreview(created);
|
|
14021
|
+
reportRef.current?.("on");
|
|
13569
14022
|
}).catch((thrown) => {
|
|
13570
14023
|
if (!live) return;
|
|
14024
|
+
reportRef.current?.({ error: thrown });
|
|
13571
14025
|
const failure = thrown;
|
|
13572
14026
|
const message = (failure.message ?? "").replace(/^meet\.[A-Za-z.()]+:\s*/, "");
|
|
13573
14027
|
const sentence = message.length > 0 ? message.charAt(0).toUpperCase() + message.slice(1) : "";
|
|
@@ -13579,7 +14033,7 @@ function useCameraPreview(deviceId2, effects) {
|
|
|
13579
14033
|
current.current = null;
|
|
13580
14034
|
setPreview(null);
|
|
13581
14035
|
};
|
|
13582
|
-
}, [deviceId2]);
|
|
14036
|
+
}, [deviceId2, attempt]);
|
|
13583
14037
|
return {
|
|
13584
14038
|
preview,
|
|
13585
14039
|
error,
|
|
@@ -13719,6 +14173,14 @@ function MeetSkinRoom({
|
|
|
13719
14173
|
}
|
|
13720
14174
|
function ShareNotice() {
|
|
13721
14175
|
const { share, actions } = useMeetCore();
|
|
14176
|
+
const [soundHintClosed, setSoundHintClosed] = (0, import_react10.useState)(null);
|
|
14177
|
+
if (share.state === "presenting" && share.audio === false && soundHintClosed !== share.at) {
|
|
14178
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "share-audio-missing", children: [
|
|
14179
|
+
"Sound not shared",
|
|
14180
|
+
/* @__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" }),
|
|
14181
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "mx-meet__link", onClick: () => setSoundHintClosed(share.at), children: "Dismiss" })
|
|
14182
|
+
] });
|
|
14183
|
+
}
|
|
13722
14184
|
if (share.state !== "refused") return null;
|
|
13723
14185
|
const text3 = 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";
|
|
13724
14186
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "share-unavailable", children: [
|
|
@@ -13751,7 +14213,20 @@ function ExitScreen({ onLeave }) {
|
|
|
13751
14213
|
const { phase, reason, host, snapshot, actions, call, connection } = useMeetCore();
|
|
13752
14214
|
const [busy, setBusy] = (0, import_react10.useState)(false);
|
|
13753
14215
|
const failure = snapshot?.failure ?? null;
|
|
13754
|
-
const retryLeft = useCountdown(
|
|
14216
|
+
const retryLeft = useCountdown(
|
|
14217
|
+
phase === "refused:lobby_rejects" || phase === "refused:too_early" ? failure?.retryAt ?? null : null
|
|
14218
|
+
);
|
|
14219
|
+
const reaskedAtZero = (0, import_react10.useRef)(false);
|
|
14220
|
+
(0, import_react10.useEffect)(() => {
|
|
14221
|
+
if (phase !== "refused:too_early") {
|
|
14222
|
+
reaskedAtZero.current = false;
|
|
14223
|
+
return;
|
|
14224
|
+
}
|
|
14225
|
+
if (retryLeft === 0 && !reaskedAtZero.current) {
|
|
14226
|
+
reaskedAtZero.current = true;
|
|
14227
|
+
actions.retry();
|
|
14228
|
+
}
|
|
14229
|
+
}, [phase, retryLeft, actions]);
|
|
13755
14230
|
const rejoin = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
13756
14231
|
import_design_system.Button,
|
|
13757
14232
|
{
|
|
@@ -13789,11 +14264,19 @@ function ExitScreen({ onLeave }) {
|
|
|
13789
14264
|
if (phase.startsWith("refused:")) {
|
|
13790
14265
|
const code = phase.slice("refused:".length);
|
|
13791
14266
|
title = REFUSED_TITLES[code] ?? "You can't join this meeting";
|
|
13792
|
-
line = code === "too_early" || code === "registration_required" || code === "cancelled" ? failure?.remedy ?? null : null;
|
|
14267
|
+
line = code === "too_early" || code === "registration_required" || code === "cancelled" || code === "wrong_account" ? failure?.remedy ?? null : null;
|
|
14268
|
+
if (code === "too_early" && retryLeft !== null && retryLeft > 0) line = `Opens in ${clockText(retryLeft)}`;
|
|
13793
14269
|
actionsNode = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
13794
14270
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system.Button, { type: "button", variant: "outline", onClick: actions.retry, children: "Try again" }),
|
|
13795
14271
|
home
|
|
13796
14272
|
] });
|
|
14273
|
+
if (code === "sign_in_required" || code === "wrong_account") {
|
|
14274
|
+
const back = typeof window === "undefined" ? "/" : `${window.location.pathname}${window.location.search}`;
|
|
14275
|
+
actionsNode = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
14276
|
+
/* @__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" }) }),
|
|
14277
|
+
home
|
|
14278
|
+
] });
|
|
14279
|
+
}
|
|
13797
14280
|
if (code === "lobby_rejects") {
|
|
13798
14281
|
if (retryLeft !== null && retryLeft > 0) {
|
|
13799
14282
|
line = `You can ask again in ${clockText(retryLeft)}`;
|
|
@@ -13831,6 +14314,27 @@ function ExitScreen({ onLeave }) {
|
|
|
13831
14314
|
] });
|
|
13832
14315
|
break;
|
|
13833
14316
|
case "disconnected":
|
|
14317
|
+
if (failure?.stage === "join" && (failure.reason ?? null) === null) {
|
|
14318
|
+
title = "Can't connect to the meeting";
|
|
14319
|
+
line = connection === "offline" ? "You're offline. Check your internet connection." : "Check your network. A VPN, firewall or proxy may be blocking the call.";
|
|
14320
|
+
notice = "cannot-connect";
|
|
14321
|
+
actionsNode = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
14322
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
14323
|
+
import_design_system.Button,
|
|
14324
|
+
{
|
|
14325
|
+
type: "button",
|
|
14326
|
+
disabled: busy,
|
|
14327
|
+
onClick: () => {
|
|
14328
|
+
setBusy(true);
|
|
14329
|
+
void actions.rejoin().catch(() => void 0).finally(() => setBusy(false));
|
|
14330
|
+
},
|
|
14331
|
+
children: "Try again"
|
|
14332
|
+
}
|
|
14333
|
+
),
|
|
14334
|
+
home
|
|
14335
|
+
] });
|
|
14336
|
+
break;
|
|
14337
|
+
}
|
|
13834
14338
|
title = "You lost your connection to the meeting";
|
|
13835
14339
|
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.";
|
|
13836
14340
|
notice = "disconnected";
|