@streamplace/components 0.7.18 → 0.7.21
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/assets/emoji-data.json +19371 -0
- package/dist/components/chat/chat-box.js +319 -0
- package/dist/components/chat/chat-message.js +87 -0
- package/dist/components/chat/chat.js +150 -0
- package/dist/components/chat/emoji-suggestions.js +35 -0
- package/dist/components/chat/mention-suggestions.js +42 -0
- package/dist/components/chat/mod-view.js +112 -0
- package/dist/components/chat/system-message.js +19 -0
- package/dist/components/dashboard/chat-panel.js +38 -0
- package/dist/components/dashboard/header.js +80 -0
- package/dist/components/dashboard/index.js +14 -0
- package/dist/components/dashboard/information-widget.js +234 -0
- package/dist/components/dashboard/mod-actions.js +71 -0
- package/dist/components/dashboard/problems.js +74 -0
- package/dist/components/icons/bluesky-icon.js +9 -0
- package/dist/components/keep-awake.js +7 -0
- package/dist/components/keep-awake.native.js +16 -0
- package/dist/components/mobile-player/fullscreen.js +76 -0
- package/dist/components/mobile-player/fullscreen.native.js +141 -0
- package/dist/components/mobile-player/player.js +94 -0
- package/dist/components/mobile-player/props.js +2 -0
- package/dist/components/mobile-player/shared.js +54 -0
- package/dist/components/mobile-player/ui/autoplay-button.js +68 -0
- package/dist/components/mobile-player/ui/countdown.js +83 -0
- package/dist/components/mobile-player/ui/index.js +12 -0
- package/dist/components/mobile-player/ui/input.js +42 -0
- package/dist/components/mobile-player/ui/metrics.js +44 -0
- package/dist/components/mobile-player/ui/report-modal.js +90 -0
- package/dist/components/mobile-player/ui/streamer-context-menu.js +7 -0
- package/dist/components/mobile-player/ui/streamer-loading-overlay.js +104 -0
- package/dist/components/mobile-player/ui/viewer-context-menu.js +51 -0
- package/dist/components/mobile-player/ui/viewer-loading-overlay.js +49 -0
- package/dist/components/mobile-player/ui/viewers.js +23 -0
- package/dist/components/mobile-player/use-webrtc.js +243 -0
- package/dist/components/mobile-player/video-async.native.js +276 -0
- package/dist/components/mobile-player/video-retry.js +29 -0
- package/dist/components/mobile-player/video.js +475 -0
- package/dist/components/mobile-player/video.native.js +56 -0
- package/dist/components/mobile-player/webrtc-diagnostics.js +110 -0
- package/dist/components/mobile-player/webrtc-primitives.js +27 -0
- package/dist/components/mobile-player/webrtc-primitives.native.js +8 -0
- package/dist/components/share/sharesheet.js +91 -0
- package/dist/components/ui/button.js +223 -0
- package/dist/components/ui/dialog.js +206 -0
- package/dist/components/ui/dropdown.js +172 -0
- package/dist/components/ui/icons.js +25 -0
- package/dist/components/ui/index.js +34 -0
- package/dist/components/ui/info-box.js +31 -0
- package/dist/components/ui/info-row.js +23 -0
- package/dist/components/ui/input.js +205 -0
- package/dist/components/ui/loader.js +10 -0
- package/dist/components/ui/primitives/button.js +125 -0
- package/dist/components/ui/primitives/input.js +206 -0
- package/dist/components/ui/primitives/modal.js +206 -0
- package/dist/components/ui/primitives/text.js +292 -0
- package/dist/components/ui/resizeable.js +121 -0
- package/dist/components/ui/slider.js +5 -0
- package/dist/components/ui/text.js +177 -0
- package/dist/components/ui/textarea.js +19 -0
- package/dist/components/ui/toast.js +175 -0
- package/dist/components/ui/view.js +252 -0
- package/dist/hooks/index.js +14 -0
- package/dist/hooks/useAvatars.js +35 -0
- package/dist/hooks/useCameraToggle.js +12 -0
- package/dist/hooks/useKeyboard.js +36 -0
- package/dist/hooks/useKeyboardSlide.js +14 -0
- package/dist/hooks/useLivestreamInfo.js +69 -0
- package/dist/hooks/useOuterAndInnerDimensions.js +30 -0
- package/dist/hooks/usePlayerDimensions.js +22 -0
- package/dist/hooks/usePointerDevice.js +71 -0
- package/dist/hooks/useSegmentDimensions.js +17 -0
- package/dist/hooks/useSegmentTiming.js +65 -0
- package/dist/index.js +34 -0
- package/dist/lib/browser.js +35 -0
- package/dist/lib/facet.js +92 -0
- package/dist/lib/system-messages.js +101 -0
- package/dist/lib/theme/atoms.js +646 -0
- package/dist/lib/theme/atoms.types.js +6 -0
- package/dist/lib/theme/index.js +35 -0
- package/dist/lib/theme/theme.js +256 -0
- package/dist/lib/theme/tokens.js +659 -0
- package/dist/lib/utils.js +105 -0
- package/dist/livestream-provider/index.js +30 -0
- package/dist/livestream-provider/websocket.js +45 -0
- package/dist/livestream-store/chat.js +308 -0
- package/dist/livestream-store/context.js +5 -0
- package/dist/livestream-store/index.js +7 -0
- package/dist/livestream-store/livestream-state.js +2 -0
- package/dist/livestream-store/livestream-store.js +58 -0
- package/dist/livestream-store/problems.js +76 -0
- package/dist/livestream-store/stream-key.js +88 -0
- package/dist/livestream-store/websocket-consumer.js +94 -0
- package/dist/player-store/context.js +5 -0
- package/dist/player-store/index.js +9 -0
- package/dist/player-store/player-provider.js +58 -0
- package/dist/player-store/player-state.js +25 -0
- package/dist/player-store/player-store.js +201 -0
- package/dist/player-store/single-player-provider.js +121 -0
- package/dist/streamplace-provider/context.js +5 -0
- package/dist/streamplace-provider/index.js +20 -0
- package/dist/streamplace-provider/poller.js +49 -0
- package/dist/streamplace-provider/xrpc.js +0 -0
- package/dist/streamplace-store/block.js +65 -0
- package/dist/streamplace-store/index.js +6 -0
- package/dist/streamplace-store/stream.js +247 -0
- package/dist/streamplace-store/streamplace-store.js +47 -0
- package/dist/streamplace-store/user.js +52 -0
- package/dist/streamplace-store/xrpc.js +15 -0
- package/dist/ui/index.js +79 -0
- package/node-compile-cache/v22.15.0-x64-efe9a9df-0/37be0eec +0 -0
- package/package.json +5 -4
- package/src/components/chat/chat-box.tsx +3 -0
- package/src/components/chat/mod-view.tsx +39 -5
- package/src/components/mobile-player/fullscreen.tsx +2 -0
- package/src/components/mobile-player/ui/autoplay-button.tsx +86 -0
- package/src/components/mobile-player/ui/index.ts +1 -0
- package/src/components/mobile-player/video.tsx +11 -1
- package/src/livestream-store/chat.tsx +22 -0
- package/src/player-store/player-provider.tsx +2 -1
- package/src/player-store/player-state.tsx +6 -0
- package/src/player-store/player-store.tsx +4 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ProblemsWrapper = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
const lucide_react_native_1 = require("lucide-react-native");
|
|
7
|
+
const react_1 = require("react");
|
|
8
|
+
const react_native_1 = require("react-native");
|
|
9
|
+
const livestream_store_1 = require("../../livestream-store");
|
|
10
|
+
const zero = tslib_1.__importStar(require("../../ui"));
|
|
11
|
+
const { bg, r, borders, p, text, layout, gap } = zero;
|
|
12
|
+
const Problems = ({ probs, onIgnore, }) => {
|
|
13
|
+
return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [gap.all[3]], children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [text.white, { fontSize: 24, fontWeight: "bold" }], children: "Optimize Your Stream" }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [text.gray[300]], children: "We've found a few things that could improve your stream's reliability." })] }), probs.map((p) => ((0, jsx_runtime_1.jsx)(react_native_1.View, { children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [
|
|
14
|
+
gap.all[2],
|
|
15
|
+
layout.flex.row,
|
|
16
|
+
layout.flex.alignCenter,
|
|
17
|
+
{ gap: 8, alignItems: "flex-start" },
|
|
18
|
+
], children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [
|
|
19
|
+
r.sm,
|
|
20
|
+
p[2],
|
|
21
|
+
{
|
|
22
|
+
width: 82,
|
|
23
|
+
textAlign: "center",
|
|
24
|
+
backgroundColor: p.severity === "error"
|
|
25
|
+
? "#7f1d1d"
|
|
26
|
+
: p.severity === "warning"
|
|
27
|
+
? "#7c2d12"
|
|
28
|
+
: "#1e3a8a",
|
|
29
|
+
color: "white",
|
|
30
|
+
fontSize: 12,
|
|
31
|
+
},
|
|
32
|
+
], children: p.severity }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [{ flex: 1 }, gap.all[1]], children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [text.white, { fontWeight: "600" }], children: p.code }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [text.gray[400], { fontSize: 14 }], children: p.message }), p.link && ((0, jsx_runtime_1.jsx)(react_native_1.Pressable, { onPress: () => p.link && react_native_1.Linking.openURL(p.link), children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [
|
|
33
|
+
layout.flex.row,
|
|
34
|
+
layout.flex.alignCenter,
|
|
35
|
+
gap.all[2],
|
|
36
|
+
], children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [{ color: "#3b82f6", fontSize: 14 }], children: "Learn More" }), (0, jsx_runtime_1.jsx)(lucide_react_native_1.ExternalLink, { size: 12, color: "#3b82f6" })] }) }))] })] }) }, p.message))), (0, jsx_runtime_1.jsx)(react_native_1.Pressable, { onPress: onIgnore, style: [
|
|
37
|
+
bg.blue[600],
|
|
38
|
+
r.md,
|
|
39
|
+
p[3],
|
|
40
|
+
layout.flex.center,
|
|
41
|
+
{ marginTop: 16 },
|
|
42
|
+
], children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [text.white, { fontWeight: "600" }], children: "Ignore" }) })] }));
|
|
43
|
+
};
|
|
44
|
+
const ProblemsWrapper = ({ children, }) => {
|
|
45
|
+
const problems = (0, livestream_store_1.useLivestreamStore)((x) => x.problems);
|
|
46
|
+
const [dismiss, setDismiss] = (0, react_1.useState)(false);
|
|
47
|
+
return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [
|
|
48
|
+
{ position: "relative", flex: 1 },
|
|
49
|
+
layout.flex.center,
|
|
50
|
+
{ flexBasis: 0 },
|
|
51
|
+
], children: [children, problems.length > 0 && !dismiss && ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
|
|
52
|
+
{
|
|
53
|
+
position: "absolute",
|
|
54
|
+
top: 0,
|
|
55
|
+
left: 0,
|
|
56
|
+
right: 0,
|
|
57
|
+
bottom: 0,
|
|
58
|
+
backgroundColor: "rgba(0, 0, 0, 0.8)",
|
|
59
|
+
zIndex: 100,
|
|
60
|
+
},
|
|
61
|
+
layout.flex.center,
|
|
62
|
+
{ justifyContent: "flex-start" },
|
|
63
|
+
p[8],
|
|
64
|
+
], children: (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
|
|
65
|
+
bg.gray[900],
|
|
66
|
+
borders.color.gray[700],
|
|
67
|
+
borders.width.thin,
|
|
68
|
+
r.lg,
|
|
69
|
+
p[4],
|
|
70
|
+
{ maxWidth: 700, width: "100%" },
|
|
71
|
+
], children: (0, jsx_runtime_1.jsx)(Problems, { probs: problems, onIgnore: () => setDismiss(true) }) }) }))] }));
|
|
72
|
+
};
|
|
73
|
+
exports.ProblemsWrapper = ProblemsWrapper;
|
|
74
|
+
exports.default = Problems;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BlueskyIcon = BlueskyIcon;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
const react_native_svg_1 = tslib_1.__importStar(require("react-native-svg"));
|
|
7
|
+
function BlueskyIcon({ size = 20, color = "#000" }) {
|
|
8
|
+
return ((0, jsx_runtime_1.jsx)(react_native_svg_1.default, { width: size, height: size, viewBox: "0 0 600 530", fill: color, children: (0, jsx_runtime_1.jsx)(react_native_svg_1.Path, { d: "M136 44c66 50 138 151 164 205 26-54 98-155 164-205 48-36 126-64 126 25 0 18-10 149-16 170-21 74-96 93-163 81 117 20 147 86 82 153-122 125-176-32-189-72-3-8-4-11-4-8 0-3-1 0-4 8-13 40-67 197-189 72-65-67-35-133 82-153-67 12-142-7-163-81-6-21-16-152-16-170 0-89 78-61 126-25z" }) }));
|
|
9
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KeepAwake = KeepAwake;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const expo_keep_awake_1 = require("expo-keep-awake");
|
|
6
|
+
const react_1 = require("react");
|
|
7
|
+
function KeepAwake() {
|
|
8
|
+
// useKeepAwake();
|
|
9
|
+
(0, react_1.useEffect)(() => {
|
|
10
|
+
(0, expo_keep_awake_1.activateKeepAwakeAsync)();
|
|
11
|
+
return () => {
|
|
12
|
+
(0, expo_keep_awake_1.deactivateKeepAwake)();
|
|
13
|
+
};
|
|
14
|
+
}, []);
|
|
15
|
+
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, {});
|
|
16
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Fullscreen = Fullscreen;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
const react_1 = require("react");
|
|
7
|
+
const __1 = require("../..");
|
|
8
|
+
const ui_1 = require("../../components/ui");
|
|
9
|
+
const video_1 = tslib_1.__importDefault(require("./video"));
|
|
10
|
+
const video_retry_1 = tslib_1.__importDefault(require("./video-retry"));
|
|
11
|
+
function Fullscreen(props) {
|
|
12
|
+
const playerId = (0, __1.getFirstPlayerID)();
|
|
13
|
+
const protocol = (0, __1.usePlayerStore)((x) => x.protocol, playerId);
|
|
14
|
+
const fullscreen = (0, __1.usePlayerStore)((x) => x.fullscreen, playerId);
|
|
15
|
+
const setFullscreen = (0, __1.usePlayerStore)((x) => x.setFullscreen, playerId);
|
|
16
|
+
const setSrc = (0, __1.usePlayerStore)((x) => x.setSrc);
|
|
17
|
+
const setAutoplayFailed = (0, __1.usePlayerStore)((x) => x.setAutoplayFailed);
|
|
18
|
+
const divRef = (0, react_1.useRef)(null);
|
|
19
|
+
const videoRef = (0, react_1.useRef)(null);
|
|
20
|
+
(0, react_1.useEffect)(() => {
|
|
21
|
+
setSrc(props.src);
|
|
22
|
+
setAutoplayFailed(false);
|
|
23
|
+
}, [props.src]);
|
|
24
|
+
(0, react_1.useEffect)(() => {
|
|
25
|
+
if (!divRef.current) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
(async () => {
|
|
29
|
+
if (fullscreen && !document.fullscreenElement) {
|
|
30
|
+
try {
|
|
31
|
+
const div = divRef.current;
|
|
32
|
+
if (typeof div.requestFullscreen === "function") {
|
|
33
|
+
await div.requestFullscreen();
|
|
34
|
+
}
|
|
35
|
+
else if (videoRef.current) {
|
|
36
|
+
if (typeof videoRef.current.webkitEnterFullscreen ===
|
|
37
|
+
"function") {
|
|
38
|
+
await videoRef.current.webkitEnterFullscreen();
|
|
39
|
+
}
|
|
40
|
+
else if (typeof videoRef.current.requestFullscreen === "function") {
|
|
41
|
+
await videoRef.current.requestFullscreen();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
setFullscreen(true);
|
|
45
|
+
}
|
|
46
|
+
catch (e) {
|
|
47
|
+
console.error("fullscreen failed", e.message);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (!fullscreen) {
|
|
51
|
+
if (document.fullscreenElement) {
|
|
52
|
+
try {
|
|
53
|
+
await document.exitFullscreen();
|
|
54
|
+
}
|
|
55
|
+
catch (e) {
|
|
56
|
+
console.error("fullscreen exit failed", e.message);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
setFullscreen(false);
|
|
60
|
+
}
|
|
61
|
+
})();
|
|
62
|
+
}, [fullscreen, protocol]);
|
|
63
|
+
(0, react_1.useEffect)(() => {
|
|
64
|
+
const listener = () => {
|
|
65
|
+
console.log("fullscreenchange", document.fullscreenElement);
|
|
66
|
+
setFullscreen(!!document.fullscreenElement);
|
|
67
|
+
};
|
|
68
|
+
document.body.addEventListener("fullscreenchange", listener);
|
|
69
|
+
document.body.addEventListener("webkitfullscreenchange", listener);
|
|
70
|
+
return () => {
|
|
71
|
+
document.body.removeEventListener("fullscreenchange", listener);
|
|
72
|
+
document.body.removeEventListener("webkitfullscreenchange", listener);
|
|
73
|
+
};
|
|
74
|
+
}, []);
|
|
75
|
+
return ((0, jsx_runtime_1.jsxs)(ui_1.View, { ref: divRef, style: { width: "100%", height: "100%", overflow: "hidden" }, children: [(0, jsx_runtime_1.jsx)(video_retry_1.default, { children: (0, jsx_runtime_1.jsx)(video_1.default, { objectFit: props.objectFit, pictureInPictureEnabled: props.pictureInPictureEnabled }) }), props.children] }));
|
|
76
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Fullscreen = Fullscreen;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
const react_1 = require("react");
|
|
7
|
+
const react_native_1 = require("react-native");
|
|
8
|
+
const react_native_edge_to_edge_1 = require("react-native-edge-to-edge");
|
|
9
|
+
const react_native_safe_area_context_1 = require("react-native-safe-area-context");
|
|
10
|
+
const __1 = require("../..");
|
|
11
|
+
const video_native_1 = tslib_1.__importDefault(require("./video.native"));
|
|
12
|
+
// Standard 16:9 video aspect ratio
|
|
13
|
+
const VIDEO_ASPECT_RATIO = 16 / 9;
|
|
14
|
+
function Fullscreen(props) {
|
|
15
|
+
const ref = (0, react_1.useRef)(null);
|
|
16
|
+
const insets = (0, react_native_safe_area_context_1.useSafeAreaInsets)();
|
|
17
|
+
const [dimensions, setDimensions] = (0, react_1.useState)(react_native_1.Dimensions.get("window"));
|
|
18
|
+
// Get state from player store
|
|
19
|
+
const protocol = (0, __1.usePlayerStore)((x) => x.protocol);
|
|
20
|
+
const fullscreen = (0, __1.usePlayerStore)((x) => x.fullscreen);
|
|
21
|
+
const setFullscreen = (0, __1.usePlayerStore)((x) => x.setFullscreen);
|
|
22
|
+
const handle = (0, __1.useLivestreamStore)((x) => x.profile?.handle);
|
|
23
|
+
const setSrc = (0, __1.usePlayerStore)((x) => x.setSrc);
|
|
24
|
+
(0, react_1.useEffect)(() => {
|
|
25
|
+
setSrc(props.src);
|
|
26
|
+
}, [props.src]);
|
|
27
|
+
// Re-calculate dimensions on orientation change
|
|
28
|
+
(0, react_1.useEffect)(() => {
|
|
29
|
+
const updateDimensions = () => {
|
|
30
|
+
setDimensions(react_native_1.Dimensions.get("window"));
|
|
31
|
+
};
|
|
32
|
+
const subscription = react_native_1.Dimensions.addEventListener("change", updateDimensions);
|
|
33
|
+
return () => {
|
|
34
|
+
subscription.remove();
|
|
35
|
+
};
|
|
36
|
+
}, []);
|
|
37
|
+
// Hide status bar when in fullscreen mode
|
|
38
|
+
(0, react_1.useEffect)(() => {
|
|
39
|
+
if (fullscreen) {
|
|
40
|
+
react_native_edge_to_edge_1.SystemBars.setHidden(true);
|
|
41
|
+
console.log("setting sidebar hidden");
|
|
42
|
+
// Handle hardware back button
|
|
43
|
+
const backHandler = react_native_1.BackHandler.addEventListener("hardwareBackPress", () => {
|
|
44
|
+
setFullscreen(false);
|
|
45
|
+
return true;
|
|
46
|
+
});
|
|
47
|
+
return () => {
|
|
48
|
+
backHandler.remove();
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
react_native_edge_to_edge_1.SystemBars.setHidden(false);
|
|
53
|
+
}
|
|
54
|
+
return () => {
|
|
55
|
+
react_native_edge_to_edge_1.SystemBars.setHidden(false);
|
|
56
|
+
};
|
|
57
|
+
}, [fullscreen, setFullscreen]);
|
|
58
|
+
// Handle fullscreen state changes for native video players
|
|
59
|
+
(0, react_1.useEffect)(() => {
|
|
60
|
+
// For WebRTC, we handle fullscreen manually via the custom implementation
|
|
61
|
+
if (protocol === __1.PlayerProtocol.WEBRTC) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
// For HLS and other protocols, sync with native fullscreen
|
|
65
|
+
if (ref.current) {
|
|
66
|
+
if (fullscreen) {
|
|
67
|
+
ref.current.enterFullscreen();
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
ref.current.exitFullscreen();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}, [fullscreen, protocol]);
|
|
74
|
+
if (fullscreen && protocol === __1.PlayerProtocol.WEBRTC) {
|
|
75
|
+
// Determine if we're in landscape mode
|
|
76
|
+
const isLandscape = dimensions.width > dimensions.height;
|
|
77
|
+
// Calculate video container dimensions based on screen size and orientation
|
|
78
|
+
let videoWidth;
|
|
79
|
+
let videoHeight;
|
|
80
|
+
if (isLandscape) {
|
|
81
|
+
// In landscape, account for safe areas and use available height
|
|
82
|
+
const availableHeight = dimensions.height - (insets.top + insets.bottom);
|
|
83
|
+
const availableWidth = dimensions.width - (insets.left + insets.right);
|
|
84
|
+
videoHeight = availableHeight;
|
|
85
|
+
videoWidth = videoHeight * VIDEO_ASPECT_RATIO;
|
|
86
|
+
// If calculated width exceeds available width, constrain and maintain aspect ratio
|
|
87
|
+
if (videoWidth > availableWidth) {
|
|
88
|
+
videoWidth = availableWidth;
|
|
89
|
+
videoHeight = videoWidth / VIDEO_ASPECT_RATIO;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
// In portrait, account for safe areas
|
|
94
|
+
const availableWidth = dimensions.width - (insets.left + insets.right);
|
|
95
|
+
videoWidth = availableWidth;
|
|
96
|
+
videoHeight = videoWidth / VIDEO_ASPECT_RATIO;
|
|
97
|
+
}
|
|
98
|
+
// Calculate position to center the video, accounting for safe areas
|
|
99
|
+
const leftPosition = (dimensions.width - videoWidth) / 2;
|
|
100
|
+
const topPosition = (dimensions.height - videoHeight) / 2;
|
|
101
|
+
// When in custom fullscreen mode
|
|
102
|
+
return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
|
|
103
|
+
styles.fullscreenContainer,
|
|
104
|
+
{
|
|
105
|
+
width: isLandscape ? dimensions.width + 40 : dimensions.width,
|
|
106
|
+
height: dimensions.height,
|
|
107
|
+
},
|
|
108
|
+
], children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [
|
|
109
|
+
styles.videoContainer,
|
|
110
|
+
{
|
|
111
|
+
width: isLandscape ? videoWidth + 40 : videoWidth,
|
|
112
|
+
height: videoHeight,
|
|
113
|
+
left: leftPosition,
|
|
114
|
+
top: topPosition,
|
|
115
|
+
},
|
|
116
|
+
], children: [(0, jsx_runtime_1.jsx)(video_native_1.default, { objectFit: props.objectFit, pictureInPictureEnabled: props.pictureInPictureEnabled }), props.children] }) }));
|
|
117
|
+
}
|
|
118
|
+
// Normal non-fullscreen mode
|
|
119
|
+
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(__1.VideoRetry, { children: (0, jsx_runtime_1.jsx)(video_native_1.default, { objectFit: props.objectFit, pictureInPictureEnabled: props.pictureInPictureEnabled }) }), props.children] }));
|
|
120
|
+
}
|
|
121
|
+
const styles = react_native_1.StyleSheet.create({
|
|
122
|
+
fullscreenContainer: {
|
|
123
|
+
position: "absolute",
|
|
124
|
+
top: 0,
|
|
125
|
+
left: 0,
|
|
126
|
+
right: 0,
|
|
127
|
+
bottom: 0,
|
|
128
|
+
backgroundColor: "#000",
|
|
129
|
+
zIndex: 9999,
|
|
130
|
+
elevation: 9999,
|
|
131
|
+
margin: 0,
|
|
132
|
+
padding: 0,
|
|
133
|
+
justifyContent: "center",
|
|
134
|
+
alignItems: "center",
|
|
135
|
+
},
|
|
136
|
+
videoContainer: {
|
|
137
|
+
position: "absolute",
|
|
138
|
+
backgroundColor: "#111",
|
|
139
|
+
overflow: "hidden",
|
|
140
|
+
},
|
|
141
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PlayerUI = void 0;
|
|
4
|
+
exports.Player = Player;
|
|
5
|
+
exports.usePlayerStatus = usePlayerStatus;
|
|
6
|
+
const tslib_1 = require("tslib");
|
|
7
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
|
+
const react_1 = require("react");
|
|
9
|
+
const atoms_1 = require("../../lib/theme/atoms");
|
|
10
|
+
const player_store_1 = require("../../player-store");
|
|
11
|
+
const streamplace_store_1 = require("../../streamplace-store");
|
|
12
|
+
const ui_1 = require("../ui");
|
|
13
|
+
const fullscreen_1 = require("./fullscreen");
|
|
14
|
+
const report_modal_1 = tslib_1.__importDefault(require("./ui/report-modal"));
|
|
15
|
+
const OFFLINE_THRESHOLD = 10000;
|
|
16
|
+
exports.PlayerUI = tslib_1.__importStar(require("./ui"));
|
|
17
|
+
function Player(props) {
|
|
18
|
+
const setIngest = (0, player_store_1.usePlayerStore)((x) => x.setIngestConnectionState);
|
|
19
|
+
const clearControlsTimeout = (0, player_store_1.usePlayerStore)((x) => x.clearControlsTimeout);
|
|
20
|
+
const setReportingURL = (0, player_store_1.usePlayerStore)((x) => x.setReportingURL);
|
|
21
|
+
const reportModalOpen = (0, player_store_1.usePlayerStore)((x) => x.reportModalOpen);
|
|
22
|
+
const setReportModalOpen = (0, player_store_1.usePlayerStore)((x) => x.setReportModalOpen);
|
|
23
|
+
const reportSubject = (0, player_store_1.usePlayerStore)((x) => x.reportSubject);
|
|
24
|
+
(0, react_1.useEffect)(() => {
|
|
25
|
+
setReportingURL(props.reportingURL ?? null);
|
|
26
|
+
}, [props.reportingURL]);
|
|
27
|
+
// Will call back every few seconds to send health updates
|
|
28
|
+
usePlayerStatus();
|
|
29
|
+
(0, react_1.useEffect)(() => {
|
|
30
|
+
setIngest(props.ingest ? "new" : null);
|
|
31
|
+
}, []);
|
|
32
|
+
if (typeof props.src !== "string") {
|
|
33
|
+
return ((0, jsx_runtime_1.jsx)(ui_1.View, { children: (0, jsx_runtime_1.jsx)(ui_1.Text, { children: "No source provided \uD83E\uDD37" }) }));
|
|
34
|
+
}
|
|
35
|
+
(0, react_1.useEffect)(() => {
|
|
36
|
+
return () => {
|
|
37
|
+
clearControlsTimeout();
|
|
38
|
+
};
|
|
39
|
+
}, []);
|
|
40
|
+
return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsxs)(ui_1.View, { style: [
|
|
41
|
+
atoms_1.zIndex[0],
|
|
42
|
+
atoms_1.w.percent[100],
|
|
43
|
+
atoms_1.h.percent[100],
|
|
44
|
+
atoms_1.flex.shrink[1],
|
|
45
|
+
atoms_1.layout.flex.center,
|
|
46
|
+
], children: [(0, jsx_runtime_1.jsx)(report_modal_1.default, { open: reportModalOpen, onOpenChange: setReportModalOpen, subject: reportSubject }), (0, jsx_runtime_1.jsx)(fullscreen_1.Fullscreen, { src: props.src, objectFit: props.objectFit, pictureInPictureEnabled: props.pictureInPictureEnabled, children: props.children })] }) }));
|
|
47
|
+
}
|
|
48
|
+
const POLL_INTERVAL = 5000;
|
|
49
|
+
function usePlayerStatus() {
|
|
50
|
+
const playerStatus = (0, player_store_1.usePlayerStore)((x) => x.status);
|
|
51
|
+
const url = (0, streamplace_store_1.useStreamplaceStore)((x) => x.url);
|
|
52
|
+
const playerEvent = (0, player_store_1.usePlayerStore)((x) => x.playerEvent);
|
|
53
|
+
const [whatDoing, setWhatDoing] = (0, react_1.useState)(player_store_1.PlayerStatus.START);
|
|
54
|
+
const [whatDid, setWhatDid] = (0, react_1.useState)({});
|
|
55
|
+
const [doingSince, setDoingSince] = (0, react_1.useState)(Date.now());
|
|
56
|
+
const [lastUpdated, setLastUpdated] = (0, react_1.useState)(0);
|
|
57
|
+
const updateWhatDid = (now) => {
|
|
58
|
+
const prev = whatDid[whatDoing] ?? 0;
|
|
59
|
+
const duration = now.getTime() - doingSince;
|
|
60
|
+
const ret = {
|
|
61
|
+
...whatDid,
|
|
62
|
+
[whatDoing]: prev + duration,
|
|
63
|
+
};
|
|
64
|
+
return ret;
|
|
65
|
+
};
|
|
66
|
+
// callback to update the status
|
|
67
|
+
(0, react_1.useEffect)(() => {
|
|
68
|
+
const now = new Date();
|
|
69
|
+
if (playerStatus !== whatDoing) {
|
|
70
|
+
setWhatDid(updateWhatDid(now));
|
|
71
|
+
setWhatDoing(playerStatus);
|
|
72
|
+
setDoingSince(now.getTime());
|
|
73
|
+
}
|
|
74
|
+
}, [playerStatus]);
|
|
75
|
+
(0, react_1.useEffect)(() => {
|
|
76
|
+
if (lastUpdated === 0) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const now = new Date();
|
|
80
|
+
const fullWhatDid = updateWhatDid(now);
|
|
81
|
+
setWhatDid({});
|
|
82
|
+
setDoingSince(now.getTime());
|
|
83
|
+
playerEvent(url, now.toISOString(), "aq-played", {
|
|
84
|
+
whatHappened: fullWhatDid,
|
|
85
|
+
});
|
|
86
|
+
}, [lastUpdated]);
|
|
87
|
+
(0, react_1.useEffect)(() => {
|
|
88
|
+
const interval = setInterval((_) => {
|
|
89
|
+
setLastUpdated(Date.now());
|
|
90
|
+
}, POLL_INTERVAL);
|
|
91
|
+
return () => clearInterval(interval);
|
|
92
|
+
}, []);
|
|
93
|
+
return [whatDoing];
|
|
94
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.srcToUrl = srcToUrl;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const __1 = require("../..");
|
|
6
|
+
const protocolSuffixes = {
|
|
7
|
+
m3u8: __1.PlayerProtocol.HLS,
|
|
8
|
+
mp4: __1.PlayerProtocol.PROGRESSIVE_MP4,
|
|
9
|
+
webm: __1.PlayerProtocol.PROGRESSIVE_WEBM,
|
|
10
|
+
webrtc: __1.PlayerProtocol.WEBRTC,
|
|
11
|
+
};
|
|
12
|
+
function srcToUrl(props, protocol) {
|
|
13
|
+
const url = (0, __1.useStreamplaceStore)((x) => x.url);
|
|
14
|
+
return (0, react_1.useMemo)(() => {
|
|
15
|
+
if (props.src.startsWith("http://") || props.src.startsWith("https://")) {
|
|
16
|
+
const segments = props.src.split(/[./]/);
|
|
17
|
+
const suffix = segments[segments.length - 1];
|
|
18
|
+
if (protocolSuffixes[suffix]) {
|
|
19
|
+
return {
|
|
20
|
+
url: props.src,
|
|
21
|
+
protocol: protocolSuffixes[suffix],
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
throw new Error(`unknown playback protocol: ${suffix}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
let outUrl;
|
|
29
|
+
if (protocol === __1.PlayerProtocol.HLS) {
|
|
30
|
+
if (props.selectedRendition === "auto") {
|
|
31
|
+
outUrl = `${url}/api/playback/${props.src}/hls/index.m3u8`;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
outUrl = `${url}/api/playback/${props.src}/hls/index.m3u8?rendition=${props.selectedRendition || "source"}`;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
else if (protocol === __1.PlayerProtocol.PROGRESSIVE_MP4) {
|
|
38
|
+
outUrl = `${url}/api/playback/${props.src}/stream.mp4`;
|
|
39
|
+
}
|
|
40
|
+
else if (protocol === __1.PlayerProtocol.PROGRESSIVE_WEBM) {
|
|
41
|
+
outUrl = `${url}/api/playback/${props.src}/stream.webm`;
|
|
42
|
+
}
|
|
43
|
+
else if (protocol === __1.PlayerProtocol.WEBRTC) {
|
|
44
|
+
outUrl = `${url}/api/playback/${props.src}/webrtc?rendition=${props.selectedRendition || "source"}`;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
throw new Error(`unknown playback protocol: ${protocol}`);
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
protocol: protocol,
|
|
51
|
+
url: outUrl,
|
|
52
|
+
};
|
|
53
|
+
}, [props.src, props.selectedRendition, protocol, url]);
|
|
54
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AutoplayButton = AutoplayButton;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const lucide_react_native_1 = require("lucide-react-native");
|
|
6
|
+
const react_native_1 = require("react-native");
|
|
7
|
+
const __1 = require("../../..");
|
|
8
|
+
const ui_1 = require("../../../ui");
|
|
9
|
+
function AutoplayButton() {
|
|
10
|
+
const autoplayFailed = (0, __1.usePlayerStore)((x) => x.autoplayFailed);
|
|
11
|
+
const setAutoplayFailed = (0, __1.usePlayerStore)((x) => x.setAutoplayFailed);
|
|
12
|
+
const setMuted = (0, __1.usePlayerStore)((x) => x.setMuted);
|
|
13
|
+
const setMuteWasForced = (0, __1.usePlayerStore)((x) => x.setMuteWasForced);
|
|
14
|
+
const setUserInteraction = (0, __1.usePlayerStore)((x) => x.setUserInteraction);
|
|
15
|
+
const videoRef = (0, __1.usePlayerStore)((x) => x.videoRef);
|
|
16
|
+
const handlePlayButtonPress = () => {
|
|
17
|
+
if (videoRef && typeof videoRef === "object" && videoRef.current) {
|
|
18
|
+
videoRef.current
|
|
19
|
+
.play()
|
|
20
|
+
.then(() => {
|
|
21
|
+
setAutoplayFailed(false);
|
|
22
|
+
setUserInteraction();
|
|
23
|
+
})
|
|
24
|
+
.catch((err) => {
|
|
25
|
+
console.error("Manual play failed", err);
|
|
26
|
+
if (err.name === "NotAllowedError") {
|
|
27
|
+
setMuted(true);
|
|
28
|
+
videoRef.current.muted = true;
|
|
29
|
+
videoRef
|
|
30
|
+
.current.play()
|
|
31
|
+
.then(() => {
|
|
32
|
+
setAutoplayFailed(false);
|
|
33
|
+
setMuteWasForced(true);
|
|
34
|
+
setUserInteraction();
|
|
35
|
+
})
|
|
36
|
+
.catch((err) => {
|
|
37
|
+
console.error("Manual muted play also failed", err);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
if (!autoplayFailed)
|
|
44
|
+
return null;
|
|
45
|
+
return ((0, jsx_runtime_1.jsx)(__1.View, { style: [
|
|
46
|
+
__1.layout.position.absolute,
|
|
47
|
+
__1.layout.flex.center,
|
|
48
|
+
ui_1.h.percent[100],
|
|
49
|
+
ui_1.w.percent[100],
|
|
50
|
+
], children: (0, jsx_runtime_1.jsx)(react_native_1.Pressable, { onPress: handlePlayButtonPress, style: [
|
|
51
|
+
{
|
|
52
|
+
flexDirection: "column",
|
|
53
|
+
alignItems: "center",
|
|
54
|
+
justifyContent: "center",
|
|
55
|
+
gap: 8,
|
|
56
|
+
},
|
|
57
|
+
], children: (0, jsx_runtime_1.jsx)(__1.View, { style: [
|
|
58
|
+
ui_1.p[4],
|
|
59
|
+
{
|
|
60
|
+
backgroundColor: "rgba(200,200,255, 0.1)",
|
|
61
|
+
borderRadius: 999,
|
|
62
|
+
borderWidth: 2,
|
|
63
|
+
borderColor: "rgba(200,200,255, 0.45)",
|
|
64
|
+
boxShadow: "0 0px 4px rgba(0, 0, 0, 1)",
|
|
65
|
+
shadowColor: "rgba(0, 0, 0, 1)",
|
|
66
|
+
},
|
|
67
|
+
], children: (0, jsx_runtime_1.jsx)(lucide_react_native_1.Play, { size: "48", color: "rgba(120,120,120,0.3)", fill: "rgba(200,200,255,1)" }) }) }) }));
|
|
68
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CountdownOverlay = CountdownOverlay;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
const react_1 = require("react");
|
|
7
|
+
const react_native_reanimated_1 = tslib_1.__importStar(require("react-native-reanimated"));
|
|
8
|
+
function CountdownOverlay({ visible, width, height, startFrom = 3, onDone, }) {
|
|
9
|
+
const [countdown, setCountdown] = (0, react_1.useState)(startFrom);
|
|
10
|
+
const startTimestamp = (0, react_native_reanimated_1.useSharedValue)(null);
|
|
11
|
+
const done = (0, react_native_reanimated_1.useSharedValue)(false);
|
|
12
|
+
// Animation values
|
|
13
|
+
const scale = (0, react_native_reanimated_1.useSharedValue)(1);
|
|
14
|
+
const opacity = (0, react_native_reanimated_1.useSharedValue)(1);
|
|
15
|
+
const updateCountdown = (value) => {
|
|
16
|
+
setCountdown(value);
|
|
17
|
+
};
|
|
18
|
+
const handleDone = () => {
|
|
19
|
+
if (onDone)
|
|
20
|
+
onDone();
|
|
21
|
+
};
|
|
22
|
+
// Accurate countdown using useFrameCallback
|
|
23
|
+
(0, react_native_reanimated_1.useFrameCallback)(({ timestamp }) => {
|
|
24
|
+
if (!visible)
|
|
25
|
+
return;
|
|
26
|
+
// Set start timestamp on first frame
|
|
27
|
+
if (startTimestamp.value === null) {
|
|
28
|
+
startTimestamp.value = timestamp;
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const elapsed = (timestamp - startTimestamp.value) / 1000; // Convert to seconds
|
|
32
|
+
const remaining = Math.max(0, startFrom - Math.floor(elapsed));
|
|
33
|
+
// Use runOnJS to call JavaScript functions from worklet
|
|
34
|
+
(0, react_native_reanimated_1.runOnJS)(updateCountdown)(remaining);
|
|
35
|
+
if (remaining === 0 && !done.value) {
|
|
36
|
+
done.value = true;
|
|
37
|
+
(0, react_native_reanimated_1.runOnJS)(handleDone)();
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
(0, react_1.useEffect)(() => {
|
|
41
|
+
if (visible) {
|
|
42
|
+
startTimestamp.value = null; // Will be set on first frame
|
|
43
|
+
setCountdown(startFrom);
|
|
44
|
+
done.value = false;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
setCountdown(startFrom);
|
|
48
|
+
}
|
|
49
|
+
}, [visible, startFrom]);
|
|
50
|
+
// Animate scale and opacity on countdown change
|
|
51
|
+
(0, react_1.useEffect)(() => {
|
|
52
|
+
if (visible && countdown > 0) {
|
|
53
|
+
scale.value = 1;
|
|
54
|
+
opacity.value = 1;
|
|
55
|
+
scale.value = (0, react_native_reanimated_1.withTiming)(1.5, { duration: 1000 });
|
|
56
|
+
opacity.value = (0, react_native_reanimated_1.withTiming)(0, { duration: 1000 });
|
|
57
|
+
}
|
|
58
|
+
}, [countdown, visible, scale, opacity]);
|
|
59
|
+
const animatedStyle = (0, react_native_reanimated_1.useAnimatedStyle)(() => ({
|
|
60
|
+
transform: [{ scale: scale.value }],
|
|
61
|
+
opacity: opacity.value,
|
|
62
|
+
}));
|
|
63
|
+
if (!visible || countdown === 0)
|
|
64
|
+
return null;
|
|
65
|
+
return ((0, jsx_runtime_1.jsx)(react_native_reanimated_1.default.View, { style: {
|
|
66
|
+
position: "absolute",
|
|
67
|
+
top: 0,
|
|
68
|
+
left: 0,
|
|
69
|
+
width,
|
|
70
|
+
height,
|
|
71
|
+
backgroundColor: "rgba(0,0,0,0.7)",
|
|
72
|
+
alignItems: "center",
|
|
73
|
+
justifyContent: "center",
|
|
74
|
+
zIndex: 1000,
|
|
75
|
+
}, children: (0, jsx_runtime_1.jsx)(react_native_reanimated_1.default.Text, { style: [
|
|
76
|
+
{
|
|
77
|
+
color: "white",
|
|
78
|
+
fontSize: 120,
|
|
79
|
+
fontWeight: "bold",
|
|
80
|
+
},
|
|
81
|
+
animatedStyle,
|
|
82
|
+
], children: typeof countdown === "number" ? countdown : "" }) }));
|
|
83
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
tslib_1.__exportStar(require("./autoplay-button"), exports);
|
|
5
|
+
tslib_1.__exportStar(require("./countdown"), exports);
|
|
6
|
+
tslib_1.__exportStar(require("./input"), exports);
|
|
7
|
+
tslib_1.__exportStar(require("./metrics"), exports);
|
|
8
|
+
tslib_1.__exportStar(require("./streamer-context-menu"), exports);
|
|
9
|
+
tslib_1.__exportStar(require("./streamer-loading-overlay"), exports);
|
|
10
|
+
tslib_1.__exportStar(require("./viewer-context-menu"), exports);
|
|
11
|
+
tslib_1.__exportStar(require("./viewer-loading-overlay"), exports);
|
|
12
|
+
tslib_1.__exportStar(require("./viewers"), exports);
|