@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.d.cts
CHANGED
|
@@ -316,6 +316,9 @@ declare function studioRoomLabels(t: (key: string, values?: Record<string, strin
|
|
|
316
316
|
pipTitle: string;
|
|
317
317
|
pipError: string;
|
|
318
318
|
screenUnsupportedDevice: string;
|
|
319
|
+
screenDownloadAppTitle: string;
|
|
320
|
+
screenDownloadAppAction: string;
|
|
321
|
+
screenDownloadAppDismiss: string;
|
|
319
322
|
screenNotAllowed: string;
|
|
320
323
|
askToShare: string;
|
|
321
324
|
shareRequested: string;
|
|
@@ -393,7 +396,7 @@ type StudioDeviceSelection = {
|
|
|
393
396
|
speaker: string;
|
|
394
397
|
};
|
|
395
398
|
|
|
396
|
-
declare function StudioRoom({ session, meeting, control, onRefreshStatus, onRejoin, labels, reducedMotion, embed, chrome, playerOrigin, windowGate, initialCameraOn, initialMuted, initialDevices, }: {
|
|
399
|
+
declare function StudioRoom({ session, meeting, control, onRefreshStatus, onRejoin, labels, reducedMotion, embed, chrome, playerOrigin, windowGate, initialCameraOn, initialMuted, initialDevices, appDownloadUrl, }: {
|
|
397
400
|
session: StudioSession;
|
|
398
401
|
meeting: StudioMeetingHandle;
|
|
399
402
|
control: StudioControlActions;
|
|
@@ -410,6 +413,7 @@ declare function StudioRoom({ session, meeting, control, onRefreshStatus, onRejo
|
|
|
410
413
|
initialCameraOn?: boolean;
|
|
411
414
|
initialMuted?: boolean;
|
|
412
415
|
initialDevices?: StudioDeviceSelection;
|
|
416
|
+
appDownloadUrl?: string;
|
|
413
417
|
}): react.JSX.Element;
|
|
414
418
|
|
|
415
419
|
declare function StudioSkeleton(): react.JSX.Element;
|
|
@@ -465,7 +469,7 @@ declare function StudioWindowGate({ lockKey, allowTakeover, labels, children, }:
|
|
|
465
469
|
children: ReactNode;
|
|
466
470
|
}): react.JSX.Element;
|
|
467
471
|
|
|
468
|
-
declare function LivoHostStudio({ token, apiUrl, labels, redeemError, embed, chrome, playerOrigin, theme, }: {
|
|
472
|
+
declare function LivoHostStudio({ token, apiUrl, labels, redeemError, embed, chrome, playerOrigin, theme, appDownloadUrl, }: {
|
|
469
473
|
token: string;
|
|
470
474
|
apiUrl?: string;
|
|
471
475
|
labels: StudioRoomLabels;
|
|
@@ -474,6 +478,7 @@ declare function LivoHostStudio({ token, apiUrl, labels, redeemError, embed, chr
|
|
|
474
478
|
chrome?: ReactNode;
|
|
475
479
|
playerOrigin?: string;
|
|
476
480
|
theme?: string | null;
|
|
481
|
+
appDownloadUrl?: string;
|
|
477
482
|
}): react.JSX.Element;
|
|
478
483
|
|
|
479
484
|
type StudioLobbyLabels = {
|
|
@@ -494,7 +499,7 @@ type StudioLobbyLabels = {
|
|
|
494
499
|
joinBusy: string;
|
|
495
500
|
endedTitle: string;
|
|
496
501
|
};
|
|
497
|
-
declare function LivoGuestStudio({ token, apiUrl, labels, lobbyLabels, embed, playerOrigin, theme, }: {
|
|
502
|
+
declare function LivoGuestStudio({ token, apiUrl, labels, lobbyLabels, embed, playerOrigin, theme, appDownloadUrl, }: {
|
|
498
503
|
token: string;
|
|
499
504
|
apiUrl?: string;
|
|
500
505
|
labels: StudioRoomLabels;
|
|
@@ -502,6 +507,7 @@ declare function LivoGuestStudio({ token, apiUrl, labels, lobbyLabels, embed, pl
|
|
|
502
507
|
embed?: boolean;
|
|
503
508
|
playerOrigin?: string;
|
|
504
509
|
theme?: string | null;
|
|
510
|
+
appDownloadUrl?: string;
|
|
505
511
|
}): react.JSX.Element;
|
|
506
512
|
|
|
507
513
|
declare function useRealtimeKitMeeting(authToken: string | null, generation?: number): {
|
package/dist/studio/index.d.ts
CHANGED
|
@@ -316,6 +316,9 @@ declare function studioRoomLabels(t: (key: string, values?: Record<string, strin
|
|
|
316
316
|
pipTitle: string;
|
|
317
317
|
pipError: string;
|
|
318
318
|
screenUnsupportedDevice: string;
|
|
319
|
+
screenDownloadAppTitle: string;
|
|
320
|
+
screenDownloadAppAction: string;
|
|
321
|
+
screenDownloadAppDismiss: string;
|
|
319
322
|
screenNotAllowed: string;
|
|
320
323
|
askToShare: string;
|
|
321
324
|
shareRequested: string;
|
|
@@ -393,7 +396,7 @@ type StudioDeviceSelection = {
|
|
|
393
396
|
speaker: string;
|
|
394
397
|
};
|
|
395
398
|
|
|
396
|
-
declare function StudioRoom({ session, meeting, control, onRefreshStatus, onRejoin, labels, reducedMotion, embed, chrome, playerOrigin, windowGate, initialCameraOn, initialMuted, initialDevices, }: {
|
|
399
|
+
declare function StudioRoom({ session, meeting, control, onRefreshStatus, onRejoin, labels, reducedMotion, embed, chrome, playerOrigin, windowGate, initialCameraOn, initialMuted, initialDevices, appDownloadUrl, }: {
|
|
397
400
|
session: StudioSession;
|
|
398
401
|
meeting: StudioMeetingHandle;
|
|
399
402
|
control: StudioControlActions;
|
|
@@ -410,6 +413,7 @@ declare function StudioRoom({ session, meeting, control, onRefreshStatus, onRejo
|
|
|
410
413
|
initialCameraOn?: boolean;
|
|
411
414
|
initialMuted?: boolean;
|
|
412
415
|
initialDevices?: StudioDeviceSelection;
|
|
416
|
+
appDownloadUrl?: string;
|
|
413
417
|
}): react.JSX.Element;
|
|
414
418
|
|
|
415
419
|
declare function StudioSkeleton(): react.JSX.Element;
|
|
@@ -465,7 +469,7 @@ declare function StudioWindowGate({ lockKey, allowTakeover, labels, children, }:
|
|
|
465
469
|
children: ReactNode;
|
|
466
470
|
}): react.JSX.Element;
|
|
467
471
|
|
|
468
|
-
declare function LivoHostStudio({ token, apiUrl, labels, redeemError, embed, chrome, playerOrigin, theme, }: {
|
|
472
|
+
declare function LivoHostStudio({ token, apiUrl, labels, redeemError, embed, chrome, playerOrigin, theme, appDownloadUrl, }: {
|
|
469
473
|
token: string;
|
|
470
474
|
apiUrl?: string;
|
|
471
475
|
labels: StudioRoomLabels;
|
|
@@ -474,6 +478,7 @@ declare function LivoHostStudio({ token, apiUrl, labels, redeemError, embed, chr
|
|
|
474
478
|
chrome?: ReactNode;
|
|
475
479
|
playerOrigin?: string;
|
|
476
480
|
theme?: string | null;
|
|
481
|
+
appDownloadUrl?: string;
|
|
477
482
|
}): react.JSX.Element;
|
|
478
483
|
|
|
479
484
|
type StudioLobbyLabels = {
|
|
@@ -494,7 +499,7 @@ type StudioLobbyLabels = {
|
|
|
494
499
|
joinBusy: string;
|
|
495
500
|
endedTitle: string;
|
|
496
501
|
};
|
|
497
|
-
declare function LivoGuestStudio({ token, apiUrl, labels, lobbyLabels, embed, playerOrigin, theme, }: {
|
|
502
|
+
declare function LivoGuestStudio({ token, apiUrl, labels, lobbyLabels, embed, playerOrigin, theme, appDownloadUrl, }: {
|
|
498
503
|
token: string;
|
|
499
504
|
apiUrl?: string;
|
|
500
505
|
labels: StudioRoomLabels;
|
|
@@ -502,6 +507,7 @@ declare function LivoGuestStudio({ token, apiUrl, labels, lobbyLabels, embed, pl
|
|
|
502
507
|
embed?: boolean;
|
|
503
508
|
playerOrigin?: string;
|
|
504
509
|
theme?: string | null;
|
|
510
|
+
appDownloadUrl?: string;
|
|
505
511
|
}): react.JSX.Element;
|
|
506
512
|
|
|
507
513
|
declare function useRealtimeKitMeeting(authToken: string | null, generation?: number): {
|
package/dist/studio/index.js
CHANGED
|
@@ -518,52 +518,38 @@ function canTakeOffAir(tiles, participantId, onAir) {
|
|
|
518
518
|
(tile) => tile.id !== participantId && isOnStage(tile.stageStatus)
|
|
519
519
|
);
|
|
520
520
|
}
|
|
521
|
+
function pipSpotlightFrom(tile, kind) {
|
|
522
|
+
return {
|
|
523
|
+
participantId: tile.id,
|
|
524
|
+
name: tile.name,
|
|
525
|
+
kind,
|
|
526
|
+
picture: tile.picture
|
|
527
|
+
};
|
|
528
|
+
}
|
|
521
529
|
function resolveStudioPipSpotlight(tiles, activeSpeakerId) {
|
|
522
530
|
const visible = tiles.filter((tile) => isOnStage(tile.stageStatus));
|
|
523
531
|
const pinned = visible.find((tile) => tile.pinned === true);
|
|
524
532
|
if (pinned) {
|
|
525
|
-
return
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
533
|
+
return pipSpotlightFrom(
|
|
534
|
+
pinned,
|
|
535
|
+
pinned.screenShareEnabled ? "screen" : pinned.videoEnabled ? "camera" : "idle"
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
const speaker = activeSpeakerId ? visible.find((tile) => tile.id === activeSpeakerId) : void 0;
|
|
539
|
+
if (speaker) {
|
|
540
|
+
return pipSpotlightFrom(speaker, speaker.videoEnabled ? "camera" : "idle");
|
|
531
541
|
}
|
|
532
542
|
const sharing = visible.find((tile) => tile.screenShareEnabled);
|
|
533
543
|
if (sharing) {
|
|
534
|
-
return
|
|
535
|
-
participantId: sharing.id,
|
|
536
|
-
name: sharing.name,
|
|
537
|
-
kind: "screen",
|
|
538
|
-
picture: sharing.picture
|
|
539
|
-
};
|
|
540
|
-
}
|
|
541
|
-
const speaker = activeSpeakerId ? visible.find((tile) => tile.id === activeSpeakerId && tile.videoEnabled) : void 0;
|
|
542
|
-
if (speaker) {
|
|
543
|
-
return {
|
|
544
|
-
participantId: speaker.id,
|
|
545
|
-
name: speaker.name,
|
|
546
|
-
kind: "camera",
|
|
547
|
-
picture: speaker.picture
|
|
548
|
-
};
|
|
544
|
+
return pipSpotlightFrom(sharing, "screen");
|
|
549
545
|
}
|
|
550
546
|
const remoteCam = visible.find((tile) => !tile.isSelf && tile.videoEnabled);
|
|
551
547
|
if (remoteCam) {
|
|
552
|
-
return
|
|
553
|
-
participantId: remoteCam.id,
|
|
554
|
-
name: remoteCam.name,
|
|
555
|
-
kind: "camera",
|
|
556
|
-
picture: remoteCam.picture
|
|
557
|
-
};
|
|
548
|
+
return pipSpotlightFrom(remoteCam, "camera");
|
|
558
549
|
}
|
|
559
550
|
const self = visible.find((tile) => tile.isSelf);
|
|
560
551
|
if (!self) return null;
|
|
561
|
-
return
|
|
562
|
-
participantId: self.id,
|
|
563
|
-
name: self.name,
|
|
564
|
-
kind: self.videoEnabled ? "camera" : "idle",
|
|
565
|
-
picture: self.picture
|
|
566
|
-
};
|
|
552
|
+
return pipSpotlightFrom(self, self.videoEnabled ? "camera" : "idle");
|
|
567
553
|
}
|
|
568
554
|
function resolveStudioPipSelfView(tiles, spotlight) {
|
|
569
555
|
const self = tiles.find((tile) => tile.isSelf);
|
|
@@ -682,6 +668,9 @@ function screenShareBlock(captureSupported, permission) {
|
|
|
682
668
|
if (permission === "NOT_ALLOWED") return "permission";
|
|
683
669
|
return null;
|
|
684
670
|
}
|
|
671
|
+
function showScreenShareControl(block) {
|
|
672
|
+
return block !== "permission";
|
|
673
|
+
}
|
|
685
674
|
|
|
686
675
|
// src/studio/lib/parse.ts
|
|
687
676
|
function isRecord(value) {
|
|
@@ -1464,6 +1453,19 @@ function DialogHeader({ className, ...props }) {
|
|
|
1464
1453
|
}
|
|
1465
1454
|
);
|
|
1466
1455
|
}
|
|
1456
|
+
function DialogFooter({ className, ...props }) {
|
|
1457
|
+
return /* @__PURE__ */ jsx(
|
|
1458
|
+
"div",
|
|
1459
|
+
{
|
|
1460
|
+
"data-slot": "dialog-footer",
|
|
1461
|
+
className: cn(
|
|
1462
|
+
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
|
1463
|
+
className
|
|
1464
|
+
),
|
|
1465
|
+
...props
|
|
1466
|
+
}
|
|
1467
|
+
);
|
|
1468
|
+
}
|
|
1467
1469
|
function DialogTitle({
|
|
1468
1470
|
className,
|
|
1469
1471
|
...props
|
|
@@ -2023,6 +2025,19 @@ var badgeVariants = cva(
|
|
|
2023
2025
|
function Badge({ className, variant, ...props }) {
|
|
2024
2026
|
return /* @__PURE__ */ jsx("span", { className: cn(badgeVariants({ variant }), className), ...props });
|
|
2025
2027
|
}
|
|
2028
|
+
|
|
2029
|
+
// src/studio/lib/studio-video-style.ts
|
|
2030
|
+
var STUDIO_VIDEO_MEDIA_STYLE = `
|
|
2031
|
+
.livo-studio-tile-video::-webkit-media-controls-start-playback-button,
|
|
2032
|
+
.livo-studio-inline-video::-webkit-media-controls-start-playback-button {
|
|
2033
|
+
display: none !important;
|
|
2034
|
+
-webkit-appearance: none;
|
|
2035
|
+
}
|
|
2036
|
+
.livo-studio-tile-video::-webkit-media-controls,
|
|
2037
|
+
.livo-studio-inline-video::-webkit-media-controls {
|
|
2038
|
+
display: none !important;
|
|
2039
|
+
}
|
|
2040
|
+
`;
|
|
2026
2041
|
function StudioPipPanel({
|
|
2027
2042
|
meeting,
|
|
2028
2043
|
labels,
|
|
@@ -2077,7 +2092,7 @@ function StudioPipPanel({
|
|
|
2077
2092
|
/* @__PURE__ */ jsx(StudioLiveBadge, { children: labels.live }),
|
|
2078
2093
|
elapsed ? /* @__PURE__ */ jsx("span", { className: "text-muted-foreground tabular-nums text-xs", children: elapsed }) : null
|
|
2079
2094
|
] }) : null,
|
|
2080
|
-
selfView ? /* @__PURE__ */ jsx(
|
|
2095
|
+
selfView && meeting.localVideoTrack() ? /* @__PURE__ */ jsx(
|
|
2081
2096
|
"div",
|
|
2082
2097
|
{
|
|
2083
2098
|
"data-testid": "pip-self-view",
|
|
@@ -2134,7 +2149,8 @@ function PipSpotlight({
|
|
|
2134
2149
|
spotlight,
|
|
2135
2150
|
cameraOffLabel
|
|
2136
2151
|
}) {
|
|
2137
|
-
|
|
2152
|
+
const track = spotlight && spotlight.kind !== "idle" ? spotlight.kind === "screen" ? meeting.participantScreenTrack(spotlight.participantId) : meeting.participantVideoTrack(spotlight.participantId) : null;
|
|
2153
|
+
if (!spotlight || spotlight.kind === "idle" || !track) {
|
|
2138
2154
|
return /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 flex flex-col items-center justify-center gap-2", children: [
|
|
2139
2155
|
spotlight?.picture ? /* @__PURE__ */ jsx(
|
|
2140
2156
|
"img",
|
|
@@ -2147,7 +2163,6 @@ function PipSpotlight({
|
|
|
2147
2163
|
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-xs", children: spotlight?.name ?? cameraOffLabel })
|
|
2148
2164
|
] });
|
|
2149
2165
|
}
|
|
2150
|
-
const track = spotlight.kind === "screen" ? meeting.participantScreenTrack(spotlight.participantId) : meeting.participantVideoTrack(spotlight.participantId);
|
|
2151
2166
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2152
2167
|
/* @__PURE__ */ jsx(
|
|
2153
2168
|
PipTrackVideo,
|
|
@@ -2166,11 +2181,7 @@ function PipTrackVideo({
|
|
|
2166
2181
|
const ref = useRef(null);
|
|
2167
2182
|
useEffect(() => {
|
|
2168
2183
|
const el = ref.current;
|
|
2169
|
-
if (!el) return;
|
|
2170
|
-
if (!track) {
|
|
2171
|
-
el.srcObject = null;
|
|
2172
|
-
return;
|
|
2173
|
-
}
|
|
2184
|
+
if (!el || !track) return;
|
|
2174
2185
|
el.setAttribute("webkit-playsinline", "");
|
|
2175
2186
|
el.srcObject = new MediaStream([track]);
|
|
2176
2187
|
void el.play().catch(() => void 0);
|
|
@@ -2178,19 +2189,24 @@ function PipTrackVideo({
|
|
|
2178
2189
|
el.srcObject = null;
|
|
2179
2190
|
};
|
|
2180
2191
|
}, [track]);
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
{
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2192
|
+
if (!track) return null;
|
|
2193
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2194
|
+
/* @__PURE__ */ jsx("style", { children: STUDIO_VIDEO_MEDIA_STYLE }),
|
|
2195
|
+
/* @__PURE__ */ jsx(
|
|
2196
|
+
"video",
|
|
2197
|
+
{
|
|
2198
|
+
ref,
|
|
2199
|
+
autoPlay: true,
|
|
2200
|
+
playsInline: true,
|
|
2201
|
+
muted: true,
|
|
2202
|
+
disableRemotePlayback: true,
|
|
2203
|
+
className: cn(
|
|
2204
|
+
"livo-studio-inline-video absolute inset-0 size-full",
|
|
2205
|
+
fit === "contain" ? "object-contain" : "object-cover"
|
|
2206
|
+
)
|
|
2207
|
+
}
|
|
2208
|
+
)
|
|
2209
|
+
] });
|
|
2194
2210
|
}
|
|
2195
2211
|
function pipScreenControls({
|
|
2196
2212
|
labels,
|
|
@@ -3217,6 +3233,7 @@ function StudioTile({
|
|
|
3217
3233
|
const onStage = isOnStage(tile.stageStatus);
|
|
3218
3234
|
const [confirm, setConfirm] = useState(null);
|
|
3219
3235
|
const showMenu = (canModerate || canManageStage) && !tile.isSelf;
|
|
3236
|
+
const hasVideoTrack = tileHasVideoTrack(meeting, tile);
|
|
3220
3237
|
const glowRef = useSpeakingGlow({
|
|
3221
3238
|
track: tile.kind === "screen" || !tile.audioEnabled ? null : meeting.participantAudioTrack(tile.id),
|
|
3222
3239
|
enabled: onStage,
|
|
@@ -3232,23 +3249,14 @@ function StudioTile({
|
|
|
3232
3249
|
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"
|
|
3233
3250
|
),
|
|
3234
3251
|
children: [
|
|
3235
|
-
tile.kind === "idle" ? /* @__PURE__ */
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
) : /* @__PURE__ */ jsx("div", { className: "bg-background flex size-16 items-center justify-center rounded-full text-xl", children: tile.name.slice(0, 1).toUpperCase() }),
|
|
3244
|
-
/* @__PURE__ */ jsx(
|
|
3245
|
-
AudioWaveform,
|
|
3246
|
-
{
|
|
3247
|
-
track: tile.isSelf ? meeting.localAudioTrack() : null,
|
|
3248
|
-
reducedMotion
|
|
3249
|
-
}
|
|
3250
|
-
)
|
|
3251
|
-
] }) : /* @__PURE__ */ jsx(
|
|
3252
|
+
tile.kind === "idle" || !hasVideoTrack ? /* @__PURE__ */ jsx(
|
|
3253
|
+
IdlePersonTile,
|
|
3254
|
+
{
|
|
3255
|
+
tile,
|
|
3256
|
+
meeting,
|
|
3257
|
+
reducedMotion
|
|
3258
|
+
}
|
|
3259
|
+
) : /* @__PURE__ */ jsx(
|
|
3252
3260
|
StudioTileVideo,
|
|
3253
3261
|
{
|
|
3254
3262
|
meeting,
|
|
@@ -3347,6 +3355,37 @@ function TileStatusIcons({
|
|
|
3347
3355
|
tile.chatCanSend === false ? /* @__PURE__ */ jsx(StatusIcon, { label: labels.muteChat, children: /* @__PURE__ */ jsx(MessageSquareOff, {}) }) : null
|
|
3348
3356
|
] });
|
|
3349
3357
|
}
|
|
3358
|
+
function tileHasVideoTrack(meeting, tile) {
|
|
3359
|
+
if (tile.kind === "screen") {
|
|
3360
|
+
return meeting.participantScreenTrack(tile.id) != null;
|
|
3361
|
+
}
|
|
3362
|
+
if (tile.kind === "idle") return false;
|
|
3363
|
+
if (tile.isSelf) return meeting.localVideoTrack() != null;
|
|
3364
|
+
return meeting.participantVideoTrack(tile.id) != null;
|
|
3365
|
+
}
|
|
3366
|
+
function IdlePersonTile({
|
|
3367
|
+
tile,
|
|
3368
|
+
meeting,
|
|
3369
|
+
reducedMotion
|
|
3370
|
+
}) {
|
|
3371
|
+
return /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 flex flex-col items-center justify-center gap-3", children: [
|
|
3372
|
+
tile.picture ? /* @__PURE__ */ jsx(
|
|
3373
|
+
"img",
|
|
3374
|
+
{
|
|
3375
|
+
src: tile.picture,
|
|
3376
|
+
alt: "",
|
|
3377
|
+
className: "size-16 rounded-full object-cover"
|
|
3378
|
+
}
|
|
3379
|
+
) : /* @__PURE__ */ jsx("div", { className: "bg-background flex size-16 items-center justify-center rounded-full text-xl", children: tile.name.slice(0, 1).toUpperCase() }),
|
|
3380
|
+
/* @__PURE__ */ jsx(
|
|
3381
|
+
AudioWaveform,
|
|
3382
|
+
{
|
|
3383
|
+
track: tile.isSelf ? meeting.localAudioTrack() : null,
|
|
3384
|
+
reducedMotion
|
|
3385
|
+
}
|
|
3386
|
+
)
|
|
3387
|
+
] });
|
|
3388
|
+
}
|
|
3350
3389
|
function StatusIcon({
|
|
3351
3390
|
label,
|
|
3352
3391
|
children
|
|
@@ -3370,23 +3409,27 @@ function StudioTileVideo({
|
|
|
3370
3409
|
meeting.attachVideo(participantId, el, kind);
|
|
3371
3410
|
return () => meeting.attachVideo(participantId, null, kind);
|
|
3372
3411
|
}, [kind, meeting, participantId]);
|
|
3373
|
-
return /* @__PURE__ */
|
|
3374
|
-
"
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
"
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3412
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3413
|
+
/* @__PURE__ */ jsx("style", { children: STUDIO_VIDEO_MEDIA_STYLE }),
|
|
3414
|
+
/* @__PURE__ */ jsx(
|
|
3415
|
+
"video",
|
|
3416
|
+
{
|
|
3417
|
+
ref,
|
|
3418
|
+
autoPlay: true,
|
|
3419
|
+
playsInline: true,
|
|
3420
|
+
muted: true,
|
|
3421
|
+
disablePictureInPicture: true,
|
|
3422
|
+
disableRemotePlayback: true,
|
|
3423
|
+
controlsList: "nodownload noplaybackrate noremoteplayback",
|
|
3424
|
+
className: cn(
|
|
3425
|
+
"livo-studio-tile-video absolute inset-0 size-full",
|
|
3426
|
+
"pointer-events-none",
|
|
3427
|
+
contain ? "object-contain" : "object-cover",
|
|
3428
|
+
mirror && "-scale-x-100"
|
|
3429
|
+
)
|
|
3430
|
+
}
|
|
3431
|
+
)
|
|
3432
|
+
] });
|
|
3390
3433
|
}
|
|
3391
3434
|
function StudioStage({
|
|
3392
3435
|
participants,
|
|
@@ -3478,6 +3521,32 @@ function StudioStage({
|
|
|
3478
3521
|
}
|
|
3479
3522
|
);
|
|
3480
3523
|
}
|
|
3524
|
+
function StudioDownloadAppDialog({
|
|
3525
|
+
open,
|
|
3526
|
+
onOpenChange,
|
|
3527
|
+
downloadUrl,
|
|
3528
|
+
labels
|
|
3529
|
+
}) {
|
|
3530
|
+
const href = downloadUrl?.trim() ?? "";
|
|
3531
|
+
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { children: [
|
|
3532
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
3533
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: labels.screenDownloadAppTitle }),
|
|
3534
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: labels.screenUnsupportedDevice })
|
|
3535
|
+
] }),
|
|
3536
|
+
/* @__PURE__ */ jsxs(DialogFooter, { children: [
|
|
3537
|
+
/* @__PURE__ */ jsx(
|
|
3538
|
+
Button,
|
|
3539
|
+
{
|
|
3540
|
+
type: "button",
|
|
3541
|
+
variant: "outline",
|
|
3542
|
+
onClick: () => onOpenChange(false),
|
|
3543
|
+
children: labels.screenDownloadAppDismiss
|
|
3544
|
+
}
|
|
3545
|
+
),
|
|
3546
|
+
href ? /* @__PURE__ */ jsx(Button, { render: /* @__PURE__ */ jsx("a", { href, target: "_blank", rel: "noreferrer" }), children: labels.screenDownloadAppAction }) : /* @__PURE__ */ jsx(Button, { type: "button", disabled: true, children: labels.screenDownloadAppAction })
|
|
3547
|
+
] })
|
|
3548
|
+
] }) });
|
|
3549
|
+
}
|
|
3481
3550
|
function StudioToolbar({
|
|
3482
3551
|
session,
|
|
3483
3552
|
meeting,
|
|
@@ -3732,7 +3801,8 @@ function StudioRoom({
|
|
|
3732
3801
|
windowGate,
|
|
3733
3802
|
initialCameraOn = true,
|
|
3734
3803
|
initialMuted = false,
|
|
3735
|
-
initialDevices
|
|
3804
|
+
initialDevices,
|
|
3805
|
+
appDownloadUrl
|
|
3736
3806
|
}) {
|
|
3737
3807
|
const [cameraOn, setCameraOn] = useState(initialCameraOn);
|
|
3738
3808
|
const [screenOn, setScreenOn] = useState(false);
|
|
@@ -3753,6 +3823,7 @@ function StudioRoom({
|
|
|
3753
3823
|
);
|
|
3754
3824
|
const [egressError, setEgressError] = useState(null);
|
|
3755
3825
|
const [shareGrant, setShareGrant] = useState("idle");
|
|
3826
|
+
const [downloadAppOpen, setDownloadAppOpen] = useState(false);
|
|
3756
3827
|
const [connectTimedOut, setConnectTimedOut] = useState(false);
|
|
3757
3828
|
const [screenPermission, setScreenPermission] = useState(
|
|
3758
3829
|
() => meeting.screenSharePermission()
|
|
@@ -3794,6 +3865,11 @@ function StudioRoom({
|
|
|
3794
3865
|
const roomState = leaveState ?? meeting.roomState();
|
|
3795
3866
|
const sessionOver = isStudioSessionOver(session.stream.status, roomState);
|
|
3796
3867
|
const rejected = isStudioRejected(roomState);
|
|
3868
|
+
const connectionState = useMemo(
|
|
3869
|
+
() => meeting.connectionState(),
|
|
3870
|
+
[meeting, tick2]
|
|
3871
|
+
);
|
|
3872
|
+
const guestConnectFailed = session.role === "guest" && (connectionState === "failed" || connectionState === "connecting" && connectTimedOut);
|
|
3797
3873
|
useBeforeUnload(active && !sessionOver);
|
|
3798
3874
|
useWakeLock(active && !sessionOver);
|
|
3799
3875
|
useEffect(() => {
|
|
@@ -3957,12 +4033,13 @@ function StudioRoom({
|
|
|
3957
4033
|
void meeting.setScreenShareEnabled(false);
|
|
3958
4034
|
}, [guestOffStage, meeting]);
|
|
3959
4035
|
useEffect(() => {
|
|
3960
|
-
if (!sessionOver && !rejected) return;
|
|
4036
|
+
if (!sessionOver && !rejected && !guestConnectFailed) return;
|
|
4037
|
+
pip.close();
|
|
3961
4038
|
setShareGrant("idle");
|
|
3962
4039
|
void meeting.setAudioEnabled(false);
|
|
3963
4040
|
void meeting.setVideoEnabled(false);
|
|
3964
4041
|
void meeting.setScreenShareEnabled(false);
|
|
3965
|
-
}, [meeting, rejected, sessionOver]);
|
|
4042
|
+
}, [guestConnectFailed, meeting, pip.close, rejected, sessionOver]);
|
|
3966
4043
|
useEffect(() => {
|
|
3967
4044
|
return meeting.subscribeMedia(() => {
|
|
3968
4045
|
const self2 = meeting.participants().find((row) => row.isSelf);
|
|
@@ -4000,10 +4077,6 @@ function StudioRoom({
|
|
|
4000
4077
|
[participants]
|
|
4001
4078
|
);
|
|
4002
4079
|
const waitlisted = useMemo(() => meeting.waitlisted(), [meeting, tick2]);
|
|
4003
|
-
const connectionState = useMemo(
|
|
4004
|
-
() => meeting.connectionState(),
|
|
4005
|
-
[meeting, tick2]
|
|
4006
|
-
);
|
|
4007
4080
|
const lastMeetingError = useMemo(
|
|
4008
4081
|
() => meeting.lastMeetingError(),
|
|
4009
4082
|
[meeting, tick2]
|
|
@@ -4021,7 +4094,7 @@ function StudioRoom({
|
|
|
4021
4094
|
captureSupported,
|
|
4022
4095
|
screenPermission
|
|
4023
4096
|
);
|
|
4024
|
-
const showScreenControl = durableScreenBlock
|
|
4097
|
+
const showScreenControl = showScreenShareControl(durableScreenBlock);
|
|
4025
4098
|
const canGoLive = session.role === "moderator" && canPublish(session.stream.status);
|
|
4026
4099
|
const showPublishButton = session.role === "moderator" && !onAir && session.stream.status !== "ended" && session.stream.status !== "error";
|
|
4027
4100
|
const self = participants.find((row) => row.isSelf);
|
|
@@ -4117,17 +4190,10 @@ function StudioRoom({
|
|
|
4117
4190
|
}, [connectionState, session.role]);
|
|
4118
4191
|
useEffect(() => {
|
|
4119
4192
|
if (inWaitingRoom || screenNoticePrimed.current) return;
|
|
4120
|
-
if (
|
|
4193
|
+
if (durableScreenBlock !== "permission") return;
|
|
4121
4194
|
screenNoticePrimed.current = true;
|
|
4122
|
-
toast.message(
|
|
4123
|
-
|
|
4124
|
-
);
|
|
4125
|
-
}, [
|
|
4126
|
-
durableScreenBlock,
|
|
4127
|
-
inWaitingRoom,
|
|
4128
|
-
labels.screenNotAllowed,
|
|
4129
|
-
labels.screenUnsupportedDevice
|
|
4130
|
-
]);
|
|
4195
|
+
toast.message(labels.screenNotAllowed);
|
|
4196
|
+
}, [durableScreenBlock, inWaitingRoom, labels.screenNotAllowed]);
|
|
4131
4197
|
useEffect(() => {
|
|
4132
4198
|
return meeting.subscribeBroadcast((message) => {
|
|
4133
4199
|
if (message.type === "host-media") {
|
|
@@ -4236,6 +4302,10 @@ function StudioRoom({
|
|
|
4236
4302
|
setCameraOn((current) => !current);
|
|
4237
4303
|
}, []);
|
|
4238
4304
|
const onToggleScreen = useCallback(() => {
|
|
4305
|
+
if (durableScreenBlock === "device") {
|
|
4306
|
+
setDownloadAppOpen(true);
|
|
4307
|
+
return;
|
|
4308
|
+
}
|
|
4239
4309
|
if (!canShareFreely || screenRequestPending) return;
|
|
4240
4310
|
if (!screenOn && !canStartScreenShare(meeting.participants())) {
|
|
4241
4311
|
toast.message(labels.screenBusy);
|
|
@@ -4245,12 +4315,17 @@ function StudioRoom({
|
|
|
4245
4315
|
setScreenOn((current) => !current);
|
|
4246
4316
|
}, [
|
|
4247
4317
|
canShareFreely,
|
|
4318
|
+
durableScreenBlock,
|
|
4248
4319
|
labels.screenBusy,
|
|
4249
4320
|
meeting,
|
|
4250
4321
|
screenOn,
|
|
4251
4322
|
screenRequestPending
|
|
4252
4323
|
]);
|
|
4253
4324
|
const onAskToShare = useCallback(async () => {
|
|
4325
|
+
if (durableScreenBlock === "device") {
|
|
4326
|
+
setDownloadAppOpen(true);
|
|
4327
|
+
return;
|
|
4328
|
+
}
|
|
4254
4329
|
const local = meeting.participants().find((row) => row.isSelf);
|
|
4255
4330
|
if (!local) return;
|
|
4256
4331
|
try {
|
|
@@ -4267,7 +4342,12 @@ function StudioRoom({
|
|
|
4267
4342
|
} catch {
|
|
4268
4343
|
toast.error(labels.shareRequestError);
|
|
4269
4344
|
}
|
|
4270
|
-
}, [
|
|
4345
|
+
}, [
|
|
4346
|
+
durableScreenBlock,
|
|
4347
|
+
labels.shareRequestError,
|
|
4348
|
+
labels.shareRequestSent,
|
|
4349
|
+
meeting
|
|
4350
|
+
]);
|
|
4271
4351
|
const verifyGrantTookEffect = useCallback(
|
|
4272
4352
|
(userId, retried = false) => {
|
|
4273
4353
|
const id = window.setTimeout(() => {
|
|
@@ -4430,7 +4510,6 @@ function StudioRoom({
|
|
|
4430
4510
|
}
|
|
4431
4511
|
) });
|
|
4432
4512
|
}
|
|
4433
|
-
const guestConnectFailed = session.role === "guest" && (connectionState === "failed" || connectionState === "connecting" && connectTimedOut);
|
|
4434
4513
|
if (guestConnectFailed) {
|
|
4435
4514
|
const detail = [meeting.roomState(), lastMeetingError].filter((value) => Boolean(value)).join(" \u2014 ");
|
|
4436
4515
|
return /* @__PURE__ */ jsx("div", { className: "flex h-dvh flex-col items-center justify-center p-6", children: /* @__PURE__ */ jsx(
|
|
@@ -4764,7 +4843,21 @@ function StudioRoom({
|
|
|
4764
4843
|
}
|
|
4765
4844
|
),
|
|
4766
4845
|
pip.pipWindow.document.body
|
|
4767
|
-
) : null
|
|
4846
|
+
) : null,
|
|
4847
|
+
/* @__PURE__ */ jsx(
|
|
4848
|
+
StudioDownloadAppDialog,
|
|
4849
|
+
{
|
|
4850
|
+
open: downloadAppOpen,
|
|
4851
|
+
onOpenChange: setDownloadAppOpen,
|
|
4852
|
+
downloadUrl: appDownloadUrl,
|
|
4853
|
+
labels: {
|
|
4854
|
+
screenDownloadAppTitle: labels.screenDownloadAppTitle,
|
|
4855
|
+
screenUnsupportedDevice: labels.screenUnsupportedDevice,
|
|
4856
|
+
screenDownloadAppAction: labels.screenDownloadAppAction,
|
|
4857
|
+
screenDownloadAppDismiss: labels.screenDownloadAppDismiss
|
|
4858
|
+
}
|
|
4859
|
+
}
|
|
4860
|
+
)
|
|
4768
4861
|
] });
|
|
4769
4862
|
}
|
|
4770
4863
|
function isOverconstrained(error) {
|
|
@@ -6307,7 +6400,8 @@ function LivoHostStudio({
|
|
|
6307
6400
|
embed = false,
|
|
6308
6401
|
chrome,
|
|
6309
6402
|
playerOrigin,
|
|
6310
|
-
theme
|
|
6403
|
+
theme,
|
|
6404
|
+
appDownloadUrl
|
|
6311
6405
|
}) {
|
|
6312
6406
|
const reducedMotion = usePrefersReducedMotion();
|
|
6313
6407
|
const [session, setSession] = useState(null);
|
|
@@ -6387,7 +6481,8 @@ function LivoHostStudio({
|
|
|
6387
6481
|
labels,
|
|
6388
6482
|
embed,
|
|
6389
6483
|
chrome,
|
|
6390
|
-
playerOrigin
|
|
6484
|
+
playerOrigin,
|
|
6485
|
+
appDownloadUrl
|
|
6391
6486
|
}
|
|
6392
6487
|
);
|
|
6393
6488
|
}
|
|
@@ -6400,7 +6495,8 @@ function LivoGuestStudio({
|
|
|
6400
6495
|
lobbyLabels,
|
|
6401
6496
|
embed = false,
|
|
6402
6497
|
playerOrigin,
|
|
6403
|
-
theme
|
|
6498
|
+
theme,
|
|
6499
|
+
appDownloadUrl
|
|
6404
6500
|
}) {
|
|
6405
6501
|
const reducedMotion = usePrefersReducedMotion();
|
|
6406
6502
|
const [session, setSession] = useState(null);
|
|
@@ -6586,7 +6682,8 @@ function LivoGuestStudio({
|
|
|
6586
6682
|
playerOrigin,
|
|
6587
6683
|
initialCameraOn: cameraOn,
|
|
6588
6684
|
initialMuted: muted,
|
|
6589
|
-
initialDevices: deviceSelection
|
|
6685
|
+
initialDevices: deviceSelection,
|
|
6686
|
+
appDownloadUrl
|
|
6590
6687
|
}
|
|
6591
6688
|
);
|
|
6592
6689
|
}
|
|
@@ -6691,6 +6788,9 @@ function studioRoomLabels(t) {
|
|
|
6691
6788
|
pipTitle: t("pipTitle"),
|
|
6692
6789
|
pipError: t("pipError"),
|
|
6693
6790
|
screenUnsupportedDevice: t("screenUnsupportedDevice"),
|
|
6791
|
+
screenDownloadAppTitle: t("screenDownloadAppTitle"),
|
|
6792
|
+
screenDownloadAppAction: t("screenDownloadAppAction"),
|
|
6793
|
+
screenDownloadAppDismiss: t("screenDownloadAppDismiss"),
|
|
6694
6794
|
screenNotAllowed: t("screenNotAllowed"),
|
|
6695
6795
|
askToShare: t("askToShare"),
|
|
6696
6796
|
shareRequested: t("shareRequested"),
|