@streamplace/components 0.9.4 → 0.9.6
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/mod-view.d.ts.map +1 -1
- package/dist/components/chat/mod-view.js +7 -79
- package/dist/components/chat/mod-view.js.map +1 -1
- package/dist/components/chat/update-stream-title-dialog.d.ts +9 -0
- package/dist/components/chat/update-stream-title-dialog.d.ts.map +1 -0
- package/dist/components/chat/update-stream-title-dialog.js +74 -0
- package/dist/components/chat/update-stream-title-dialog.js.map +1 -0
- package/dist/components/content-metadata/content-metadata-form.d.ts.map +1 -1
- package/dist/components/content-metadata/content-metadata-form.js +7 -8
- package/dist/components/content-metadata/content-metadata-form.js.map +1 -1
- package/dist/components/ui/admonition.d.ts +14 -0
- package/dist/components/ui/admonition.d.ts.map +1 -0
- package/dist/components/ui/admonition.js +117 -0
- package/dist/components/ui/admonition.js.map +1 -0
- package/dist/components/ui/button.d.ts +1 -0
- package/dist/components/ui/button.d.ts.map +1 -1
- package/dist/components/ui/button.js +2 -2
- package/dist/components/ui/button.js.map +1 -1
- package/dist/components/ui/index.d.ts +1 -0
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/components/ui/index.js +1 -0
- package/dist/components/ui/index.js.map +1 -1
- package/dist/components/ui/primitives/button.d.ts +3 -2
- package/dist/components/ui/primitives/button.d.ts.map +1 -1
- package/dist/components/ui/primitives/button.js +20 -2
- package/dist/components/ui/primitives/button.js.map +1 -1
- package/dist/components/ui/resizeable.d.ts.map +1 -1
- package/dist/components/ui/resizeable.js +2 -1
- package/dist/components/ui/resizeable.js.map +1 -1
- package/dist/components/ui/text.d.ts +2 -1
- package/dist/components/ui/text.d.ts.map +1 -1
- package/dist/components/ui/text.js.map +1 -1
- package/dist/components/ui/textarea.d.ts.map +1 -1
- package/dist/components/ui/textarea.js +3 -1
- package/dist/components/ui/textarea.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/theme/atoms.d.ts +148 -148
- package/dist/lib/theme/tokens.d.ts +11 -11
- package/dist/lib/theme/tokens.js +11 -11
- package/dist/utils/did.d.ts +13 -0
- package/dist/utils/did.d.ts.map +1 -0
- package/dist/utils/did.js +43 -0
- package/dist/utils/did.js.map +1 -0
- package/locales/en-US/settings.ftl +2 -1
- package/node-compile-cache/v22.15.0-x64-efe9a9df-0/37be0eec +0 -0
- package/package.json +2 -2
- package/src/components/chat/mod-view.tsx +0 -205
- package/src/components/chat/update-stream-title-dialog.tsx +169 -0
- package/src/components/content-metadata/content-metadata-form.tsx +37 -10
- package/src/components/ui/admonition.tsx +177 -0
- package/src/components/ui/button.tsx +3 -0
- package/src/components/ui/index.ts +1 -0
- package/src/components/ui/primitives/button.tsx +37 -11
- package/src/components/ui/resizeable.tsx +2 -1
- package/src/components/ui/text.tsx +11 -1
- package/src/components/ui/textarea.tsx +3 -0
- package/src/index.tsx +2 -0
- package/src/lib/theme/tokens.ts +11 -11
- package/src/utils/did.ts +61 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { forwardRef, useCallback, useEffect, useState } from "react";
|
|
2
|
-
import { ScrollView, View } from "react-native";
|
|
2
|
+
import { Linking, Pressable, ScrollView, View } from "react-native";
|
|
3
3
|
import {
|
|
4
4
|
CONTENT_WARNINGS,
|
|
5
5
|
LICENSE_OPTIONS,
|
|
6
6
|
} from "../../lib/metadata-constants";
|
|
7
7
|
|
|
8
|
+
import { ExternalLink } from "lucide-react-native";
|
|
8
9
|
import {
|
|
9
10
|
PlaceStreamMetadataConfiguration,
|
|
10
11
|
PlaceStreamMetadataContentRights,
|
|
@@ -21,6 +22,7 @@ import {
|
|
|
21
22
|
} from "../../streamplace-store/streamplace-store";
|
|
22
23
|
import { usePDSAgent } from "../../streamplace-store/xrpc";
|
|
23
24
|
import * as zero from "../../ui";
|
|
25
|
+
import { Admonition } from "../ui";
|
|
24
26
|
import { Button } from "../ui/button";
|
|
25
27
|
import { Checkbox } from "../ui/checkbox";
|
|
26
28
|
import { Input } from "../ui/input";
|
|
@@ -41,7 +43,6 @@ export interface ContentMetadataFormProps {
|
|
|
41
43
|
style?: any;
|
|
42
44
|
}
|
|
43
45
|
|
|
44
|
-
// ButtonSelector component (same as in livestream-panel)
|
|
45
46
|
const ButtonSelector = ({
|
|
46
47
|
values,
|
|
47
48
|
selectedValue,
|
|
@@ -55,12 +56,13 @@ const ButtonSelector = ({
|
|
|
55
56
|
disabledValues?: string[];
|
|
56
57
|
style?: any[];
|
|
57
58
|
}) => (
|
|
58
|
-
<View style={[layout.flex.row, gap.all[1], ...style]}>
|
|
59
|
+
<View style={[layout.flex.row, gap.all[1], layout.flex.wrap.wrap, ...style]}>
|
|
59
60
|
{values.map(({ label, value }) => (
|
|
60
61
|
<Button
|
|
61
62
|
key={value}
|
|
62
63
|
variant={selectedValue === value ? "primary" : "secondary"}
|
|
63
64
|
size="pill"
|
|
65
|
+
width="min"
|
|
64
66
|
disabled={disabledValues.includes(value)}
|
|
65
67
|
onPress={() => setSelectedValue(value)}
|
|
66
68
|
style={[
|
|
@@ -71,10 +73,8 @@ const ButtonSelector = ({
|
|
|
71
73
|
]}
|
|
72
74
|
>
|
|
73
75
|
<Text
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
{ fontSize: 14, fontWeight: "600" },
|
|
77
|
-
]}
|
|
76
|
+
size="sm"
|
|
77
|
+
style={[selectedValue === value ? text.white : text.gray[300]]}
|
|
78
78
|
>
|
|
79
79
|
{label}
|
|
80
80
|
</Text>
|
|
@@ -93,6 +93,7 @@ export const ContentMetadataForm = forwardRef<any, ContentMetadataFormProps>(
|
|
|
93
93
|
const getContentMetadata = useGetContentMetadata();
|
|
94
94
|
const saveContentMetadata = useSaveContentMetadata();
|
|
95
95
|
const toast = useToast();
|
|
96
|
+
const th = zero.useTheme();
|
|
96
97
|
|
|
97
98
|
// Local state for metadata
|
|
98
99
|
const [contentWarnings, setContentWarnings] = useState<string[]>([]);
|
|
@@ -364,7 +365,7 @@ export const ContentMetadataForm = forwardRef<any, ContentMetadataFormProps>(
|
|
|
364
365
|
]}
|
|
365
366
|
selectedValue={activeSection}
|
|
366
367
|
setSelectedValue={setActiveSection}
|
|
367
|
-
style={[{ marginVertical: -2
|
|
368
|
+
style={[{ marginVertical: -2 }]}
|
|
368
369
|
/>
|
|
369
370
|
</View>
|
|
370
371
|
|
|
@@ -379,8 +380,7 @@ export const ContentMetadataForm = forwardRef<any, ContentMetadataFormProps>(
|
|
|
379
380
|
gap.all[2],
|
|
380
381
|
]}
|
|
381
382
|
>
|
|
382
|
-
<Text>Content Warnings</Text>
|
|
383
|
-
<Text muted>(optional)</Text>
|
|
383
|
+
<Text size="lg">Content Warnings</Text>
|
|
384
384
|
</View>
|
|
385
385
|
<View style={[gap.all[2], w.percent[100]]}>
|
|
386
386
|
{CONTENT_WARNINGS.map((warning) => (
|
|
@@ -398,6 +398,33 @@ export const ContentMetadataForm = forwardRef<any, ContentMetadataFormProps>(
|
|
|
398
398
|
</View>
|
|
399
399
|
))}
|
|
400
400
|
</View>
|
|
401
|
+
<Admonition variant="info" size="sm">
|
|
402
|
+
<Text size="sm">
|
|
403
|
+
You are required to disclose if your content is not suitable
|
|
404
|
+
for certain viewers.
|
|
405
|
+
</Text>
|
|
406
|
+
</Admonition>
|
|
407
|
+
<Admonition variant="warning" size="sm">
|
|
408
|
+
<Text size="sm">
|
|
409
|
+
Your node may prohibit some of this content. Read the
|
|
410
|
+
community guidelines to make sure.{" "}
|
|
411
|
+
<Pressable
|
|
412
|
+
onPress={() =>
|
|
413
|
+
Linking.openURL(
|
|
414
|
+
"https://blog.stream.place/3mcqwibo4ks2w",
|
|
415
|
+
)
|
|
416
|
+
}
|
|
417
|
+
>
|
|
418
|
+
<Text size="sm" color={zero.colors.blue[400]}>
|
|
419
|
+
Learn more{" "}
|
|
420
|
+
<ExternalLink
|
|
421
|
+
size="14"
|
|
422
|
+
style={{ marginVertical: -2 }}
|
|
423
|
+
/>
|
|
424
|
+
</Text>
|
|
425
|
+
</Pressable>
|
|
426
|
+
</Text>
|
|
427
|
+
</Admonition>
|
|
401
428
|
</View>
|
|
402
429
|
)}
|
|
403
430
|
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { AlertCircle, CheckCircle, Info, XCircle } from "lucide-react-native";
|
|
2
|
+
import { View, ViewStyle } from "react-native";
|
|
3
|
+
import { useTheme } from "../../ui";
|
|
4
|
+
import { Text } from "./text";
|
|
5
|
+
|
|
6
|
+
type AdmonitionVariant = "default" | "success" | "error" | "info" | "warning";
|
|
7
|
+
type AdmonitionSize = "sm" | "md" | "lg";
|
|
8
|
+
|
|
9
|
+
type AdmonitionProps = {
|
|
10
|
+
variant?: AdmonitionVariant;
|
|
11
|
+
size?: AdmonitionSize;
|
|
12
|
+
title?: string;
|
|
13
|
+
children?: React.ReactNode;
|
|
14
|
+
iconLeft?: React.ComponentType<any>;
|
|
15
|
+
style?: ViewStyle;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export function Admonition({
|
|
19
|
+
variant = "default",
|
|
20
|
+
size = "md",
|
|
21
|
+
title,
|
|
22
|
+
children,
|
|
23
|
+
iconLeft,
|
|
24
|
+
style,
|
|
25
|
+
}: AdmonitionProps) {
|
|
26
|
+
const { theme, icons } = useTheme();
|
|
27
|
+
|
|
28
|
+
const defaultIconLeft = (() => {
|
|
29
|
+
if (iconLeft) return iconLeft;
|
|
30
|
+
switch (variant) {
|
|
31
|
+
case "success":
|
|
32
|
+
return CheckCircle;
|
|
33
|
+
case "error":
|
|
34
|
+
return XCircle;
|
|
35
|
+
case "info":
|
|
36
|
+
return Info;
|
|
37
|
+
case "warning":
|
|
38
|
+
return AlertCircle;
|
|
39
|
+
default:
|
|
40
|
+
return Info;
|
|
41
|
+
}
|
|
42
|
+
})();
|
|
43
|
+
|
|
44
|
+
const FinalIconLeft = defaultIconLeft;
|
|
45
|
+
|
|
46
|
+
const variantStyles: Record<AdmonitionVariant, ViewStyle> = {
|
|
47
|
+
default: {
|
|
48
|
+
backgroundColor: theme.colors.secondary,
|
|
49
|
+
borderColor: theme.colors.border,
|
|
50
|
+
},
|
|
51
|
+
success: {
|
|
52
|
+
backgroundColor: theme.colors.success + "15",
|
|
53
|
+
borderColor: theme.colors.success,
|
|
54
|
+
},
|
|
55
|
+
error: {
|
|
56
|
+
backgroundColor: theme.colors.destructive + "15",
|
|
57
|
+
borderColor: theme.colors.destructive,
|
|
58
|
+
},
|
|
59
|
+
info: {
|
|
60
|
+
backgroundColor: theme.colors.info + "15",
|
|
61
|
+
borderColor: theme.colors.info,
|
|
62
|
+
},
|
|
63
|
+
warning: {
|
|
64
|
+
backgroundColor: theme.colors.warning + "15",
|
|
65
|
+
borderColor: theme.colors.warning,
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const iconColor = (() => {
|
|
70
|
+
switch (variant) {
|
|
71
|
+
case "success":
|
|
72
|
+
return theme.colors.success;
|
|
73
|
+
case "error":
|
|
74
|
+
return theme.colors.destructive;
|
|
75
|
+
case "info":
|
|
76
|
+
return theme.colors.info;
|
|
77
|
+
case "warning":
|
|
78
|
+
return theme.colors.warning;
|
|
79
|
+
default:
|
|
80
|
+
return theme.colors.foreground;
|
|
81
|
+
}
|
|
82
|
+
})();
|
|
83
|
+
|
|
84
|
+
const sizeConfig = (() => {
|
|
85
|
+
switch (size) {
|
|
86
|
+
case "sm":
|
|
87
|
+
return {
|
|
88
|
+
borderRadius: 8,
|
|
89
|
+
padding: 12,
|
|
90
|
+
gap: 6,
|
|
91
|
+
iconSize: icons.size.md,
|
|
92
|
+
titleSize: "base" as const,
|
|
93
|
+
contentSize: "sm" as const,
|
|
94
|
+
innerGap: 8,
|
|
95
|
+
};
|
|
96
|
+
case "lg":
|
|
97
|
+
return {
|
|
98
|
+
borderRadius: 16,
|
|
99
|
+
padding: 20,
|
|
100
|
+
gap: 12,
|
|
101
|
+
iconSize: icons.size.xl,
|
|
102
|
+
titleSize: "xl" as const,
|
|
103
|
+
contentSize: "lg" as const,
|
|
104
|
+
innerGap: 16,
|
|
105
|
+
};
|
|
106
|
+
case "md":
|
|
107
|
+
default:
|
|
108
|
+
return {
|
|
109
|
+
borderRadius: 12,
|
|
110
|
+
padding: 16,
|
|
111
|
+
gap: 8,
|
|
112
|
+
iconSize: icons.size.lg,
|
|
113
|
+
titleSize: "lg" as const,
|
|
114
|
+
contentSize: "base" as const,
|
|
115
|
+
innerGap: 12,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
})();
|
|
119
|
+
|
|
120
|
+
let childrenIn = (
|
|
121
|
+
<View
|
|
122
|
+
style={{
|
|
123
|
+
paddingLeft: title ? sizeConfig.iconSize + sizeConfig.innerGap : 0,
|
|
124
|
+
}}
|
|
125
|
+
>
|
|
126
|
+
{typeof children === "string" ? (
|
|
127
|
+
<Text
|
|
128
|
+
size={sizeConfig.contentSize}
|
|
129
|
+
style={{ color: theme.colors.cardForeground, flexWrap: "wrap" }}
|
|
130
|
+
>
|
|
131
|
+
{children}
|
|
132
|
+
</Text>
|
|
133
|
+
) : (
|
|
134
|
+
children
|
|
135
|
+
)}
|
|
136
|
+
</View>
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<View
|
|
141
|
+
style={[
|
|
142
|
+
{
|
|
143
|
+
borderRadius: sizeConfig.borderRadius,
|
|
144
|
+
borderWidth: 1,
|
|
145
|
+
padding: sizeConfig.padding,
|
|
146
|
+
gap: sizeConfig.gap,
|
|
147
|
+
},
|
|
148
|
+
variantStyles[variant],
|
|
149
|
+
style,
|
|
150
|
+
]}
|
|
151
|
+
>
|
|
152
|
+
<View
|
|
153
|
+
style={{
|
|
154
|
+
flexDirection: "row",
|
|
155
|
+
alignItems: "flex-start",
|
|
156
|
+
gap: sizeConfig.innerGap,
|
|
157
|
+
}}
|
|
158
|
+
>
|
|
159
|
+
{FinalIconLeft && (
|
|
160
|
+
<FinalIconLeft size={sizeConfig.iconSize} color={iconColor} />
|
|
161
|
+
)}
|
|
162
|
+
{title ? (
|
|
163
|
+
<Text
|
|
164
|
+
size={sizeConfig.titleSize}
|
|
165
|
+
weight="semibold"
|
|
166
|
+
style={{ flex: 1 }}
|
|
167
|
+
>
|
|
168
|
+
{title}
|
|
169
|
+
</Text>
|
|
170
|
+
) : (
|
|
171
|
+
children && <View style={{ flex: 1 }}>{childrenIn}</View>
|
|
172
|
+
)}
|
|
173
|
+
</View>
|
|
174
|
+
{children && title && childrenIn}
|
|
175
|
+
</View>
|
|
176
|
+
);
|
|
177
|
+
}
|
|
@@ -41,6 +41,7 @@ export interface ButtonProps
|
|
|
41
41
|
loading?: boolean;
|
|
42
42
|
loadingText?: string;
|
|
43
43
|
width?: "full" | "min" | number;
|
|
44
|
+
hoverStyle?: ButtonPrimitiveProps["hoverStyle"];
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
export const Button = forwardRef<any, ButtonProps>(
|
|
@@ -56,6 +57,7 @@ export const Button = forwardRef<any, ButtonProps>(
|
|
|
56
57
|
disabled,
|
|
57
58
|
style,
|
|
58
59
|
width = "full",
|
|
60
|
+
hoverStyle,
|
|
59
61
|
...props
|
|
60
62
|
},
|
|
61
63
|
ref,
|
|
@@ -222,6 +224,7 @@ export const Button = forwardRef<any, ButtonProps>(
|
|
|
222
224
|
ref={ref}
|
|
223
225
|
disabled={disabled || loading}
|
|
224
226
|
style={[buttonStyle, sizeStyles.button, widthStyle, style]}
|
|
227
|
+
hoverStyle={hoverStyle}
|
|
225
228
|
{...props}
|
|
226
229
|
>
|
|
227
230
|
<ButtonPrimitive.Content style={sizeStyles.inner}>
|
|
@@ -1,19 +1,21 @@
|
|
|
1
|
-
import React, { forwardRef } from "react";
|
|
1
|
+
import React, { forwardRef, useState } from "react";
|
|
2
2
|
import {
|
|
3
3
|
AccessibilityRole,
|
|
4
4
|
GestureResponderEvent,
|
|
5
|
+
Platform,
|
|
6
|
+
Pressable,
|
|
7
|
+
PressableProps,
|
|
8
|
+
StyleProp,
|
|
5
9
|
StyleSheet,
|
|
6
10
|
Text,
|
|
7
11
|
TextProps,
|
|
8
|
-
TouchableOpacity,
|
|
9
|
-
TouchableOpacityProps,
|
|
10
12
|
View,
|
|
11
13
|
ViewProps,
|
|
14
|
+
ViewStyle,
|
|
12
15
|
} from "react-native";
|
|
13
16
|
|
|
14
17
|
// Base button primitive interface
|
|
15
|
-
export interface ButtonPrimitiveProps
|
|
16
|
-
extends Omit<TouchableOpacityProps, "onPress"> {
|
|
18
|
+
export interface ButtonPrimitiveProps extends Omit<PressableProps, "onPress"> {
|
|
17
19
|
onPress?: (event: GestureResponderEvent) => void;
|
|
18
20
|
disabled?: boolean;
|
|
19
21
|
loading?: boolean;
|
|
@@ -21,11 +23,12 @@ export interface ButtonPrimitiveProps
|
|
|
21
23
|
accessibilityLabel?: string;
|
|
22
24
|
accessibilityHint?: string;
|
|
23
25
|
testID?: string;
|
|
26
|
+
hoverStyle?: StyleProp<ViewStyle>;
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
// Button root primitive - handles all touch interactions
|
|
27
30
|
export const ButtonRoot = forwardRef<
|
|
28
|
-
React.ComponentRef<typeof
|
|
31
|
+
React.ComponentRef<typeof Pressable>,
|
|
29
32
|
ButtonPrimitiveProps
|
|
30
33
|
>(
|
|
31
34
|
(
|
|
@@ -43,11 +46,13 @@ export const ButtonRoot = forwardRef<
|
|
|
43
46
|
accessibilityState,
|
|
44
47
|
testID,
|
|
45
48
|
style,
|
|
46
|
-
|
|
49
|
+
hoverStyle,
|
|
47
50
|
...props
|
|
48
51
|
},
|
|
49
52
|
ref,
|
|
50
53
|
) => {
|
|
54
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
55
|
+
|
|
51
56
|
const handlePress = React.useCallback(
|
|
52
57
|
(event: GestureResponderEvent) => {
|
|
53
58
|
if (!disabled && !loading && onPress) {
|
|
@@ -84,15 +89,26 @@ export const ButtonRoot = forwardRef<
|
|
|
84
89
|
[disabled, loading, onLongPress],
|
|
85
90
|
);
|
|
86
91
|
|
|
92
|
+
const handleHoverIn = React.useCallback(() => {
|
|
93
|
+
if (!disabled && !loading) {
|
|
94
|
+
setIsHovered(true);
|
|
95
|
+
}
|
|
96
|
+
}, [disabled, loading]);
|
|
97
|
+
|
|
98
|
+
const handleHoverOut = React.useCallback(() => {
|
|
99
|
+
setIsHovered(false);
|
|
100
|
+
}, []);
|
|
101
|
+
|
|
87
102
|
return (
|
|
88
|
-
<
|
|
103
|
+
<Pressable
|
|
89
104
|
ref={ref}
|
|
90
105
|
onPress={handlePress}
|
|
91
106
|
onPressIn={handlePressIn}
|
|
92
107
|
onPressOut={handlePressOut}
|
|
93
108
|
onLongPress={handleLongPress}
|
|
109
|
+
onHoverIn={handleHoverIn}
|
|
110
|
+
onHoverOut={handleHoverOut}
|
|
94
111
|
disabled={disabled || loading}
|
|
95
|
-
activeOpacity={disabled || loading ? 1 : activeOpacity}
|
|
96
112
|
accessibilityRole={accessibilityRole}
|
|
97
113
|
accessibilityLabel={accessibilityLabel}
|
|
98
114
|
accessibilityHint={accessibilityHint}
|
|
@@ -104,13 +120,15 @@ export const ButtonRoot = forwardRef<
|
|
|
104
120
|
testID={testID}
|
|
105
121
|
style={[
|
|
106
122
|
primitiveStyles.button,
|
|
123
|
+
primitiveStyles.transition,
|
|
107
124
|
(disabled || loading) && primitiveStyles.disabled,
|
|
108
|
-
style,
|
|
125
|
+
style as any,
|
|
126
|
+
isHovered && hoverStyle,
|
|
109
127
|
]}
|
|
110
128
|
{...props}
|
|
111
129
|
>
|
|
112
130
|
{children}
|
|
113
|
-
</
|
|
131
|
+
</Pressable>
|
|
114
132
|
);
|
|
115
133
|
},
|
|
116
134
|
);
|
|
@@ -245,6 +263,14 @@ const primitiveStyles = StyleSheet.create({
|
|
|
245
263
|
alignItems: "center",
|
|
246
264
|
justifyContent: "center",
|
|
247
265
|
},
|
|
266
|
+
transition:
|
|
267
|
+
Platform.OS === "web"
|
|
268
|
+
? // probably fine if web-only
|
|
269
|
+
({
|
|
270
|
+
transitionDuration: "150ms",
|
|
271
|
+
transitionProperty: "background-color, border-color, color",
|
|
272
|
+
} as any)
|
|
273
|
+
: undefined,
|
|
248
274
|
disabled: {
|
|
249
275
|
opacity: 0.5,
|
|
250
276
|
},
|
|
@@ -62,7 +62,17 @@ const textVariants = cva("", {
|
|
|
62
62
|
|
|
63
63
|
export interface TextProps
|
|
64
64
|
extends Omit<TextPrimitiveProps, "variant" | "size" | "weight" | "color">,
|
|
65
|
-
VariantProps<typeof textVariants> {
|
|
65
|
+
Omit<VariantProps<typeof textVariants>, "color"> {
|
|
66
|
+
// Override color to accept hex values and custom strings
|
|
67
|
+
color?:
|
|
68
|
+
| "default"
|
|
69
|
+
| "muted"
|
|
70
|
+
| "primary"
|
|
71
|
+
| "secondary"
|
|
72
|
+
| "destructive"
|
|
73
|
+
| "success"
|
|
74
|
+
| "warning"
|
|
75
|
+
| (string & {});
|
|
66
76
|
// Additional convenience props
|
|
67
77
|
muted?: boolean;
|
|
68
78
|
bold?: boolean;
|
|
@@ -5,9 +5,11 @@ import {
|
|
|
5
5
|
import * as React from "react";
|
|
6
6
|
import { Platform, TextInput, type TextInputProps } from "react-native";
|
|
7
7
|
import { bg, borders, flex, p, text } from "../../lib/theme/atoms";
|
|
8
|
+
import { useTheme } from "../../ui";
|
|
8
9
|
|
|
9
10
|
const Textarea = React.forwardRef<TextInput, TextInputProps>(
|
|
10
11
|
({ style, multiline = true, numberOfLines = 4, ...props }, ref) => {
|
|
12
|
+
let th = useTheme();
|
|
11
13
|
// Detect if we're inside a bottom sheet
|
|
12
14
|
let isInBottomSheet = false;
|
|
13
15
|
try {
|
|
@@ -41,6 +43,7 @@ const Textarea = React.forwardRef<TextInput, TextInputProps>(
|
|
|
41
43
|
multiline={multiline}
|
|
42
44
|
numberOfLines={numberOfLines}
|
|
43
45
|
textAlignVertical="top"
|
|
46
|
+
placeholderTextColor={th.theme.colors.textMuted}
|
|
44
47
|
{...props}
|
|
45
48
|
/>
|
|
46
49
|
);
|
package/src/index.tsx
CHANGED
|
@@ -34,12 +34,14 @@ export * from "./lib/theme";
|
|
|
34
34
|
export * from "./components/chat/chat";
|
|
35
35
|
export * from "./components/chat/chat-box";
|
|
36
36
|
export * from "./components/chat/system-message";
|
|
37
|
+
export * from "./components/chat/update-stream-title-dialog";
|
|
37
38
|
export { default as VideoRetry } from "./components/mobile-player/video-retry";
|
|
38
39
|
export * from "./lib/system-messages";
|
|
39
40
|
|
|
40
41
|
export * from "./components/stream-notification";
|
|
41
42
|
export * from "./lib/stream-notifications";
|
|
42
43
|
|
|
44
|
+
export * from "./utils/did";
|
|
43
45
|
export * from "./utils/format-handle";
|
|
44
46
|
|
|
45
47
|
export { DanmuOverlay } from "./components/danmu/danmu-overlay";
|
package/src/lib/theme/tokens.ts
CHANGED
|
@@ -337,17 +337,17 @@ export const colors = {
|
|
|
337
337
|
},
|
|
338
338
|
|
|
339
339
|
warning: {
|
|
340
|
-
50: "#
|
|
341
|
-
100: "#
|
|
342
|
-
200: "#
|
|
343
|
-
300: "#
|
|
344
|
-
400: "#
|
|
345
|
-
500: "#
|
|
346
|
-
600: "#
|
|
347
|
-
700: "#
|
|
348
|
-
800: "#
|
|
349
|
-
900: "#
|
|
350
|
-
950: "#
|
|
340
|
+
50: "#fffaf0",
|
|
341
|
+
100: "#ffe6c7",
|
|
342
|
+
200: "#ffd99e",
|
|
343
|
+
300: "#ffcc75",
|
|
344
|
+
400: "#ffb94e",
|
|
345
|
+
500: "#ff9e1f",
|
|
346
|
+
600: "#e67e00",
|
|
347
|
+
700: "#cc6600",
|
|
348
|
+
800: "#998c00",
|
|
349
|
+
900: "#664200",
|
|
350
|
+
950: "#332900",
|
|
351
351
|
},
|
|
352
352
|
|
|
353
353
|
// iOS system colors (adaptive)
|
package/src/utils/did.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export interface DIDDocument {
|
|
2
|
+
id: string;
|
|
3
|
+
service?: Array<{
|
|
4
|
+
id: string;
|
|
5
|
+
type?: string;
|
|
6
|
+
serviceEndpoint?: string;
|
|
7
|
+
}>;
|
|
8
|
+
[key: string]: any;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function resolveDIDDocument(did: string): Promise<DIDDocument> {
|
|
12
|
+
let didDocUrl: string;
|
|
13
|
+
|
|
14
|
+
if (did.startsWith("did:web:")) {
|
|
15
|
+
// For did:web, construct the URL directly
|
|
16
|
+
const domain = did.replace("did:web:", "").replace(/:/g, "/");
|
|
17
|
+
didDocUrl = `https://${domain}/.well-known/did.json`;
|
|
18
|
+
} else if (did.startsWith("did:plc:")) {
|
|
19
|
+
// For did:plc, use plc.directory
|
|
20
|
+
didDocUrl = `https://plc.directory/${did}`;
|
|
21
|
+
} else {
|
|
22
|
+
throw new Error(`Unsupported DID method: ${did}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const response = await fetch(didDocUrl);
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
`Failed to resolve DID document for ${did}: ${response.status}`,
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return response.json();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function getPDSServiceEndpoint(didDoc: DIDDocument): string {
|
|
36
|
+
const pdsService = didDoc.service?.find((s) => s.id === "#atproto_pds");
|
|
37
|
+
|
|
38
|
+
if (!pdsService?.serviceEndpoint) {
|
|
39
|
+
throw new Error("No PDS service endpoint found in DID document");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return pdsService.serviceEndpoint;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export async function getBlob(
|
|
46
|
+
did: string,
|
|
47
|
+
cid: string,
|
|
48
|
+
didDoc?: DIDDocument,
|
|
49
|
+
): Promise<Blob> {
|
|
50
|
+
const doc = didDoc || (await resolveDIDDocument(did));
|
|
51
|
+
const pdsEndpoint = getPDSServiceEndpoint(doc);
|
|
52
|
+
|
|
53
|
+
const blobUrl = `${pdsEndpoint}/xrpc/com.atproto.sync.getBlob?did=${did}&cid=${cid}`;
|
|
54
|
+
|
|
55
|
+
const response = await fetch(blobUrl);
|
|
56
|
+
if (!response.ok) {
|
|
57
|
+
throw new Error(`Failed to fetch blob: ${response.status}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return response.blob();
|
|
61
|
+
}
|