@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.
Files changed (122) hide show
  1. package/dist/assets/emoji-data.json +19371 -0
  2. package/dist/components/chat/chat-box.js +319 -0
  3. package/dist/components/chat/chat-message.js +87 -0
  4. package/dist/components/chat/chat.js +150 -0
  5. package/dist/components/chat/emoji-suggestions.js +35 -0
  6. package/dist/components/chat/mention-suggestions.js +42 -0
  7. package/dist/components/chat/mod-view.js +112 -0
  8. package/dist/components/chat/system-message.js +19 -0
  9. package/dist/components/dashboard/chat-panel.js +38 -0
  10. package/dist/components/dashboard/header.js +80 -0
  11. package/dist/components/dashboard/index.js +14 -0
  12. package/dist/components/dashboard/information-widget.js +234 -0
  13. package/dist/components/dashboard/mod-actions.js +71 -0
  14. package/dist/components/dashboard/problems.js +74 -0
  15. package/dist/components/icons/bluesky-icon.js +9 -0
  16. package/dist/components/keep-awake.js +7 -0
  17. package/dist/components/keep-awake.native.js +16 -0
  18. package/dist/components/mobile-player/fullscreen.js +76 -0
  19. package/dist/components/mobile-player/fullscreen.native.js +141 -0
  20. package/dist/components/mobile-player/player.js +94 -0
  21. package/dist/components/mobile-player/props.js +2 -0
  22. package/dist/components/mobile-player/shared.js +54 -0
  23. package/dist/components/mobile-player/ui/autoplay-button.js +68 -0
  24. package/dist/components/mobile-player/ui/countdown.js +83 -0
  25. package/dist/components/mobile-player/ui/index.js +12 -0
  26. package/dist/components/mobile-player/ui/input.js +42 -0
  27. package/dist/components/mobile-player/ui/metrics.js +44 -0
  28. package/dist/components/mobile-player/ui/report-modal.js +90 -0
  29. package/dist/components/mobile-player/ui/streamer-context-menu.js +7 -0
  30. package/dist/components/mobile-player/ui/streamer-loading-overlay.js +104 -0
  31. package/dist/components/mobile-player/ui/viewer-context-menu.js +51 -0
  32. package/dist/components/mobile-player/ui/viewer-loading-overlay.js +49 -0
  33. package/dist/components/mobile-player/ui/viewers.js +23 -0
  34. package/dist/components/mobile-player/use-webrtc.js +243 -0
  35. package/dist/components/mobile-player/video-async.native.js +276 -0
  36. package/dist/components/mobile-player/video-retry.js +29 -0
  37. package/dist/components/mobile-player/video.js +475 -0
  38. package/dist/components/mobile-player/video.native.js +56 -0
  39. package/dist/components/mobile-player/webrtc-diagnostics.js +110 -0
  40. package/dist/components/mobile-player/webrtc-primitives.js +27 -0
  41. package/dist/components/mobile-player/webrtc-primitives.native.js +8 -0
  42. package/dist/components/share/sharesheet.js +91 -0
  43. package/dist/components/ui/button.js +223 -0
  44. package/dist/components/ui/dialog.js +206 -0
  45. package/dist/components/ui/dropdown.js +172 -0
  46. package/dist/components/ui/icons.js +25 -0
  47. package/dist/components/ui/index.js +34 -0
  48. package/dist/components/ui/info-box.js +31 -0
  49. package/dist/components/ui/info-row.js +23 -0
  50. package/dist/components/ui/input.js +205 -0
  51. package/dist/components/ui/loader.js +10 -0
  52. package/dist/components/ui/primitives/button.js +125 -0
  53. package/dist/components/ui/primitives/input.js +206 -0
  54. package/dist/components/ui/primitives/modal.js +206 -0
  55. package/dist/components/ui/primitives/text.js +292 -0
  56. package/dist/components/ui/resizeable.js +121 -0
  57. package/dist/components/ui/slider.js +5 -0
  58. package/dist/components/ui/text.js +177 -0
  59. package/dist/components/ui/textarea.js +19 -0
  60. package/dist/components/ui/toast.js +175 -0
  61. package/dist/components/ui/view.js +252 -0
  62. package/dist/hooks/index.js +14 -0
  63. package/dist/hooks/useAvatars.js +35 -0
  64. package/dist/hooks/useCameraToggle.js +12 -0
  65. package/dist/hooks/useKeyboard.js +36 -0
  66. package/dist/hooks/useKeyboardSlide.js +14 -0
  67. package/dist/hooks/useLivestreamInfo.js +69 -0
  68. package/dist/hooks/useOuterAndInnerDimensions.js +30 -0
  69. package/dist/hooks/usePlayerDimensions.js +22 -0
  70. package/dist/hooks/usePointerDevice.js +71 -0
  71. package/dist/hooks/useSegmentDimensions.js +17 -0
  72. package/dist/hooks/useSegmentTiming.js +65 -0
  73. package/dist/index.js +34 -0
  74. package/dist/lib/browser.js +35 -0
  75. package/dist/lib/facet.js +92 -0
  76. package/dist/lib/system-messages.js +101 -0
  77. package/dist/lib/theme/atoms.js +646 -0
  78. package/dist/lib/theme/atoms.types.js +6 -0
  79. package/dist/lib/theme/index.js +35 -0
  80. package/dist/lib/theme/theme.js +256 -0
  81. package/dist/lib/theme/tokens.js +659 -0
  82. package/dist/lib/utils.js +105 -0
  83. package/dist/livestream-provider/index.js +30 -0
  84. package/dist/livestream-provider/websocket.js +45 -0
  85. package/dist/livestream-store/chat.js +308 -0
  86. package/dist/livestream-store/context.js +5 -0
  87. package/dist/livestream-store/index.js +7 -0
  88. package/dist/livestream-store/livestream-state.js +2 -0
  89. package/dist/livestream-store/livestream-store.js +58 -0
  90. package/dist/livestream-store/problems.js +76 -0
  91. package/dist/livestream-store/stream-key.js +88 -0
  92. package/dist/livestream-store/websocket-consumer.js +94 -0
  93. package/dist/player-store/context.js +5 -0
  94. package/dist/player-store/index.js +9 -0
  95. package/dist/player-store/player-provider.js +58 -0
  96. package/dist/player-store/player-state.js +25 -0
  97. package/dist/player-store/player-store.js +201 -0
  98. package/dist/player-store/single-player-provider.js +121 -0
  99. package/dist/streamplace-provider/context.js +5 -0
  100. package/dist/streamplace-provider/index.js +20 -0
  101. package/dist/streamplace-provider/poller.js +49 -0
  102. package/dist/streamplace-provider/xrpc.js +0 -0
  103. package/dist/streamplace-store/block.js +65 -0
  104. package/dist/streamplace-store/index.js +6 -0
  105. package/dist/streamplace-store/stream.js +247 -0
  106. package/dist/streamplace-store/streamplace-store.js +47 -0
  107. package/dist/streamplace-store/user.js +52 -0
  108. package/dist/streamplace-store/xrpc.js +15 -0
  109. package/dist/ui/index.js +79 -0
  110. package/node-compile-cache/v22.15.0-x64-efe9a9df-0/37be0eec +0 -0
  111. package/package.json +5 -4
  112. package/src/components/chat/chat-box.tsx +3 -0
  113. package/src/components/chat/mod-view.tsx +39 -5
  114. package/src/components/mobile-player/fullscreen.tsx +2 -0
  115. package/src/components/mobile-player/ui/autoplay-button.tsx +86 -0
  116. package/src/components/mobile-player/ui/index.ts +1 -0
  117. package/src/components/mobile-player/video.tsx +11 -1
  118. package/src/livestream-store/chat.tsx +22 -0
  119. package/src/player-store/player-provider.tsx +2 -1
  120. package/src/player-store/player-state.tsx +6 -0
  121. package/src/player-store/player-store.tsx +4 -0
  122. 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,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KeepAwake = KeepAwake;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ function KeepAwake() {
6
+ return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, {});
7
+ }
@@ -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,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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);