@livo-tv/sdk 1.4.0 → 1.5.0
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 +213 -113
- package/dist/studio/index.cjs.map +1 -1
- package/dist/studio/index.d.cts +9 -3
- package/dist/studio/index.d.ts +9 -3
- package/dist/studio/index.js +213 -113
- package/dist/studio/index.js.map +1 -1
- package/dist/styles.css +5 -0
- package/package.json +2 -2
package/dist/studio/index.cjs
CHANGED
|
@@ -542,52 +542,38 @@ function canTakeOffAir(tiles, participantId, onAir) {
|
|
|
542
542
|
(tile) => tile.id !== participantId && isOnStage(tile.stageStatus)
|
|
543
543
|
);
|
|
544
544
|
}
|
|
545
|
+
function pipSpotlightFrom(tile, kind) {
|
|
546
|
+
return {
|
|
547
|
+
participantId: tile.id,
|
|
548
|
+
name: tile.name,
|
|
549
|
+
kind,
|
|
550
|
+
picture: tile.picture
|
|
551
|
+
};
|
|
552
|
+
}
|
|
545
553
|
function resolveStudioPipSpotlight(tiles, activeSpeakerId) {
|
|
546
554
|
const visible = tiles.filter((tile) => isOnStage(tile.stageStatus));
|
|
547
555
|
const pinned = visible.find((tile) => tile.pinned === true);
|
|
548
556
|
if (pinned) {
|
|
549
|
-
return
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
557
|
+
return pipSpotlightFrom(
|
|
558
|
+
pinned,
|
|
559
|
+
pinned.screenShareEnabled ? "screen" : pinned.videoEnabled ? "camera" : "idle"
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
const speaker = activeSpeakerId ? visible.find((tile) => tile.id === activeSpeakerId) : void 0;
|
|
563
|
+
if (speaker) {
|
|
564
|
+
return pipSpotlightFrom(speaker, speaker.videoEnabled ? "camera" : "idle");
|
|
555
565
|
}
|
|
556
566
|
const sharing = visible.find((tile) => tile.screenShareEnabled);
|
|
557
567
|
if (sharing) {
|
|
558
|
-
return
|
|
559
|
-
participantId: sharing.id,
|
|
560
|
-
name: sharing.name,
|
|
561
|
-
kind: "screen",
|
|
562
|
-
picture: sharing.picture
|
|
563
|
-
};
|
|
564
|
-
}
|
|
565
|
-
const speaker = activeSpeakerId ? visible.find((tile) => tile.id === activeSpeakerId && tile.videoEnabled) : void 0;
|
|
566
|
-
if (speaker) {
|
|
567
|
-
return {
|
|
568
|
-
participantId: speaker.id,
|
|
569
|
-
name: speaker.name,
|
|
570
|
-
kind: "camera",
|
|
571
|
-
picture: speaker.picture
|
|
572
|
-
};
|
|
568
|
+
return pipSpotlightFrom(sharing, "screen");
|
|
573
569
|
}
|
|
574
570
|
const remoteCam = visible.find((tile) => !tile.isSelf && tile.videoEnabled);
|
|
575
571
|
if (remoteCam) {
|
|
576
|
-
return
|
|
577
|
-
participantId: remoteCam.id,
|
|
578
|
-
name: remoteCam.name,
|
|
579
|
-
kind: "camera",
|
|
580
|
-
picture: remoteCam.picture
|
|
581
|
-
};
|
|
572
|
+
return pipSpotlightFrom(remoteCam, "camera");
|
|
582
573
|
}
|
|
583
574
|
const self = visible.find((tile) => tile.isSelf);
|
|
584
575
|
if (!self) return null;
|
|
585
|
-
return
|
|
586
|
-
participantId: self.id,
|
|
587
|
-
name: self.name,
|
|
588
|
-
kind: self.videoEnabled ? "camera" : "idle",
|
|
589
|
-
picture: self.picture
|
|
590
|
-
};
|
|
576
|
+
return pipSpotlightFrom(self, self.videoEnabled ? "camera" : "idle");
|
|
591
577
|
}
|
|
592
578
|
function resolveStudioPipSelfView(tiles, spotlight) {
|
|
593
579
|
const self = tiles.find((tile) => tile.isSelf);
|
|
@@ -706,6 +692,9 @@ function screenShareBlock(captureSupported, permission) {
|
|
|
706
692
|
if (permission === "NOT_ALLOWED") return "permission";
|
|
707
693
|
return null;
|
|
708
694
|
}
|
|
695
|
+
function showScreenShareControl(block) {
|
|
696
|
+
return block !== "permission";
|
|
697
|
+
}
|
|
709
698
|
|
|
710
699
|
// src/studio/lib/parse.ts
|
|
711
700
|
function isRecord(value) {
|
|
@@ -1488,6 +1477,19 @@ function DialogHeader({ className, ...props }) {
|
|
|
1488
1477
|
}
|
|
1489
1478
|
);
|
|
1490
1479
|
}
|
|
1480
|
+
function DialogFooter({ className, ...props }) {
|
|
1481
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1482
|
+
"div",
|
|
1483
|
+
{
|
|
1484
|
+
"data-slot": "dialog-footer",
|
|
1485
|
+
className: cn(
|
|
1486
|
+
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
|
1487
|
+
className
|
|
1488
|
+
),
|
|
1489
|
+
...props
|
|
1490
|
+
}
|
|
1491
|
+
);
|
|
1492
|
+
}
|
|
1491
1493
|
function DialogTitle({
|
|
1492
1494
|
className,
|
|
1493
1495
|
...props
|
|
@@ -2047,6 +2049,19 @@ var badgeVariants = classVarianceAuthority.cva(
|
|
|
2047
2049
|
function Badge({ className, variant, ...props }) {
|
|
2048
2050
|
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(badgeVariants({ variant }), className), ...props });
|
|
2049
2051
|
}
|
|
2052
|
+
|
|
2053
|
+
// src/studio/lib/studio-video-style.ts
|
|
2054
|
+
var STUDIO_VIDEO_MEDIA_STYLE = `
|
|
2055
|
+
.livo-studio-tile-video::-webkit-media-controls-start-playback-button,
|
|
2056
|
+
.livo-studio-inline-video::-webkit-media-controls-start-playback-button {
|
|
2057
|
+
display: none !important;
|
|
2058
|
+
-webkit-appearance: none;
|
|
2059
|
+
}
|
|
2060
|
+
.livo-studio-tile-video::-webkit-media-controls,
|
|
2061
|
+
.livo-studio-inline-video::-webkit-media-controls {
|
|
2062
|
+
display: none !important;
|
|
2063
|
+
}
|
|
2064
|
+
`;
|
|
2050
2065
|
function StudioPipPanel({
|
|
2051
2066
|
meeting,
|
|
2052
2067
|
labels,
|
|
@@ -2101,7 +2116,7 @@ function StudioPipPanel({
|
|
|
2101
2116
|
/* @__PURE__ */ jsxRuntime.jsx(StudioLiveBadge, { children: labels.live }),
|
|
2102
2117
|
elapsed ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground tabular-nums text-xs", children: elapsed }) : null
|
|
2103
2118
|
] }) : null,
|
|
2104
|
-
selfView ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2119
|
+
selfView && meeting.localVideoTrack() ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2105
2120
|
"div",
|
|
2106
2121
|
{
|
|
2107
2122
|
"data-testid": "pip-self-view",
|
|
@@ -2158,7 +2173,8 @@ function PipSpotlight({
|
|
|
2158
2173
|
spotlight,
|
|
2159
2174
|
cameraOffLabel
|
|
2160
2175
|
}) {
|
|
2161
|
-
|
|
2176
|
+
const track = spotlight && spotlight.kind !== "idle" ? spotlight.kind === "screen" ? meeting.participantScreenTrack(spotlight.participantId) : meeting.participantVideoTrack(spotlight.participantId) : null;
|
|
2177
|
+
if (!spotlight || spotlight.kind === "idle" || !track) {
|
|
2162
2178
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute inset-0 flex flex-col items-center justify-center gap-2", children: [
|
|
2163
2179
|
spotlight?.picture ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2164
2180
|
"img",
|
|
@@ -2171,7 +2187,6 @@ function PipSpotlight({
|
|
|
2171
2187
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-xs", children: spotlight?.name ?? cameraOffLabel })
|
|
2172
2188
|
] });
|
|
2173
2189
|
}
|
|
2174
|
-
const track = spotlight.kind === "screen" ? meeting.participantScreenTrack(spotlight.participantId) : meeting.participantVideoTrack(spotlight.participantId);
|
|
2175
2190
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2176
2191
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2177
2192
|
PipTrackVideo,
|
|
@@ -2190,11 +2205,7 @@ function PipTrackVideo({
|
|
|
2190
2205
|
const ref = react.useRef(null);
|
|
2191
2206
|
react.useEffect(() => {
|
|
2192
2207
|
const el = ref.current;
|
|
2193
|
-
if (!el) return;
|
|
2194
|
-
if (!track) {
|
|
2195
|
-
el.srcObject = null;
|
|
2196
|
-
return;
|
|
2197
|
-
}
|
|
2208
|
+
if (!el || !track) return;
|
|
2198
2209
|
el.setAttribute("webkit-playsinline", "");
|
|
2199
2210
|
el.srcObject = new MediaStream([track]);
|
|
2200
2211
|
void el.play().catch(() => void 0);
|
|
@@ -2202,19 +2213,24 @@ function PipTrackVideo({
|
|
|
2202
2213
|
el.srcObject = null;
|
|
2203
2214
|
};
|
|
2204
2215
|
}, [track]);
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
{
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2216
|
+
if (!track) return null;
|
|
2217
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2218
|
+
/* @__PURE__ */ jsxRuntime.jsx("style", { children: STUDIO_VIDEO_MEDIA_STYLE }),
|
|
2219
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2220
|
+
"video",
|
|
2221
|
+
{
|
|
2222
|
+
ref,
|
|
2223
|
+
autoPlay: true,
|
|
2224
|
+
playsInline: true,
|
|
2225
|
+
muted: true,
|
|
2226
|
+
disableRemotePlayback: true,
|
|
2227
|
+
className: cn(
|
|
2228
|
+
"livo-studio-inline-video absolute inset-0 size-full",
|
|
2229
|
+
fit === "contain" ? "object-contain" : "object-cover"
|
|
2230
|
+
)
|
|
2231
|
+
}
|
|
2232
|
+
)
|
|
2233
|
+
] });
|
|
2218
2234
|
}
|
|
2219
2235
|
function pipScreenControls({
|
|
2220
2236
|
labels,
|
|
@@ -3241,6 +3257,7 @@ function StudioTile({
|
|
|
3241
3257
|
const onStage = isOnStage(tile.stageStatus);
|
|
3242
3258
|
const [confirm, setConfirm] = react.useState(null);
|
|
3243
3259
|
const showMenu = (canModerate || canManageStage) && !tile.isSelf;
|
|
3260
|
+
const hasVideoTrack = tileHasVideoTrack(meeting, tile);
|
|
3244
3261
|
const glowRef = useSpeakingGlow({
|
|
3245
3262
|
track: tile.kind === "screen" || !tile.audioEnabled ? null : meeting.participantAudioTrack(tile.id),
|
|
3246
3263
|
enabled: onStage,
|
|
@@ -3256,23 +3273,14 @@ function StudioTile({
|
|
|
3256
3273
|
compact ? "aspect-video w-40 shrink-0 md:w-full" : tile.kind === "screen" ? "min-h-0 flex-1 md:min-h-48" : fill ? "size-full min-h-0 md:aspect-video md:h-auto" : "aspect-video"
|
|
3257
3274
|
),
|
|
3258
3275
|
children: [
|
|
3259
|
-
tile.kind === "idle" ? /* @__PURE__ */ jsxRuntime.
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-background flex size-16 items-center justify-center rounded-full text-xl", children: tile.name.slice(0, 1).toUpperCase() }),
|
|
3268
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3269
|
-
AudioWaveform,
|
|
3270
|
-
{
|
|
3271
|
-
track: tile.isSelf ? meeting.localAudioTrack() : null,
|
|
3272
|
-
reducedMotion
|
|
3273
|
-
}
|
|
3274
|
-
)
|
|
3275
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
3276
|
+
tile.kind === "idle" || !hasVideoTrack ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3277
|
+
IdlePersonTile,
|
|
3278
|
+
{
|
|
3279
|
+
tile,
|
|
3280
|
+
meeting,
|
|
3281
|
+
reducedMotion
|
|
3282
|
+
}
|
|
3283
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
3276
3284
|
StudioTileVideo,
|
|
3277
3285
|
{
|
|
3278
3286
|
meeting,
|
|
@@ -3371,6 +3379,37 @@ function TileStatusIcons({
|
|
|
3371
3379
|
tile.chatCanSend === false ? /* @__PURE__ */ jsxRuntime.jsx(StatusIcon, { label: labels.muteChat, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquareOff, {}) }) : null
|
|
3372
3380
|
] });
|
|
3373
3381
|
}
|
|
3382
|
+
function tileHasVideoTrack(meeting, tile) {
|
|
3383
|
+
if (tile.kind === "screen") {
|
|
3384
|
+
return meeting.participantScreenTrack(tile.id) != null;
|
|
3385
|
+
}
|
|
3386
|
+
if (tile.kind === "idle") return false;
|
|
3387
|
+
if (tile.isSelf) return meeting.localVideoTrack() != null;
|
|
3388
|
+
return meeting.participantVideoTrack(tile.id) != null;
|
|
3389
|
+
}
|
|
3390
|
+
function IdlePersonTile({
|
|
3391
|
+
tile,
|
|
3392
|
+
meeting,
|
|
3393
|
+
reducedMotion
|
|
3394
|
+
}) {
|
|
3395
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute inset-0 flex flex-col items-center justify-center gap-3", children: [
|
|
3396
|
+
tile.picture ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3397
|
+
"img",
|
|
3398
|
+
{
|
|
3399
|
+
src: tile.picture,
|
|
3400
|
+
alt: "",
|
|
3401
|
+
className: "size-16 rounded-full object-cover"
|
|
3402
|
+
}
|
|
3403
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-background flex size-16 items-center justify-center rounded-full text-xl", children: tile.name.slice(0, 1).toUpperCase() }),
|
|
3404
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3405
|
+
AudioWaveform,
|
|
3406
|
+
{
|
|
3407
|
+
track: tile.isSelf ? meeting.localAudioTrack() : null,
|
|
3408
|
+
reducedMotion
|
|
3409
|
+
}
|
|
3410
|
+
)
|
|
3411
|
+
] });
|
|
3412
|
+
}
|
|
3374
3413
|
function StatusIcon({
|
|
3375
3414
|
label,
|
|
3376
3415
|
children
|
|
@@ -3394,23 +3433,27 @@ function StudioTileVideo({
|
|
|
3394
3433
|
meeting.attachVideo(participantId, el, kind);
|
|
3395
3434
|
return () => meeting.attachVideo(participantId, null, kind);
|
|
3396
3435
|
}, [kind, meeting, participantId]);
|
|
3397
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
3398
|
-
"
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
"
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3436
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3437
|
+
/* @__PURE__ */ jsxRuntime.jsx("style", { children: STUDIO_VIDEO_MEDIA_STYLE }),
|
|
3438
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3439
|
+
"video",
|
|
3440
|
+
{
|
|
3441
|
+
ref,
|
|
3442
|
+
autoPlay: true,
|
|
3443
|
+
playsInline: true,
|
|
3444
|
+
muted: true,
|
|
3445
|
+
disablePictureInPicture: true,
|
|
3446
|
+
disableRemotePlayback: true,
|
|
3447
|
+
controlsList: "nodownload noplaybackrate noremoteplayback",
|
|
3448
|
+
className: cn(
|
|
3449
|
+
"livo-studio-tile-video absolute inset-0 size-full",
|
|
3450
|
+
"pointer-events-none",
|
|
3451
|
+
contain ? "object-contain" : "object-cover",
|
|
3452
|
+
mirror && "-scale-x-100"
|
|
3453
|
+
)
|
|
3454
|
+
}
|
|
3455
|
+
)
|
|
3456
|
+
] });
|
|
3414
3457
|
}
|
|
3415
3458
|
function StudioStage({
|
|
3416
3459
|
participants,
|
|
@@ -3502,6 +3545,32 @@ function StudioStage({
|
|
|
3502
3545
|
}
|
|
3503
3546
|
);
|
|
3504
3547
|
}
|
|
3548
|
+
function StudioDownloadAppDialog({
|
|
3549
|
+
open,
|
|
3550
|
+
onOpenChange,
|
|
3551
|
+
downloadUrl,
|
|
3552
|
+
labels
|
|
3553
|
+
}) {
|
|
3554
|
+
const href = downloadUrl?.trim() ?? "";
|
|
3555
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(DialogContent, { children: [
|
|
3556
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DialogHeader, { children: [
|
|
3557
|
+
/* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { children: labels.screenDownloadAppTitle }),
|
|
3558
|
+
/* @__PURE__ */ jsxRuntime.jsx(DialogDescription, { children: labels.screenUnsupportedDevice })
|
|
3559
|
+
] }),
|
|
3560
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DialogFooter, { children: [
|
|
3561
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3562
|
+
Button,
|
|
3563
|
+
{
|
|
3564
|
+
type: "button",
|
|
3565
|
+
variant: "outline",
|
|
3566
|
+
onClick: () => onOpenChange(false),
|
|
3567
|
+
children: labels.screenDownloadAppDismiss
|
|
3568
|
+
}
|
|
3569
|
+
),
|
|
3570
|
+
href ? /* @__PURE__ */ jsxRuntime.jsx(Button, { render: /* @__PURE__ */ jsxRuntime.jsx("a", { href, target: "_blank", rel: "noreferrer" }), children: labels.screenDownloadAppAction }) : /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "button", disabled: true, children: labels.screenDownloadAppAction })
|
|
3571
|
+
] })
|
|
3572
|
+
] }) });
|
|
3573
|
+
}
|
|
3505
3574
|
function StudioToolbar({
|
|
3506
3575
|
session,
|
|
3507
3576
|
meeting,
|
|
@@ -3756,7 +3825,8 @@ function StudioRoom({
|
|
|
3756
3825
|
windowGate,
|
|
3757
3826
|
initialCameraOn = true,
|
|
3758
3827
|
initialMuted = false,
|
|
3759
|
-
initialDevices
|
|
3828
|
+
initialDevices,
|
|
3829
|
+
appDownloadUrl
|
|
3760
3830
|
}) {
|
|
3761
3831
|
const [cameraOn, setCameraOn] = react.useState(initialCameraOn);
|
|
3762
3832
|
const [screenOn, setScreenOn] = react.useState(false);
|
|
@@ -3777,6 +3847,7 @@ function StudioRoom({
|
|
|
3777
3847
|
);
|
|
3778
3848
|
const [egressError, setEgressError] = react.useState(null);
|
|
3779
3849
|
const [shareGrant, setShareGrant] = react.useState("idle");
|
|
3850
|
+
const [downloadAppOpen, setDownloadAppOpen] = react.useState(false);
|
|
3780
3851
|
const [connectTimedOut, setConnectTimedOut] = react.useState(false);
|
|
3781
3852
|
const [screenPermission, setScreenPermission] = react.useState(
|
|
3782
3853
|
() => meeting.screenSharePermission()
|
|
@@ -3818,6 +3889,11 @@ function StudioRoom({
|
|
|
3818
3889
|
const roomState = leaveState ?? meeting.roomState();
|
|
3819
3890
|
const sessionOver = isStudioSessionOver(session.stream.status, roomState);
|
|
3820
3891
|
const rejected = isStudioRejected(roomState);
|
|
3892
|
+
const connectionState = react.useMemo(
|
|
3893
|
+
() => meeting.connectionState(),
|
|
3894
|
+
[meeting, tick2]
|
|
3895
|
+
);
|
|
3896
|
+
const guestConnectFailed = session.role === "guest" && (connectionState === "failed" || connectionState === "connecting" && connectTimedOut);
|
|
3821
3897
|
useBeforeUnload(active && !sessionOver);
|
|
3822
3898
|
useWakeLock(active && !sessionOver);
|
|
3823
3899
|
react.useEffect(() => {
|
|
@@ -3981,12 +4057,13 @@ function StudioRoom({
|
|
|
3981
4057
|
void meeting.setScreenShareEnabled(false);
|
|
3982
4058
|
}, [guestOffStage, meeting]);
|
|
3983
4059
|
react.useEffect(() => {
|
|
3984
|
-
if (!sessionOver && !rejected) return;
|
|
4060
|
+
if (!sessionOver && !rejected && !guestConnectFailed) return;
|
|
4061
|
+
pip.close();
|
|
3985
4062
|
setShareGrant("idle");
|
|
3986
4063
|
void meeting.setAudioEnabled(false);
|
|
3987
4064
|
void meeting.setVideoEnabled(false);
|
|
3988
4065
|
void meeting.setScreenShareEnabled(false);
|
|
3989
|
-
}, [meeting, rejected, sessionOver]);
|
|
4066
|
+
}, [guestConnectFailed, meeting, pip.close, rejected, sessionOver]);
|
|
3990
4067
|
react.useEffect(() => {
|
|
3991
4068
|
return meeting.subscribeMedia(() => {
|
|
3992
4069
|
const self2 = meeting.participants().find((row) => row.isSelf);
|
|
@@ -4024,10 +4101,6 @@ function StudioRoom({
|
|
|
4024
4101
|
[participants]
|
|
4025
4102
|
);
|
|
4026
4103
|
const waitlisted = react.useMemo(() => meeting.waitlisted(), [meeting, tick2]);
|
|
4027
|
-
const connectionState = react.useMemo(
|
|
4028
|
-
() => meeting.connectionState(),
|
|
4029
|
-
[meeting, tick2]
|
|
4030
|
-
);
|
|
4031
4104
|
const lastMeetingError = react.useMemo(
|
|
4032
4105
|
() => meeting.lastMeetingError(),
|
|
4033
4106
|
[meeting, tick2]
|
|
@@ -4045,7 +4118,7 @@ function StudioRoom({
|
|
|
4045
4118
|
captureSupported,
|
|
4046
4119
|
screenPermission
|
|
4047
4120
|
);
|
|
4048
|
-
const showScreenControl = durableScreenBlock
|
|
4121
|
+
const showScreenControl = showScreenShareControl(durableScreenBlock);
|
|
4049
4122
|
const canGoLive = session.role === "moderator" && canPublish(session.stream.status);
|
|
4050
4123
|
const showPublishButton = session.role === "moderator" && !onAir && session.stream.status !== "ended" && session.stream.status !== "error";
|
|
4051
4124
|
const self = participants.find((row) => row.isSelf);
|
|
@@ -4141,17 +4214,10 @@ function StudioRoom({
|
|
|
4141
4214
|
}, [connectionState, session.role]);
|
|
4142
4215
|
react.useEffect(() => {
|
|
4143
4216
|
if (inWaitingRoom || screenNoticePrimed.current) return;
|
|
4144
|
-
if (
|
|
4217
|
+
if (durableScreenBlock !== "permission") return;
|
|
4145
4218
|
screenNoticePrimed.current = true;
|
|
4146
|
-
sonner.toast.message(
|
|
4147
|
-
|
|
4148
|
-
);
|
|
4149
|
-
}, [
|
|
4150
|
-
durableScreenBlock,
|
|
4151
|
-
inWaitingRoom,
|
|
4152
|
-
labels.screenNotAllowed,
|
|
4153
|
-
labels.screenUnsupportedDevice
|
|
4154
|
-
]);
|
|
4219
|
+
sonner.toast.message(labels.screenNotAllowed);
|
|
4220
|
+
}, [durableScreenBlock, inWaitingRoom, labels.screenNotAllowed]);
|
|
4155
4221
|
react.useEffect(() => {
|
|
4156
4222
|
return meeting.subscribeBroadcast((message) => {
|
|
4157
4223
|
if (message.type === "host-media") {
|
|
@@ -4260,6 +4326,10 @@ function StudioRoom({
|
|
|
4260
4326
|
setCameraOn((current) => !current);
|
|
4261
4327
|
}, []);
|
|
4262
4328
|
const onToggleScreen = react.useCallback(() => {
|
|
4329
|
+
if (durableScreenBlock === "device") {
|
|
4330
|
+
setDownloadAppOpen(true);
|
|
4331
|
+
return;
|
|
4332
|
+
}
|
|
4263
4333
|
if (!canShareFreely || screenRequestPending) return;
|
|
4264
4334
|
if (!screenOn && !canStartScreenShare(meeting.participants())) {
|
|
4265
4335
|
sonner.toast.message(labels.screenBusy);
|
|
@@ -4269,12 +4339,17 @@ function StudioRoom({
|
|
|
4269
4339
|
setScreenOn((current) => !current);
|
|
4270
4340
|
}, [
|
|
4271
4341
|
canShareFreely,
|
|
4342
|
+
durableScreenBlock,
|
|
4272
4343
|
labels.screenBusy,
|
|
4273
4344
|
meeting,
|
|
4274
4345
|
screenOn,
|
|
4275
4346
|
screenRequestPending
|
|
4276
4347
|
]);
|
|
4277
4348
|
const onAskToShare = react.useCallback(async () => {
|
|
4349
|
+
if (durableScreenBlock === "device") {
|
|
4350
|
+
setDownloadAppOpen(true);
|
|
4351
|
+
return;
|
|
4352
|
+
}
|
|
4278
4353
|
const local = meeting.participants().find((row) => row.isSelf);
|
|
4279
4354
|
if (!local) return;
|
|
4280
4355
|
try {
|
|
@@ -4291,7 +4366,12 @@ function StudioRoom({
|
|
|
4291
4366
|
} catch {
|
|
4292
4367
|
sonner.toast.error(labels.shareRequestError);
|
|
4293
4368
|
}
|
|
4294
|
-
}, [
|
|
4369
|
+
}, [
|
|
4370
|
+
durableScreenBlock,
|
|
4371
|
+
labels.shareRequestError,
|
|
4372
|
+
labels.shareRequestSent,
|
|
4373
|
+
meeting
|
|
4374
|
+
]);
|
|
4295
4375
|
const verifyGrantTookEffect = react.useCallback(
|
|
4296
4376
|
(userId, retried = false) => {
|
|
4297
4377
|
const id = window.setTimeout(() => {
|
|
@@ -4454,7 +4534,6 @@ function StudioRoom({
|
|
|
4454
4534
|
}
|
|
4455
4535
|
) });
|
|
4456
4536
|
}
|
|
4457
|
-
const guestConnectFailed = session.role === "guest" && (connectionState === "failed" || connectionState === "connecting" && connectTimedOut);
|
|
4458
4537
|
if (guestConnectFailed) {
|
|
4459
4538
|
const detail = [meeting.roomState(), lastMeetingError].filter((value) => Boolean(value)).join(" \u2014 ");
|
|
4460
4539
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-dvh flex-col items-center justify-center p-6", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -4788,7 +4867,21 @@ function StudioRoom({
|
|
|
4788
4867
|
}
|
|
4789
4868
|
),
|
|
4790
4869
|
pip.pipWindow.document.body
|
|
4791
|
-
) : null
|
|
4870
|
+
) : null,
|
|
4871
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4872
|
+
StudioDownloadAppDialog,
|
|
4873
|
+
{
|
|
4874
|
+
open: downloadAppOpen,
|
|
4875
|
+
onOpenChange: setDownloadAppOpen,
|
|
4876
|
+
downloadUrl: appDownloadUrl,
|
|
4877
|
+
labels: {
|
|
4878
|
+
screenDownloadAppTitle: labels.screenDownloadAppTitle,
|
|
4879
|
+
screenUnsupportedDevice: labels.screenUnsupportedDevice,
|
|
4880
|
+
screenDownloadAppAction: labels.screenDownloadAppAction,
|
|
4881
|
+
screenDownloadAppDismiss: labels.screenDownloadAppDismiss
|
|
4882
|
+
}
|
|
4883
|
+
}
|
|
4884
|
+
)
|
|
4792
4885
|
] });
|
|
4793
4886
|
}
|
|
4794
4887
|
function isOverconstrained(error) {
|
|
@@ -6331,7 +6424,8 @@ function LivoHostStudio({
|
|
|
6331
6424
|
embed = false,
|
|
6332
6425
|
chrome,
|
|
6333
6426
|
playerOrigin,
|
|
6334
|
-
theme
|
|
6427
|
+
theme,
|
|
6428
|
+
appDownloadUrl
|
|
6335
6429
|
}) {
|
|
6336
6430
|
const reducedMotion = usePrefersReducedMotion();
|
|
6337
6431
|
const [session, setSession] = react.useState(null);
|
|
@@ -6411,7 +6505,8 @@ function LivoHostStudio({
|
|
|
6411
6505
|
labels,
|
|
6412
6506
|
embed,
|
|
6413
6507
|
chrome,
|
|
6414
|
-
playerOrigin
|
|
6508
|
+
playerOrigin,
|
|
6509
|
+
appDownloadUrl
|
|
6415
6510
|
}
|
|
6416
6511
|
);
|
|
6417
6512
|
}
|
|
@@ -6424,7 +6519,8 @@ function LivoGuestStudio({
|
|
|
6424
6519
|
lobbyLabels,
|
|
6425
6520
|
embed = false,
|
|
6426
6521
|
playerOrigin,
|
|
6427
|
-
theme
|
|
6522
|
+
theme,
|
|
6523
|
+
appDownloadUrl
|
|
6428
6524
|
}) {
|
|
6429
6525
|
const reducedMotion = usePrefersReducedMotion();
|
|
6430
6526
|
const [session, setSession] = react.useState(null);
|
|
@@ -6610,7 +6706,8 @@ function LivoGuestStudio({
|
|
|
6610
6706
|
playerOrigin,
|
|
6611
6707
|
initialCameraOn: cameraOn,
|
|
6612
6708
|
initialMuted: muted,
|
|
6613
|
-
initialDevices: deviceSelection
|
|
6709
|
+
initialDevices: deviceSelection,
|
|
6710
|
+
appDownloadUrl
|
|
6614
6711
|
}
|
|
6615
6712
|
);
|
|
6616
6713
|
}
|
|
@@ -6715,6 +6812,9 @@ function studioRoomLabels(t) {
|
|
|
6715
6812
|
pipTitle: t("pipTitle"),
|
|
6716
6813
|
pipError: t("pipError"),
|
|
6717
6814
|
screenUnsupportedDevice: t("screenUnsupportedDevice"),
|
|
6815
|
+
screenDownloadAppTitle: t("screenDownloadAppTitle"),
|
|
6816
|
+
screenDownloadAppAction: t("screenDownloadAppAction"),
|
|
6817
|
+
screenDownloadAppDismiss: t("screenDownloadAppDismiss"),
|
|
6718
6818
|
screenNotAllowed: t("screenNotAllowed"),
|
|
6719
6819
|
askToShare: t("askToShare"),
|
|
6720
6820
|
shareRequested: t("shareRequested"),
|