@streamplace/components 0.7.0 → 0.7.1
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/mobile-player/ui/index.js +1 -0
- package/dist/components/mobile-player/ui/viewer-context-menu.js +1 -1
- package/dist/components/mobile-player/ui/viewers.js +19 -0
- package/dist/components/mobile-player/video.native.js +21 -4
- package/dist/components/ui/resizeable.js +10 -3
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useSegmentDimensions.js +14 -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-efe9a9df-0/67b1eb60 +0 -0
- package/node-compile-cache/v22.15.0-x64-efe9a9df-0/7c275f90 +0 -0
- package/package.json +4 -4
- package/src/components/mobile-player/ui/index.ts +1 -0
- package/src/components/mobile-player/ui/viewer-context-menu.tsx +5 -4
- package/src/components/mobile-player/ui/viewers.tsx +32 -0
- package/src/components/mobile-player/video.native.tsx +50 -5
- package/src/components/ui/resizeable.tsx +15 -3
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useSegmentDimensions.tsx +18 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -16,5 +16,5 @@ export function ContextMenu() {
|
|
|
16
16
|
const setLowLatency = (value) => {
|
|
17
17
|
setProtocol(value ? PlayerProtocol.WEBRTC : PlayerProtocol.HLS);
|
|
18
18
|
};
|
|
19
|
-
return (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { children: _jsx(Menu, { size: 32, color: colors.gray[200] }) }), _jsxs(ResponsiveDropdownMenuContent, { children: [_jsx(DropdownMenuGroup, { title: "Resolution", children: _jsxs(DropdownMenuRadioGroup, { value: quality, onValueChange: setQuality, children: [_jsx(DropdownMenuRadioItem, { value: "source", children: _jsx(Text, { children: "Source" }) }), qualities.map((r) => (_jsx(DropdownMenuRadioItem, { value: r.name, children: _jsx(Text, { children: r.name }) })))] }) }),
|
|
19
|
+
return (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { children: _jsx(Menu, { size: 32, color: colors.gray[200] }) }), _jsxs(ResponsiveDropdownMenuContent, { children: [_jsx(DropdownMenuGroup, { title: "Resolution", children: _jsxs(DropdownMenuRadioGroup, { value: quality, onValueChange: setQuality, children: [_jsx(DropdownMenuRadioItem, { value: "source", children: _jsx(Text, { children: "Source (Original Quality)" }) }), qualities.map((r) => (_jsx(DropdownMenuRadioItem, { value: r.name, children: _jsx(Text, { children: r.name }) })))] }) }), _jsx(DropdownMenuGroup, { title: "Advanced", children: _jsx(DropdownMenuCheckboxItem, { checked: lowLatency, onCheckedChange: () => setLowLatency(!lowLatency), children: _jsx(Text, { children: "Low Latency" }) }) }), _jsx(DropdownMenuInfo, { description: "Reduces the delay between video and chat for a more real-time experience." }), _jsx(DropdownMenuGroup, { children: _jsx(DropdownMenuCheckboxItem, { checked: debugInfo, onCheckedChange: () => setShowDebugInfo(!debugInfo), children: _jsx(Text, { children: "Show Debug Info" }) }) })] })] }));
|
|
20
20
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Eye } from "lucide-react-native";
|
|
3
|
+
import * as atoms from "../../../lib/theme/atoms";
|
|
4
|
+
import { useViewers } from "../../../livestream-store";
|
|
5
|
+
import { Text, View } from "../../ui";
|
|
6
|
+
export function Viewers() {
|
|
7
|
+
const viewers = useViewers();
|
|
8
|
+
return (_jsxs(View, { style: [
|
|
9
|
+
atoms.layout.flex.center,
|
|
10
|
+
atoms.layout.flex.row,
|
|
11
|
+
atoms.gap.all[2],
|
|
12
|
+
], children: [_jsx(Eye, { color: "#fd5050" }), _jsx(Text, { style: {
|
|
13
|
+
color: "#fd5050",
|
|
14
|
+
textShadowColor: "black",
|
|
15
|
+
textShadowOffset: { width: -1, height: 1 },
|
|
16
|
+
textShadowRadius: 3,
|
|
17
|
+
fontSize: 16,
|
|
18
|
+
}, children: new Intl.NumberFormat(undefined, { notation: "compact" }).format(viewers || 0) })] }));
|
|
19
|
+
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useVideoPlayer, VideoView } from "expo-video";
|
|
3
|
+
import { ArrowRight } from "lucide-react-native";
|
|
3
4
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
5
|
+
import { Linking } from "react-native";
|
|
4
6
|
import { RTCView, RTCView as RTCViewIngest, } from "react-native-webrtc";
|
|
5
|
-
import { IngestMediaSource, PlayerStatus as IngestPlayerStatus, PlayerProtocol, PlayerStatus, Text, usePlayerStore as useIngestPlayerStore, usePlayerStore, useStreamplaceStore, View, } from "../..";
|
|
6
|
-
import { borderRadius, colors, p } from "../../lib/theme/atoms";
|
|
7
|
+
import { Button, IngestMediaSource, PlayerStatus as IngestPlayerStatus, PlayerProtocol, PlayerStatus, Text, usePlayerStore as useIngestPlayerStore, usePlayerStore, useStreamplaceStore, View, } from "../..";
|
|
8
|
+
import { borderRadius, colors, fontWeight, gap, h, layout, m, p, } from "../../lib/theme/atoms";
|
|
7
9
|
import { srcToUrl } from "./shared";
|
|
8
10
|
import useWebRTC, { useWebRTCIngest } from "./use-webrtc";
|
|
9
11
|
import { mediaDevices } from "./webrtc-primitives.native";
|
|
@@ -205,10 +207,25 @@ export function NativeIngestPlayer() {
|
|
|
205
207
|
})
|
|
206
208
|
.then((stream) => {
|
|
207
209
|
setLocalMediaStream(stream);
|
|
210
|
+
let errs = [];
|
|
211
|
+
if (stream.getAudioTracks().length === 0) {
|
|
212
|
+
console.warn("No audio tracks found in user media stream");
|
|
213
|
+
errs.push("microphone");
|
|
214
|
+
}
|
|
215
|
+
if (stream.getVideoTracks().length === 0) {
|
|
216
|
+
console.warn("No video tracks found in user media stream");
|
|
217
|
+
errs.push("camera");
|
|
218
|
+
}
|
|
219
|
+
if (errs.length > 0) {
|
|
220
|
+
setError(new Error(`We could not access your ${errs.join(" and ")}. To stream, you need to give us permission to access these.`));
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
setError(null);
|
|
224
|
+
}
|
|
208
225
|
})
|
|
209
226
|
.catch((e) => {
|
|
210
227
|
console.error("error getting user media", e);
|
|
211
|
-
setError(new Error("We could not access your camera or microphone.
|
|
228
|
+
setError(new Error("We could not access your camera or microphone. To stream, you need to give us permission to access these."));
|
|
212
229
|
});
|
|
213
230
|
}
|
|
214
231
|
}, [ingestMediaSource, ingestCamera]);
|
|
@@ -228,7 +245,7 @@ export function NativeIngestPlayer() {
|
|
|
228
245
|
return null;
|
|
229
246
|
}
|
|
230
247
|
if (error) {
|
|
231
|
-
return (_jsxs(View, { backgroundColor: colors.destructive[900], style: [p[4], { borderRadius: borderRadius.md }], children: [_jsx(View, { children: _jsx(Text, { children: "Error encountered!" }) }), _jsx(Text, { children: error.message })] }));
|
|
248
|
+
return (_jsxs(View, { backgroundColor: colors.destructive[900], style: [p[4], m[4], gap.all[2], { borderRadius: borderRadius.md }], children: [_jsx(View, { children: _jsx(Text, { style: [fontWeight.semibold], size: "2xl", children: "Error encountered!" }) }), _jsx(Text, { children: error.message }), error.message.includes("To stream, you need to give us permission to access these.") && (_jsx(Button, { onPress: Linking.openSettings, style: [h[10]], variant: "secondary", children: _jsxs(View, { style: [layout.flex.row, gap.all[1]], children: [_jsx(Text, { children: "Open Settings" }), " ", _jsx(ArrowRight, { color: "white", size: "18" })] }) }))] }));
|
|
232
249
|
}
|
|
233
250
|
return (_jsx(RTCViewIngest, { mirror: ingestCamera !== "environment", objectFit: "contain", streamURL: localMediaStream.toURL(), zOrder: 0, style: {
|
|
234
251
|
minWidth: "100%",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { ChevronUp } from "lucide-react-native";
|
|
3
|
+
import { useEffect } from "react";
|
|
3
4
|
import { Dimensions } from "react-native";
|
|
4
5
|
import { Gesture, GestureDetector, Pressable, } from "react-native-gesture-handler";
|
|
5
6
|
import Animated, { Extrapolation, interpolate, useAnimatedStyle, useSharedValue, withSpring, } from "react-native-reanimated";
|
|
@@ -10,7 +11,7 @@ import { View } from "./view";
|
|
|
10
11
|
const AnimatedView = Animated.createAnimatedComponent(View);
|
|
11
12
|
const { height: SCREEN_HEIGHT } = Dimensions.get("window");
|
|
12
13
|
const SPRING_CONFIG = { damping: 20, stiffness: 100 };
|
|
13
|
-
export function Resizable({ isPlayerRatioGreater, style = {}, children, }) {
|
|
14
|
+
export function Resizable({ startingPercentage, isPlayerRatioGreater, style = {}, children, }) {
|
|
14
15
|
const { slideKeyboard } = useKeyboardSlide();
|
|
15
16
|
const { bottom: safeBottom } = useSafeAreaInsets();
|
|
16
17
|
const MAX_HEIGHT = (SCREEN_HEIGHT - safeBottom) * 0.5;
|
|
@@ -18,6 +19,11 @@ export function Resizable({ isPlayerRatioGreater, style = {}, children, }) {
|
|
|
18
19
|
const COLLAPSE_HEIGHT = (SCREEN_HEIGHT - safeBottom) * 0.1;
|
|
19
20
|
const sheetHeight = useSharedValue(MIN_HEIGHT);
|
|
20
21
|
const startHeight = useSharedValue(MIN_HEIGHT);
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
setTimeout(() => {
|
|
24
|
+
sheetHeight.value = withSpring(startingPercentage ? startingPercentage * SCREEN_HEIGHT : MIN_HEIGHT, SPRING_CONFIG);
|
|
25
|
+
}, 1000);
|
|
26
|
+
}, []);
|
|
21
27
|
const panGesture = Gesture.Pan()
|
|
22
28
|
.onStart(() => {
|
|
23
29
|
startHeight.value = sheetHeight.value;
|
|
@@ -38,7 +44,9 @@ export function Resizable({ isPlayerRatioGreater, style = {}, children, }) {
|
|
|
38
44
|
opacity: interpolate(sheetHeight.value, [MIN_HEIGHT, COLLAPSE_HEIGHT], [0, 1], Extrapolation.CLAMP),
|
|
39
45
|
transform: [
|
|
40
46
|
{
|
|
41
|
-
translateY: slideKeyboard
|
|
47
|
+
translateY: slideKeyboard +
|
|
48
|
+
Math.max(0, -sheetHeight.value) +
|
|
49
|
+
(slideKeyboard < 0 ? 0 : -safeBottom),
|
|
42
50
|
},
|
|
43
51
|
],
|
|
44
52
|
}));
|
|
@@ -57,7 +65,6 @@ export function Resizable({ isPlayerRatioGreater, style = {}, children, }) {
|
|
|
57
65
|
w.percent[100],
|
|
58
66
|
layout.flex.center,
|
|
59
67
|
zIndex[1],
|
|
60
|
-
{ marginBottom: safeBottom },
|
|
61
68
|
], children: _jsx(Pressable, { onPress: () => {
|
|
62
69
|
sheetHeight.value =
|
|
63
70
|
sheetHeight.value === MIN_HEIGHT
|
package/dist/hooks/index.js
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useLivestreamStore } from "../livestream-store";
|
|
2
|
+
export function useSegmentDimensions() {
|
|
3
|
+
const latestSegment = useLivestreamStore((x) => x.segment);
|
|
4
|
+
let seg = latestSegment?.video && latestSegment.video[0];
|
|
5
|
+
let ratio = {
|
|
6
|
+
height: seg?.height || 0,
|
|
7
|
+
width: seg?.width || 0,
|
|
8
|
+
};
|
|
9
|
+
return {
|
|
10
|
+
isPlayerRatioGreater: ratio.width > ratio.height,
|
|
11
|
+
height: ratio.height,
|
|
12
|
+
width: ratio.width,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@streamplace/components",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "Streamplace React (Native) Components",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
16
|
"build": "tsc",
|
|
17
|
-
"
|
|
17
|
+
"prepare": "pnpm run build",
|
|
18
18
|
"start": "tsc --watch --preserveWatchOutput"
|
|
19
19
|
},
|
|
20
20
|
"keywords": [
|
|
@@ -44,12 +44,12 @@
|
|
|
44
44
|
"react-native-safe-area-context": "5.4.1",
|
|
45
45
|
"react-native-webrtc": "git+https://github.com/streamplace/react-native-webrtc.git#6b8472a771ac47f89217d327058a8a4124a6ae56",
|
|
46
46
|
"react-use-websocket": "^4.13.0",
|
|
47
|
-
"streamplace": "0.7.
|
|
47
|
+
"streamplace": "0.7.1",
|
|
48
48
|
"viem": "^2.21.44",
|
|
49
49
|
"zustand": "^5.0.5"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"react": "*"
|
|
53
53
|
},
|
|
54
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "e092ac26f32426cfb14ec3cbda96265ad2fbae12"
|
|
55
55
|
}
|
|
@@ -39,7 +39,7 @@ export function ContextMenu() {
|
|
|
39
39
|
<DropdownMenuGroup title="Resolution">
|
|
40
40
|
<DropdownMenuRadioGroup value={quality} onValueChange={setQuality}>
|
|
41
41
|
<DropdownMenuRadioItem value="source">
|
|
42
|
-
<Text>Source</Text>
|
|
42
|
+
<Text>Source (Original Quality)</Text>
|
|
43
43
|
</DropdownMenuRadioItem>
|
|
44
44
|
{qualities.map((r) => (
|
|
45
45
|
<DropdownMenuRadioItem value={r.name}>
|
|
@@ -55,15 +55,16 @@ export function ContextMenu() {
|
|
|
55
55
|
>
|
|
56
56
|
<Text>Low Latency</Text>
|
|
57
57
|
</DropdownMenuCheckboxItem>
|
|
58
|
-
|
|
58
|
+
</DropdownMenuGroup>
|
|
59
|
+
<DropdownMenuInfo description="Reduces the delay between video and chat for a more real-time experience." />
|
|
60
|
+
<DropdownMenuGroup>
|
|
59
61
|
<DropdownMenuCheckboxItem
|
|
60
62
|
checked={debugInfo}
|
|
61
63
|
onCheckedChange={() => setShowDebugInfo(!debugInfo)}
|
|
62
64
|
>
|
|
63
|
-
<Text>
|
|
65
|
+
<Text>Show Debug Info</Text>
|
|
64
66
|
</DropdownMenuCheckboxItem>
|
|
65
67
|
</DropdownMenuGroup>
|
|
66
|
-
<DropdownMenuInfo description="Lowers the delay between video and chat messages." />
|
|
67
68
|
</ResponsiveDropdownMenuContent>
|
|
68
69
|
</DropdownMenu>
|
|
69
70
|
);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Eye } from "lucide-react-native";
|
|
2
|
+
import * as atoms from "../../../lib/theme/atoms";
|
|
3
|
+
import { useViewers } from "../../../livestream-store";
|
|
4
|
+
import { Text, View } from "../../ui";
|
|
5
|
+
|
|
6
|
+
export function Viewers() {
|
|
7
|
+
const viewers = useViewers();
|
|
8
|
+
return (
|
|
9
|
+
<View
|
|
10
|
+
style={[
|
|
11
|
+
atoms.layout.flex.center,
|
|
12
|
+
atoms.layout.flex.row,
|
|
13
|
+
atoms.gap.all[2],
|
|
14
|
+
]}
|
|
15
|
+
>
|
|
16
|
+
<Eye color="#fd5050" />
|
|
17
|
+
<Text
|
|
18
|
+
style={{
|
|
19
|
+
color: "#fd5050",
|
|
20
|
+
textShadowColor: "black",
|
|
21
|
+
textShadowOffset: { width: -1, height: 1 },
|
|
22
|
+
textShadowRadius: 3,
|
|
23
|
+
fontSize: 16,
|
|
24
|
+
}}
|
|
25
|
+
>
|
|
26
|
+
{new Intl.NumberFormat(undefined, { notation: "compact" }).format(
|
|
27
|
+
viewers || 0,
|
|
28
|
+
)}
|
|
29
|
+
</Text>
|
|
30
|
+
</View>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { useVideoPlayer, VideoPlayerEvents, VideoView } from "expo-video";
|
|
2
|
+
import { ArrowRight } from "lucide-react-native";
|
|
2
3
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
|
-
import { LayoutChangeEvent } from "react-native";
|
|
4
|
+
import { LayoutChangeEvent, Linking } from "react-native";
|
|
4
5
|
import {
|
|
5
6
|
MediaStream,
|
|
6
7
|
RTCView,
|
|
7
8
|
RTCView as RTCViewIngest,
|
|
8
9
|
} from "react-native-webrtc";
|
|
9
10
|
import {
|
|
11
|
+
Button,
|
|
10
12
|
IngestMediaSource,
|
|
11
13
|
PlayerStatus as IngestPlayerStatus,
|
|
12
14
|
PlayerProtocol,
|
|
@@ -17,7 +19,16 @@ import {
|
|
|
17
19
|
useStreamplaceStore,
|
|
18
20
|
View,
|
|
19
21
|
} from "../..";
|
|
20
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
borderRadius,
|
|
24
|
+
colors,
|
|
25
|
+
fontWeight,
|
|
26
|
+
gap,
|
|
27
|
+
h,
|
|
28
|
+
layout,
|
|
29
|
+
m,
|
|
30
|
+
p,
|
|
31
|
+
} from "../../lib/theme/atoms";
|
|
21
32
|
import { srcToUrl } from "./shared";
|
|
22
33
|
import useWebRTC, { useWebRTCIngest } from "./use-webrtc";
|
|
23
34
|
import { mediaDevices, WebRTCMediaStream } from "./webrtc-primitives.native";
|
|
@@ -301,12 +312,31 @@ export function NativeIngestPlayer() {
|
|
|
301
312
|
})
|
|
302
313
|
.then((stream: WebRTCMediaStream) => {
|
|
303
314
|
setLocalMediaStream(stream);
|
|
315
|
+
|
|
316
|
+
let errs: string[] = [];
|
|
317
|
+
if (stream.getAudioTracks().length === 0) {
|
|
318
|
+
console.warn("No audio tracks found in user media stream");
|
|
319
|
+
errs.push("microphone");
|
|
320
|
+
}
|
|
321
|
+
if (stream.getVideoTracks().length === 0) {
|
|
322
|
+
console.warn("No video tracks found in user media stream");
|
|
323
|
+
errs.push("camera");
|
|
324
|
+
}
|
|
325
|
+
if (errs.length > 0) {
|
|
326
|
+
setError(
|
|
327
|
+
new Error(
|
|
328
|
+
`We could not access your ${errs.join(" and ")}. To stream, you need to give us permission to access these.`,
|
|
329
|
+
),
|
|
330
|
+
);
|
|
331
|
+
} else {
|
|
332
|
+
setError(null);
|
|
333
|
+
}
|
|
304
334
|
})
|
|
305
335
|
.catch((e: any) => {
|
|
306
336
|
console.error("error getting user media", e);
|
|
307
337
|
setError(
|
|
308
338
|
new Error(
|
|
309
|
-
"We could not access your camera or microphone.
|
|
339
|
+
"We could not access your camera or microphone. To stream, you need to give us permission to access these.",
|
|
310
340
|
),
|
|
311
341
|
);
|
|
312
342
|
});
|
|
@@ -334,12 +364,27 @@ export function NativeIngestPlayer() {
|
|
|
334
364
|
return (
|
|
335
365
|
<View
|
|
336
366
|
backgroundColor={colors.destructive[900]}
|
|
337
|
-
style={[p[4], { borderRadius: borderRadius.md }]}
|
|
367
|
+
style={[p[4], m[4], gap.all[2], { borderRadius: borderRadius.md }]}
|
|
338
368
|
>
|
|
339
369
|
<View>
|
|
340
|
-
<Text
|
|
370
|
+
<Text style={[fontWeight.semibold]} size="2xl">
|
|
371
|
+
Error encountered!
|
|
372
|
+
</Text>
|
|
341
373
|
</View>
|
|
342
374
|
<Text>{error.message}</Text>
|
|
375
|
+
{error.message.includes(
|
|
376
|
+
"To stream, you need to give us permission to access these.",
|
|
377
|
+
) && (
|
|
378
|
+
<Button
|
|
379
|
+
onPress={Linking.openSettings}
|
|
380
|
+
style={[h[10]]}
|
|
381
|
+
variant="secondary"
|
|
382
|
+
>
|
|
383
|
+
<View style={[layout.flex.row, gap.all[1]]}>
|
|
384
|
+
<Text>Open Settings</Text> <ArrowRight color="white" size="18" />
|
|
385
|
+
</View>
|
|
386
|
+
</Button>
|
|
387
|
+
)}
|
|
343
388
|
</View>
|
|
344
389
|
);
|
|
345
390
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ChevronUp } from "lucide-react-native";
|
|
2
|
-
import { ComponentProps } from "react";
|
|
2
|
+
import { ComponentProps, useEffect } from "react";
|
|
3
3
|
import { Dimensions } from "react-native";
|
|
4
4
|
import {
|
|
5
5
|
Gesture,
|
|
@@ -23,6 +23,7 @@ const AnimatedView = Animated.createAnimatedComponent(View);
|
|
|
23
23
|
const { height: SCREEN_HEIGHT } = Dimensions.get("window");
|
|
24
24
|
|
|
25
25
|
type ResizableChatSheetProps = {
|
|
26
|
+
startingPercentage?: number;
|
|
26
27
|
isPlayerRatioGreater: boolean;
|
|
27
28
|
style?: ComponentProps<typeof AnimatedView>["style"];
|
|
28
29
|
children?: React.ReactNode;
|
|
@@ -31,6 +32,7 @@ type ResizableChatSheetProps = {
|
|
|
31
32
|
const SPRING_CONFIG = { damping: 20, stiffness: 100 };
|
|
32
33
|
|
|
33
34
|
export function Resizable({
|
|
35
|
+
startingPercentage,
|
|
34
36
|
isPlayerRatioGreater,
|
|
35
37
|
style = {},
|
|
36
38
|
children,
|
|
@@ -44,6 +46,15 @@ export function Resizable({
|
|
|
44
46
|
const sheetHeight = useSharedValue(MIN_HEIGHT);
|
|
45
47
|
const startHeight = useSharedValue(MIN_HEIGHT);
|
|
46
48
|
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
setTimeout(() => {
|
|
51
|
+
sheetHeight.value = withSpring(
|
|
52
|
+
startingPercentage ? startingPercentage * SCREEN_HEIGHT : MIN_HEIGHT,
|
|
53
|
+
SPRING_CONFIG,
|
|
54
|
+
);
|
|
55
|
+
}, 1000);
|
|
56
|
+
}, []);
|
|
57
|
+
|
|
47
58
|
const panGesture = Gesture.Pan()
|
|
48
59
|
.onStart(() => {
|
|
49
60
|
startHeight.value = sheetHeight.value;
|
|
@@ -70,7 +81,9 @@ export function Resizable({
|
|
|
70
81
|
transform: [
|
|
71
82
|
{
|
|
72
83
|
translateY:
|
|
73
|
-
slideKeyboard
|
|
84
|
+
slideKeyboard +
|
|
85
|
+
Math.max(0, -sheetHeight.value) +
|
|
86
|
+
(slideKeyboard < 0 ? 0 : -safeBottom),
|
|
74
87
|
},
|
|
75
88
|
],
|
|
76
89
|
}));
|
|
@@ -94,7 +107,6 @@ export function Resizable({
|
|
|
94
107
|
w.percent[100],
|
|
95
108
|
layout.flex.center,
|
|
96
109
|
zIndex[1],
|
|
97
|
-
{ marginBottom: safeBottom },
|
|
98
110
|
]}
|
|
99
111
|
>
|
|
100
112
|
<Pressable
|
package/src/hooks/index.ts
CHANGED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useLivestreamStore } from "../livestream-store";
|
|
2
|
+
|
|
3
|
+
export function useSegmentDimensions() {
|
|
4
|
+
const latestSegment = useLivestreamStore((x) => x.segment);
|
|
5
|
+
|
|
6
|
+
let seg = latestSegment?.video && latestSegment.video[0];
|
|
7
|
+
|
|
8
|
+
let ratio = {
|
|
9
|
+
height: seg?.height || 0,
|
|
10
|
+
width: seg?.width || 0,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
isPlayerRatioGreater: ratio.width > ratio.height,
|
|
15
|
+
height: ratio.height,
|
|
16
|
+
width: ratio.width,
|
|
17
|
+
};
|
|
18
|
+
}
|