@streamplace/components 0.9.15 → 0.10.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/badge.d.ts.map +1 -1
- package/dist/components/chat/badge.js +4 -2
- package/dist/components/chat/badge.js.map +1 -1
- package/dist/components/chat/chat-box.d.ts +2 -1
- package/dist/components/chat/chat-box.d.ts.map +1 -1
- package/dist/components/chat/chat-box.js +1 -1
- package/dist/components/chat/chat-box.js.map +1 -1
- package/dist/components/chat/chat-message.d.ts.map +1 -1
- package/dist/components/chat/chat-message.js +3 -3
- package/dist/components/chat/chat-message.js.map +1 -1
- package/dist/components/chat/chat.d.ts +4 -2
- package/dist/components/chat/chat.d.ts.map +1 -1
- package/dist/components/chat/chat.js +19 -5
- package/dist/components/chat/chat.js.map +1 -1
- package/dist/components/chat/mod-view.d.ts.map +1 -1
- package/dist/components/chat/mod-view.js +31 -6
- package/dist/components/chat/mod-view.js.map +1 -1
- package/dist/components/chat/teleport-modal.d.ts.map +1 -1
- package/dist/components/chat/teleport-modal.js +5 -4
- package/dist/components/chat/teleport-modal.js.map +1 -1
- package/dist/components/chat/user-profile-card.d.ts.map +1 -1
- package/dist/components/chat/user-profile-card.js +11 -8
- package/dist/components/chat/user-profile-card.js.map +1 -1
- package/dist/components/dashboard/moderator-panel.js +7 -1
- package/dist/components/dashboard/moderator-panel.js.map +1 -1
- package/dist/components/error-boundary.d.ts +15 -0
- package/dist/components/error-boundary.d.ts.map +1 -0
- package/dist/components/error-boundary.js +29 -0
- package/dist/components/error-boundary.js.map +1 -0
- package/dist/components/mobile-player/props.d.ts +1 -0
- package/dist/components/mobile-player/props.d.ts.map +1 -1
- package/dist/components/mobile-player/rotation-lock.js +2 -2
- package/dist/components/mobile-player/rotation-lock.js.map +1 -1
- package/dist/components/mobile-player/ui/viewer-context-menu.d.ts.map +1 -1
- package/dist/components/mobile-player/ui/viewer-context-menu.js +3 -2
- package/dist/components/mobile-player/ui/viewer-context-menu.js.map +1 -1
- package/dist/components/mobile-player/ui/viewer-loading-overlay.d.ts.map +1 -1
- package/dist/components/mobile-player/ui/viewer-loading-overlay.js +1 -0
- package/dist/components/mobile-player/ui/viewer-loading-overlay.js.map +1 -1
- package/dist/components/mobile-player/video-async.native.d.ts.map +1 -1
- package/dist/components/mobile-player/video-async.native.js +9 -2
- package/dist/components/mobile-player/video-async.native.js.map +1 -1
- package/dist/components/mobile-player/video.d.ts +3 -3
- package/dist/components/mobile-player/video.d.ts.map +1 -1
- package/dist/components/mobile-player/video.js.map +1 -1
- package/dist/components/stream-notification/pin-notification.d.ts +7 -0
- package/dist/components/stream-notification/pin-notification.d.ts.map +1 -0
- package/dist/components/stream-notification/pin-notification.js +63 -0
- package/dist/components/stream-notification/pin-notification.js.map +1 -0
- package/dist/components/ui/dropdown.d.ts.map +1 -1
- package/dist/components/ui/dropdown.js +3 -2
- package/dist/components/ui/dropdown.js.map +1 -1
- package/dist/components/ui/dropdown.native.d.ts.map +1 -1
- package/dist/components/ui/dropdown.native.js +3 -1
- package/dist/components/ui/dropdown.native.js.map +1 -1
- package/dist/components/ui/icons.js +1 -1
- package/dist/components/ui/icons.js.map +1 -1
- package/dist/components/ui/menu.d.ts +0 -8
- package/dist/components/ui/menu.d.ts.map +1 -1
- package/dist/components/ui/menu.js +1 -35
- package/dist/components/ui/menu.js.map +1 -1
- package/dist/components/ui/primitives/input.d.ts +4 -4
- package/dist/components/ui/primitives/input.d.ts.map +1 -1
- package/dist/components/ui/primitives/input.js +10 -3
- package/dist/components/ui/primitives/input.js.map +1 -1
- package/dist/components/ui/resizeable.d.ts +1 -1
- package/dist/components/ui/resizeable.d.ts.map +1 -1
- package/dist/components/ui/resizeable.js +8 -5
- package/dist/components/ui/resizeable.js.map +1 -1
- package/dist/components/ui/text.d.ts +1 -1
- package/dist/components/ui/toast.js +4 -4
- package/dist/components/ui/toast.js.map +1 -1
- package/dist/context/profile-cache.d.ts.map +1 -1
- package/dist/context/profile-cache.js +2 -1
- package/dist/context/profile-cache.js.map +1 -1
- package/dist/hooks/useSegmentTiming.js +1 -1
- package/dist/hooks/useSegmentTiming.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/facet.js +1 -1
- package/dist/lib/facet.js.map +1 -1
- package/dist/lib/stream-notifications.d.ts +7 -0
- package/dist/lib/stream-notifications.d.ts.map +1 -1
- package/dist/lib/stream-notifications.js +21 -0
- package/dist/lib/stream-notifications.js.map +1 -1
- package/dist/lib/theme/atoms.d.ts +252 -252
- package/dist/lib/theme/theme.js +2 -2
- package/dist/lib/theme/theme.js.map +1 -1
- package/dist/livestream-provider/index.d.ts +1 -0
- package/dist/livestream-provider/index.d.ts.map +1 -1
- package/dist/livestream-provider/index.js +35 -3
- package/dist/livestream-provider/index.js.map +1 -1
- package/dist/livestream-store/chat.d.ts +2 -0
- package/dist/livestream-store/chat.d.ts.map +1 -1
- package/dist/livestream-store/chat.js +80 -1
- package/dist/livestream-store/chat.js.map +1 -1
- package/dist/livestream-store/livestream-state.d.ts +2 -1
- package/dist/livestream-store/livestream-state.d.ts.map +1 -1
- package/dist/livestream-store/livestream-store.d.ts +3 -2
- package/dist/livestream-store/livestream-store.d.ts.map +1 -1
- package/dist/livestream-store/livestream-store.js +4 -1
- package/dist/livestream-store/livestream-store.js.map +1 -1
- package/dist/livestream-store/websocket-consumer.d.ts.map +1 -1
- package/dist/livestream-store/websocket-consumer.js +14 -0
- package/dist/livestream-store/websocket-consumer.js.map +1 -1
- package/dist/streamplace-store/moderation.d.ts +1 -0
- package/dist/streamplace-store/moderation.d.ts.map +1 -1
- package/dist/streamplace-store/moderation.js +1 -0
- package/dist/streamplace-store/moderation.js.map +1 -1
- package/dist/streamplace-store/moderator-management.d.ts +1 -1
- package/dist/streamplace-store/moderator-management.d.ts.map +1 -1
- package/dist/streamplace-store/stream.d.ts +1 -1
- package/dist/streamplace-store/stream.d.ts.map +1 -1
- package/dist/streamplace-store/streamplace-store.d.ts +0 -1
- package/dist/streamplace-store/streamplace-store.d.ts.map +1 -1
- package/dist/streamplace-store/streamplace-store.js +1 -4
- package/dist/streamplace-store/streamplace-store.js.map +1 -1
- package/dist/streamplace-store/user.d.ts +1 -1
- package/dist/streamplace-store/user.d.ts.map +1 -1
- package/dist/streamplace-store/xrpc.js +4 -4
- package/dist/streamplace-store/xrpc.js.map +1 -1
- package/node-compile-cache/v22.15.0-x64-efe9a9df-0/37be0eec +0 -0
- package/package.json +19 -18
- package/src/components/chat/badge.tsx +4 -2
- package/src/components/chat/chat-box.tsx +2 -0
- package/src/components/chat/chat-message.tsx +4 -5
- package/src/components/chat/chat.tsx +32 -5
- package/src/components/chat/mod-view.tsx +82 -3
- package/src/components/chat/teleport-modal.tsx +4 -3
- package/src/components/chat/user-profile-card.tsx +20 -8
- package/src/components/dashboard/moderator-panel.tsx +13 -2
- package/src/components/error-boundary.tsx +42 -0
- package/src/components/mobile-player/props.tsx +1 -0
- package/src/components/mobile-player/rotation-lock.tsx +2 -2
- package/src/components/mobile-player/ui/viewer-context-menu.tsx +3 -2
- package/src/components/mobile-player/ui/viewer-loading-overlay.tsx +1 -0
- package/src/components/mobile-player/video-async.native.tsx +8 -1
- package/src/components/mobile-player/video.tsx +7 -5
- package/src/components/stream-notification/pin-notification.tsx +135 -0
- package/src/components/ui/dropdown.native.tsx +5 -1
- package/src/components/ui/dropdown.tsx +3 -2
- package/src/components/ui/icons.tsx +1 -1
- package/src/components/ui/menu.tsx +56 -62
- package/src/components/ui/primitives/input.tsx +18 -9
- package/src/components/ui/resizeable.tsx +11 -6
- package/src/components/ui/toast.tsx +1 -1
- package/src/context/profile-cache.tsx +7 -1
- package/src/hooks/useSegmentTiming.tsx +1 -1
- package/src/index.tsx +2 -0
- package/src/lib/facet.ts +1 -1
- package/src/lib/stream-notifications.ts +28 -0
- package/src/lib/theme/theme.tsx +1 -1
- package/src/livestream-provider/index.tsx +38 -2
- package/src/livestream-store/chat.tsx +92 -0
- package/src/livestream-store/livestream-state.tsx +2 -0
- package/src/livestream-store/livestream-store.tsx +4 -0
- package/src/livestream-store/websocket-consumer.tsx +15 -0
- package/src/streamplace-store/moderation.tsx +2 -0
- package/src/streamplace-store/moderator-management.tsx +1 -1
- package/src/streamplace-store/streamplace-store.tsx +0 -2
- package/src/streamplace-store/xrpc.tsx +1 -1
- package/node-compile-cache/v22.15.0-x64-92db9086-0/37be0eec +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Text, View } from "react-native";
|
|
3
|
+
|
|
4
|
+
type ErrorBoundaryProps = {
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
type ErrorBoundaryState = {
|
|
9
|
+
hasError: boolean;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export class ErrorBoundary extends React.Component<
|
|
13
|
+
ErrorBoundaryProps,
|
|
14
|
+
ErrorBoundaryState
|
|
15
|
+
> {
|
|
16
|
+
constructor(props: ErrorBoundaryProps) {
|
|
17
|
+
super(props);
|
|
18
|
+
this.state = { hasError: false };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
static getDerivedStateFromError(error: any): ErrorBoundaryState {
|
|
22
|
+
// Update state so the next render will show the fallback UI.
|
|
23
|
+
return { hasError: true };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
componentDidCatch(error: any, info: any) {
|
|
27
|
+
console.error("<ErrorBoundary> caught:", error);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
render() {
|
|
31
|
+
if (this.state.hasError) {
|
|
32
|
+
// You can render any custom fallback UI
|
|
33
|
+
return (
|
|
34
|
+
<View>
|
|
35
|
+
<Text>Error</Text>
|
|
36
|
+
</View>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return this.props.children;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -64,12 +64,12 @@ export const RotationProvider: React.FC<RotationProviderProps> = ({
|
|
|
64
64
|
try {
|
|
65
65
|
await ScreenOrientation.unlockAsync();
|
|
66
66
|
await ScreenOrientation.lockAsync(
|
|
67
|
-
ScreenOrientation.OrientationLock.
|
|
67
|
+
ScreenOrientation.OrientationLock.LANDSCAPE_LEFT,
|
|
68
68
|
);
|
|
69
69
|
setIsLocked(true);
|
|
70
70
|
|
|
71
71
|
// set current orientation to landscape right
|
|
72
|
-
setCurrentOrientation(ScreenOrientation.Orientation.
|
|
72
|
+
setCurrentOrientation(ScreenOrientation.Orientation.LANDSCAPE_LEFT);
|
|
73
73
|
|
|
74
74
|
if (__DEV__) {
|
|
75
75
|
console.log("📲 Manual landscape");
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { useRootContext } from "@rn-primitives/dropdown-menu";
|
|
2
|
+
import { Image } from "expo-image";
|
|
2
3
|
import { Cog } from "lucide-react-native";
|
|
3
4
|
import { useState } from "react";
|
|
4
|
-
import {
|
|
5
|
+
import { Linking, Platform, Pressable, View } from "react-native";
|
|
5
6
|
import Animated, {
|
|
6
7
|
Easing,
|
|
7
8
|
useAnimatedStyle,
|
|
@@ -161,7 +162,7 @@ export function ContextMenu({
|
|
|
161
162
|
uri: avatars[profile?.did]?.avatar,
|
|
162
163
|
}}
|
|
163
164
|
style={{ width: 42, height: 42, borderRadius: 999 }}
|
|
164
|
-
|
|
165
|
+
contentFit="cover"
|
|
165
166
|
/>
|
|
166
167
|
)}
|
|
167
168
|
<View style={{ flex: 1, minWidth: 0 }}>
|
|
@@ -91,6 +91,9 @@ export function NativeVideo(props?: {
|
|
|
91
91
|
|
|
92
92
|
const handleLayout = useCallback((event: LayoutChangeEvent) => {
|
|
93
93
|
const { width, height } = event.nativeEvent.layout;
|
|
94
|
+
if (dimensions.width === width && dimensions.height === height) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
94
97
|
setDimensions({ width, height });
|
|
95
98
|
setPlayerWidth(width);
|
|
96
99
|
setPlayerHeight(height);
|
|
@@ -165,7 +168,6 @@ export function NativeVideo(props?: {
|
|
|
165
168
|
<VideoView
|
|
166
169
|
ref={videoRef}
|
|
167
170
|
player={player}
|
|
168
|
-
allowsFullscreen
|
|
169
171
|
nativeControls={fullscreen}
|
|
170
172
|
onFullscreenEnter={() => {
|
|
171
173
|
setFullscreen(true);
|
|
@@ -175,6 +177,11 @@ export function NativeVideo(props?: {
|
|
|
175
177
|
}}
|
|
176
178
|
allowsPictureInPicture={props?.pictureInPictureEnabled !== false}
|
|
177
179
|
onLayout={handleLayout}
|
|
180
|
+
style={{
|
|
181
|
+
minWidth: "100%",
|
|
182
|
+
minHeight: "100%",
|
|
183
|
+
flex: 1,
|
|
184
|
+
}}
|
|
178
185
|
/>
|
|
179
186
|
</>
|
|
180
187
|
);
|
|
@@ -36,12 +36,14 @@ function assignVideoRef(
|
|
|
36
36
|
|
|
37
37
|
type VideoProps = {
|
|
38
38
|
url: string;
|
|
39
|
-
videoRef?: React.RefObject<HTMLVideoElement>;
|
|
39
|
+
videoRef?: React.RefObject<HTMLVideoElement | null>;
|
|
40
40
|
objectFit?: "contain" | "cover";
|
|
41
41
|
pictureInPictureEnabled?: boolean;
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
-
function useVideoDimensions(
|
|
44
|
+
function useVideoDimensions(
|
|
45
|
+
videoRef: React.RefObject<HTMLVideoElement | null>,
|
|
46
|
+
) {
|
|
45
47
|
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
|
|
46
48
|
|
|
47
49
|
useEffect(() => {
|
|
@@ -133,7 +135,7 @@ const updateEvents = {
|
|
|
133
135
|
|
|
134
136
|
const VideoElement = forwardRef<
|
|
135
137
|
HTMLVideoElement,
|
|
136
|
-
VideoProps & { videoRef?: React.RefObject<HTMLVideoElement> }
|
|
138
|
+
VideoProps & { videoRef?: React.RefObject<HTMLVideoElement | null> }
|
|
137
139
|
>((props, ref) => {
|
|
138
140
|
const x = usePlayerStore((x) => x);
|
|
139
141
|
const url = useStreamplaceStore((x) => x.url);
|
|
@@ -359,7 +361,7 @@ export function HLSPlayer(props: VideoProps) {
|
|
|
359
361
|
}
|
|
360
362
|
|
|
361
363
|
export function WebRTCPlayer(
|
|
362
|
-
props: VideoProps & { videoRef?: React.RefObject<HTMLVideoElement> },
|
|
364
|
+
props: VideoProps & { videoRef?: React.RefObject<HTMLVideoElement | null> },
|
|
363
365
|
) {
|
|
364
366
|
const [webrtcError, setWebrtcError] = useState<string | null>(null);
|
|
365
367
|
const setStatus = usePlayerStore((x) => x.setStatus);
|
|
@@ -432,7 +434,7 @@ export function WebRTCPlayerInner({
|
|
|
432
434
|
width,
|
|
433
435
|
height,
|
|
434
436
|
}: {
|
|
435
|
-
videoRef?: React.RefObject<HTMLVideoElement>;
|
|
437
|
+
videoRef?: React.RefObject<HTMLVideoElement | null>;
|
|
436
438
|
url: string;
|
|
437
439
|
width?: string | number;
|
|
438
440
|
height?: string | number;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { EyeOff, Pin, X } from "lucide-react-native";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { Linking, Pressable, View } from "react-native";
|
|
4
|
+
import { PinnedRecordViewHydrated, PlaceStreamChatProfile } from "streamplace";
|
|
5
|
+
import {
|
|
6
|
+
Text,
|
|
7
|
+
useCanModerate,
|
|
8
|
+
useLivestreamStore,
|
|
9
|
+
useTheme,
|
|
10
|
+
zero,
|
|
11
|
+
} from "../../";
|
|
12
|
+
import { RichtextSegment, segmentize } from "../../lib/facet";
|
|
13
|
+
import { formatHandleWithAt } from "../../utils/format-handle";
|
|
14
|
+
|
|
15
|
+
const getRgbColor = (color?: PlaceStreamChatProfile.Color) =>
|
|
16
|
+
color ? `rgb(${color.red}, ${color.green}, ${color.blue})` : undefined;
|
|
17
|
+
|
|
18
|
+
function renderSegment(segment: RichtextSegment, index: number) {
|
|
19
|
+
if (segment.features && segment.features.length > 0) {
|
|
20
|
+
const ftr = segment.features[0];
|
|
21
|
+
if (ftr.$type === "app.bsky.richtext.facet#link") {
|
|
22
|
+
return (
|
|
23
|
+
<Text
|
|
24
|
+
key={index}
|
|
25
|
+
style={[{ color: "#007AFF" }]}
|
|
26
|
+
onPress={() => Linking.openURL((ftr as any).uri || "")}
|
|
27
|
+
>
|
|
28
|
+
{segment.text}
|
|
29
|
+
</Text>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
if (ftr.$type === "app.bsky.richtext.facet#mention") {
|
|
33
|
+
return (
|
|
34
|
+
<Text key={index} style={[{ color: "#007AFF" }]}>
|
|
35
|
+
{segment.text}
|
|
36
|
+
</Text>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return <Text key={index}>{segment.text}</Text>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function PinnedCommentNotification({
|
|
44
|
+
pinnedComment,
|
|
45
|
+
onDismiss,
|
|
46
|
+
onUnpin,
|
|
47
|
+
}: {
|
|
48
|
+
pinnedComment: PinnedRecordViewHydrated;
|
|
49
|
+
onDismiss: () => void;
|
|
50
|
+
onUnpin: () => void;
|
|
51
|
+
}) {
|
|
52
|
+
const z = useTheme();
|
|
53
|
+
const message = pinnedComment.message;
|
|
54
|
+
const pinnedByColor = (pinnedComment.pinnedBy as any)?.color || "#bebebe";
|
|
55
|
+
const record = pinnedComment.record;
|
|
56
|
+
|
|
57
|
+
const currentStreamer = useLivestreamStore((state) => state.profile?.did);
|
|
58
|
+
|
|
59
|
+
console.log("checking if we can mod", currentStreamer);
|
|
60
|
+
|
|
61
|
+
const canActuallyPin = useCanModerate(currentStreamer)?.canPin;
|
|
62
|
+
|
|
63
|
+
const messageRecord = message?.record as any;
|
|
64
|
+
|
|
65
|
+
const [expiresAt] = useState<Date | null>(
|
|
66
|
+
record.expiresAt ? new Date(record.expiresAt) : null,
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (!expiresAt) return;
|
|
71
|
+
const remaining = expiresAt.getTime() - Date.now();
|
|
72
|
+
if (remaining <= 0) {
|
|
73
|
+
onDismiss();
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const timeout = setTimeout(onDismiss, remaining);
|
|
77
|
+
return () => clearTimeout(timeout);
|
|
78
|
+
}, [expiresAt, onDismiss]);
|
|
79
|
+
|
|
80
|
+
const authorName = message ? formatHandleWithAt(message.author) : "unknown";
|
|
81
|
+
const authorColor = getRgbColor((message as any)?.chatProfile?.color);
|
|
82
|
+
|
|
83
|
+
const segments = messageRecord
|
|
84
|
+
? segmentize(messageRecord.text, messageRecord.facets)
|
|
85
|
+
: [];
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<View style={[zero.bg.neutral[900], zero.r.lg, { overflow: "hidden" }]}>
|
|
89
|
+
<View
|
|
90
|
+
style={[
|
|
91
|
+
zero.layout.flex.row,
|
|
92
|
+
zero.layout.flex.alignCenter,
|
|
93
|
+
zero.px[3],
|
|
94
|
+
zero.py[2],
|
|
95
|
+
{ gap: 8 },
|
|
96
|
+
]}
|
|
97
|
+
>
|
|
98
|
+
<View style={{ transform: [{ rotate: "-25deg" }] }}>
|
|
99
|
+
<Pin
|
|
100
|
+
size={24}
|
|
101
|
+
color={authorColor || z.theme.colors.primary}
|
|
102
|
+
fill={authorColor || z.theme.colors.primary}
|
|
103
|
+
/>
|
|
104
|
+
</View>
|
|
105
|
+
<View
|
|
106
|
+
style={[zero.layout.flex.column, zero.flex.values[1], { gap: 4 }]}
|
|
107
|
+
>
|
|
108
|
+
<View style={[zero.layout.flex.row, { gap: 4, flexWrap: "wrap" }]}>
|
|
109
|
+
<Text
|
|
110
|
+
style={[
|
|
111
|
+
{
|
|
112
|
+
fontWeight: "600",
|
|
113
|
+
color: authorColor || zero.colors.gray[200],
|
|
114
|
+
},
|
|
115
|
+
]}
|
|
116
|
+
>
|
|
117
|
+
{authorName}
|
|
118
|
+
</Text>
|
|
119
|
+
{segments.map((seg, i) => renderSegment(seg, i))}
|
|
120
|
+
</View>
|
|
121
|
+
</View>
|
|
122
|
+
<View style={[zero.layout.flex.row, { gap: 4 }]}>
|
|
123
|
+
{canActuallyPin && (
|
|
124
|
+
<Pressable onPress={onUnpin} style={{ padding: 4 }}>
|
|
125
|
+
<X size={16} color={zero.colors.gray[400]} />
|
|
126
|
+
</Pressable>
|
|
127
|
+
)}
|
|
128
|
+
<Pressable onPress={onDismiss} style={{ padding: 4 }}>
|
|
129
|
+
<EyeOff size={16} color={zero.colors.gray[400]} />
|
|
130
|
+
</Pressable>
|
|
131
|
+
</View>
|
|
132
|
+
</View>
|
|
133
|
+
</View>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
@@ -358,7 +358,11 @@ export const DropdownMenuSubTrigger = forwardRef<
|
|
|
358
358
|
const { icons } = useTheme();
|
|
359
359
|
|
|
360
360
|
React.useEffect(() => {
|
|
361
|
-
if (
|
|
361
|
+
if (
|
|
362
|
+
subMenuContext &&
|
|
363
|
+
subMenuTitle &&
|
|
364
|
+
subMenuContext.title !== subMenuTitle
|
|
365
|
+
) {
|
|
362
366
|
subMenuContext.setTitle(subMenuTitle);
|
|
363
367
|
}
|
|
364
368
|
}, [subMenuContext, subMenuTitle]);
|
|
@@ -74,7 +74,7 @@ export const DropdownMenuSubTrigger = forwardRef<
|
|
|
74
74
|
inset?: boolean;
|
|
75
75
|
children?: React.ReactNode;
|
|
76
76
|
}
|
|
77
|
-
>(({ inset, children, subMenuTitle, ...props }, ref) => {
|
|
77
|
+
>(({ inset, children, subMenuTitle, style, ...props }, ref) => {
|
|
78
78
|
const { icons } = useTheme();
|
|
79
79
|
const { open } = DropdownMenuPrimitive.useSubContext();
|
|
80
80
|
const Icon =
|
|
@@ -96,6 +96,7 @@ export const DropdownMenuSubTrigger = forwardRef<
|
|
|
96
96
|
layout.flex.alignCenter,
|
|
97
97
|
p[2],
|
|
98
98
|
pr[8],
|
|
99
|
+
style,
|
|
99
100
|
]}
|
|
100
101
|
>
|
|
101
102
|
{children}
|
|
@@ -513,7 +514,7 @@ export const DropdownMenuGroup = forwardRef<
|
|
|
513
514
|
const { theme } = useTheme();
|
|
514
515
|
const { inset, title, children, ...rest } = props;
|
|
515
516
|
return (
|
|
516
|
-
<View style={[
|
|
517
|
+
<View style={[inset && gap[2]]} ref={ref} {...rest}>
|
|
517
518
|
{title && (
|
|
518
519
|
<Text style={[{ color: theme.colors.textMuted }, pb[1], pl[2]]}>
|
|
519
520
|
{title}
|
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Children,
|
|
3
|
-
cloneElement,
|
|
4
|
-
forwardRef,
|
|
5
|
-
isValidElement,
|
|
6
|
-
ReactNode,
|
|
7
|
-
} from "react";
|
|
1
|
+
import { forwardRef, ReactNode } from "react";
|
|
8
2
|
import { Animated, Platform, View, ViewStyle } from "react-native";
|
|
9
3
|
import { Gesture, GestureDetector } from "react-native-gesture-handler";
|
|
10
4
|
import {
|
|
@@ -285,63 +279,63 @@ export const MenuInfo = forwardRef<View, MenuInfoProps>(
|
|
|
285
279
|
},
|
|
286
280
|
);
|
|
287
281
|
|
|
288
|
-
export interface MenuDraggableGroupProps {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
}
|
|
282
|
+
// export interface MenuDraggableGroupProps {
|
|
283
|
+
// children: ReactNode;
|
|
284
|
+
// onMove: (fromIndex: number, toIndex: number) => void;
|
|
285
|
+
// onDragEnd: (fromIndex: number, toIndex: number) => void;
|
|
286
|
+
// dragHandle?: ReactNode;
|
|
287
|
+
// style?: ViewStyle;
|
|
288
|
+
// }
|
|
295
289
|
|
|
296
|
-
export const MenuDraggableGroup = forwardRef<View, MenuDraggableGroupProps>(
|
|
297
|
-
|
|
298
|
-
|
|
290
|
+
// export const MenuDraggableGroup = forwardRef<View, MenuDraggableGroupProps>(
|
|
291
|
+
// ({ children, onMove, onDragEnd, dragHandle, style }, ref) => {
|
|
292
|
+
// const { theme } = useTheme();
|
|
299
293
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
294
|
+
// const childrenArray = Children.toArray(children);
|
|
295
|
+
// const draggableItems = childrenArray.filter(
|
|
296
|
+
// (child) =>
|
|
297
|
+
// isValidElement(child) &&
|
|
298
|
+
// (child.type === MenuItem || child.type === MenuSeparator),
|
|
299
|
+
// );
|
|
306
300
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
301
|
+
// let itemIndex = 0;
|
|
302
|
+
// const enhancedChildren = Children.map(children, (child) => {
|
|
303
|
+
// if (isValidElement(child)) {
|
|
304
|
+
// if (child.type === MenuItem) {
|
|
305
|
+
// const currentIndex = itemIndex;
|
|
306
|
+
// itemIndex++;
|
|
313
307
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
308
|
+
// return cloneElement(child, {
|
|
309
|
+
// draggable: true,
|
|
310
|
+
// dragHandle: dragHandle || (child && child.props.dragHandle),
|
|
311
|
+
// _dragIndex: currentIndex,
|
|
312
|
+
// _dragTotalItems: draggableItems.filter(
|
|
313
|
+
// (c) => isValidElement(c) && c.type === MenuItem,
|
|
314
|
+
// ).length,
|
|
315
|
+
// _onDragMove: onMove,
|
|
316
|
+
// _onDragEnd: onDragEnd,
|
|
317
|
+
// } as any);
|
|
318
|
+
// }
|
|
319
|
+
// if (child.type === MenuSeparator) {
|
|
320
|
+
// return child;
|
|
321
|
+
// }
|
|
322
|
+
// }
|
|
323
|
+
// return child;
|
|
324
|
+
// });
|
|
331
325
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
);
|
|
326
|
+
// return (
|
|
327
|
+
// <View
|
|
328
|
+
// ref={ref}
|
|
329
|
+
// style={[
|
|
330
|
+
// { backgroundColor: theme.colors.muted + "c0" },
|
|
331
|
+
// Platform.OS === "web" ? [px[1], py[1]] : p[1],
|
|
332
|
+
// gap.all[1],
|
|
333
|
+
// { borderRadius: borderRadius.lg },
|
|
334
|
+
// style,
|
|
335
|
+
// ]}
|
|
336
|
+
// >
|
|
337
|
+
// {enhancedChildren}
|
|
338
|
+
// </View>
|
|
339
|
+
// );
|
|
340
|
+
// },
|
|
341
|
+
// );
|
|
@@ -4,28 +4,29 @@ import {
|
|
|
4
4
|
} from "@gorhom/bottom-sheet";
|
|
5
5
|
import React, { forwardRef } from "react";
|
|
6
6
|
import {
|
|
7
|
-
|
|
7
|
+
BlurEvent,
|
|
8
|
+
FocusEvent,
|
|
8
9
|
Platform,
|
|
9
10
|
StyleSheet,
|
|
10
11
|
Text,
|
|
11
12
|
TextInput,
|
|
12
|
-
TextInputFocusEventData,
|
|
13
13
|
TextInputProps,
|
|
14
14
|
TextProps,
|
|
15
15
|
TouchableOpacity,
|
|
16
16
|
View,
|
|
17
17
|
ViewProps,
|
|
18
18
|
} from "react-native";
|
|
19
|
-
import
|
|
19
|
+
import * as tokens from "../../../lib/theme/tokens";
|
|
20
20
|
|
|
21
21
|
// Base input primitive interface
|
|
22
|
-
export interface InputPrimitiveProps
|
|
22
|
+
export interface InputPrimitiveProps
|
|
23
|
+
extends Omit<TextInputProps, "onChange" | "onFocus" | "onBlur"> {
|
|
23
24
|
error?: boolean;
|
|
24
25
|
disabled?: boolean;
|
|
25
26
|
loading?: boolean;
|
|
26
27
|
onChange?: (text: string) => void;
|
|
27
|
-
onFocus?: (event:
|
|
28
|
-
onBlur?: (event:
|
|
28
|
+
onFocus?: (event: FocusEvent) => void;
|
|
29
|
+
onBlur?: (event: BlurEvent) => void;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
// Input root primitive - the main TextInput component
|
|
@@ -75,7 +76,7 @@ export const InputRoot = forwardRef<any, InputPrimitiveProps>(
|
|
|
75
76
|
);
|
|
76
77
|
|
|
77
78
|
const handleFocus = React.useCallback(
|
|
78
|
-
(event:
|
|
79
|
+
(event: FocusEvent) => {
|
|
79
80
|
setIsFocused(true);
|
|
80
81
|
if (onFocus) {
|
|
81
82
|
onFocus(event);
|
|
@@ -85,7 +86,7 @@ export const InputRoot = forwardRef<any, InputPrimitiveProps>(
|
|
|
85
86
|
);
|
|
86
87
|
|
|
87
88
|
const handleBlur = React.useCallback(
|
|
88
|
-
(event:
|
|
89
|
+
(event: BlurEvent) => {
|
|
89
90
|
setIsFocused(false);
|
|
90
91
|
if (onBlur) {
|
|
91
92
|
onBlur(event);
|
|
@@ -272,13 +273,21 @@ export const InputAddon = forwardRef<
|
|
|
272
273
|
style,
|
|
273
274
|
];
|
|
274
275
|
|
|
276
|
+
// Filter out null event handlers for TouchableOpacity compatibility
|
|
277
|
+
const { onBlur, onFocus, ...restProps } = props;
|
|
278
|
+
const touchableProps = {
|
|
279
|
+
...restProps,
|
|
280
|
+
...(onBlur !== null && onBlur !== undefined && { onBlur }),
|
|
281
|
+
...(onFocus !== null && onFocus !== undefined && { onFocus }),
|
|
282
|
+
};
|
|
283
|
+
|
|
275
284
|
if (touchable && onPress) {
|
|
276
285
|
return (
|
|
277
286
|
<TouchableOpacity
|
|
278
287
|
ref={ref as React.Ref<React.ComponentRef<typeof TouchableOpacity>>}
|
|
279
288
|
style={addonStyle as any}
|
|
280
289
|
onPress={onPress}
|
|
281
|
-
{...
|
|
290
|
+
{...touchableProps}
|
|
282
291
|
>
|
|
283
292
|
{children}
|
|
284
293
|
</TouchableOpacity>
|
|
@@ -7,12 +7,14 @@ import {
|
|
|
7
7
|
Pressable,
|
|
8
8
|
} from "react-native-gesture-handler";
|
|
9
9
|
import Animated, {
|
|
10
|
+
Easing,
|
|
10
11
|
Extrapolation,
|
|
11
12
|
interpolate,
|
|
12
13
|
runOnJS,
|
|
13
14
|
useAnimatedStyle,
|
|
14
15
|
useSharedValue,
|
|
15
16
|
withSpring,
|
|
17
|
+
withTiming,
|
|
16
18
|
} from "react-native-reanimated";
|
|
17
19
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
18
20
|
import { useKeyboardSlide } from "../../hooks";
|
|
@@ -23,6 +25,11 @@ const AnimatedView = Animated.createAnimatedComponent(View);
|
|
|
23
25
|
|
|
24
26
|
const { height: SCREEN_HEIGHT } = Dimensions.get("window");
|
|
25
27
|
|
|
28
|
+
const TIMING_CONFIG = {
|
|
29
|
+
duration: 400,
|
|
30
|
+
easing: Easing.out(Easing.quad),
|
|
31
|
+
};
|
|
32
|
+
|
|
26
33
|
type ResizableChatSheetProps = {
|
|
27
34
|
startingPercentage?: number;
|
|
28
35
|
isPlayerRatioGreater: boolean;
|
|
@@ -31,8 +38,6 @@ type ResizableChatSheetProps = {
|
|
|
31
38
|
renderAbove?: (isCollapsed: boolean) => React.ReactNode;
|
|
32
39
|
};
|
|
33
40
|
|
|
34
|
-
const SPRING_CONFIG = { damping: 20, stiffness: 100 };
|
|
35
|
-
|
|
36
41
|
export function Resizable({
|
|
37
42
|
startingPercentage,
|
|
38
43
|
isPlayerRatioGreater,
|
|
@@ -56,7 +61,7 @@ export function Resizable({
|
|
|
56
61
|
const targetHeight = startingPercentage
|
|
57
62
|
? startingPercentage * SCREEN_HEIGHT
|
|
58
63
|
: MIN_HEIGHT;
|
|
59
|
-
sheetHeight.value =
|
|
64
|
+
sheetHeight.value = withTiming(targetHeight, TIMING_CONFIG);
|
|
60
65
|
setIsCollapsed(targetHeight < COLLAPSE_HEIGHT);
|
|
61
66
|
}, 1000);
|
|
62
67
|
}, []);
|
|
@@ -73,7 +78,7 @@ export function Resizable({
|
|
|
73
78
|
|
|
74
79
|
const nowCollapsed = newHeight < COLLAPSE_HEIGHT;
|
|
75
80
|
if (nowCollapsed && !wasCollapsed.value) {
|
|
76
|
-
sheetHeight.value =
|
|
81
|
+
sheetHeight.value = withTiming(MIN_HEIGHT, TIMING_CONFIG);
|
|
77
82
|
wasCollapsed.value = true;
|
|
78
83
|
runOnJS(setIsCollapsed)(true);
|
|
79
84
|
} else if (!nowCollapsed && wasCollapsed.value) {
|
|
@@ -139,8 +144,8 @@ export function Resizable({
|
|
|
139
144
|
onPress={() => {
|
|
140
145
|
const isCurrentlyCollapsed = sheetHeight.value === MIN_HEIGHT;
|
|
141
146
|
sheetHeight.value = isCurrentlyCollapsed
|
|
142
|
-
?
|
|
143
|
-
:
|
|
147
|
+
? withTiming(MAX_HEIGHT, TIMING_CONFIG)
|
|
148
|
+
: withTiming(MIN_HEIGHT, TIMING_CONFIG);
|
|
144
149
|
setIsCollapsed(!isCurrentlyCollapsed);
|
|
145
150
|
}}
|
|
146
151
|
>
|
|
@@ -21,7 +21,7 @@ import Animated, {
|
|
|
21
21
|
} from "react-native-reanimated";
|
|
22
22
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
23
23
|
import { Circle, Svg } from "react-native-svg";
|
|
24
|
-
import { useTheme } from "../../
|
|
24
|
+
import { useTheme } from "../../lib/theme/theme";
|
|
25
25
|
import { Button } from "./button";
|
|
26
26
|
import { Text } from "./text";
|
|
27
27
|
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
createContext,
|
|
4
4
|
useCallback,
|
|
5
5
|
useContext,
|
|
6
|
+
useMemo,
|
|
6
7
|
useRef,
|
|
7
8
|
useState,
|
|
8
9
|
} from "react";
|
|
@@ -85,8 +86,13 @@ export function ProfileCacheProvider({
|
|
|
85
86
|
[flush],
|
|
86
87
|
);
|
|
87
88
|
|
|
89
|
+
const value = useMemo(
|
|
90
|
+
() => ({ profiles, requestProfiles }),
|
|
91
|
+
[profiles, requestProfiles],
|
|
92
|
+
);
|
|
93
|
+
|
|
88
94
|
return (
|
|
89
|
-
<ProfileCacheContext.Provider value={
|
|
95
|
+
<ProfileCacheContext.Provider value={value}>
|
|
90
96
|
{children}
|
|
91
97
|
</ProfileCacheContext.Provider>
|
|
92
98
|
);
|
|
@@ -22,7 +22,7 @@ function getLiveConnectionQuality(
|
|
|
22
22
|
export function useSegmentTiming() {
|
|
23
23
|
const latestSegment = useLivestreamStore((x) => x.segment);
|
|
24
24
|
const [segmentDeltas, setSegmentDeltas] = useState<number[]>([]);
|
|
25
|
-
const prevSegmentRef = useRef
|
|
25
|
+
const prevSegmentRef = useRef(latestSegment);
|
|
26
26
|
const prevTimestampRef = useRef<number | null>(null);
|
|
27
27
|
const ls = useLivestream();
|
|
28
28
|
|