@streamplace/components 0.7.13 → 0.7.15

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/package.json +13 -16
  2. package/src/components/mobile-player/fullscreen.native.tsx +15 -20
  3. package/src/components/mobile-player/fullscreen.tsx +10 -2
  4. package/src/components/mobile-player/player.tsx +7 -1
  5. package/src/components/mobile-player/props.tsx +2 -0
  6. package/src/components/mobile-player/video.native.tsx +28 -11
  7. package/src/components/mobile-player/video.tsx +14 -3
  8. package/src/hooks/useLivestreamInfo.ts +6 -2
  9. package/src/lib/browser.ts +27 -0
  10. package/src/livestream-store/stream-key.tsx +1 -28
  11. package/src/streamplace-store/stream.tsx +51 -13
  12. package/dist/assets/emoji-data.json +0 -19371
  13. package/dist/components/chat/chat-box.js +0 -314
  14. package/dist/components/chat/chat-message.js +0 -87
  15. package/dist/components/chat/chat.js +0 -149
  16. package/dist/components/chat/emoji-suggestions.js +0 -35
  17. package/dist/components/chat/mention-suggestions.js +0 -42
  18. package/dist/components/chat/mod-view.js +0 -94
  19. package/dist/components/chat/system-message.js +0 -19
  20. package/dist/components/dashboard/chat-panel.js +0 -38
  21. package/dist/components/dashboard/header.js +0 -80
  22. package/dist/components/dashboard/index.js +0 -14
  23. package/dist/components/dashboard/information-widget.js +0 -234
  24. package/dist/components/dashboard/mod-actions.js +0 -71
  25. package/dist/components/dashboard/problems.js +0 -74
  26. package/dist/components/icons/bluesky-icon.js +0 -9
  27. package/dist/components/keep-awake.js +0 -7
  28. package/dist/components/keep-awake.native.js +0 -16
  29. package/dist/components/mobile-player/fullscreen.js +0 -74
  30. package/dist/components/mobile-player/fullscreen.native.js +0 -155
  31. package/dist/components/mobile-player/player.js +0 -94
  32. package/dist/components/mobile-player/props.js +0 -2
  33. package/dist/components/mobile-player/shared.js +0 -54
  34. package/dist/components/mobile-player/ui/countdown.js +0 -83
  35. package/dist/components/mobile-player/ui/index.js +0 -11
  36. package/dist/components/mobile-player/ui/input.js +0 -42
  37. package/dist/components/mobile-player/ui/metrics.js +0 -44
  38. package/dist/components/mobile-player/ui/report-modal.js +0 -90
  39. package/dist/components/mobile-player/ui/streamer-context-menu.js +0 -7
  40. package/dist/components/mobile-player/ui/streamer-loading-overlay.js +0 -104
  41. package/dist/components/mobile-player/ui/viewer-context-menu.js +0 -51
  42. package/dist/components/mobile-player/ui/viewer-loading-overlay.js +0 -49
  43. package/dist/components/mobile-player/ui/viewers.js +0 -23
  44. package/dist/components/mobile-player/use-webrtc.js +0 -243
  45. package/dist/components/mobile-player/video-retry.js +0 -29
  46. package/dist/components/mobile-player/video.js +0 -460
  47. package/dist/components/mobile-player/video.native.js +0 -276
  48. package/dist/components/mobile-player/webrtc-diagnostics.js +0 -110
  49. package/dist/components/mobile-player/webrtc-primitives.js +0 -27
  50. package/dist/components/mobile-player/webrtc-primitives.native.js +0 -8
  51. package/dist/components/share/sharesheet.js +0 -91
  52. package/dist/components/ui/button.js +0 -223
  53. package/dist/components/ui/dialog.js +0 -206
  54. package/dist/components/ui/dropdown.js +0 -172
  55. package/dist/components/ui/icons.js +0 -25
  56. package/dist/components/ui/index.js +0 -34
  57. package/dist/components/ui/info-box.js +0 -31
  58. package/dist/components/ui/info-row.js +0 -23
  59. package/dist/components/ui/input.js +0 -205
  60. package/dist/components/ui/loader.js +0 -10
  61. package/dist/components/ui/primitives/button.js +0 -125
  62. package/dist/components/ui/primitives/input.js +0 -206
  63. package/dist/components/ui/primitives/modal.js +0 -206
  64. package/dist/components/ui/primitives/text.js +0 -292
  65. package/dist/components/ui/resizeable.js +0 -121
  66. package/dist/components/ui/slider.js +0 -5
  67. package/dist/components/ui/text.js +0 -177
  68. package/dist/components/ui/textarea.js +0 -19
  69. package/dist/components/ui/toast.js +0 -175
  70. package/dist/components/ui/view.js +0 -252
  71. package/dist/hooks/index.js +0 -14
  72. package/dist/hooks/useAvatars.js +0 -35
  73. package/dist/hooks/useCameraToggle.js +0 -12
  74. package/dist/hooks/useKeyboard.js +0 -36
  75. package/dist/hooks/useKeyboardSlide.js +0 -14
  76. package/dist/hooks/useLivestreamInfo.js +0 -65
  77. package/dist/hooks/useOuterAndInnerDimensions.js +0 -30
  78. package/dist/hooks/usePlayerDimensions.js +0 -22
  79. package/dist/hooks/usePointerDevice.js +0 -71
  80. package/dist/hooks/useSegmentDimensions.js +0 -17
  81. package/dist/hooks/useSegmentTiming.js +0 -65
  82. package/dist/index.js +0 -34
  83. package/dist/lib/facet.js +0 -92
  84. package/dist/lib/system-messages.js +0 -101
  85. package/dist/lib/theme/atoms.js +0 -646
  86. package/dist/lib/theme/atoms.types.js +0 -6
  87. package/dist/lib/theme/index.js +0 -35
  88. package/dist/lib/theme/theme.js +0 -256
  89. package/dist/lib/theme/tokens.js +0 -659
  90. package/dist/lib/utils.js +0 -105
  91. package/dist/livestream-provider/index.js +0 -30
  92. package/dist/livestream-provider/websocket.js +0 -45
  93. package/dist/livestream-store/chat.js +0 -286
  94. package/dist/livestream-store/context.js +0 -5
  95. package/dist/livestream-store/index.js +0 -7
  96. package/dist/livestream-store/livestream-state.js +0 -2
  97. package/dist/livestream-store/livestream-store.js +0 -58
  98. package/dist/livestream-store/problems.js +0 -76
  99. package/dist/livestream-store/stream-key.js +0 -119
  100. package/dist/livestream-store/websocket-consumer.js +0 -94
  101. package/dist/player-store/context.js +0 -5
  102. package/dist/player-store/index.js +0 -9
  103. package/dist/player-store/player-provider.js +0 -57
  104. package/dist/player-store/player-state.js +0 -25
  105. package/dist/player-store/player-store.js +0 -199
  106. package/dist/player-store/single-player-provider.js +0 -121
  107. package/dist/streamplace-provider/context.js +0 -5
  108. package/dist/streamplace-provider/index.js +0 -20
  109. package/dist/streamplace-provider/poller.js +0 -49
  110. package/dist/streamplace-provider/xrpc.js +0 -0
  111. package/dist/streamplace-store/block.js +0 -65
  112. package/dist/streamplace-store/index.js +0 -6
  113. package/dist/streamplace-store/stream.js +0 -218
  114. package/dist/streamplace-store/streamplace-store.js +0 -47
  115. package/dist/streamplace-store/user.js +0 -52
  116. package/dist/streamplace-store/xrpc.js +0 -15
  117. package/dist/ui/index.js +0 -79
  118. package/node-compile-cache/v22.15.0-x64-efe9a9df-0/37be0eec +0 -0
  119. package/node-compile-cache/v22.15.0-x64-efe9a9df-0/56540125 +0 -0
  120. package/node-compile-cache/v22.15.0-x64-efe9a9df-0/67b1eb60 +0 -0
  121. package/node-compile-cache/v22.15.0-x64-efe9a9df-0/7c275f90 +0 -0
  122. package/tsconfig.tsbuildinfo +0 -1
