@livo-tv/sdk 1.2.2 → 1.2.4
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/dist/studio/index.cjs +197 -65
- package/dist/studio/index.cjs.map +1 -1
- package/dist/studio/index.d.cts +6 -0
- package/dist/studio/index.d.ts +6 -0
- package/dist/studio/index.js +197 -65
- package/dist/studio/index.js.map +1 -1
- package/package.json +1 -1
package/dist/studio/index.cjs
CHANGED
|
@@ -1899,6 +1899,7 @@ function StudioGuestWaiting({
|
|
|
1899
1899
|
muted,
|
|
1900
1900
|
labels,
|
|
1901
1901
|
settingsOpen,
|
|
1902
|
+
statusLabel,
|
|
1902
1903
|
onToggleCamera,
|
|
1903
1904
|
onToggleMute,
|
|
1904
1905
|
onOpenSettings,
|
|
@@ -1930,7 +1931,7 @@ function StudioGuestWaiting({
|
|
|
1930
1931
|
reducedMotion
|
|
1931
1932
|
}
|
|
1932
1933
|
),
|
|
1933
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-center text-sm", role: "status", children: labels.waitingHost }),
|
|
1934
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-center text-sm", role: "status", children: statusLabel ?? labels.waitingHost }),
|
|
1934
1935
|
/* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-center gap-2", children: [
|
|
1935
1936
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1936
1937
|
ControlIcon,
|
|
@@ -2299,13 +2300,13 @@ function BubbleGroup({ className, ...props }) {
|
|
|
2299
2300
|
"div",
|
|
2300
2301
|
{
|
|
2301
2302
|
"data-slot": "bubble-group",
|
|
2302
|
-
className: cn("flex min-w-
|
|
2303
|
+
className: cn("flex min-w-min flex-col gap-2", className),
|
|
2303
2304
|
...props
|
|
2304
2305
|
}
|
|
2305
2306
|
);
|
|
2306
2307
|
}
|
|
2307
2308
|
var bubbleVariants = classVarianceAuthority.cva(
|
|
2308
|
-
"group/bubble relative flex w-fit min-w-
|
|
2309
|
+
"group/bubble relative flex w-fit min-w-min max-w-full flex-col gap-1 group-data-[align=end]/message:self-end data-[align=end]:self-end data-[variant=ghost]:max-w-full",
|
|
2309
2310
|
{
|
|
2310
2311
|
variants: {
|
|
2311
2312
|
variant: {
|
|
@@ -2350,7 +2351,7 @@ function BubbleContent({
|
|
|
2350
2351
|
props: mergeProps.mergeProps(
|
|
2351
2352
|
{
|
|
2352
2353
|
className: cn(
|
|
2353
|
-
"w-auto max-w-full rounded-xl border border-transparent px-3 py-2 text-sm leading-relaxed [overflow-wrap:break-word] group-data-[align=end]/bubble:self-end [button]:text-left [button,a]:transition-colors [button,a]:outline-none [button,a]:focus-visible:border-ring [button,a]:focus-visible:ring-3 [button,a]:focus-visible:ring-ring/50",
|
|
2354
|
+
"w-auto max-w-full whitespace-pre-wrap rounded-xl border border-transparent px-3 py-2 text-sm leading-relaxed [overflow-wrap:break-word] group-data-[align=end]/bubble:self-end [button]:text-left [button,a]:transition-colors [button,a]:outline-none [button,a]:focus-visible:border-ring [button,a]:focus-visible:ring-3 [button,a]:focus-visible:ring-ring/50",
|
|
2354
2355
|
className
|
|
2355
2356
|
)
|
|
2356
2357
|
},
|
|
@@ -2534,7 +2535,7 @@ function StudioChatPanel({
|
|
|
2534
2535
|
const bubbles = group.rows.map((row) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2535
2536
|
"div",
|
|
2536
2537
|
{
|
|
2537
|
-
className: group.mine ? "ml-auto flex
|
|
2538
|
+
className: group.mine ? "ml-auto flex max-w-[80%] flex-col gap-1 self-end" : "flex max-w-[80%] flex-col gap-1 self-start",
|
|
2538
2539
|
children: [
|
|
2539
2540
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2540
2541
|
Bubble,
|
|
@@ -3514,6 +3515,7 @@ function StudioToolbar({
|
|
|
3514
3515
|
] }) });
|
|
3515
3516
|
}
|
|
3516
3517
|
var STAGE_GRANT_VERIFY_MS = 4e3;
|
|
3518
|
+
var STUDIO_CONNECT_WATCHDOG_MS = 12e3;
|
|
3517
3519
|
function StudioRoom({
|
|
3518
3520
|
session,
|
|
3519
3521
|
meeting,
|
|
@@ -3549,6 +3551,7 @@ function StudioRoom({
|
|
|
3549
3551
|
);
|
|
3550
3552
|
const [egressError, setEgressError] = react.useState(null);
|
|
3551
3553
|
const [shareGrant, setShareGrant] = react.useState("idle");
|
|
3554
|
+
const [connectTimedOut, setConnectTimedOut] = react.useState(false);
|
|
3552
3555
|
const [screenPermission, setScreenPermission] = react.useState(
|
|
3553
3556
|
() => meeting.screenSharePermission()
|
|
3554
3557
|
);
|
|
@@ -3795,6 +3798,14 @@ function StudioRoom({
|
|
|
3795
3798
|
[participants]
|
|
3796
3799
|
);
|
|
3797
3800
|
const waitlisted = react.useMemo(() => meeting.waitlisted(), [meeting, tick]);
|
|
3801
|
+
const connectionState = react.useMemo(
|
|
3802
|
+
() => meeting.connectionState(),
|
|
3803
|
+
[meeting, tick]
|
|
3804
|
+
);
|
|
3805
|
+
const lastMeetingError = react.useMemo(
|
|
3806
|
+
() => meeting.lastMeetingError(),
|
|
3807
|
+
[meeting, tick]
|
|
3808
|
+
);
|
|
3798
3809
|
const canManageStage = meeting.canManageStage();
|
|
3799
3810
|
const stageEnabled = meeting.stageEnabled();
|
|
3800
3811
|
const stageRequests = react.useMemo(
|
|
@@ -3892,6 +3903,16 @@ function StudioRoom({
|
|
|
3892
3903
|
}
|
|
3893
3904
|
}
|
|
3894
3905
|
}, [labels.knock, session.role, waitlisted]);
|
|
3906
|
+
react.useEffect(() => {
|
|
3907
|
+
if (session.role !== "guest" || connectionState !== "connecting") {
|
|
3908
|
+
setConnectTimedOut(false);
|
|
3909
|
+
return;
|
|
3910
|
+
}
|
|
3911
|
+
const id = window.setTimeout(() => {
|
|
3912
|
+
setConnectTimedOut(true);
|
|
3913
|
+
}, STUDIO_CONNECT_WATCHDOG_MS);
|
|
3914
|
+
return () => window.clearTimeout(id);
|
|
3915
|
+
}, [connectionState, session.role]);
|
|
3895
3916
|
react.useEffect(() => {
|
|
3896
3917
|
if (inWaitingRoom || screenNoticePrimed.current) return;
|
|
3897
3918
|
if (!durableScreenBlock) return;
|
|
@@ -4207,7 +4228,20 @@ function StudioRoom({
|
|
|
4207
4228
|
}
|
|
4208
4229
|
) });
|
|
4209
4230
|
}
|
|
4210
|
-
|
|
4231
|
+
const guestConnectFailed = session.role === "guest" && (connectionState === "failed" || connectionState === "connecting" && connectTimedOut);
|
|
4232
|
+
if (guestConnectFailed) {
|
|
4233
|
+
const detail = [meeting.roomState(), lastMeetingError].filter((value) => Boolean(value)).join(" \u2014 ");
|
|
4234
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-dvh flex-col items-center justify-center p-6", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4235
|
+
EmptyState,
|
|
4236
|
+
{
|
|
4237
|
+
title: labels.connectFailed,
|
|
4238
|
+
description: detail || labels.connectFailed,
|
|
4239
|
+
actionLabel: onRejoin ? labels.retry : void 0,
|
|
4240
|
+
onAction: onRejoin
|
|
4241
|
+
}
|
|
4242
|
+
) });
|
|
4243
|
+
}
|
|
4244
|
+
if (session.role === "guest" && (connectionState === "waitlisted" || connectionState === "connecting")) {
|
|
4211
4245
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4212
4246
|
StudioGuestWaiting,
|
|
4213
4247
|
{
|
|
@@ -4217,6 +4251,7 @@ function StudioRoom({
|
|
|
4217
4251
|
muted,
|
|
4218
4252
|
labels,
|
|
4219
4253
|
settingsOpen,
|
|
4254
|
+
statusLabel: connectionState === "connecting" ? labels.connecting : labels.waitingHost,
|
|
4220
4255
|
onToggleCamera,
|
|
4221
4256
|
onToggleMute: () => void onToggleMute(),
|
|
4222
4257
|
onOpenSettings: () => setSettingsOpen(true),
|
|
@@ -4742,8 +4777,8 @@ function StudioGuestLobby({
|
|
|
4742
4777
|
}
|
|
4743
4778
|
}
|
|
4744
4779
|
async function onTestSpeaker() {
|
|
4745
|
-
|
|
4746
|
-
|
|
4780
|
+
if (testingSpeaker) return;
|
|
4781
|
+
const speakerId = effective.speaker || devices.speaker[0]?.deviceId || void 0;
|
|
4747
4782
|
setTestingSpeaker(true);
|
|
4748
4783
|
try {
|
|
4749
4784
|
await playSpeakerTest(speakerId);
|
|
@@ -4811,7 +4846,7 @@ function StudioGuestLobby({
|
|
|
4811
4846
|
type: "button",
|
|
4812
4847
|
size: "sm",
|
|
4813
4848
|
variant: "outline",
|
|
4814
|
-
disabled:
|
|
4849
|
+
disabled: testingSpeaker,
|
|
4815
4850
|
onClick: () => void onTestSpeaker(),
|
|
4816
4851
|
children: deviceLabels.testSpeaker
|
|
4817
4852
|
}
|
|
@@ -4893,7 +4928,11 @@ function isRecorder(participant) {
|
|
|
4893
4928
|
function resolvedName(participant, nameOverrides) {
|
|
4894
4929
|
return nameOverrides.get(participant.id) ?? (participant.userId ? nameOverrides.get(participant.userId) : void 0) ?? participant.name?.trim() ?? "Guest";
|
|
4895
4930
|
}
|
|
4896
|
-
function
|
|
4931
|
+
function resolveTileStageStatus(participant, stageEnabled, fallback) {
|
|
4932
|
+
if (!stageEnabled) return "ON_STAGE";
|
|
4933
|
+
return participant.stageStatus ?? fallback ?? "OFF_STAGE";
|
|
4934
|
+
}
|
|
4935
|
+
function tileFrom(participant, selfId, pinnedIds, chatSend, nameOverrides, stageStatus) {
|
|
4897
4936
|
return {
|
|
4898
4937
|
id: participant.id,
|
|
4899
4938
|
userId: participant.userId ?? "",
|
|
@@ -4903,7 +4942,7 @@ function tileFrom(participant, selfId, pinnedIds, chatSend, nameOverrides) {
|
|
|
4903
4942
|
videoEnabled: participant.videoEnabled === true,
|
|
4904
4943
|
screenShareEnabled: participant.screenShareEnabled === true,
|
|
4905
4944
|
picture: participant.picture ?? null,
|
|
4906
|
-
stageStatus
|
|
4945
|
+
stageStatus,
|
|
4907
4946
|
pinned: participant.isPinned === true || pinnedIds.has(participant.id),
|
|
4908
4947
|
chatCanSend: chatSend.get(participant.id) ?? true
|
|
4909
4948
|
};
|
|
@@ -5049,6 +5088,38 @@ function isStageDisabledError(error) {
|
|
|
5049
5088
|
const message = typeof row.message === "string" ? row.message : "";
|
|
5050
5089
|
return message.includes("ERR2003") || message.toLowerCase().includes("stage is disabled");
|
|
5051
5090
|
}
|
|
5091
|
+
function isStageBusyError(error) {
|
|
5092
|
+
if (!error || typeof error !== "object") return false;
|
|
5093
|
+
const row = error;
|
|
5094
|
+
if (row.name === "UnsupportedConcurrentMethodExecution") return true;
|
|
5095
|
+
if (row.code === "2006" || row.code === 2006) return true;
|
|
5096
|
+
const message = typeof row.message === "string" ? row.message : "";
|
|
5097
|
+
return message.includes("ERR2006") || message.includes("Unsupported concurrent calls") || message.includes("Action not permitted in current stage status");
|
|
5098
|
+
}
|
|
5099
|
+
function isRtkMeetingError(error) {
|
|
5100
|
+
if (!error || typeof error !== "object") return false;
|
|
5101
|
+
const row = error;
|
|
5102
|
+
const name = typeof row.name === "string" ? row.name : "";
|
|
5103
|
+
const message = typeof row.message === "string" ? row.message : "";
|
|
5104
|
+
return name === "ClientError" || name === "TransportConnectionError" || message.includes("ERR1608") || message.includes("No speaker found") || message.includes("Socket is not connected");
|
|
5105
|
+
}
|
|
5106
|
+
function meetingErrorText(error) {
|
|
5107
|
+
if (error instanceof Error && error.message.trim()) return error.message;
|
|
5108
|
+
if (error && typeof error === "object") {
|
|
5109
|
+
const message = error.message;
|
|
5110
|
+
if (typeof message === "string" && message.trim()) return message;
|
|
5111
|
+
}
|
|
5112
|
+
return "Studio failed";
|
|
5113
|
+
}
|
|
5114
|
+
async function settledDeviceList(load) {
|
|
5115
|
+
if (!load) return [];
|
|
5116
|
+
try {
|
|
5117
|
+
const [result] = await Promise.allSettled([load()]);
|
|
5118
|
+
return result?.status === "fulfilled" ? result.value : [];
|
|
5119
|
+
} catch {
|
|
5120
|
+
return [];
|
|
5121
|
+
}
|
|
5122
|
+
}
|
|
5052
5123
|
function waitMs(ms) {
|
|
5053
5124
|
return new Promise((resolve) => {
|
|
5054
5125
|
setTimeout(resolve, ms);
|
|
@@ -5086,8 +5157,9 @@ function studioMeetingFromRtk(raw) {
|
|
|
5086
5157
|
const pendingPanelists = /* @__PURE__ */ new Set();
|
|
5087
5158
|
const chatSend = /* @__PURE__ */ new Map();
|
|
5088
5159
|
const nameOverrides = /* @__PURE__ */ new Map();
|
|
5089
|
-
let
|
|
5160
|
+
let joinStageInFlight = null;
|
|
5090
5161
|
let joinedStageLocally = false;
|
|
5162
|
+
let lastMeetingError = null;
|
|
5091
5163
|
const historyMessages = [];
|
|
5092
5164
|
let speakerSinkId = null;
|
|
5093
5165
|
let lastSpeakerId = typeof meeting.participants?.lastActiveSpeaker === "string" ? meeting.participants.lastActiveSpeaker : null;
|
|
@@ -5183,47 +5255,75 @@ function studioMeetingFromRtk(raw) {
|
|
|
5183
5255
|
return [...byId.values()].sort((a, b) => a.time - b.time);
|
|
5184
5256
|
}
|
|
5185
5257
|
function currentStageStatus() {
|
|
5186
|
-
return meeting.
|
|
5258
|
+
return meeting.self?.stageStatus ?? meeting.stage?.status;
|
|
5259
|
+
}
|
|
5260
|
+
function resolveConnectionState() {
|
|
5261
|
+
const self = meeting.self;
|
|
5262
|
+
const roomState = self?.roomState ?? null;
|
|
5263
|
+
if (roomState === "waitlisted") return "waitlisted";
|
|
5264
|
+
if (roomState === "joined" || self?.roomJoined === true) return "joined";
|
|
5265
|
+
if (lastMeetingError) return "failed";
|
|
5266
|
+
return "connecting";
|
|
5267
|
+
}
|
|
5268
|
+
function recordMeetingError(error) {
|
|
5269
|
+
if (!isRtkMeetingError(error)) return;
|
|
5270
|
+
lastMeetingError = meetingErrorText(error);
|
|
5271
|
+
notifyParticipants();
|
|
5272
|
+
}
|
|
5273
|
+
function onUnhandledRejection(event) {
|
|
5274
|
+
recordMeetingError(event.reason);
|
|
5275
|
+
}
|
|
5276
|
+
if (typeof window !== "undefined") {
|
|
5277
|
+
window.addEventListener("unhandledrejection", onUnhandledRejection);
|
|
5187
5278
|
}
|
|
5188
5279
|
async function republishCameraIfOn() {
|
|
5189
5280
|
if (meeting.self?.videoEnabled !== true) return;
|
|
5190
5281
|
await meeting.self.disableVideo?.();
|
|
5191
5282
|
await meeting.self.enableVideo?.();
|
|
5192
5283
|
}
|
|
5193
|
-
async function
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5284
|
+
async function runJoinStageAttempts() {
|
|
5285
|
+
let lastError;
|
|
5286
|
+
for (let attempt = 0; attempt < STAGE_JOIN_MAX_ATTEMPTS; attempt++) {
|
|
5287
|
+
if (currentStageStatus() === "ON_STAGE") {
|
|
5288
|
+
notifyStage();
|
|
5289
|
+
return;
|
|
5290
|
+
}
|
|
5291
|
+
try {
|
|
5292
|
+
await meeting.stage?.join?.();
|
|
5293
|
+
joinedStageLocally = true;
|
|
5294
|
+
await republishCameraIfOn();
|
|
5295
|
+
notifyStage();
|
|
5296
|
+
return;
|
|
5297
|
+
} catch (error) {
|
|
5298
|
+
lastError = error;
|
|
5299
|
+
if (isStageDisabledError(error)) {
|
|
5199
5300
|
notifyStage();
|
|
5200
5301
|
return;
|
|
5201
5302
|
}
|
|
5202
|
-
|
|
5203
|
-
await meeting.stage?.join?.();
|
|
5204
|
-
joinedStageLocally = true;
|
|
5205
|
-
await republishCameraIfOn();
|
|
5206
|
-
notifyStage();
|
|
5207
|
-
return;
|
|
5208
|
-
} catch (error) {
|
|
5209
|
-
if (isStageDisabledError(error)) {
|
|
5210
|
-
notifyStage();
|
|
5211
|
-
return;
|
|
5212
|
-
}
|
|
5213
|
-
console.warn("studio: stage join failed", error);
|
|
5214
|
-
if (attempt === STAGE_JOIN_MAX_ATTEMPTS - 1) {
|
|
5215
|
-
notifyStage();
|
|
5216
|
-
return;
|
|
5217
|
-
}
|
|
5303
|
+
if (isStageBusyError(error)) {
|
|
5218
5304
|
await waitMs(STAGE_JOIN_RETRY_MS);
|
|
5219
|
-
|
|
5220
|
-
notifyStage();
|
|
5221
|
-
return;
|
|
5222
|
-
}
|
|
5305
|
+
continue;
|
|
5223
5306
|
}
|
|
5307
|
+
console.warn("studio: stage join failed", error);
|
|
5308
|
+
if (attempt === STAGE_JOIN_MAX_ATTEMPTS - 1) break;
|
|
5309
|
+
await waitMs(STAGE_JOIN_RETRY_MS);
|
|
5224
5310
|
}
|
|
5311
|
+
}
|
|
5312
|
+
notifyStage();
|
|
5313
|
+
if (currentStageStatus() !== "ON_STAGE") {
|
|
5314
|
+
throw lastError instanceof Error ? lastError : new Error("stage join failed");
|
|
5315
|
+
}
|
|
5316
|
+
}
|
|
5317
|
+
async function joinStageWithRetry() {
|
|
5318
|
+
if (joinStageInFlight) {
|
|
5319
|
+
await joinStageInFlight;
|
|
5320
|
+
return;
|
|
5321
|
+
}
|
|
5322
|
+
joinStageInFlight = runJoinStageAttempts();
|
|
5323
|
+
try {
|
|
5324
|
+
await joinStageInFlight;
|
|
5225
5325
|
} finally {
|
|
5226
|
-
|
|
5326
|
+
joinStageInFlight = null;
|
|
5227
5327
|
}
|
|
5228
5328
|
}
|
|
5229
5329
|
async function autoJoinStage() {
|
|
@@ -5231,18 +5331,27 @@ function studioMeetingFromRtk(raw) {
|
|
|
5231
5331
|
notifyStage();
|
|
5232
5332
|
return;
|
|
5233
5333
|
}
|
|
5234
|
-
|
|
5334
|
+
try {
|
|
5335
|
+
await joinStageWithRetry();
|
|
5336
|
+
} catch {
|
|
5337
|
+
notifyStage();
|
|
5338
|
+
}
|
|
5235
5339
|
}
|
|
5236
5340
|
async function ensureStageJoined() {
|
|
5237
5341
|
if (currentStageStatus() !== "ACCEPTED_TO_JOIN_STAGE") return;
|
|
5238
|
-
|
|
5342
|
+
try {
|
|
5343
|
+
await joinStageWithRetry();
|
|
5344
|
+
} catch {
|
|
5345
|
+
notifyStage();
|
|
5346
|
+
}
|
|
5239
5347
|
}
|
|
5240
5348
|
async function joinStageIfAllowed() {
|
|
5241
5349
|
if (meeting.self?.permissions?.stageAccess !== "ALLOWED") return;
|
|
5242
5350
|
if (currentStageStatus() === "ON_STAGE") return;
|
|
5243
5351
|
try {
|
|
5244
|
-
await
|
|
5352
|
+
await joinStageWithRetry();
|
|
5245
5353
|
} catch {
|
|
5354
|
+
notifyStage();
|
|
5246
5355
|
}
|
|
5247
5356
|
}
|
|
5248
5357
|
function queuePanelist(guest, fallbackId) {
|
|
@@ -5278,6 +5387,8 @@ function studioMeetingFromRtk(raw) {
|
|
|
5278
5387
|
listen(meeting.self, "audioUpdate", rebindMedia);
|
|
5279
5388
|
listen(meeting.self, "pinned", notifyPinned);
|
|
5280
5389
|
listen(meeting.self, "unpinned", notifyPinned);
|
|
5390
|
+
listen(meeting.self, "waitlisted", notifyParticipants);
|
|
5391
|
+
listen(meeting.self, "roomJoined", notifyParticipants);
|
|
5281
5392
|
const joined = meeting.participants?.joined;
|
|
5282
5393
|
listen(joined, "participantJoined", (payload) => {
|
|
5283
5394
|
void grantIfPending(payload);
|
|
@@ -5345,6 +5456,9 @@ function studioMeetingFromRtk(raw) {
|
|
|
5345
5456
|
await joinStageIfAllowed();
|
|
5346
5457
|
},
|
|
5347
5458
|
async leave() {
|
|
5459
|
+
if (typeof window !== "undefined") {
|
|
5460
|
+
window.removeEventListener("unhandledrejection", onUnhandledRejection);
|
|
5461
|
+
}
|
|
5348
5462
|
await meeting.leave?.();
|
|
5349
5463
|
},
|
|
5350
5464
|
async setAudioEnabled(enabled) {
|
|
@@ -5445,7 +5559,7 @@ function studioMeetingFromRtk(raw) {
|
|
|
5445
5559
|
notifyStage();
|
|
5446
5560
|
},
|
|
5447
5561
|
async joinStage() {
|
|
5448
|
-
if (
|
|
5562
|
+
if (meeting.self?.stageStatus === "ON_STAGE") {
|
|
5449
5563
|
notifyStage();
|
|
5450
5564
|
return;
|
|
5451
5565
|
}
|
|
@@ -5454,25 +5568,15 @@ function studioMeetingFromRtk(raw) {
|
|
|
5454
5568
|
notifyStage();
|
|
5455
5569
|
return;
|
|
5456
5570
|
}
|
|
5457
|
-
|
|
5458
|
-
await meeting.stage?.join?.();
|
|
5459
|
-
} catch {
|
|
5460
|
-
const userId = meeting.self?.userId;
|
|
5461
|
-
if (userId) {
|
|
5462
|
-
await meeting.stage?.grantAccess?.([userId]);
|
|
5463
|
-
}
|
|
5464
|
-
await meeting.stage?.join?.();
|
|
5465
|
-
}
|
|
5466
|
-
joinedStageLocally = true;
|
|
5467
|
-
await republishCameraIfOn();
|
|
5468
|
-
notifyStage();
|
|
5571
|
+
await joinStageWithRetry();
|
|
5469
5572
|
},
|
|
5470
5573
|
async leaveStage() {
|
|
5471
5574
|
await meeting.stage?.leave?.();
|
|
5575
|
+
joinedStageLocally = false;
|
|
5472
5576
|
notifyStage();
|
|
5473
5577
|
},
|
|
5474
5578
|
stageStatus() {
|
|
5475
|
-
return meeting.
|
|
5579
|
+
return meeting.self?.stageStatus ?? meeting.stage?.status ?? "OFF_STAGE";
|
|
5476
5580
|
},
|
|
5477
5581
|
stageRequests() {
|
|
5478
5582
|
if (meeting.self?.permissions?.acceptStageRequests !== true) {
|
|
@@ -5550,10 +5654,27 @@ function studioMeetingFromRtk(raw) {
|
|
|
5550
5654
|
participants() {
|
|
5551
5655
|
const self = meeting.self;
|
|
5552
5656
|
const pinned = pinnedIdSet();
|
|
5553
|
-
const
|
|
5657
|
+
const stageEnabled = self?.permissions?.stageEnabled !== false;
|
|
5658
|
+
const joinedRows = asList(meeting.participants?.joined).filter((row) => row.id !== self?.id && !isRecorder(row)).map(
|
|
5659
|
+
(row) => tileFrom(
|
|
5660
|
+
row,
|
|
5661
|
+
self?.id,
|
|
5662
|
+
pinned,
|
|
5663
|
+
chatSend,
|
|
5664
|
+
nameOverrides,
|
|
5665
|
+
resolveTileStageStatus(row, stageEnabled)
|
|
5666
|
+
)
|
|
5667
|
+
);
|
|
5554
5668
|
if (self?.id && !isRecorder(self)) {
|
|
5555
5669
|
joinedRows.unshift(
|
|
5556
|
-
tileFrom(
|
|
5670
|
+
tileFrom(
|
|
5671
|
+
self,
|
|
5672
|
+
self.id,
|
|
5673
|
+
pinned,
|
|
5674
|
+
chatSend,
|
|
5675
|
+
nameOverrides,
|
|
5676
|
+
resolveTileStageStatus(self, stageEnabled, meeting.stage?.status)
|
|
5677
|
+
)
|
|
5557
5678
|
);
|
|
5558
5679
|
}
|
|
5559
5680
|
return joinedRows;
|
|
@@ -5567,9 +5688,13 @@ function studioMeetingFromRtk(raw) {
|
|
|
5567
5688
|
}));
|
|
5568
5689
|
},
|
|
5569
5690
|
inWaitingRoom() {
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5691
|
+
return meeting.self?.roomState === "waitlisted";
|
|
5692
|
+
},
|
|
5693
|
+
connectionState() {
|
|
5694
|
+
return resolveConnectionState();
|
|
5695
|
+
},
|
|
5696
|
+
lastMeetingError() {
|
|
5697
|
+
return lastMeetingError;
|
|
5573
5698
|
},
|
|
5574
5699
|
moderatorIds() {
|
|
5575
5700
|
const ids = [];
|
|
@@ -5689,9 +5814,9 @@ function studioMeetingFromRtk(raw) {
|
|
|
5689
5814
|
async listDevices() {
|
|
5690
5815
|
const self = meeting.self;
|
|
5691
5816
|
const [audio, video, speaker] = await Promise.all([
|
|
5692
|
-
self?.getAudioDevices?.()
|
|
5693
|
-
self?.getVideoDevices?.()
|
|
5694
|
-
self?.getSpeakerDevices?.()
|
|
5817
|
+
settledDeviceList(self?.getAudioDevices?.bind(self)),
|
|
5818
|
+
settledDeviceList(self?.getVideoDevices?.bind(self)),
|
|
5819
|
+
settledDeviceList(self?.getSpeakerDevices?.bind(self))
|
|
5695
5820
|
]);
|
|
5696
5821
|
return {
|
|
5697
5822
|
audio: asOptions(audio),
|
|
@@ -5704,6 +5829,10 @@ function studioMeetingFromRtk(raw) {
|
|
|
5704
5829
|
if (kind === "speaker") {
|
|
5705
5830
|
speakerSinkId = deviceId;
|
|
5706
5831
|
for (const el of audioEls.values()) applySink(el, speakerSinkId);
|
|
5832
|
+
const speakers = await settledDeviceList(
|
|
5833
|
+
meeting.self?.getSpeakerDevices?.bind(meeting.self)
|
|
5834
|
+
);
|
|
5835
|
+
if (speakers.length === 0) return;
|
|
5707
5836
|
}
|
|
5708
5837
|
await meeting.self?.setDevice?.({
|
|
5709
5838
|
deviceId,
|
|
@@ -6246,6 +6375,9 @@ function studioRoomLabels(t) {
|
|
|
6246
6375
|
preview: t("preview"),
|
|
6247
6376
|
waiting: t("waiting"),
|
|
6248
6377
|
waitingHost: t("waitingHost"),
|
|
6378
|
+
connecting: t("connecting"),
|
|
6379
|
+
connectFailed: t("connectFailed"),
|
|
6380
|
+
retry: t("retry"),
|
|
6249
6381
|
admit: t("admit"),
|
|
6250
6382
|
deny: t("deny"),
|
|
6251
6383
|
denied: t("denied"),
|