@streamplace/components 0.7.2 → 0.7.7
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/components/chat/chat-box.js +212 -24
- package/dist/components/chat/chat-message.js +5 -5
- package/dist/components/chat/chat.js +83 -5
- package/dist/components/chat/emoji-suggestions.js +35 -0
- package/dist/components/chat/mod-view.js +59 -8
- package/dist/components/chat/system-message.js +19 -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 +2 -1
- package/dist/components/mobile-player/fullscreen.native.js +3 -3
- package/dist/components/mobile-player/player.js +15 -30
- package/dist/components/mobile-player/ui/index.js +2 -0
- package/dist/components/mobile-player/ui/report-modal.js +90 -0
- package/dist/components/mobile-player/ui/streamer-loading-overlay.js +104 -0
- package/dist/components/mobile-player/ui/viewer-context-menu.js +20 -1
- package/dist/components/mobile-player/ui/viewer-loading-overlay.js +49 -0
- package/dist/components/mobile-player/use-webrtc.js +7 -1
- package/dist/components/mobile-player/video-retry.js +29 -0
- package/dist/components/mobile-player/video.js +84 -9
- package/dist/components/mobile-player/video.native.js +24 -10
- package/dist/components/share/sharesheet.js +91 -0
- package/dist/components/ui/dialog.js +1 -1
- package/dist/components/ui/dropdown.js +6 -6
- package/dist/components/ui/index.js +2 -0
- package/dist/components/ui/primitives/modal.js +0 -1
- package/dist/components/ui/resizeable.js +20 -11
- package/dist/components/ui/slider.js +5 -0
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/usePointerDevice.js +71 -0
- package/dist/index.js +10 -3
- package/dist/lib/system-messages.js +101 -0
- package/dist/livestream-store/chat.js +111 -18
- package/dist/livestream-store/livestream-store.js +3 -0
- package/dist/livestream-store/problems.js +76 -0
- package/dist/livestream-store/websocket-consumer.js +39 -4
- package/dist/player-store/player-store.js +33 -4
- package/dist/streamplace-store/block.js +51 -12
- package/dist/streamplace-store/stream.js +44 -23
- package/dist/ui/index.js +79 -0
- package/node-compile-cache/v22.15.0-x64-efe9a9df-0/37be0eec +0 -0
- package/node-compile-cache/v22.15.0-x64-efe9a9df-0/56540125 +0 -0
- package/node-compile-cache/{v22.15.0-x64-92db9086-0 → v22.15.0-x64-efe9a9df-0}/67b1eb60 +0 -0
- package/node-compile-cache/{v22.15.0-x64-92db9086-0 → v22.15.0-x64-efe9a9df-0}/7c275f90 +0 -0
- package/package.json +6 -2
- package/src/components/chat/chat-box.tsx +295 -25
- package/src/components/chat/chat-message.tsx +6 -7
- package/src/components/chat/chat.tsx +192 -41
- package/src/components/chat/emoji-suggestions.tsx +94 -0
- package/src/components/chat/mod-view.tsx +119 -40
- package/src/components/chat/system-message.tsx +38 -0
- package/src/components/icons/bluesky-icon.tsx +9 -0
- package/src/components/keep-awake.native.tsx +13 -0
- package/src/components/keep-awake.tsx +3 -0
- package/src/components/mobile-player/fullscreen.native.tsx +12 -3
- package/src/components/mobile-player/fullscreen.tsx +10 -3
- package/src/components/mobile-player/player.tsx +28 -36
- package/src/components/mobile-player/props.tsx +1 -0
- package/src/components/mobile-player/ui/index.ts +2 -0
- package/src/components/mobile-player/ui/report-modal.tsx +195 -0
- package/src/components/mobile-player/ui/streamer-loading-overlay.tsx +154 -0
- package/src/components/mobile-player/ui/viewer-context-menu.tsx +31 -3
- package/src/components/mobile-player/ui/viewer-loading-overlay.tsx +66 -0
- package/src/components/mobile-player/use-webrtc.tsx +10 -2
- package/src/components/mobile-player/video-retry.tsx +28 -0
- package/src/components/mobile-player/video.native.tsx +24 -10
- package/src/components/mobile-player/video.tsx +100 -21
- package/src/components/share/sharesheet.tsx +185 -0
- package/src/components/ui/dialog.tsx +1 -1
- package/src/components/ui/dropdown.tsx +13 -13
- package/src/components/ui/index.ts +2 -0
- package/src/components/ui/primitives/modal.tsx +0 -1
- package/src/components/ui/resizeable.tsx +26 -15
- package/src/components/ui/slider.tsx +1 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/usePointerDevice.ts +89 -0
- package/src/index.tsx +11 -2
- package/src/lib/system-messages.ts +135 -0
- package/src/livestream-store/chat.tsx +145 -17
- package/src/livestream-store/livestream-state.tsx +10 -0
- package/src/livestream-store/livestream-store.tsx +3 -0
- package/src/livestream-store/problems.tsx +96 -0
- package/src/livestream-store/websocket-consumer.tsx +44 -4
- package/src/player-store/player-state.tsx +25 -4
- package/src/player-store/player-store.tsx +43 -5
- package/src/streamplace-store/block.tsx +55 -13
- package/src/streamplace-store/stream.tsx +66 -35
- package/src/ui/index.ts +86 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/node-compile-cache/v22.15.0-x64-92db9086-0/37be0eec +0 -0
- package/node-compile-cache/v22.15.0-x64-92db9086-0/56540125 +0 -0
|
@@ -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
|
+
}
|
|
@@ -7,6 +7,7 @@ const react_1 = require("react");
|
|
|
7
7
|
const __1 = require("../..");
|
|
8
8
|
const ui_1 = require("../../components/ui");
|
|
9
9
|
const video_1 = tslib_1.__importDefault(require("./video"));
|
|
10
|
+
const video_retry_1 = tslib_1.__importDefault(require("./video-retry"));
|
|
10
11
|
function Fullscreen(props) {
|
|
11
12
|
const playerId = (0, __1.getFirstPlayerID)();
|
|
12
13
|
const protocol = (0, __1.usePlayerStore)((x) => x.protocol, playerId);
|
|
@@ -69,5 +70,5 @@ function Fullscreen(props) {
|
|
|
69
70
|
document.body.removeEventListener("webkitfullscreenchange", listener);
|
|
70
71
|
};
|
|
71
72
|
}, []);
|
|
72
|
-
return ((0, jsx_runtime_1.
|
|
73
|
+
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, {}) }), props.children] }));
|
|
73
74
|
}
|
|
@@ -119,7 +119,7 @@ function Fullscreen(props) {
|
|
|
119
119
|
width: isLandscape ? dimensions.width + 40 : dimensions.width,
|
|
120
120
|
height: dimensions.height,
|
|
121
121
|
},
|
|
122
|
-
], children: (0, jsx_runtime_1.
|
|
122
|
+
], children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [
|
|
123
123
|
styles.videoContainer,
|
|
124
124
|
{
|
|
125
125
|
width: isLandscape ? videoWidth + 40 : videoWidth,
|
|
@@ -127,10 +127,10 @@ function Fullscreen(props) {
|
|
|
127
127
|
left: leftPosition,
|
|
128
128
|
top: topPosition,
|
|
129
129
|
},
|
|
130
|
-
], children: (0, jsx_runtime_1.jsx)(video_native_1.default, {}) }) }));
|
|
130
|
+
], children: [(0, jsx_runtime_1.jsx)(video_native_1.default, {}), props.children] }) }));
|
|
131
131
|
}
|
|
132
132
|
// Normal non-fullscreen mode
|
|
133
|
-
return ((0, jsx_runtime_1.
|
|
133
|
+
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, {}) }), props.children] }));
|
|
134
134
|
}
|
|
135
135
|
const styles = react_native_1.StyleSheet.create({
|
|
136
136
|
fullscreenContainer: {
|
|
@@ -7,18 +7,23 @@ const tslib_1 = require("tslib");
|
|
|
7
7
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
8
|
const react_1 = require("react");
|
|
9
9
|
const atoms_1 = require("../../lib/theme/atoms");
|
|
10
|
-
const livestream_store_1 = require("../../livestream-store");
|
|
11
10
|
const player_store_1 = require("../../player-store");
|
|
12
11
|
const streamplace_store_1 = require("../../streamplace-store");
|
|
13
12
|
const ui_1 = require("../ui");
|
|
14
13
|
const fullscreen_1 = require("./fullscreen");
|
|
14
|
+
const report_modal_1 = tslib_1.__importDefault(require("./ui/report-modal"));
|
|
15
15
|
const OFFLINE_THRESHOLD = 10000;
|
|
16
16
|
exports.PlayerUI = tslib_1.__importStar(require("./ui"));
|
|
17
17
|
function Player(props) {
|
|
18
|
-
const playing = (0, player_store_1.usePlayerStore)((x) => x.status === player_store_1.PlayerStatus.PLAYING);
|
|
19
|
-
const setOffline = (0, player_store_1.usePlayerStore)((x) => x.setOffline);
|
|
20
18
|
const setIngest = (0, player_store_1.usePlayerStore)((x) => x.setIngestConnectionState);
|
|
21
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]);
|
|
22
27
|
// Will call back every few seconds to send health updates
|
|
23
28
|
usePlayerStatus();
|
|
24
29
|
(0, react_1.useEffect)(() => {
|
|
@@ -32,33 +37,13 @@ function Player(props) {
|
|
|
32
37
|
clearControlsTimeout();
|
|
33
38
|
};
|
|
34
39
|
}, []);
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (!segment) {
|
|
43
|
-
setOffline(false);
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
const startTime = Date.parse(segment.startTime);
|
|
47
|
-
if (!startTime) {
|
|
48
|
-
console.error("startTime is not a number", segment.startTime);
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
const timeSinceStart = Date.now() - startTime;
|
|
52
|
-
if (timeSinceStart > OFFLINE_THRESHOLD) {
|
|
53
|
-
setOffline(true);
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
const handle = setTimeout(() => {
|
|
57
|
-
setLastCheck(Date.now());
|
|
58
|
-
}, 1000);
|
|
59
|
-
return () => clearTimeout(handle);
|
|
60
|
-
}, [segment, playing, lastCheck]);
|
|
61
|
-
return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsx)(ui_1.View, { style: [atoms_1.zIndex[0], atoms_1.flex.values[1], atoms_1.w.percent[100], atoms_1.layout.flex.center], children: (0, jsx_runtime_1.jsx)(fullscreen_1.Fullscreen, { src: props.src }) }) }));
|
|
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, children: props.children })] }) }));
|
|
62
47
|
}
|
|
63
48
|
const POLL_INTERVAL = 5000;
|
|
64
49
|
function usePlayerStatus() {
|
|
@@ -5,5 +5,7 @@ tslib_1.__exportStar(require("./countdown"), exports);
|
|
|
5
5
|
tslib_1.__exportStar(require("./input"), exports);
|
|
6
6
|
tslib_1.__exportStar(require("./metrics"), exports);
|
|
7
7
|
tslib_1.__exportStar(require("./streamer-context-menu"), exports);
|
|
8
|
+
tslib_1.__exportStar(require("./streamer-loading-overlay"), exports);
|
|
8
9
|
tslib_1.__exportStar(require("./viewer-context-menu"), exports);
|
|
10
|
+
tslib_1.__exportStar(require("./viewer-loading-overlay"), exports);
|
|
9
11
|
tslib_1.__exportStar(require("./viewers"), exports);
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ReportModal = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const api_1 = require("@atproto/api");
|
|
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 __1 = require("../../..");
|
|
10
|
+
const livestream_store_1 = require("../../../livestream-store");
|
|
11
|
+
const ui_1 = require("../../ui");
|
|
12
|
+
// AT Protocol moderation reason types with proper labels
|
|
13
|
+
const REPORT_REASONS = [
|
|
14
|
+
{
|
|
15
|
+
value: api_1.ComAtprotoModerationDefs.REASONSPAM,
|
|
16
|
+
label: "Spam",
|
|
17
|
+
description: "Excessive unwanted promotion, replies, mentions",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
value: api_1.ComAtprotoModerationDefs.REASONVIOLATION,
|
|
21
|
+
label: "Rule Violation",
|
|
22
|
+
description: "Direct, blatant violation of laws or terms of service",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
value: api_1.ComAtprotoModerationDefs.REASONMISLEADING,
|
|
26
|
+
label: "Misleading Content",
|
|
27
|
+
description: "Misleading identity, affiliation, or content",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
value: api_1.ComAtprotoModerationDefs.REASONSEXUAL,
|
|
31
|
+
label: "Sexual Content",
|
|
32
|
+
description: "Unwanted or mislabeled sexual content",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
value: api_1.ComAtprotoModerationDefs.REASONRUDE,
|
|
36
|
+
label: "Harassment",
|
|
37
|
+
description: "Rude, harassing, explicit, or otherwise unwelcoming behavior",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
value: api_1.ComAtprotoModerationDefs.REASONOTHER,
|
|
41
|
+
label: "Other",
|
|
42
|
+
description: "Reports not falling under another report category",
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
const ReportModal = ({ open, onOpenChange, onSubmit, subject, title = "Report", description = "Why are you submitting this report?", }) => {
|
|
46
|
+
const [selectedReason, setSelectedReason] = (0, react_1.useState)(null);
|
|
47
|
+
const [additionalComments, setAdditionalComments] = (0, react_1.useState)("");
|
|
48
|
+
const [isSubmitting, setIsSubmitting] = (0, react_1.useState)(false);
|
|
49
|
+
const [submitError, setSubmitError] = (0, react_1.useState)(null);
|
|
50
|
+
const submitReport = (0, livestream_store_1.useSubmitReport)();
|
|
51
|
+
const handleCancel = () => {
|
|
52
|
+
setSelectedReason(null);
|
|
53
|
+
setAdditionalComments("");
|
|
54
|
+
setSubmitError(null);
|
|
55
|
+
onOpenChange(false);
|
|
56
|
+
};
|
|
57
|
+
const handleSubmit = async () => {
|
|
58
|
+
if (!selectedReason)
|
|
59
|
+
return;
|
|
60
|
+
setIsSubmitting(true);
|
|
61
|
+
setSubmitError(null);
|
|
62
|
+
try {
|
|
63
|
+
submitReport(subject, selectedReason, additionalComments.trim() || undefined);
|
|
64
|
+
// Reset form and close modal on success
|
|
65
|
+
setSelectedReason(null);
|
|
66
|
+
setAdditionalComments("");
|
|
67
|
+
onOpenChange(false);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
console.error("Failed to submit report:", error);
|
|
71
|
+
setSubmitError("Failed to submit report. Please try again.");
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
setIsSubmitting(false);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
return ((0, jsx_runtime_1.jsxs)(ui_1.Dialog, { open: open, onOpenChange: onOpenChange, title: title, description: description, showCloseButton: true, variant: "default", size: "md", dismissible: false, position: "center", children: [(0, jsx_runtime_1.jsxs)(ui_1.ModalContent, { style: [__1.zero.pb[2]], children: [REPORT_REASONS.map((reason) => ((0, jsx_runtime_1.jsxs)(react_native_1.TouchableOpacity, { onPress: () => setSelectedReason(reason.value), style: [
|
|
78
|
+
__1.zero.layout.flex.row,
|
|
79
|
+
__1.zero.gap.all[2],
|
|
80
|
+
__1.zero.py[3],
|
|
81
|
+
__1.zero.px[3],
|
|
82
|
+
__1.zero.borderRadius[8],
|
|
83
|
+
__1.zero.layout.flex.alignCenter,
|
|
84
|
+
selectedReason === reason.value && {
|
|
85
|
+
backgroundColor: "rgba(0, 122, 255, 0.1)",
|
|
86
|
+
},
|
|
87
|
+
], children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { children: selectedReason === reason.value ? (0, jsx_runtime_1.jsx)(lucide_react_native_1.CheckCircle, {}) : (0, jsx_runtime_1.jsx)(lucide_react_native_1.Circle, {}) }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [__1.zero.layout.flex.column, __1.zero.gap.all[1], __1.zero.flex[1]], children: [(0, jsx_runtime_1.jsx)(ui_1.Text, { style: [{ fontWeight: "600" }], children: reason.label }), (0, jsx_runtime_1.jsx)(ui_1.Text, { style: [{ fontSize: 14, color: "rgba(255,255,255,0.7)" }], children: reason.description })] })] }, reason.value))), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [__1.zero.pb[4], __1.zero.mt[4], __1.zero.px[2]], children: [(0, jsx_runtime_1.jsx)(ui_1.Text, { style: [__1.zero.mb[2]], children: "Additional Comments (optional)" }), (0, jsx_runtime_1.jsx)(ui_1.Textarea, { maxLength: 500, numberOfLines: 3, value: additionalComments, onChangeText: setAdditionalComments, placeholder: "Provide additional context for this report..." }), submitError && ((0, jsx_runtime_1.jsx)(ui_1.Text, { style: [__1.zero.mt[2], { color: "red", fontSize: 14 }], children: submitError }))] })] }), (0, jsx_runtime_1.jsxs)(ui_1.DialogFooter, { children: [(0, jsx_runtime_1.jsx)(ui_1.Button, { variant: "secondary", onPress: handleCancel, disabled: isSubmitting, children: (0, jsx_runtime_1.jsx)(ui_1.Text, { children: "Cancel" }) }), (0, jsx_runtime_1.jsx)(ui_1.Button, { variant: "primary", onPress: handleSubmit, disabled: !selectedReason || isSubmitting, children: isSubmitting ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(lucide_react_native_1.Loader2, { style: [{ marginRight: 8 }] }), (0, jsx_runtime_1.jsx)(ui_1.Text, { children: "Submitting..." })] })) : ((0, jsx_runtime_1.jsx)(ui_1.Text, { children: "Submit" })) })] })] }));
|
|
88
|
+
};
|
|
89
|
+
exports.ReportModal = ReportModal;
|
|
90
|
+
exports.default = exports.ReportModal;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LoadingOverlay = LoadingOverlay;
|
|
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
|
+
const atoms_1 = require("../../../lib/theme/atoms");
|
|
9
|
+
const defaultMessages = [
|
|
10
|
+
"Creating the stream",
|
|
11
|
+
"Uploading thumbnails",
|
|
12
|
+
"Getting things ready",
|
|
13
|
+
"Doing some magic",
|
|
14
|
+
"Preparing something special",
|
|
15
|
+
"Reticulating splines",
|
|
16
|
+
"Making it nice",
|
|
17
|
+
"Flipping some switches",
|
|
18
|
+
"Adding good vibes",
|
|
19
|
+
"Almost there",
|
|
20
|
+
"Summoning your Persona",
|
|
21
|
+
"Awakening our true selves",
|
|
22
|
+
"Fusion in progress",
|
|
23
|
+
"Equipping the right materia",
|
|
24
|
+
];
|
|
25
|
+
function LoadingOverlay({ visible, width, height, subtitle, messages = defaultMessages, interval = 3000, }) {
|
|
26
|
+
const [currentIndex, setCurrentIndex] = (0, react_1.useState)(0);
|
|
27
|
+
const [shouldRender, setShouldRender] = (0, react_1.useState)(visible);
|
|
28
|
+
// Animation values
|
|
29
|
+
const translateY = (0, react_native_reanimated_1.useSharedValue)(0);
|
|
30
|
+
const opacity = (0, react_native_reanimated_1.useSharedValue)(1);
|
|
31
|
+
const wholeOpacity = (0, react_native_reanimated_1.useSharedValue)(0);
|
|
32
|
+
// Handle fade-in and fade-out animations
|
|
33
|
+
(0, react_1.useEffect)(() => {
|
|
34
|
+
if (visible) {
|
|
35
|
+
setShouldRender(true); // Ensure the component is mounted
|
|
36
|
+
wholeOpacity.value = (0, react_native_reanimated_1.withTiming)(1, { duration: 500 }); // Fade in
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
wholeOpacity.value = (0, react_native_reanimated_1.withTiming)(0, { duration: 500 }, () => {
|
|
40
|
+
// Unmount after fade-out
|
|
41
|
+
(0, react_native_reanimated_1.runOnJS)(setShouldRender)(false);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}, [visible]);
|
|
45
|
+
// Cycle messages on a timer
|
|
46
|
+
(0, react_1.useEffect)(() => {
|
|
47
|
+
if (!visible) {
|
|
48
|
+
setCurrentIndex(0);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const timeout = setTimeout(() => {
|
|
52
|
+
setCurrentIndex((prev) => (prev + 1) % messages.length);
|
|
53
|
+
}, interval);
|
|
54
|
+
return () => clearTimeout(timeout);
|
|
55
|
+
}, [visible, currentIndex, interval, messages.length]);
|
|
56
|
+
// Trigger animation on each message change
|
|
57
|
+
(0, react_1.useEffect)(() => {
|
|
58
|
+
if (!visible)
|
|
59
|
+
return;
|
|
60
|
+
const fadeDuration = Math.min(interval / 2, 250); // Simplified fade duration
|
|
61
|
+
// Reset animation values
|
|
62
|
+
translateY.value = 20;
|
|
63
|
+
opacity.value = 0;
|
|
64
|
+
// Sequential fade-in and fade-out
|
|
65
|
+
translateY.value = (0, react_native_reanimated_1.withTiming)(0, { duration: fadeDuration });
|
|
66
|
+
opacity.value = (0, react_native_reanimated_1.withTiming)(1, { duration: fadeDuration }, () => {
|
|
67
|
+
// add a delay for interval - (fadeDuration*2)
|
|
68
|
+
translateY.value = (0, react_native_reanimated_1.withDelay)(interval - fadeDuration * 2, (0, react_native_reanimated_1.withTiming)(-10, { duration: fadeDuration }));
|
|
69
|
+
opacity.value = (0, react_native_reanimated_1.withDelay)(interval - fadeDuration * 2, (0, react_native_reanimated_1.withTiming)(0, { duration: fadeDuration }));
|
|
70
|
+
});
|
|
71
|
+
}, [currentIndex, visible]);
|
|
72
|
+
const animatedStyle = (0, react_native_reanimated_1.useAnimatedStyle)(() => {
|
|
73
|
+
return {
|
|
74
|
+
transform: [{ translateY: translateY.value }],
|
|
75
|
+
opacity: opacity.value,
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
const wholeAnimatedStyle = (0, react_native_reanimated_1.useAnimatedStyle)(() => ({
|
|
79
|
+
opacity: wholeOpacity.value,
|
|
80
|
+
}));
|
|
81
|
+
if (!shouldRender)
|
|
82
|
+
return null;
|
|
83
|
+
return ((0, jsx_runtime_1.jsxs)(react_native_reanimated_1.default.View, { style: [
|
|
84
|
+
{
|
|
85
|
+
position: "absolute",
|
|
86
|
+
top: 0,
|
|
87
|
+
left: 0,
|
|
88
|
+
width,
|
|
89
|
+
height,
|
|
90
|
+
backgroundColor: "rgba(0,0,0,0.7)",
|
|
91
|
+
alignItems: "center",
|
|
92
|
+
justifyContent: "center",
|
|
93
|
+
zIndex: 1000,
|
|
94
|
+
},
|
|
95
|
+
wholeAnimatedStyle,
|
|
96
|
+
], children: [(0, jsx_runtime_1.jsx)(react_native_reanimated_1.default.Text, { style: [
|
|
97
|
+
{
|
|
98
|
+
color: "white",
|
|
99
|
+
fontSize: 24,
|
|
100
|
+
fontWeight: "bold",
|
|
101
|
+
},
|
|
102
|
+
animatedStyle,
|
|
103
|
+
], children: messages[currentIndex] }), (0, jsx_runtime_1.jsx)(react_native_reanimated_1.default.Text, { style: [atoms_1.pt[5], { color: "#a0a0a0" }], children: subtitle })] }));
|
|
104
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ContextMenu = ContextMenu;
|
|
4
|
+
exports.ReportButton = ReportButton;
|
|
4
5
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
const dropdown_menu_1 = require("@rn-primitives/dropdown-menu");
|
|
5
7
|
const lucide_react_native_1 = require("lucide-react-native");
|
|
6
8
|
const theme_1 = require("../../../lib/theme");
|
|
7
9
|
const livestream_store_1 = require("../../../livestream-store");
|
|
@@ -19,5 +21,22 @@ function ContextMenu() {
|
|
|
19
21
|
const setLowLatency = (value) => {
|
|
20
22
|
setProtocol(value ? player_store_1.PlayerProtocol.WEBRTC : player_store_1.PlayerProtocol.HLS);
|
|
21
23
|
};
|
|
22
|
-
return ((0, jsx_runtime_1.jsxs)(ui_1.DropdownMenu, { children: [(0, jsx_runtime_1.jsx)(ui_1.DropdownMenuTrigger, { children: (0, jsx_runtime_1.jsx)(lucide_react_native_1.
|
|
24
|
+
return ((0, jsx_runtime_1.jsxs)(ui_1.DropdownMenu, { children: [(0, jsx_runtime_1.jsx)(ui_1.DropdownMenuTrigger, { children: (0, jsx_runtime_1.jsx)(lucide_react_native_1.Settings, { color: theme_1.colors.gray[200] }) }), (0, jsx_runtime_1.jsxs)(ui_1.ResponsiveDropdownMenuContent, { side: "top", align: "end", children: [(0, jsx_runtime_1.jsx)(ui_1.DropdownMenuGroup, { title: "Resolution", children: (0, jsx_runtime_1.jsxs)(ui_1.DropdownMenuRadioGroup, { value: quality, onValueChange: setQuality, children: [(0, jsx_runtime_1.jsx)(ui_1.DropdownMenuRadioItem, { value: "source", children: (0, jsx_runtime_1.jsx)(ui_1.Text, { children: "Source (Original Quality)" }) }), qualities.map((r) => ((0, jsx_runtime_1.jsx)(ui_1.DropdownMenuRadioItem, { value: r.name, children: (0, jsx_runtime_1.jsx)(ui_1.Text, { children: r.name }) })))] }) }), (0, jsx_runtime_1.jsx)(ui_1.DropdownMenuGroup, { title: "Advanced", children: (0, jsx_runtime_1.jsx)(ui_1.DropdownMenuCheckboxItem, { checked: lowLatency, onCheckedChange: () => setLowLatency(!lowLatency), children: (0, jsx_runtime_1.jsx)(ui_1.Text, { children: "Low Latency" }) }) }), (0, jsx_runtime_1.jsx)(ui_1.DropdownMenuInfo, { description: "Reduces the delay between video and chat for a more real-time experience." }), (0, jsx_runtime_1.jsx)(ui_1.DropdownMenuGroup, { children: (0, jsx_runtime_1.jsx)(ui_1.DropdownMenuCheckboxItem, { checked: debugInfo, onCheckedChange: () => setShowDebugInfo(!debugInfo), children: (0, jsx_runtime_1.jsx)(ui_1.Text, { children: "Show Debug Info" }) }) }), (0, jsx_runtime_1.jsx)(ui_1.DropdownMenuGroup, { title: "Report", children: (0, jsx_runtime_1.jsx)(ReportButton, {}) })] })] }));
|
|
25
|
+
}
|
|
26
|
+
function ReportButton() {
|
|
27
|
+
const livestream = (0, livestream_store_1.useLivestreamStore)((x) => x.livestream);
|
|
28
|
+
const setReportModalOpen = (0, player_store_1.usePlayerStore)((x) => x.setReportModalOpen);
|
|
29
|
+
const setReportSubject = (0, player_store_1.usePlayerStore)((x) => x.setReportSubject);
|
|
30
|
+
const { onOpenChange } = (0, dropdown_menu_1.useRootContext)();
|
|
31
|
+
return ((0, jsx_runtime_1.jsx)(ui_1.DropdownMenuItem, { onPress: () => {
|
|
32
|
+
if (!livestream)
|
|
33
|
+
return;
|
|
34
|
+
onOpenChange?.(false);
|
|
35
|
+
setReportModalOpen(true);
|
|
36
|
+
setReportSubject({
|
|
37
|
+
$type: "com.atproto.repo.strongRef",
|
|
38
|
+
uri: livestream.uri,
|
|
39
|
+
cid: livestream.cid,
|
|
40
|
+
});
|
|
41
|
+
}, children: (0, jsx_runtime_1.jsx)(ui_1.Text, { children: "Report Livestream..." }) }));
|
|
23
42
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ViewerLoadingOverlay = ViewerLoadingOverlay;
|
|
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_reanimated_1 = tslib_1.__importStar(require("react-native-reanimated"));
|
|
9
|
+
const __1 = require("../../..");
|
|
10
|
+
function ViewerLoadingOverlay() {
|
|
11
|
+
const status = (0, __1.usePlayerStore)((x) => x.status);
|
|
12
|
+
const theme = (0, __1.useTheme)();
|
|
13
|
+
const opacity = (0, react_native_reanimated_1.useSharedValue)(0);
|
|
14
|
+
(0, react_1.useEffect)(() => {
|
|
15
|
+
if (status === __1.PlayerStatus.PLAYING || status === __1.PlayerStatus.SUSPEND) {
|
|
16
|
+
opacity.value = (0, react_native_reanimated_1.withTiming)(0, { duration: 300 });
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
opacity.value = (0, react_native_reanimated_1.withTiming)(1, { duration: 300 });
|
|
20
|
+
}
|
|
21
|
+
}, [status, opacity]);
|
|
22
|
+
const animatedStyle = (0, react_native_reanimated_1.useAnimatedStyle)(() => {
|
|
23
|
+
return {
|
|
24
|
+
opacity: opacity.value,
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
if (status === __1.PlayerStatus.PLAYING) {
|
|
28
|
+
return (0, jsx_runtime_1.jsx)(__1.KeepAwake, {});
|
|
29
|
+
}
|
|
30
|
+
if (status === __1.PlayerStatus.SUSPEND) {
|
|
31
|
+
return null; // No overlay when stopped
|
|
32
|
+
}
|
|
33
|
+
let spinner = (0, jsx_runtime_1.jsx)(__1.Loader, { size: "large" });
|
|
34
|
+
if (status === __1.PlayerStatus.PAUSE) {
|
|
35
|
+
spinner = (0, jsx_runtime_1.jsx)(lucide_react_native_1.Play, { size: "$12", color: theme.styles.text.primary["color"] });
|
|
36
|
+
}
|
|
37
|
+
return ((0, jsx_runtime_1.jsx)(react_native_reanimated_1.default.View, { style: [
|
|
38
|
+
{
|
|
39
|
+
position: "absolute",
|
|
40
|
+
width: "100%",
|
|
41
|
+
height: "100%",
|
|
42
|
+
zIndex: 998,
|
|
43
|
+
alignItems: "center",
|
|
44
|
+
justifyContent: "center",
|
|
45
|
+
backgroundColor: "rgba(0,0,0,0.3)",
|
|
46
|
+
},
|
|
47
|
+
animatedStyle,
|
|
48
|
+
], children: spinner }));
|
|
49
|
+
}
|
|
@@ -9,6 +9,7 @@ const webrtc_primitives_1 = require("./webrtc-primitives");
|
|
|
9
9
|
function useWebRTC(endpoint) {
|
|
10
10
|
const [mediaStream, setMediaStream] = (0, react_1.useState)(null);
|
|
11
11
|
const [stuck, setStuck] = (0, react_1.useState)(false);
|
|
12
|
+
const setStatus = (0, __1.usePlayerStore)((x) => x.setStatus);
|
|
12
13
|
const lastChange = (0, react_1.useRef)(0);
|
|
13
14
|
(0, react_1.useEffect)(() => {
|
|
14
15
|
const peerConnection = new webrtc_primitives_1.RTCPeerConnection({
|
|
@@ -29,7 +30,10 @@ function useWebRTC(endpoint) {
|
|
|
29
30
|
});
|
|
30
31
|
peerConnection.addEventListener("connectionstatechange", () => {
|
|
31
32
|
console.log("connection state change", peerConnection.connectionState);
|
|
32
|
-
if (peerConnection.connectionState === "closed"
|
|
33
|
+
if (peerConnection.connectionState === "closed" ||
|
|
34
|
+
peerConnection.connectionState === "failed" ||
|
|
35
|
+
peerConnection.connectionState === "disconnected") {
|
|
36
|
+
console.log("setting stuck to true", peerConnection.connectionState);
|
|
33
37
|
setStuck(true);
|
|
34
38
|
}
|
|
35
39
|
if (peerConnection.connectionState !== "connected") {
|
|
@@ -50,6 +54,7 @@ function useWebRTC(endpoint) {
|
|
|
50
54
|
if (lastAudioFramesReceived !== audioFramesReceived) {
|
|
51
55
|
lastAudioFramesReceived = audioFramesReceived;
|
|
52
56
|
lastChange.current = Date.now();
|
|
57
|
+
setStatus(__1.PlayerStatus.PLAYING);
|
|
53
58
|
setStuck(false);
|
|
54
59
|
}
|
|
55
60
|
}
|
|
@@ -58,6 +63,7 @@ function useWebRTC(endpoint) {
|
|
|
58
63
|
if (lastFramesReceived !== framesReceived) {
|
|
59
64
|
lastFramesReceived = framesReceived;
|
|
60
65
|
lastChange.current = Date.now();
|
|
66
|
+
setStatus(__1.PlayerStatus.PLAYING);
|
|
61
67
|
setStuck(false);
|
|
62
68
|
}
|
|
63
69
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = VideoRetry;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
const react_1 = tslib_1.__importStar(require("react"));
|
|
7
|
+
const __1 = require("../..");
|
|
8
|
+
function VideoRetry(props) {
|
|
9
|
+
const retryTimeoutRef = (0, react_1.useRef)(null);
|
|
10
|
+
const [retries, setRetries] = (0, react_1.useState)(0);
|
|
11
|
+
const playing = (0, __1.usePlayerStore)((x) => x.status === __1.PlayerStatus.PLAYING);
|
|
12
|
+
(0, react_1.useEffect)(() => {
|
|
13
|
+
if (!playing) {
|
|
14
|
+
const jitter = 500 + Math.random() * 1500;
|
|
15
|
+
retryTimeoutRef.current = setTimeout(() => {
|
|
16
|
+
console.log("Retrying video playback...");
|
|
17
|
+
setRetries((prevRetries) => prevRetries + 1);
|
|
18
|
+
}, jitter);
|
|
19
|
+
}
|
|
20
|
+
return () => {
|
|
21
|
+
if (retryTimeoutRef.current) {
|
|
22
|
+
console.log("Clearing retry timeout");
|
|
23
|
+
clearTimeout(retryTimeoutRef.current);
|
|
24
|
+
retryTimeoutRef.current = null;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}, [!playing]);
|
|
28
|
+
return (0, jsx_runtime_1.jsx)(react_1.default.Fragment, { children: props.children }, retries);
|
|
29
|
+
}
|
|
@@ -14,6 +14,7 @@ const react_1 = require("react");
|
|
|
14
14
|
const __1 = require("../..");
|
|
15
15
|
const atoms_1 = require("../../lib/theme/atoms");
|
|
16
16
|
const index_1 = require("../ui/index");
|
|
17
|
+
const loader_1 = require("../ui/loader");
|
|
17
18
|
const shared_1 = require("./shared");
|
|
18
19
|
const use_webrtc_1 = tslib_1.__importStar(require("./use-webrtc"));
|
|
19
20
|
const webrtc_diagnostics_1 = require("./webrtc-diagnostics");
|
|
@@ -102,23 +103,56 @@ const VideoElement = (0, react_1.forwardRef)((props, ref) => {
|
|
|
102
103
|
};
|
|
103
104
|
const [firstAttempt, setFirstAttempt] = (0, react_1.useState)(true);
|
|
104
105
|
const localVideoRef = props.videoRef ?? (0, react_1.useRef)(null);
|
|
106
|
+
// setPipAction comes from Zustand store
|
|
107
|
+
(0, react_1.useEffect)(() => {
|
|
108
|
+
if (typeof x.setPipAction === "function") {
|
|
109
|
+
const fn = () => {
|
|
110
|
+
if (localVideoRef.current) {
|
|
111
|
+
try {
|
|
112
|
+
localVideoRef.current.requestPictureInPicture?.();
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
console.error("Error requesting Picture-in-Picture:", err);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
console.log("No video ref available for PiP");
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
x.setPipAction(fn);
|
|
123
|
+
}
|
|
124
|
+
// Cleanup on unmount
|
|
125
|
+
return () => {
|
|
126
|
+
if (typeof x.setPipAction === "function") {
|
|
127
|
+
x.setPipAction(undefined);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}, []);
|
|
105
131
|
const canPlayThrough = (e) => {
|
|
132
|
+
console.log("canPlayThrough called", {
|
|
133
|
+
firstAttempt,
|
|
134
|
+
videoRef: !!localVideoRef.current,
|
|
135
|
+
});
|
|
136
|
+
setStatus(__1.PlayerStatus.PLAYING);
|
|
106
137
|
event("canplaythrough")(e);
|
|
107
138
|
if (firstAttempt && localVideoRef.current) {
|
|
108
139
|
setFirstAttempt(false);
|
|
140
|
+
console.log("Attempting to play video");
|
|
109
141
|
localVideoRef.current.play().catch((err) => {
|
|
142
|
+
console.log("error playing video", err.name);
|
|
110
143
|
if (err.name === "NotAllowedError") {
|
|
111
144
|
if (localVideoRef.current) {
|
|
145
|
+
console.log("Setting muted and retrying");
|
|
112
146
|
setMuted(true);
|
|
113
147
|
localVideoRef.current.muted = true;
|
|
114
148
|
localVideoRef.current
|
|
115
149
|
.play()
|
|
116
150
|
.then(() => {
|
|
117
|
-
console.
|
|
151
|
+
console.log("Muted play succeeded");
|
|
118
152
|
setMuteWasForced(true);
|
|
119
153
|
})
|
|
120
154
|
.catch((err) => {
|
|
121
|
-
console.error("
|
|
155
|
+
console.error("Muted play also failed", err);
|
|
122
156
|
});
|
|
123
157
|
}
|
|
124
158
|
}
|
|
@@ -148,15 +182,24 @@ const VideoElement = (0, react_1.forwardRef)((props, ref) => {
|
|
|
148
182
|
ref.current =
|
|
149
183
|
videoElement;
|
|
150
184
|
}
|
|
151
|
-
|
|
152
|
-
// localVideoRef.current = videoElement;
|
|
153
|
-
// }
|
|
185
|
+
localVideoRef.current = videoElement;
|
|
154
186
|
};
|
|
155
|
-
|
|
187
|
+
const eventLogger = (evType) => (e) => {
|
|
188
|
+
console.log("📺 Video event:", evType);
|
|
189
|
+
const now = new Date();
|
|
190
|
+
if (updateEvents[evType]) {
|
|
191
|
+
x.setStatus(evType);
|
|
192
|
+
}
|
|
193
|
+
console.log("Sending", evType, "status to", url);
|
|
194
|
+
playerEvent(url, now.toISOString(), evType, {});
|
|
195
|
+
};
|
|
196
|
+
return ((0, jsx_runtime_1.jsx)("video", { autoPlay: true, playsInline: true, ref: handleVideoRef, controls: false, src: ingest ? undefined : props.url, muted: muted, crossOrigin: "anonymous", onMouseMove: setUserInteraction, onClick: setUserInteraction, onAbort: event("abort"), onCanPlay: eventLogger, onCanPlayThroughCapture: eventLogger, onCanPlayThrough: canPlayThrough, onEmptied: event("emptied"), onEncrypted: event("encrypted"), onEnded: event("ended"), onError: event("error"), onLoadedData: event("loadeddata"), onLoadedMetadata: event("loadedmetadata"), onLoadStart: event("loadstart"), onPause: event("pause"), onPlay: event("play"), onPlaying: event("playing"), onRateChange: event("ratechange"), onSeeked: event("seeked"), onSeeking: event("seeking"), onStalled: event("stalled"), onSuspend: event("suspend"), onVolumeChange: event("volumechange"), onWaiting: event("waiting"), style: {
|
|
156
197
|
objectFit: "contain",
|
|
157
198
|
backgroundColor: "transparent",
|
|
158
199
|
width: "100%",
|
|
159
200
|
height: "100%",
|
|
201
|
+
maxWidth: "100%",
|
|
202
|
+
maxHeight: "100%",
|
|
160
203
|
transform: ingest ? "scaleX(-1)" : undefined,
|
|
161
204
|
} }));
|
|
162
205
|
});
|
|
@@ -238,6 +281,14 @@ function WebRTCPlayer(props) {
|
|
|
238
281
|
}
|
|
239
282
|
return (0, jsx_runtime_1.jsx)(WebRTCPlayerInner, { url: props.url, videoRef: props.videoRef });
|
|
240
283
|
}
|
|
284
|
+
const connectionStatusMessages = {
|
|
285
|
+
initializing: "Starting up...",
|
|
286
|
+
connecting: "Connecting...",
|
|
287
|
+
"connection-failed": "Connecting...",
|
|
288
|
+
connected: "Connected",
|
|
289
|
+
reconnecting: "Reconnecting...",
|
|
290
|
+
checking: "Checking connection...",
|
|
291
|
+
};
|
|
241
292
|
function WebRTCPlayerInner({ videoRef, url, width, height, }) {
|
|
242
293
|
const [connectionStatus, setConnectionStatus] = (0, react_1.useState)("initializing");
|
|
243
294
|
const status = (0, __1.usePlayerStore)((x) => x.status);
|
|
@@ -260,7 +311,7 @@ function WebRTCPlayerInner({ videoRef, url, width, height, }) {
|
|
|
260
311
|
if (stuck && status === __1.PlayerStatus.PLAYING) {
|
|
261
312
|
setStatus(__1.PlayerStatus.STALLED);
|
|
262
313
|
}
|
|
263
|
-
if (!stuck &&
|
|
314
|
+
if (!stuck && status === __1.PlayerStatus.STALLED) {
|
|
264
315
|
setStatus(__1.PlayerStatus.PLAYING);
|
|
265
316
|
}
|
|
266
317
|
}, [stuck, status, mediaStream]);
|
|
@@ -303,7 +354,19 @@ function WebRTCPlayerInner({ videoRef, url, width, height, }) {
|
|
|
303
354
|
videoRef.current.srcObject = mediaStream;
|
|
304
355
|
}, [mediaStream]);
|
|
305
356
|
if (!mediaStream) {
|
|
306
|
-
|
|
357
|
+
const isError = connectionStatus === "connection-failed";
|
|
358
|
+
return ((0, jsx_runtime_1.jsx)(index_1.View, { backgroundColor: "#111", style: {
|
|
359
|
+
minWidth: "100%",
|
|
360
|
+
minHeight: "100%",
|
|
361
|
+
display: "flex",
|
|
362
|
+
alignItems: "center",
|
|
363
|
+
justifyContent: "center",
|
|
364
|
+
}, children: (0, jsx_runtime_1.jsxs)(index_1.View, { style: {
|
|
365
|
+
borderRadius: atoms_1.borderRadius.md,
|
|
366
|
+
padding: 24,
|
|
367
|
+
alignItems: "center",
|
|
368
|
+
gap: 16,
|
|
369
|
+
}, children: [!isError && (0, jsx_runtime_1.jsx)(loader_1.Loader, { size: "large" }), (0, jsx_runtime_1.jsx)(index_1.Text, { size: "lg", weight: "semibold", children: connectionStatusMessages[connectionStatus] || "Connecting..." })] }) }));
|
|
307
370
|
}
|
|
308
371
|
return (0, jsx_runtime_1.jsx)(VideoElement, { url: url, ref: videoRef });
|
|
309
372
|
}
|
|
@@ -379,7 +442,19 @@ function WebcamIngestPlayer(props) {
|
|
|
379
442
|
videoElement.srcObject = localMediaStream;
|
|
380
443
|
}, [videoElement, localMediaStream]);
|
|
381
444
|
if (error) {
|
|
382
|
-
return ((0, jsx_runtime_1.
|
|
445
|
+
return ((0, jsx_runtime_1.jsx)(index_1.View, { backgroundColor: "#111", style: {
|
|
446
|
+
minWidth: "100%",
|
|
447
|
+
minHeight: "100%",
|
|
448
|
+
display: "flex",
|
|
449
|
+
alignItems: "center",
|
|
450
|
+
justifyContent: "center",
|
|
451
|
+
}, children: (0, jsx_runtime_1.jsx)(index_1.View, { backgroundColor: atoms_1.colors.destructive[900], style: {
|
|
452
|
+
borderRadius: atoms_1.borderRadius.md,
|
|
453
|
+
padding: 24,
|
|
454
|
+
alignItems: "center",
|
|
455
|
+
gap: 16,
|
|
456
|
+
maxWidth: 400,
|
|
457
|
+
}, children: (0, jsx_runtime_1.jsx)(index_1.Text, { size: "xl", weight: "extrabold", color: "default", children: error.message }) }) }));
|
|
383
458
|
}
|
|
384
459
|
return (0, jsx_runtime_1.jsx)(VideoElement, { ...props, ref: handleRef });
|
|
385
460
|
}
|