@@ -1,90 +0,0 @@
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;
@@ -1,7 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.StreamContextMenu = StreamContextMenu;
4
- const jsx_runtime_1 = require("react/jsx-runtime");
5
- function StreamContextMenu() {
6
- return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, {});
7
- }
@@ -1,104 +0,0 @@
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,51 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ContextMenu = ContextMenu;
4
- exports.ReportButton = ReportButton;
5
- const jsx_runtime_1 = require("react/jsx-runtime");
6
- const dropdown_menu_1 = require("@rn-primitives/dropdown-menu");
7
- const lucide_react_native_1 = require("lucide-react-native");
8
- const react_native_1 = require("react-native");
9
- const theme_1 = require("../../../lib/theme");
10
- const livestream_store_1 = require("../../../livestream-store");
11
- const player_store_1 = require("../../../player-store/");
12
- const ui_1 = require("../../ui");
13
- function ContextMenu({ dropdownPortalContainer, }) {
14
- const quality = (0, player_store_1.usePlayerStore)((x) => x.selectedRendition);
15
- const setQuality = (0, player_store_1.usePlayerStore)((x) => x.setSelectedRendition);
16
- const qualities = (0, livestream_store_1.useLivestreamStore)((x) => x.renditions);
17
- const protocol = (0, player_store_1.usePlayerStore)((x) => x.protocol);
18
- const setProtocol = (0, player_store_1.usePlayerStore)((x) => x.setProtocol);
19
- const debugInfo = (0, player_store_1.usePlayerStore)((x) => x.showDebugInfo);
20
- const setShowDebugInfo = (0, player_store_1.usePlayerStore)((x) => x.setShowDebugInfo);
21
- const livestream = (0, livestream_store_1.useLivestreamStore)((x) => x.livestream);
22
- const setReportModalOpen = (0, player_store_1.usePlayerStore)((x) => x.setReportModalOpen);
23
- const setReportSubject = (0, player_store_1.usePlayerStore)((x) => x.setReportSubject);
24
- const lowLatency = protocol === "webrtc";
25
- const setLowLatency = (value) => {
26
- setProtocol(value ? player_store_1.PlayerProtocol.WEBRTC : player_store_1.PlayerProtocol.HLS);
27
- };
28
- // are we on mobile? then do dropdowns
29
- const isMobile = react_native_1.Platform.OS === "ios" || react_native_1.Platform.OS === "android";
30
- // dummy portal for mobile
31
- const Portal = isMobile ? react_native_1.View : ui_1.DropdownMenuPortal;
32
- // render the responsive version on mobile as we can't fullscreen there
33
- const DropdownMenuContent = isMobile
34
- ? ui_1.ResponsiveDropdownMenuContent
35
- : ui_1.DropdownMenuContentWithoutPortal;
36
- 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.jsx)(Portal, { container: dropdownPortalContainer, children: (0, jsx_runtime_1.jsxs)(DropdownMenuContent, { 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, { livestream: livestream, setReportModalOpen: setReportModalOpen, setReportSubject: setReportSubject }) })] }) })] }));
37
- }
38
- function ReportButton({ livestream, setReportModalOpen, setReportSubject, }) {
39
- const { onOpenChange } = (0, dropdown_menu_1.useRootContext)();
40
- return ((0, jsx_runtime_1.jsx)(ui_1.DropdownMenuItem, { onPress: () => {
41
- if (!livestream)
42
- return;
43
- onOpenChange?.(false);
44
- setReportModalOpen(true);
45
- setReportSubject({
46
- $type: "com.atproto.repo.strongRef",
47
- uri: livestream.uri,
48
- cid: livestream.cid,
49
- });
50
- }, children: (0, jsx_runtime_1.jsx)(ui_1.Text, { children: "Report Livestream..." }) }));
51
- }
@@ -1,49 +0,0 @@
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
- }
@@ -1,23 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Viewers = Viewers;
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 atoms = tslib_1.__importStar(require("../../../lib/theme/atoms"));
8
- const livestream_store_1 = require("../../../livestream-store");
9
- const ui_1 = require("../../ui");
10
- function Viewers() {
11
- const viewers = (0, livestream_store_1.useViewers)();
12
- return ((0, jsx_runtime_1.jsxs)(ui_1.View, { style: [
13
- atoms.layout.flex.center,
14
- atoms.layout.flex.row,
15
- atoms.gap.all[2],
16
- ], children: [(0, jsx_runtime_1.jsx)(lucide_react_native_1.Eye, { color: "#fd5050" }), (0, jsx_runtime_1.jsx)(ui_1.Text, { style: {
17
- color: "#fd5050",
18
- textShadowColor: "black",
19
- textShadowOffset: { width: -1, height: 1 },
20
- textShadowRadius: 3,
21
- fontSize: 16,
22
- }, children: new Intl.NumberFormat(undefined, { notation: "compact" }).format(viewers || 0) })] }));
23
- }
@@ -1,243 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.default = useWebRTC;
4
- exports.negotiateConnectionWithClientOffer = negotiateConnectionWithClientOffer;
5
- exports.useWebRTCIngest = useWebRTCIngest;
6
- const react_1 = require("react");
7
- const __1 = require("../..");
8
- const webrtc_primitives_1 = require("./webrtc-primitives");
9
- function useWebRTC(endpoint) {
10
- const [mediaStream, setMediaStream] = (0, react_1.useState)(null);
11
- const [stuck, setStuck] = (0, react_1.useState)(false);
12
- const setStatus = (0, __1.usePlayerStore)((x) => x.setStatus);
13
- const lastChange = (0, react_1.useRef)(0);
14
- (0, react_1.useEffect)(() => {
15
- const peerConnection = new webrtc_primitives_1.RTCPeerConnection({
16
- bundlePolicy: "max-bundle",
17
- });
18
- peerConnection.addTransceiver("video", {
19
- direction: "recvonly",
20
- });
21
- peerConnection.addTransceiver("audio", {
22
- direction: "recvonly",
23
- });
24
- peerConnection.addEventListener("track", (event) => {
25
- const track = event.track;
26
- if (!track) {
27
- return;
28
- }
29
- setMediaStream(event.streams[0]);
30
- });
31
- peerConnection.addEventListener("connectionstatechange", () => {
32
- console.log("connection state change", peerConnection.connectionState);
33
- if (peerConnection.connectionState === "closed" ||
34
- peerConnection.connectionState === "failed" ||
35
- peerConnection.connectionState === "disconnected") {
36
- console.log("setting stuck to true", peerConnection.connectionState);
37
- setStuck(true);
38
- }
39
- if (peerConnection.connectionState !== "connected") {
40
- return;
41
- }
42
- });
43
- peerConnection.addEventListener("negotiationneeded", () => {
44
- negotiateConnectionWithClientOffer(peerConnection, endpoint);
45
- });
46
- let lastFramesReceived = 0;
47
- let lastAudioFramesReceived = 0;
48
- const handle = setInterval(async () => {
49
- const stats = await peerConnection.getStats();
50
- stats.forEach((stat) => {
51
- const mediaType = stat.mediaType /* web */ ?? stat.kind; /* native */
52
- if (stat.type === "inbound-rtp" && mediaType === "audio") {
53
- const audioFramesReceived = stat.lastPacketReceivedTimestamp;
54
- if (lastAudioFramesReceived !== audioFramesReceived) {
55
- lastAudioFramesReceived = audioFramesReceived;
56
- lastChange.current = Date.now();
57
- setStatus(__1.PlayerStatus.PLAYING);
58
- setStuck(false);
59
- }
60
- }
61
- if (stat.type === "inbound-rtp" && mediaType === "video") {
62
- const framesReceived = stat.framesReceived;
63
- if (lastFramesReceived !== framesReceived) {
64
- lastFramesReceived = framesReceived;
65
- lastChange.current = Date.now();
66
- setStatus(__1.PlayerStatus.PLAYING);
67
- setStuck(false);
68
- }
69
- }
70
- });
71
- if (Date.now() - lastChange.current > 2000) {
72
- setStuck(true);
73
- }
74
- }, 200);
75
- return () => {
76
- clearInterval(handle);
77
- peerConnection.close();
78
- };
79
- }, [endpoint]);
80
- return [mediaStream, stuck];
81
- }
82
- /**
83
- * Performs the actual SDP exchange.
84
- *
85
- * 1. Constructs the client's SDP offer
86
- * 2. Sends the SDP offer to the server,
87
- * 3. Awaits the server's offer.
88
- *
89
- * SDP describes what kind of media we can send and how the server and client communicate.
90
- *
91
- * https://developer.mozilla.org/en-US/docs/Glossary/SDP
92
- * https://www.ietf.org/archive/id/draft-ietf-wish-whip-01.html#name-protocol-operation
93
- */
94
- async function negotiateConnectionWithClientOffer(peerConnection, endpoint, bearerToken) {
95
- /** https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/createOffer */
96
- const offer = await peerConnection.createOffer({
97
- offerToReceiveAudio: true,
98
- offerToReceiveVideo: true,
99
- });
100
- /** https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/setLocalDescription */
101
- await peerConnection.setLocalDescription(offer);
102
- /** Wait for ICE gathering to complete */
103
- let ofr = await waitToCompleteICEGathering(peerConnection);
104
- if (!ofr) {
105
- throw Error("failed to gather ICE candidates for offer");
106
- }
107
- /**
108
- * As long as the connection is open, attempt to...
109
- */
110
- while (peerConnection.connectionState !== "closed") {
111
- try {
112
- /**
113
- * This response contains the server's SDP offer.
114
- * This specifies how the client should communicate,
115
- * and what kind of media client and server have negotiated to exchange.
116
- */
117
- let response = await postSDPOffer(`${endpoint}`, ofr.sdp, bearerToken);
118
- if (response.status === 201) {
119
- let answerSDP = await response.text();
120
- if (peerConnection.connectionState === "closed") {
121
- return;
122
- }
123
- await peerConnection.setRemoteDescription(new webrtc_primitives_1.RTCSessionDescription({ type: "answer", sdp: answerSDP }));
124
- return response.headers.get("Location");
125
- }
126
- else if (response.status === 405) {
127
- console.log("Remember to update the URL passed into the WHIP or WHEP client");
128
- }
129
- else {
130
- const errorMessage = await response.text();
131
- console.error(errorMessage);
132
- }
133
- }
134
- catch (e) {
135
- console.error(`posting sdp offer failed: ${e}`);
136
- }
137
- /** Limit reconnection attempts to at-most once every 5 seconds */
138
- await new Promise((r) => setTimeout(r, 5000));
139
- }
140
- }
141
- async function postSDPOffer(endpoint, data, bearerToken) {
142
- return await fetch(endpoint, {
143
- method: "POST",
144
- mode: "cors",
145
- headers: {
146
- "content-type": "application/sdp",
147
- ...(bearerToken ? { Authorization: `Bearer ${bearerToken}` } : {}),
148
- },
149
- body: data,
150
- });
151
- }
152
- /**
153
- * Receives an RTCPeerConnection and waits until
154
- * the connection is initialized or a timeout passes.
155
- *
156
- * https://www.ietf.org/archive/id/draft-ietf-wish-whip-01.html#section-4.1
157
- * https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/iceGatheringState
158
- * https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/icegatheringstatechange_event
159
- */
160
- async function waitToCompleteICEGathering(peerConnection) {
161
- return new Promise((resolve) => {
162
- /** Wait at most 1 second for ICE gathering. */
163
- setTimeout(function () {
164
- if (peerConnection.connectionState === "closed") {
165
- return;
166
- }
167
- resolve(peerConnection.localDescription);
168
- }, 1000);
169
- peerConnection.addEventListener("icegatheringstatechange", (ev) => {
170
- if (peerConnection.iceGatheringState === "complete") {
171
- resolve(peerConnection.localDescription);
172
- }
173
- });
174
- });
175
- }
176
- function useWebRTCIngest({ endpoint, }) {
177
- const [mediaStream, setMediaStream] = (0, react_1.useState)(null);
178
- const ingestConnectionState = (0, __1.usePlayerStore)((x) => x.ingestConnectionState);
179
- const setIngestConnectionState = (0, __1.usePlayerStore)((x) => x.setIngestConnectionState);
180
- const storedKey = (0, __1.useStreamKey)();
181
- const [peerConnection, setPeerConnection] = (0, react_1.useState)(null);
182
- const videoTransceiver = (0, react_1.useRef)(null);
183
- const audioTransceiver = (0, react_1.useRef)(null);
184
- const [retryTime, setRetryTime] = (0, react_1.useState)(0);
185
- const ingestLive = (0, __1.usePlayerStore)((x) => x.ingestLive);
186
- // "Outer loop": when we need a new peer connection, this sets that up
187
- (0, react_1.useEffect)(() => {
188
- if (!storedKey) {
189
- return;
190
- }
191
- if (!ingestLive) {
192
- return;
193
- }
194
- const peerConnection = new webrtc_primitives_1.RTCPeerConnection({
195
- bundlePolicy: "max-bundle",
196
- });
197
- videoTransceiver.current = peerConnection.addTransceiver("video", {
198
- direction: "sendonly",
199
- });
200
- audioTransceiver.current = peerConnection.addTransceiver("audio", {
201
- direction: "sendonly",
202
- });
203
- peerConnection.addEventListener("connectionstatechange", (ev) => {
204
- setIngestConnectionState(peerConnection.connectionState);
205
- console.log("connection state change", peerConnection.connectionState);
206
- if (peerConnection.connectionState === "failed") {
207
- setRetryTime(Date.now());
208
- }
209
- });
210
- peerConnection.addEventListener("negotiationneeded", (ev) => {
211
- negotiateConnectionWithClientOffer(peerConnection, endpoint, storedKey.streamKey?.privateKey);
212
- });
213
- peerConnection.addEventListener("track", (ev) => {
214
- console.log(ev);
215
- });
216
- setPeerConnection(peerConnection);
217
- return () => {
218
- peerConnection.close();
219
- };
220
- }, [endpoint, storedKey.streamKey?.privateKey, retryTime, ingestLive]);
221
- // "Inner loop": when our tracks change, we update the transceivers
222
- (0, react_1.useEffect)(() => {
223
- if (!mediaStream) {
224
- return;
225
- }
226
- if (!peerConnection) {
227
- return;
228
- }
229
- if (!ingestLive) {
230
- return;
231
- }
232
- for (const track of mediaStream.getTracks()) {
233
- console.log("adding track", track.kind, track.label, track.enabled, track.readyState);
234
- if (track.kind === "video") {
235
- videoTransceiver.current?.sender?.replaceTrack(track);
236
- }
237
- else if (track.kind === "audio") {
238
- audioTransceiver.current?.sender?.replaceTrack(track);
239
- }
240
- }
241
- }, [peerConnection, mediaStream, ingestLive]);
242
- return [mediaStream, setMediaStream];
243
- }
@@ -1,29 +0,0 @@
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
- }