@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.d.cts
CHANGED
|
@@ -126,6 +126,7 @@ type StudioDevices = {
|
|
|
126
126
|
type StudioVideoKind = "camera" | "screen";
|
|
127
127
|
type StudioHostMediaKind = "audio" | "video" | "screen";
|
|
128
128
|
type StudioScreenSharePermission = "ALLOWED" | "NOT_ALLOWED" | "CAN_REQUEST";
|
|
129
|
+
type StudioConnectionState = "joined" | "waitlisted" | "connecting" | "failed";
|
|
129
130
|
type StudioBroadcast = {
|
|
130
131
|
type: "host-media";
|
|
131
132
|
kind: StudioHostMediaKind;
|
|
@@ -190,6 +191,8 @@ type StudioMeetingHandle = {
|
|
|
190
191
|
participants: () => StudioParticipantTile[];
|
|
191
192
|
waitlisted: () => StudioWaitlistedGuest[];
|
|
192
193
|
inWaitingRoom: () => boolean;
|
|
194
|
+
connectionState: () => StudioConnectionState;
|
|
195
|
+
lastMeetingError: () => string | null;
|
|
193
196
|
moderatorIds: () => string[];
|
|
194
197
|
localAudioTrack: () => MediaStreamTrack | null;
|
|
195
198
|
localVideoTrack: () => MediaStreamTrack | null;
|
|
@@ -219,6 +222,9 @@ declare function studioRoomLabels(t: (key: string, values?: Record<string, strin
|
|
|
219
222
|
preview: string;
|
|
220
223
|
waiting: string;
|
|
221
224
|
waitingHost: string;
|
|
225
|
+
connecting: string;
|
|
226
|
+
connectFailed: string;
|
|
227
|
+
retry: string;
|
|
222
228
|
admit: string;
|
|
223
229
|
deny: string;
|
|
224
230
|
denied: string;
|
package/dist/studio/index.d.ts
CHANGED
|
@@ -126,6 +126,7 @@ type StudioDevices = {
|
|
|
126
126
|
type StudioVideoKind = "camera" | "screen";
|
|
127
127
|
type StudioHostMediaKind = "audio" | "video" | "screen";
|
|
128
128
|
type StudioScreenSharePermission = "ALLOWED" | "NOT_ALLOWED" | "CAN_REQUEST";
|
|
129
|
+
type StudioConnectionState = "joined" | "waitlisted" | "connecting" | "failed";
|
|
129
130
|
type StudioBroadcast = {
|
|
130
131
|
type: "host-media";
|
|
131
132
|
kind: StudioHostMediaKind;
|
|
@@ -190,6 +191,8 @@ type StudioMeetingHandle = {
|
|
|
190
191
|
participants: () => StudioParticipantTile[];
|
|
191
192
|
waitlisted: () => StudioWaitlistedGuest[];
|
|
192
193
|
inWaitingRoom: () => boolean;
|
|
194
|
+
connectionState: () => StudioConnectionState;
|
|
195
|
+
lastMeetingError: () => string | null;
|
|
193
196
|
moderatorIds: () => string[];
|
|
194
197
|
localAudioTrack: () => MediaStreamTrack | null;
|
|
195
198
|
localVideoTrack: () => MediaStreamTrack | null;
|
|
@@ -219,6 +222,9 @@ declare function studioRoomLabels(t: (key: string, values?: Record<string, strin
|
|
|
219
222
|
preview: string;
|
|
220
223
|
waiting: string;
|
|
221
224
|
waitingHost: string;
|
|
225
|
+
connecting: string;
|
|
226
|
+
connectFailed: string;
|
|
227
|
+
retry: string;
|
|
222
228
|
admit: string;
|
|
223
229
|
deny: string;
|
|
224
230
|
denied: string;
|
package/dist/studio/index.js
CHANGED
|
@@ -1875,6 +1875,7 @@ function StudioGuestWaiting({
|
|
|
1875
1875
|
muted,
|
|
1876
1876
|
labels,
|
|
1877
1877
|
settingsOpen,
|
|
1878
|
+
statusLabel,
|
|
1878
1879
|
onToggleCamera,
|
|
1879
1880
|
onToggleMute,
|
|
1880
1881
|
onOpenSettings,
|
|
@@ -1906,7 +1907,7 @@ function StudioGuestWaiting({
|
|
|
1906
1907
|
reducedMotion
|
|
1907
1908
|
}
|
|
1908
1909
|
),
|
|
1909
|
-
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-center text-sm", role: "status", children: labels.waitingHost }),
|
|
1910
|
+
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-center text-sm", role: "status", children: statusLabel ?? labels.waitingHost }),
|
|
1910
1911
|
/* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs("div", { className: "flex justify-center gap-2", children: [
|
|
1911
1912
|
/* @__PURE__ */ jsx(
|
|
1912
1913
|
ControlIcon,
|
|
@@ -2275,13 +2276,13 @@ function BubbleGroup({ className, ...props }) {
|
|
|
2275
2276
|
"div",
|
|
2276
2277
|
{
|
|
2277
2278
|
"data-slot": "bubble-group",
|
|
2278
|
-
className: cn("flex min-w-
|
|
2279
|
+
className: cn("flex min-w-min flex-col gap-2", className),
|
|
2279
2280
|
...props
|
|
2280
2281
|
}
|
|
2281
2282
|
);
|
|
2282
2283
|
}
|
|
2283
2284
|
var bubbleVariants = cva(
|
|
2284
|
-
"group/bubble relative flex w-fit min-w-
|
|
2285
|
+
"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",
|
|
2285
2286
|
{
|
|
2286
2287
|
variants: {
|
|
2287
2288
|
variant: {
|
|
@@ -2326,7 +2327,7 @@ function BubbleContent({
|
|
|
2326
2327
|
props: mergeProps(
|
|
2327
2328
|
{
|
|
2328
2329
|
className: cn(
|
|
2329
|
-
"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",
|
|
2330
|
+
"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",
|
|
2330
2331
|
className
|
|
2331
2332
|
)
|
|
2332
2333
|
},
|
|
@@ -2510,7 +2511,7 @@ function StudioChatPanel({
|
|
|
2510
2511
|
const bubbles = group.rows.map((row) => /* @__PURE__ */ jsxs(
|
|
2511
2512
|
"div",
|
|
2512
2513
|
{
|
|
2513
|
-
className: group.mine ? "ml-auto flex
|
|
2514
|
+
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",
|
|
2514
2515
|
children: [
|
|
2515
2516
|
/* @__PURE__ */ jsx(
|
|
2516
2517
|
Bubble,
|
|
@@ -3490,6 +3491,7 @@ function StudioToolbar({
|
|
|
3490
3491
|
] }) });
|
|
3491
3492
|
}
|
|
3492
3493
|
var STAGE_GRANT_VERIFY_MS = 4e3;
|
|
3494
|
+
var STUDIO_CONNECT_WATCHDOG_MS = 12e3;
|
|
3493
3495
|
function StudioRoom({
|
|
3494
3496
|
session,
|
|
3495
3497
|
meeting,
|
|
@@ -3525,6 +3527,7 @@ function StudioRoom({
|
|
|
3525
3527
|
);
|
|
3526
3528
|
const [egressError, setEgressError] = useState(null);
|
|
3527
3529
|
const [shareGrant, setShareGrant] = useState("idle");
|
|
3530
|
+
const [connectTimedOut, setConnectTimedOut] = useState(false);
|
|
3528
3531
|
const [screenPermission, setScreenPermission] = useState(
|
|
3529
3532
|
() => meeting.screenSharePermission()
|
|
3530
3533
|
);
|
|
@@ -3771,6 +3774,14 @@ function StudioRoom({
|
|
|
3771
3774
|
[participants]
|
|
3772
3775
|
);
|
|
3773
3776
|
const waitlisted = useMemo(() => meeting.waitlisted(), [meeting, tick]);
|
|
3777
|
+
const connectionState = useMemo(
|
|
3778
|
+
() => meeting.connectionState(),
|
|
3779
|
+
[meeting, tick]
|
|
3780
|
+
);
|
|
3781
|
+
const lastMeetingError = useMemo(
|
|
3782
|
+
() => meeting.lastMeetingError(),
|
|
3783
|
+
[meeting, tick]
|
|
3784
|
+
);
|
|
3774
3785
|
const canManageStage = meeting.canManageStage();
|
|
3775
3786
|
const stageEnabled = meeting.stageEnabled();
|
|
3776
3787
|
const stageRequests = useMemo(
|
|
@@ -3868,6 +3879,16 @@ function StudioRoom({
|
|
|
3868
3879
|
}
|
|
3869
3880
|
}
|
|
3870
3881
|
}, [labels.knock, session.role, waitlisted]);
|
|
3882
|
+
useEffect(() => {
|
|
3883
|
+
if (session.role !== "guest" || connectionState !== "connecting") {
|
|
3884
|
+
setConnectTimedOut(false);
|
|
3885
|
+
return;
|
|
3886
|
+
}
|
|
3887
|
+
const id = window.setTimeout(() => {
|
|
3888
|
+
setConnectTimedOut(true);
|
|
3889
|
+
}, STUDIO_CONNECT_WATCHDOG_MS);
|
|
3890
|
+
return () => window.clearTimeout(id);
|
|
3891
|
+
}, [connectionState, session.role]);
|
|
3871
3892
|
useEffect(() => {
|
|
3872
3893
|
if (inWaitingRoom || screenNoticePrimed.current) return;
|
|
3873
3894
|
if (!durableScreenBlock) return;
|
|
@@ -4183,7 +4204,20 @@ function StudioRoom({
|
|
|
4183
4204
|
}
|
|
4184
4205
|
) });
|
|
4185
4206
|
}
|
|
4186
|
-
|
|
4207
|
+
const guestConnectFailed = session.role === "guest" && (connectionState === "failed" || connectionState === "connecting" && connectTimedOut);
|
|
4208
|
+
if (guestConnectFailed) {
|
|
4209
|
+
const detail = [meeting.roomState(), lastMeetingError].filter((value) => Boolean(value)).join(" \u2014 ");
|
|
4210
|
+
return /* @__PURE__ */ jsx("div", { className: "flex h-dvh flex-col items-center justify-center p-6", children: /* @__PURE__ */ jsx(
|
|
4211
|
+
EmptyState,
|
|
4212
|
+
{
|
|
4213
|
+
title: labels.connectFailed,
|
|
4214
|
+
description: detail || labels.connectFailed,
|
|
4215
|
+
actionLabel: onRejoin ? labels.retry : void 0,
|
|
4216
|
+
onAction: onRejoin
|
|
4217
|
+
}
|
|
4218
|
+
) });
|
|
4219
|
+
}
|
|
4220
|
+
if (session.role === "guest" && (connectionState === "waitlisted" || connectionState === "connecting")) {
|
|
4187
4221
|
return /* @__PURE__ */ jsx(
|
|
4188
4222
|
StudioGuestWaiting,
|
|
4189
4223
|
{
|
|
@@ -4193,6 +4227,7 @@ function StudioRoom({
|
|
|
4193
4227
|
muted,
|
|
4194
4228
|
labels,
|
|
4195
4229
|
settingsOpen,
|
|
4230
|
+
statusLabel: connectionState === "connecting" ? labels.connecting : labels.waitingHost,
|
|
4196
4231
|
onToggleCamera,
|
|
4197
4232
|
onToggleMute: () => void onToggleMute(),
|
|
4198
4233
|
onOpenSettings: () => setSettingsOpen(true),
|
|
@@ -4718,8 +4753,8 @@ function StudioGuestLobby({
|
|
|
4718
4753
|
}
|
|
4719
4754
|
}
|
|
4720
4755
|
async function onTestSpeaker() {
|
|
4721
|
-
|
|
4722
|
-
|
|
4756
|
+
if (testingSpeaker) return;
|
|
4757
|
+
const speakerId = effective.speaker || devices.speaker[0]?.deviceId || void 0;
|
|
4723
4758
|
setTestingSpeaker(true);
|
|
4724
4759
|
try {
|
|
4725
4760
|
await playSpeakerTest(speakerId);
|
|
@@ -4787,7 +4822,7 @@ function StudioGuestLobby({
|
|
|
4787
4822
|
type: "button",
|
|
4788
4823
|
size: "sm",
|
|
4789
4824
|
variant: "outline",
|
|
4790
|
-
disabled:
|
|
4825
|
+
disabled: testingSpeaker,
|
|
4791
4826
|
onClick: () => void onTestSpeaker(),
|
|
4792
4827
|
children: deviceLabels.testSpeaker
|
|
4793
4828
|
}
|
|
@@ -4869,7 +4904,11 @@ function isRecorder(participant) {
|
|
|
4869
4904
|
function resolvedName(participant, nameOverrides) {
|
|
4870
4905
|
return nameOverrides.get(participant.id) ?? (participant.userId ? nameOverrides.get(participant.userId) : void 0) ?? participant.name?.trim() ?? "Guest";
|
|
4871
4906
|
}
|
|
4872
|
-
function
|
|
4907
|
+
function resolveTileStageStatus(participant, stageEnabled, fallback) {
|
|
4908
|
+
if (!stageEnabled) return "ON_STAGE";
|
|
4909
|
+
return participant.stageStatus ?? fallback ?? "OFF_STAGE";
|
|
4910
|
+
}
|
|
4911
|
+
function tileFrom(participant, selfId, pinnedIds, chatSend, nameOverrides, stageStatus) {
|
|
4873
4912
|
return {
|
|
4874
4913
|
id: participant.id,
|
|
4875
4914
|
userId: participant.userId ?? "",
|
|
@@ -4879,7 +4918,7 @@ function tileFrom(participant, selfId, pinnedIds, chatSend, nameOverrides) {
|
|
|
4879
4918
|
videoEnabled: participant.videoEnabled === true,
|
|
4880
4919
|
screenShareEnabled: participant.screenShareEnabled === true,
|
|
4881
4920
|
picture: participant.picture ?? null,
|
|
4882
|
-
stageStatus
|
|
4921
|
+
stageStatus,
|
|
4883
4922
|
pinned: participant.isPinned === true || pinnedIds.has(participant.id),
|
|
4884
4923
|
chatCanSend: chatSend.get(participant.id) ?? true
|
|
4885
4924
|
};
|
|
@@ -5025,6 +5064,38 @@ function isStageDisabledError(error) {
|
|
|
5025
5064
|
const message = typeof row.message === "string" ? row.message : "";
|
|
5026
5065
|
return message.includes("ERR2003") || message.toLowerCase().includes("stage is disabled");
|
|
5027
5066
|
}
|
|
5067
|
+
function isStageBusyError(error) {
|
|
5068
|
+
if (!error || typeof error !== "object") return false;
|
|
5069
|
+
const row = error;
|
|
5070
|
+
if (row.name === "UnsupportedConcurrentMethodExecution") return true;
|
|
5071
|
+
if (row.code === "2006" || row.code === 2006) return true;
|
|
5072
|
+
const message = typeof row.message === "string" ? row.message : "";
|
|
5073
|
+
return message.includes("ERR2006") || message.includes("Unsupported concurrent calls") || message.includes("Action not permitted in current stage status");
|
|
5074
|
+
}
|
|
5075
|
+
function isRtkMeetingError(error) {
|
|
5076
|
+
if (!error || typeof error !== "object") return false;
|
|
5077
|
+
const row = error;
|
|
5078
|
+
const name = typeof row.name === "string" ? row.name : "";
|
|
5079
|
+
const message = typeof row.message === "string" ? row.message : "";
|
|
5080
|
+
return name === "ClientError" || name === "TransportConnectionError" || message.includes("ERR1608") || message.includes("No speaker found") || message.includes("Socket is not connected");
|
|
5081
|
+
}
|
|
5082
|
+
function meetingErrorText(error) {
|
|
5083
|
+
if (error instanceof Error && error.message.trim()) return error.message;
|
|
5084
|
+
if (error && typeof error === "object") {
|
|
5085
|
+
const message = error.message;
|
|
5086
|
+
if (typeof message === "string" && message.trim()) return message;
|
|
5087
|
+
}
|
|
5088
|
+
return "Studio failed";
|
|
5089
|
+
}
|
|
5090
|
+
async function settledDeviceList(load) {
|
|
5091
|
+
if (!load) return [];
|
|
5092
|
+
try {
|
|
5093
|
+
const [result] = await Promise.allSettled([load()]);
|
|
5094
|
+
return result?.status === "fulfilled" ? result.value : [];
|
|
5095
|
+
} catch {
|
|
5096
|
+
return [];
|
|
5097
|
+
}
|
|
5098
|
+
}
|
|
5028
5099
|
function waitMs(ms) {
|
|
5029
5100
|
return new Promise((resolve) => {
|
|
5030
5101
|
setTimeout(resolve, ms);
|
|
@@ -5062,8 +5133,9 @@ function studioMeetingFromRtk(raw) {
|
|
|
5062
5133
|
const pendingPanelists = /* @__PURE__ */ new Set();
|
|
5063
5134
|
const chatSend = /* @__PURE__ */ new Map();
|
|
5064
5135
|
const nameOverrides = /* @__PURE__ */ new Map();
|
|
5065
|
-
let
|
|
5136
|
+
let joinStageInFlight = null;
|
|
5066
5137
|
let joinedStageLocally = false;
|
|
5138
|
+
let lastMeetingError = null;
|
|
5067
5139
|
const historyMessages = [];
|
|
5068
5140
|
let speakerSinkId = null;
|
|
5069
5141
|
let lastSpeakerId = typeof meeting.participants?.lastActiveSpeaker === "string" ? meeting.participants.lastActiveSpeaker : null;
|
|
@@ -5159,47 +5231,75 @@ function studioMeetingFromRtk(raw) {
|
|
|
5159
5231
|
return [...byId.values()].sort((a, b) => a.time - b.time);
|
|
5160
5232
|
}
|
|
5161
5233
|
function currentStageStatus() {
|
|
5162
|
-
return meeting.
|
|
5234
|
+
return meeting.self?.stageStatus ?? meeting.stage?.status;
|
|
5235
|
+
}
|
|
5236
|
+
function resolveConnectionState() {
|
|
5237
|
+
const self = meeting.self;
|
|
5238
|
+
const roomState = self?.roomState ?? null;
|
|
5239
|
+
if (roomState === "waitlisted") return "waitlisted";
|
|
5240
|
+
if (roomState === "joined" || self?.roomJoined === true) return "joined";
|
|
5241
|
+
if (lastMeetingError) return "failed";
|
|
5242
|
+
return "connecting";
|
|
5243
|
+
}
|
|
5244
|
+
function recordMeetingError(error) {
|
|
5245
|
+
if (!isRtkMeetingError(error)) return;
|
|
5246
|
+
lastMeetingError = meetingErrorText(error);
|
|
5247
|
+
notifyParticipants();
|
|
5248
|
+
}
|
|
5249
|
+
function onUnhandledRejection(event) {
|
|
5250
|
+
recordMeetingError(event.reason);
|
|
5251
|
+
}
|
|
5252
|
+
if (typeof window !== "undefined") {
|
|
5253
|
+
window.addEventListener("unhandledrejection", onUnhandledRejection);
|
|
5163
5254
|
}
|
|
5164
5255
|
async function republishCameraIfOn() {
|
|
5165
5256
|
if (meeting.self?.videoEnabled !== true) return;
|
|
5166
5257
|
await meeting.self.disableVideo?.();
|
|
5167
5258
|
await meeting.self.enableVideo?.();
|
|
5168
5259
|
}
|
|
5169
|
-
async function
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5260
|
+
async function runJoinStageAttempts() {
|
|
5261
|
+
let lastError;
|
|
5262
|
+
for (let attempt = 0; attempt < STAGE_JOIN_MAX_ATTEMPTS; attempt++) {
|
|
5263
|
+
if (currentStageStatus() === "ON_STAGE") {
|
|
5264
|
+
notifyStage();
|
|
5265
|
+
return;
|
|
5266
|
+
}
|
|
5267
|
+
try {
|
|
5268
|
+
await meeting.stage?.join?.();
|
|
5269
|
+
joinedStageLocally = true;
|
|
5270
|
+
await republishCameraIfOn();
|
|
5271
|
+
notifyStage();
|
|
5272
|
+
return;
|
|
5273
|
+
} catch (error) {
|
|
5274
|
+
lastError = error;
|
|
5275
|
+
if (isStageDisabledError(error)) {
|
|
5175
5276
|
notifyStage();
|
|
5176
5277
|
return;
|
|
5177
5278
|
}
|
|
5178
|
-
|
|
5179
|
-
await meeting.stage?.join?.();
|
|
5180
|
-
joinedStageLocally = true;
|
|
5181
|
-
await republishCameraIfOn();
|
|
5182
|
-
notifyStage();
|
|
5183
|
-
return;
|
|
5184
|
-
} catch (error) {
|
|
5185
|
-
if (isStageDisabledError(error)) {
|
|
5186
|
-
notifyStage();
|
|
5187
|
-
return;
|
|
5188
|
-
}
|
|
5189
|
-
console.warn("studio: stage join failed", error);
|
|
5190
|
-
if (attempt === STAGE_JOIN_MAX_ATTEMPTS - 1) {
|
|
5191
|
-
notifyStage();
|
|
5192
|
-
return;
|
|
5193
|
-
}
|
|
5279
|
+
if (isStageBusyError(error)) {
|
|
5194
5280
|
await waitMs(STAGE_JOIN_RETRY_MS);
|
|
5195
|
-
|
|
5196
|
-
notifyStage();
|
|
5197
|
-
return;
|
|
5198
|
-
}
|
|
5281
|
+
continue;
|
|
5199
5282
|
}
|
|
5283
|
+
console.warn("studio: stage join failed", error);
|
|
5284
|
+
if (attempt === STAGE_JOIN_MAX_ATTEMPTS - 1) break;
|
|
5285
|
+
await waitMs(STAGE_JOIN_RETRY_MS);
|
|
5200
5286
|
}
|
|
5287
|
+
}
|
|
5288
|
+
notifyStage();
|
|
5289
|
+
if (currentStageStatus() !== "ON_STAGE") {
|
|
5290
|
+
throw lastError instanceof Error ? lastError : new Error("stage join failed");
|
|
5291
|
+
}
|
|
5292
|
+
}
|
|
5293
|
+
async function joinStageWithRetry() {
|
|
5294
|
+
if (joinStageInFlight) {
|
|
5295
|
+
await joinStageInFlight;
|
|
5296
|
+
return;
|
|
5297
|
+
}
|
|
5298
|
+
joinStageInFlight = runJoinStageAttempts();
|
|
5299
|
+
try {
|
|
5300
|
+
await joinStageInFlight;
|
|
5201
5301
|
} finally {
|
|
5202
|
-
|
|
5302
|
+
joinStageInFlight = null;
|
|
5203
5303
|
}
|
|
5204
5304
|
}
|
|
5205
5305
|
async function autoJoinStage() {
|
|
@@ -5207,18 +5307,27 @@ function studioMeetingFromRtk(raw) {
|
|
|
5207
5307
|
notifyStage();
|
|
5208
5308
|
return;
|
|
5209
5309
|
}
|
|
5210
|
-
|
|
5310
|
+
try {
|
|
5311
|
+
await joinStageWithRetry();
|
|
5312
|
+
} catch {
|
|
5313
|
+
notifyStage();
|
|
5314
|
+
}
|
|
5211
5315
|
}
|
|
5212
5316
|
async function ensureStageJoined() {
|
|
5213
5317
|
if (currentStageStatus() !== "ACCEPTED_TO_JOIN_STAGE") return;
|
|
5214
|
-
|
|
5318
|
+
try {
|
|
5319
|
+
await joinStageWithRetry();
|
|
5320
|
+
} catch {
|
|
5321
|
+
notifyStage();
|
|
5322
|
+
}
|
|
5215
5323
|
}
|
|
5216
5324
|
async function joinStageIfAllowed() {
|
|
5217
5325
|
if (meeting.self?.permissions?.stageAccess !== "ALLOWED") return;
|
|
5218
5326
|
if (currentStageStatus() === "ON_STAGE") return;
|
|
5219
5327
|
try {
|
|
5220
|
-
await
|
|
5328
|
+
await joinStageWithRetry();
|
|
5221
5329
|
} catch {
|
|
5330
|
+
notifyStage();
|
|
5222
5331
|
}
|
|
5223
5332
|
}
|
|
5224
5333
|
function queuePanelist(guest, fallbackId) {
|
|
@@ -5254,6 +5363,8 @@ function studioMeetingFromRtk(raw) {
|
|
|
5254
5363
|
listen(meeting.self, "audioUpdate", rebindMedia);
|
|
5255
5364
|
listen(meeting.self, "pinned", notifyPinned);
|
|
5256
5365
|
listen(meeting.self, "unpinned", notifyPinned);
|
|
5366
|
+
listen(meeting.self, "waitlisted", notifyParticipants);
|
|
5367
|
+
listen(meeting.self, "roomJoined", notifyParticipants);
|
|
5257
5368
|
const joined = meeting.participants?.joined;
|
|
5258
5369
|
listen(joined, "participantJoined", (payload) => {
|
|
5259
5370
|
void grantIfPending(payload);
|
|
@@ -5321,6 +5432,9 @@ function studioMeetingFromRtk(raw) {
|
|
|
5321
5432
|
await joinStageIfAllowed();
|
|
5322
5433
|
},
|
|
5323
5434
|
async leave() {
|
|
5435
|
+
if (typeof window !== "undefined") {
|
|
5436
|
+
window.removeEventListener("unhandledrejection", onUnhandledRejection);
|
|
5437
|
+
}
|
|
5324
5438
|
await meeting.leave?.();
|
|
5325
5439
|
},
|
|
5326
5440
|
async setAudioEnabled(enabled) {
|
|
@@ -5421,7 +5535,7 @@ function studioMeetingFromRtk(raw) {
|
|
|
5421
5535
|
notifyStage();
|
|
5422
5536
|
},
|
|
5423
5537
|
async joinStage() {
|
|
5424
|
-
if (
|
|
5538
|
+
if (meeting.self?.stageStatus === "ON_STAGE") {
|
|
5425
5539
|
notifyStage();
|
|
5426
5540
|
return;
|
|
5427
5541
|
}
|
|
@@ -5430,25 +5544,15 @@ function studioMeetingFromRtk(raw) {
|
|
|
5430
5544
|
notifyStage();
|
|
5431
5545
|
return;
|
|
5432
5546
|
}
|
|
5433
|
-
|
|
5434
|
-
await meeting.stage?.join?.();
|
|
5435
|
-
} catch {
|
|
5436
|
-
const userId = meeting.self?.userId;
|
|
5437
|
-
if (userId) {
|
|
5438
|
-
await meeting.stage?.grantAccess?.([userId]);
|
|
5439
|
-
}
|
|
5440
|
-
await meeting.stage?.join?.();
|
|
5441
|
-
}
|
|
5442
|
-
joinedStageLocally = true;
|
|
5443
|
-
await republishCameraIfOn();
|
|
5444
|
-
notifyStage();
|
|
5547
|
+
await joinStageWithRetry();
|
|
5445
5548
|
},
|
|
5446
5549
|
async leaveStage() {
|
|
5447
5550
|
await meeting.stage?.leave?.();
|
|
5551
|
+
joinedStageLocally = false;
|
|
5448
5552
|
notifyStage();
|
|
5449
5553
|
},
|
|
5450
5554
|
stageStatus() {
|
|
5451
|
-
return meeting.
|
|
5555
|
+
return meeting.self?.stageStatus ?? meeting.stage?.status ?? "OFF_STAGE";
|
|
5452
5556
|
},
|
|
5453
5557
|
stageRequests() {
|
|
5454
5558
|
if (meeting.self?.permissions?.acceptStageRequests !== true) {
|
|
@@ -5526,10 +5630,27 @@ function studioMeetingFromRtk(raw) {
|
|
|
5526
5630
|
participants() {
|
|
5527
5631
|
const self = meeting.self;
|
|
5528
5632
|
const pinned = pinnedIdSet();
|
|
5529
|
-
const
|
|
5633
|
+
const stageEnabled = self?.permissions?.stageEnabled !== false;
|
|
5634
|
+
const joinedRows = asList(meeting.participants?.joined).filter((row) => row.id !== self?.id && !isRecorder(row)).map(
|
|
5635
|
+
(row) => tileFrom(
|
|
5636
|
+
row,
|
|
5637
|
+
self?.id,
|
|
5638
|
+
pinned,
|
|
5639
|
+
chatSend,
|
|
5640
|
+
nameOverrides,
|
|
5641
|
+
resolveTileStageStatus(row, stageEnabled)
|
|
5642
|
+
)
|
|
5643
|
+
);
|
|
5530
5644
|
if (self?.id && !isRecorder(self)) {
|
|
5531
5645
|
joinedRows.unshift(
|
|
5532
|
-
tileFrom(
|
|
5646
|
+
tileFrom(
|
|
5647
|
+
self,
|
|
5648
|
+
self.id,
|
|
5649
|
+
pinned,
|
|
5650
|
+
chatSend,
|
|
5651
|
+
nameOverrides,
|
|
5652
|
+
resolveTileStageStatus(self, stageEnabled, meeting.stage?.status)
|
|
5653
|
+
)
|
|
5533
5654
|
);
|
|
5534
5655
|
}
|
|
5535
5656
|
return joinedRows;
|
|
@@ -5543,9 +5664,13 @@ function studioMeetingFromRtk(raw) {
|
|
|
5543
5664
|
}));
|
|
5544
5665
|
},
|
|
5545
5666
|
inWaitingRoom() {
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5667
|
+
return meeting.self?.roomState === "waitlisted";
|
|
5668
|
+
},
|
|
5669
|
+
connectionState() {
|
|
5670
|
+
return resolveConnectionState();
|
|
5671
|
+
},
|
|
5672
|
+
lastMeetingError() {
|
|
5673
|
+
return lastMeetingError;
|
|
5549
5674
|
},
|
|
5550
5675
|
moderatorIds() {
|
|
5551
5676
|
const ids = [];
|
|
@@ -5665,9 +5790,9 @@ function studioMeetingFromRtk(raw) {
|
|
|
5665
5790
|
async listDevices() {
|
|
5666
5791
|
const self = meeting.self;
|
|
5667
5792
|
const [audio, video, speaker] = await Promise.all([
|
|
5668
|
-
self?.getAudioDevices?.()
|
|
5669
|
-
self?.getVideoDevices?.()
|
|
5670
|
-
self?.getSpeakerDevices?.()
|
|
5793
|
+
settledDeviceList(self?.getAudioDevices?.bind(self)),
|
|
5794
|
+
settledDeviceList(self?.getVideoDevices?.bind(self)),
|
|
5795
|
+
settledDeviceList(self?.getSpeakerDevices?.bind(self))
|
|
5671
5796
|
]);
|
|
5672
5797
|
return {
|
|
5673
5798
|
audio: asOptions(audio),
|
|
@@ -5680,6 +5805,10 @@ function studioMeetingFromRtk(raw) {
|
|
|
5680
5805
|
if (kind === "speaker") {
|
|
5681
5806
|
speakerSinkId = deviceId;
|
|
5682
5807
|
for (const el of audioEls.values()) applySink(el, speakerSinkId);
|
|
5808
|
+
const speakers = await settledDeviceList(
|
|
5809
|
+
meeting.self?.getSpeakerDevices?.bind(meeting.self)
|
|
5810
|
+
);
|
|
5811
|
+
if (speakers.length === 0) return;
|
|
5683
5812
|
}
|
|
5684
5813
|
await meeting.self?.setDevice?.({
|
|
5685
5814
|
deviceId,
|
|
@@ -6222,6 +6351,9 @@ function studioRoomLabels(t) {
|
|
|
6222
6351
|
preview: t("preview"),
|
|
6223
6352
|
waiting: t("waiting"),
|
|
6224
6353
|
waitingHost: t("waitingHost"),
|
|
6354
|
+
connecting: t("connecting"),
|
|
6355
|
+
connectFailed: t("connectFailed"),
|
|
6356
|
+
retry: t("retry"),
|
|
6225
6357
|
admit: t("admit"),
|
|
6226
6358
|
deny: t("deny"),
|
|
6227
6359
|
denied: t("denied"),
|