@streamplace/components 0.8.5 → 0.8.8
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/content-metadata/content-metadata-form.js +6 -22
- package/dist/components/content-metadata/content-rights.js +18 -47
- package/dist/components/content-metadata/content-warning-badge.js +42 -0
- package/dist/components/content-metadata/content-warnings.js +15 -48
- package/dist/components/content-metadata/index.js +3 -1
- package/dist/components/mobile-player/ui/viewer-context-menu.js +17 -11
- package/dist/components/ui/checkbox.js +6 -18
- package/dist/components/ui/dropdown.js +16 -11
- package/dist/components/ui/index.js +2 -0
- package/dist/components/ui/primitives/text.js +19 -4
- package/dist/components/ui/select.js +44 -75
- package/dist/hooks/useLivestreamInfo.js +1 -1
- package/dist/streamplace-store/stream.js +11 -9
- package/node-compile-cache/v22.15.0-x64-efe9a9df-0/37be0eec +0 -0
- package/package.json +2 -2
- package/src/components/content-metadata/content-metadata-form.tsx +9 -49
- package/src/components/content-metadata/content-rights.tsx +31 -56
- package/src/components/content-metadata/content-warning-badge.tsx +94 -0
- package/src/components/content-metadata/content-warnings.tsx +46 -58
- package/src/components/content-metadata/index.tsx +2 -0
- package/src/components/mobile-player/ui/viewer-context-menu.tsx +33 -1
- package/src/components/ui/checkbox.tsx +23 -21
- package/src/components/ui/dropdown.tsx +60 -40
- package/src/components/ui/index.ts +2 -0
- package/src/components/ui/primitives/text.tsx +24 -4
- package/src/components/ui/select.tsx +97 -125
- package/src/hooks/useLivestreamInfo.ts +1 -1
- package/src/streamplace-store/stream.tsx +16 -10
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -5,85 +5,54 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
5
5
|
const lucide_react_native_1 = require("lucide-react-native");
|
|
6
6
|
const react_1 = require("react");
|
|
7
7
|
const react_native_1 = require("react-native");
|
|
8
|
+
const __1 = require("../..");
|
|
8
9
|
const theme_1 = require("../../lib/theme/theme");
|
|
10
|
+
const ui_1 = require("../../ui");
|
|
11
|
+
const dropdown_1 = require("./dropdown");
|
|
9
12
|
const text_1 = require("./text");
|
|
13
|
+
const { layout, px, py, borders, r, gap } = __1.zero;
|
|
10
14
|
exports.Select = (0, react_1.forwardRef)(({ value, onValueChange, placeholder = "Select...", items, disabled = false, style, }, ref) => {
|
|
11
15
|
const { theme } = (0, theme_1.useTheme)();
|
|
12
|
-
const [isOpen, setIsOpen] = (0, react_1.useState)(false);
|
|
13
16
|
const selectedItem = items.find((item) => item.value === value);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
return ((0, jsx_runtime_1.jsxs)(dropdown_1.DropdownMenu, { children: [(0, jsx_runtime_1.jsx)(dropdown_1.DropdownMenuTrigger, { disabled: disabled, children: (0, jsx_runtime_1.jsx)(react_native_1.View, { ref: ref, style: [
|
|
18
|
+
{
|
|
19
|
+
width: "100%",
|
|
20
|
+
paddingHorizontal: theme.spacing[3],
|
|
21
|
+
paddingVertical: theme.spacing[3],
|
|
22
|
+
borderWidth: 1,
|
|
23
|
+
borderColor: theme.colors.border,
|
|
24
|
+
borderRadius: theme.borderRadius.md,
|
|
25
|
+
backgroundColor: disabled
|
|
26
|
+
? theme.colors.muted
|
|
27
|
+
: theme.colors.card,
|
|
28
|
+
minHeight: theme.touchTargets.minimum,
|
|
29
|
+
opacity: disabled ? 0.5 : 1,
|
|
30
|
+
},
|
|
31
|
+
style,
|
|
32
|
+
], children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: {
|
|
33
|
+
flexDirection: "row",
|
|
34
|
+
alignItems: "center",
|
|
35
|
+
justifyContent: "space-between",
|
|
36
|
+
width: "100%",
|
|
37
|
+
gap: 8,
|
|
38
|
+
}, children: [(0, jsx_runtime_1.jsx)(text_1.Text, { style: {
|
|
39
|
+
fontSize: 16,
|
|
40
|
+
color: disabled
|
|
41
|
+
? theme.colors.textDisabled
|
|
42
|
+
: theme.colors.text,
|
|
43
|
+
flex: 1,
|
|
44
|
+
}, children: selectedItem?.label || placeholder }), (0, jsx_runtime_1.jsx)(lucide_react_native_1.ChevronDown, { size: 16, color: theme.colors.textMuted })] }) }) }), (0, jsx_runtime_1.jsx)(dropdown_1.ResponsiveDropdownMenuContent, { align: "start", style: [
|
|
45
|
+
{
|
|
46
|
+
maxHeight: 400,
|
|
47
|
+
},
|
|
48
|
+
], children: items.map((item, index) => ((0, jsx_runtime_1.jsxs)(react_native_1.View, { children: [(0, jsx_runtime_1.jsx)(dropdown_1.DropdownMenuItem, { onPress: () => onValueChange(item.value), children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: {
|
|
49
|
+
flexDirection: "row",
|
|
50
|
+
alignItems: "center",
|
|
51
|
+
justifyContent: "space-between",
|
|
52
|
+
width: "100%",
|
|
53
|
+
gap: 8,
|
|
54
|
+
}, children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [gap.all[1], py[1], ui_1.flex.values[1]], children: [(0, jsx_runtime_1.jsx)(text_1.Text, { style: {
|
|
55
|
+
fontWeight: item.value === value ? "500" : "400",
|
|
56
|
+
}, color: item.value === value ? "primary" : "default", children: item.label }), item.description && ((0, jsx_runtime_1.jsx)(text_1.Text, { size: "sm", color: "muted", children: item.description }))] }), item.value === value ? ((0, jsx_runtime_1.jsx)(lucide_react_native_1.Check, { size: 16, color: theme.colors.primary })) : ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: { width: 16 } }))] }) }), index < items.length - 1 && (0, jsx_runtime_1.jsx)(dropdown_1.DropdownMenuSeparator, {})] }, item.value))) })] }));
|
|
26
57
|
});
|
|
27
58
|
exports.Select.displayName = "Select";
|
|
28
|
-
function createStyles(theme, disabled) {
|
|
29
|
-
return react_native_1.StyleSheet.create({
|
|
30
|
-
container: {
|
|
31
|
-
flexDirection: "row",
|
|
32
|
-
alignItems: "center",
|
|
33
|
-
justifyContent: "space-between",
|
|
34
|
-
paddingHorizontal: theme.spacing[3],
|
|
35
|
-
paddingVertical: theme.spacing[3],
|
|
36
|
-
borderWidth: 1,
|
|
37
|
-
borderColor: theme.colors.border,
|
|
38
|
-
borderRadius: theme.borderRadius.md,
|
|
39
|
-
backgroundColor: disabled ? theme.colors.muted : theme.colors.card,
|
|
40
|
-
minHeight: theme.touchTargets.minimum,
|
|
41
|
-
},
|
|
42
|
-
value: {
|
|
43
|
-
fontSize: 16,
|
|
44
|
-
color: disabled ? theme.colors.textDisabled : theme.colors.text,
|
|
45
|
-
flex: 1,
|
|
46
|
-
},
|
|
47
|
-
overlay: {
|
|
48
|
-
flex: 1,
|
|
49
|
-
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
50
|
-
justifyContent: "center",
|
|
51
|
-
alignItems: "center",
|
|
52
|
-
},
|
|
53
|
-
dropdown: {
|
|
54
|
-
backgroundColor: theme.colors.background,
|
|
55
|
-
borderRadius: theme.borderRadius.md,
|
|
56
|
-
borderWidth: 1,
|
|
57
|
-
borderColor: theme.colors.border,
|
|
58
|
-
maxHeight: 300,
|
|
59
|
-
width: "90%",
|
|
60
|
-
maxWidth: 400,
|
|
61
|
-
...theme.shadows.lg,
|
|
62
|
-
},
|
|
63
|
-
list: {
|
|
64
|
-
maxHeight: 300,
|
|
65
|
-
},
|
|
66
|
-
item: {
|
|
67
|
-
paddingHorizontal: theme.spacing[4],
|
|
68
|
-
paddingVertical: theme.spacing[3],
|
|
69
|
-
borderBottomWidth: 1,
|
|
70
|
-
borderBottomColor: theme.colors.border,
|
|
71
|
-
},
|
|
72
|
-
selectedItem: {
|
|
73
|
-
backgroundColor: theme.colors.primary,
|
|
74
|
-
},
|
|
75
|
-
itemText: {
|
|
76
|
-
fontSize: 16,
|
|
77
|
-
color: theme.colors.text,
|
|
78
|
-
},
|
|
79
|
-
selectedItemText: {
|
|
80
|
-
color: theme.colors.primaryForeground,
|
|
81
|
-
fontWeight: "500",
|
|
82
|
-
},
|
|
83
|
-
itemDescription: {
|
|
84
|
-
fontSize: 14,
|
|
85
|
-
color: theme.colors.textMuted,
|
|
86
|
-
marginTop: theme.spacing[1],
|
|
87
|
-
},
|
|
88
|
-
});
|
|
89
|
-
}
|
|
@@ -96,21 +96,16 @@ function useCreateStreamRecord() {
|
|
|
96
96
|
let agent = (0, xrpc_1.usePDSAgent)();
|
|
97
97
|
let url = (0, streamplace_store_1.useUrl)();
|
|
98
98
|
const uploadThumbnail = useUploadThumbnail();
|
|
99
|
-
return async ({ title, customThumbnail, submitPost,
|
|
100
|
-
if (
|
|
99
|
+
return async ({ title, customThumbnail, submitPost, canonicalUrl, notificationSettings, }) => {
|
|
100
|
+
if (typeof submitPost !== "boolean") {
|
|
101
101
|
submitPost = true;
|
|
102
102
|
}
|
|
103
|
-
if (!customUrl) {
|
|
104
|
-
customUrl = null;
|
|
105
|
-
}
|
|
106
103
|
if (!agent) {
|
|
107
104
|
throw new Error("No PDS agent found");
|
|
108
105
|
}
|
|
109
106
|
if (!agent.did) {
|
|
110
107
|
throw new Error("No user DID found, assuming not logged in");
|
|
111
108
|
}
|
|
112
|
-
// Use customUrl if provided, otherwise fall back to the store URL
|
|
113
|
-
const finalUrl = customUrl || url;
|
|
114
109
|
const u = new URL(url);
|
|
115
110
|
let thumbnail = undefined;
|
|
116
111
|
if (customThumbnail) {
|
|
@@ -178,19 +173,26 @@ function useCreateStreamRecord() {
|
|
|
178
173
|
window.navigator) {
|
|
179
174
|
platVersion = (0, browser_1.getBrowserName)(window.navigator.userAgent);
|
|
180
175
|
}
|
|
176
|
+
const thisUrl = `${url}/${profile.data.handle}`;
|
|
177
|
+
if (!canonicalUrl) {
|
|
178
|
+
canonicalUrl = thisUrl;
|
|
179
|
+
}
|
|
181
180
|
const record = {
|
|
182
181
|
$type: "place.stream.livestream",
|
|
183
182
|
title: title,
|
|
184
|
-
url:
|
|
183
|
+
url: thisUrl,
|
|
185
184
|
createdAt: new Date().toISOString(),
|
|
186
185
|
// would match up with e.g. https://stream.place/iame.li
|
|
187
|
-
canonicalUrl:
|
|
186
|
+
canonicalUrl: canonicalUrl,
|
|
188
187
|
// user agent style string
|
|
189
188
|
// e.g. `@streamplace/components/0.1.0 (ios, 32.0)`
|
|
190
189
|
agent: `@streamplace/components/${package_json_1.default.version} (${platform}, ${platVersion})`,
|
|
191
190
|
post: newPost,
|
|
192
191
|
thumb: thumbnail,
|
|
193
192
|
};
|
|
193
|
+
if (notificationSettings) {
|
|
194
|
+
record.notificationSettings = notificationSettings;
|
|
195
|
+
}
|
|
194
196
|
await agent.com.atproto.repo.createRecord({
|
|
195
197
|
repo: agent.did,
|
|
196
198
|
collection: "place.stream.livestream",
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@streamplace/components",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.8",
|
|
4
4
|
"description": "Streamplace React (Native) Components",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "src/index.tsx",
|
|
@@ -56,5 +56,5 @@
|
|
|
56
56
|
"start": "tsc --watch --preserveWatchOutput",
|
|
57
57
|
"prepare": "tsc"
|
|
58
58
|
},
|
|
59
|
-
"gitHead": "
|
|
59
|
+
"gitHead": "87db57ebb4908da9997777417493d55e64d08c6d"
|
|
60
60
|
}
|
|
@@ -376,26 +376,11 @@ export const ContentMetadataForm = forwardRef<any, ContentMetadataFormProps>(
|
|
|
376
376
|
layout.flex.row,
|
|
377
377
|
layout.flex.alignCenter,
|
|
378
378
|
w.percent[100],
|
|
379
|
+
gap.all[2],
|
|
379
380
|
]}
|
|
380
381
|
>
|
|
381
|
-
<Text
|
|
382
|
-
|
|
383
|
-
text.neutral[300],
|
|
384
|
-
{
|
|
385
|
-
minWidth: 100,
|
|
386
|
-
textAlign: "left",
|
|
387
|
-
paddingBottom: 8,
|
|
388
|
-
fontSize: 14,
|
|
389
|
-
},
|
|
390
|
-
]}
|
|
391
|
-
>
|
|
392
|
-
Content Warnings
|
|
393
|
-
</Text>
|
|
394
|
-
<Text
|
|
395
|
-
style={[text.gray[500], { fontSize: 12, paddingBottom: 8 }]}
|
|
396
|
-
>
|
|
397
|
-
optional
|
|
398
|
-
</Text>
|
|
382
|
+
<Text>Content Warnings</Text>
|
|
383
|
+
<Text muted>(optional)</Text>
|
|
399
384
|
</View>
|
|
400
385
|
<View style={[gap.all[2], w.percent[100]]}>
|
|
401
386
|
{CONTENT_WARNINGS.map((warning) => (
|
|
@@ -424,26 +409,11 @@ export const ContentMetadataForm = forwardRef<any, ContentMetadataFormProps>(
|
|
|
424
409
|
layout.flex.row,
|
|
425
410
|
layout.flex.alignCenter,
|
|
426
411
|
w.percent[100],
|
|
412
|
+
gap.all[2],
|
|
427
413
|
]}
|
|
428
414
|
>
|
|
429
|
-
<Text
|
|
430
|
-
|
|
431
|
-
text.neutral[300],
|
|
432
|
-
{
|
|
433
|
-
minWidth: 100,
|
|
434
|
-
textAlign: "left",
|
|
435
|
-
paddingBottom: 8,
|
|
436
|
-
fontSize: 14,
|
|
437
|
-
},
|
|
438
|
-
]}
|
|
439
|
-
>
|
|
440
|
-
Content Rights
|
|
441
|
-
</Text>
|
|
442
|
-
<Text
|
|
443
|
-
style={[text.gray[500], { fontSize: 12, paddingBottom: 8 }]}
|
|
444
|
-
>
|
|
445
|
-
optional
|
|
446
|
-
</Text>
|
|
415
|
+
<Text>Content Rights</Text>
|
|
416
|
+
<Text muted>(optional)</Text>
|
|
447
417
|
</View>
|
|
448
418
|
|
|
449
419
|
<View style={[gap.all[3], w.percent[100]]}>
|
|
@@ -674,21 +644,11 @@ export const ContentMetadataForm = forwardRef<any, ContentMetadataFormProps>(
|
|
|
674
644
|
layout.flex.row,
|
|
675
645
|
layout.flex.alignCenter,
|
|
676
646
|
w.percent[100],
|
|
647
|
+
gap.all[2],
|
|
677
648
|
]}
|
|
678
649
|
>
|
|
679
|
-
<Text
|
|
680
|
-
|
|
681
|
-
text.neutral[300],
|
|
682
|
-
{ minWidth: 100, textAlign: "left", paddingBottom: 8 },
|
|
683
|
-
]}
|
|
684
|
-
>
|
|
685
|
-
Distribution
|
|
686
|
-
</Text>
|
|
687
|
-
<Text
|
|
688
|
-
style={[text.gray[500], { fontSize: 12, paddingBottom: 8 }]}
|
|
689
|
-
>
|
|
690
|
-
optional
|
|
691
|
-
</Text>
|
|
650
|
+
<Text>Distribution</Text>
|
|
651
|
+
<Text muted>(optional)</Text>
|
|
692
652
|
</View>
|
|
693
653
|
|
|
694
654
|
{/* allow everyone to distribute your content */}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { forwardRef } from "react";
|
|
2
|
-
import { StyleSheet
|
|
1
|
+
import { forwardRef, type ComponentProps } from "react";
|
|
2
|
+
import { StyleSheet } from "react-native";
|
|
3
|
+
import { useTheme, zero } from "../..";
|
|
3
4
|
import { LICENSE_URL_LABELS } from "../../lib/metadata-constants";
|
|
4
|
-
import { useTheme } from "../../lib/theme/theme";
|
|
5
5
|
import { Text } from "../ui/text";
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
const { layout, gap, mt, text: textStyles } = zero;
|
|
8
|
+
|
|
9
|
+
export interface ContentRightsProps extends ComponentProps<typeof Text> {
|
|
8
10
|
contentRights: {
|
|
9
11
|
creator?: string;
|
|
10
12
|
copyrightNotice?: string;
|
|
@@ -15,16 +17,13 @@ export interface ContentRightsProps {
|
|
|
15
17
|
compact?: boolean;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
export const ContentRights = forwardRef<
|
|
19
|
-
({ contentRights }, ref) => {
|
|
20
|
-
const {
|
|
21
|
-
|
|
20
|
+
export const ContentRights = forwardRef<Text, ContentRightsProps>(
|
|
21
|
+
({ contentRights, compact, ...rest }, ref) => {
|
|
22
|
+
const { zero } = useTheme();
|
|
22
23
|
if (!contentRights || Object.keys(contentRights).length === 0) {
|
|
23
24
|
return null;
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
const styles = createStyles(theme);
|
|
27
|
-
|
|
28
27
|
const formatLicense = (license: string) => {
|
|
29
28
|
return LICENSE_URL_LABELS[license] || license;
|
|
30
29
|
};
|
|
@@ -38,7 +37,11 @@ export const ContentRights = forwardRef<any, ContentRightsProps>(
|
|
|
38
37
|
// }
|
|
39
38
|
|
|
40
39
|
if (contentRights.copyrightYear) {
|
|
41
|
-
elements.push(
|
|
40
|
+
elements.push(
|
|
41
|
+
`© ${contentRights.copyrightYear.toString()}${contentRights.creditLine ? " " + contentRights.creditLine : ""}`,
|
|
42
|
+
);
|
|
43
|
+
} else if (contentRights.creditLine) {
|
|
44
|
+
elements.push(contentRights.creditLine);
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
if (contentRights.license) {
|
|
@@ -49,56 +52,28 @@ export const ContentRights = forwardRef<any, ContentRightsProps>(
|
|
|
49
52
|
elements.push(contentRights.copyrightNotice);
|
|
50
53
|
}
|
|
51
54
|
|
|
52
|
-
if (
|
|
53
|
-
elements
|
|
55
|
+
if (elements.length > 0) {
|
|
56
|
+
elements[0] = "Stream content is " + elements[0];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (elements.length == 0) {
|
|
60
|
+
return null;
|
|
54
61
|
}
|
|
55
62
|
|
|
56
63
|
return (
|
|
57
|
-
<
|
|
58
|
-
|
|
59
|
-
|
|
64
|
+
<Text
|
|
65
|
+
ref={ref}
|
|
66
|
+
style={[
|
|
67
|
+
zero.text.mutedForeground,
|
|
68
|
+
mt[1],
|
|
69
|
+
StyleSheet.flatten(rest.style),
|
|
70
|
+
]}
|
|
71
|
+
{...rest}
|
|
72
|
+
>
|
|
73
|
+
{elements.join(" • ")}
|
|
74
|
+
</Text>
|
|
60
75
|
);
|
|
61
76
|
},
|
|
62
77
|
);
|
|
63
78
|
|
|
64
79
|
ContentRights.displayName = "ContentRights";
|
|
65
|
-
|
|
66
|
-
function createStyles(theme: any) {
|
|
67
|
-
return StyleSheet.create({
|
|
68
|
-
container: {
|
|
69
|
-
paddingVertical: theme.spacing[3],
|
|
70
|
-
},
|
|
71
|
-
title: {
|
|
72
|
-
fontSize: 14,
|
|
73
|
-
fontWeight: "600",
|
|
74
|
-
color: theme.colors.text,
|
|
75
|
-
marginBottom: theme.spacing[2],
|
|
76
|
-
},
|
|
77
|
-
content: {
|
|
78
|
-
gap: theme.spacing[2],
|
|
79
|
-
},
|
|
80
|
-
row: {
|
|
81
|
-
flexDirection: "row",
|
|
82
|
-
gap: theme.spacing[2],
|
|
83
|
-
},
|
|
84
|
-
label: {
|
|
85
|
-
fontSize: 13,
|
|
86
|
-
color: theme.colors.textMuted,
|
|
87
|
-
},
|
|
88
|
-
value: {
|
|
89
|
-
fontSize: 13,
|
|
90
|
-
color: theme.colors.text,
|
|
91
|
-
},
|
|
92
|
-
compactContainer: {
|
|
93
|
-
flexDirection: "row",
|
|
94
|
-
gap: theme.spacing[2],
|
|
95
|
-
flexWrap: "wrap",
|
|
96
|
-
marginTop: theme.spacing[1],
|
|
97
|
-
},
|
|
98
|
-
compactText: {
|
|
99
|
-
fontSize: 14,
|
|
100
|
-
fontWeight: "500",
|
|
101
|
-
color: theme.colors.text,
|
|
102
|
-
},
|
|
103
|
-
});
|
|
104
|
-
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { AlertTriangle, ChevronDown } from "lucide-react-native";
|
|
2
|
+
import { View } from "react-native";
|
|
3
|
+
import { zero } from "../..";
|
|
4
|
+
import { C2PA_WARNING_LABELS } from "../../lib/metadata-constants";
|
|
5
|
+
import { useTheme } from "../../lib/theme/theme";
|
|
6
|
+
import { pt, r } from "../../ui";
|
|
7
|
+
import {
|
|
8
|
+
DropdownMenu,
|
|
9
|
+
DropdownMenuTrigger,
|
|
10
|
+
ResponsiveDropdownMenuContent,
|
|
11
|
+
} from "../ui/dropdown";
|
|
12
|
+
import { Text } from "../ui/text";
|
|
13
|
+
|
|
14
|
+
const { px, py, gap, layout } = zero;
|
|
15
|
+
|
|
16
|
+
export interface ContentWarningBadgeProps {
|
|
17
|
+
warnings: string[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function ContentWarningBadge({ warnings }: ContentWarningBadgeProps) {
|
|
21
|
+
const { theme } = useTheme();
|
|
22
|
+
|
|
23
|
+
const getWarningLabel = (warning: string): string => {
|
|
24
|
+
return C2PA_WARNING_LABELS[warning] || warning;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
if (!warnings || warnings.length === 0) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<DropdownMenu>
|
|
33
|
+
<DropdownMenuTrigger>
|
|
34
|
+
<View
|
|
35
|
+
style={[
|
|
36
|
+
layout.flex.row,
|
|
37
|
+
layout.flex.align.center,
|
|
38
|
+
gap.all[2],
|
|
39
|
+
px[3],
|
|
40
|
+
py[2],
|
|
41
|
+
r.md,
|
|
42
|
+
{ backgroundColor: theme.colors.warning + "20" },
|
|
43
|
+
]}
|
|
44
|
+
>
|
|
45
|
+
<AlertTriangle size={14} color={theme.colors.warningForeground} />
|
|
46
|
+
<Text
|
|
47
|
+
size="sm"
|
|
48
|
+
weight="semibold"
|
|
49
|
+
style={{ color: theme.colors.warningForeground }}
|
|
50
|
+
>
|
|
51
|
+
Intended for certain audiences
|
|
52
|
+
</Text>
|
|
53
|
+
<ChevronDown size={14} color={theme.colors.warningForeground} />
|
|
54
|
+
</View>
|
|
55
|
+
</DropdownMenuTrigger>
|
|
56
|
+
|
|
57
|
+
<ResponsiveDropdownMenuContent>
|
|
58
|
+
<View style={[layout.flex.column, px[2], pt[2]]}>
|
|
59
|
+
<Text>Heads up!</Text>
|
|
60
|
+
<Text>This stream may contain:</Text>
|
|
61
|
+
</View>
|
|
62
|
+
<View
|
|
63
|
+
style={[
|
|
64
|
+
layout.flex.row,
|
|
65
|
+
{ flexWrap: "wrap" },
|
|
66
|
+
gap.all[2],
|
|
67
|
+
px[2],
|
|
68
|
+
py[2],
|
|
69
|
+
]}
|
|
70
|
+
>
|
|
71
|
+
{warnings.map((warning, index) => (
|
|
72
|
+
<View
|
|
73
|
+
key={index}
|
|
74
|
+
style={[
|
|
75
|
+
{ backgroundColor: theme.colors.warning },
|
|
76
|
+
px[3],
|
|
77
|
+
py[1],
|
|
78
|
+
r.full,
|
|
79
|
+
]}
|
|
80
|
+
>
|
|
81
|
+
<Text
|
|
82
|
+
size="sm"
|
|
83
|
+
weight="semibold"
|
|
84
|
+
style={{ color: theme.colors.warningForeground }}
|
|
85
|
+
>
|
|
86
|
+
{getWarningLabel(warning)}
|
|
87
|
+
</Text>
|
|
88
|
+
</View>
|
|
89
|
+
))}
|
|
90
|
+
</View>
|
|
91
|
+
</ResponsiveDropdownMenuContent>
|
|
92
|
+
</DropdownMenu>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
@@ -1,34 +1,50 @@
|
|
|
1
1
|
import { forwardRef } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { View } from "react-native";
|
|
3
|
+
import { zero } from "../..";
|
|
3
4
|
import { C2PA_WARNING_LABELS } from "../../lib/metadata-constants";
|
|
4
5
|
import { useTheme } from "../../lib/theme/theme";
|
|
5
6
|
import { Text } from "../ui/text";
|
|
6
7
|
|
|
8
|
+
const { layout, gap, bg, r, p, px, py, text: textStyles, borders } = zero;
|
|
9
|
+
|
|
7
10
|
export interface ContentWarningsProps {
|
|
8
11
|
warnings: string[];
|
|
9
12
|
compact?: boolean;
|
|
10
13
|
}
|
|
11
14
|
|
|
12
|
-
export const ContentWarnings = forwardRef<
|
|
13
|
-
({ warnings, compact = false }, ref) => {
|
|
15
|
+
export const ContentWarnings = forwardRef<View, ContentWarningsProps>(
|
|
16
|
+
({ warnings, compact = false, ...rest }, ref) => {
|
|
14
17
|
const { theme } = useTheme();
|
|
15
18
|
|
|
16
19
|
if (!warnings || warnings.length === 0) {
|
|
17
20
|
return null;
|
|
18
21
|
}
|
|
19
22
|
|
|
20
|
-
const styles = createStyles(theme, compact);
|
|
21
|
-
|
|
22
23
|
const getWarningLabel = (warning: string): string => {
|
|
23
24
|
return C2PA_WARNING_LABELS[warning] || warning;
|
|
24
25
|
};
|
|
25
26
|
|
|
26
27
|
if (compact) {
|
|
27
28
|
return (
|
|
28
|
-
<View
|
|
29
|
+
<View
|
|
30
|
+
ref={ref}
|
|
31
|
+
style={[layout.flex.row, layout.flex.wrap.wrap, gap.all[1]]}
|
|
32
|
+
{...rest}
|
|
33
|
+
>
|
|
29
34
|
{warnings.map((warning, index) => (
|
|
30
|
-
<View
|
|
31
|
-
|
|
35
|
+
<View
|
|
36
|
+
key={index}
|
|
37
|
+
style={[
|
|
38
|
+
{ backgroundColor: theme.colors.warning },
|
|
39
|
+
r.full,
|
|
40
|
+
px[2],
|
|
41
|
+
{ paddingVertical: 2 },
|
|
42
|
+
]}
|
|
43
|
+
>
|
|
44
|
+
<Text
|
|
45
|
+
size="sm"
|
|
46
|
+
style={[{ color: theme.colors.warningForeground }]}
|
|
47
|
+
>
|
|
32
48
|
{getWarningLabel(warning)}
|
|
33
49
|
</Text>
|
|
34
50
|
</View>
|
|
@@ -38,12 +54,29 @@ export const ContentWarnings = forwardRef<any, ContentWarningsProps>(
|
|
|
38
54
|
}
|
|
39
55
|
|
|
40
56
|
return (
|
|
41
|
-
<View ref={ref} style={
|
|
42
|
-
<Text
|
|
43
|
-
|
|
57
|
+
<View ref={ref} style={[layout.flex.column, gap.all[2]]} {...rest}>
|
|
58
|
+
<Text
|
|
59
|
+
style={[{ fontSize: 14, fontWeight: "600" }, textStyles.gray[900]]}
|
|
60
|
+
>
|
|
61
|
+
Content Warnings
|
|
62
|
+
</Text>
|
|
63
|
+
<View style={[layout.flex.row, layout.flex.wrap.wrap, gap.all[2]]}>
|
|
44
64
|
{warnings.map((warning, index) => (
|
|
45
|
-
<View
|
|
46
|
-
|
|
65
|
+
<View
|
|
66
|
+
key={index}
|
|
67
|
+
style={[
|
|
68
|
+
{ backgroundColor: theme.colors.warning },
|
|
69
|
+
r.full,
|
|
70
|
+
px[3],
|
|
71
|
+
py[1],
|
|
72
|
+
]}
|
|
73
|
+
>
|
|
74
|
+
<Text
|
|
75
|
+
size="sm"
|
|
76
|
+
style={[{ color: theme.colors.warningForeground }]}
|
|
77
|
+
>
|
|
78
|
+
{getWarningLabel(warning)}
|
|
79
|
+
</Text>
|
|
47
80
|
</View>
|
|
48
81
|
))}
|
|
49
82
|
</View>
|
|
@@ -53,48 +86,3 @@ export const ContentWarnings = forwardRef<any, ContentWarningsProps>(
|
|
|
53
86
|
);
|
|
54
87
|
|
|
55
88
|
ContentWarnings.displayName = "ContentWarnings";
|
|
56
|
-
|
|
57
|
-
function createStyles(theme: any, compact: boolean) {
|
|
58
|
-
return StyleSheet.create({
|
|
59
|
-
container: {
|
|
60
|
-
flexDirection: "column",
|
|
61
|
-
gap: theme.spacing[2],
|
|
62
|
-
},
|
|
63
|
-
title: {
|
|
64
|
-
fontSize: 14,
|
|
65
|
-
fontWeight: "600",
|
|
66
|
-
color: theme.colors.text,
|
|
67
|
-
},
|
|
68
|
-
warningsContainer: {
|
|
69
|
-
flexDirection: "row",
|
|
70
|
-
flexWrap: "wrap",
|
|
71
|
-
gap: theme.spacing[2],
|
|
72
|
-
},
|
|
73
|
-
warning: {
|
|
74
|
-
backgroundColor: theme.colors.warning,
|
|
75
|
-
borderRadius: theme.borderRadius.md,
|
|
76
|
-
padding: theme.spacing[2],
|
|
77
|
-
},
|
|
78
|
-
warningText: {
|
|
79
|
-
color: theme.colors.warningForeground,
|
|
80
|
-
fontSize: 12,
|
|
81
|
-
fontWeight: "500",
|
|
82
|
-
},
|
|
83
|
-
compactContainer: {
|
|
84
|
-
flexDirection: "row",
|
|
85
|
-
flexWrap: "wrap",
|
|
86
|
-
gap: theme.spacing[1],
|
|
87
|
-
},
|
|
88
|
-
compactWarning: {
|
|
89
|
-
backgroundColor: theme.colors.warning,
|
|
90
|
-
borderRadius: theme.borderRadius.full,
|
|
91
|
-
paddingHorizontal: 10,
|
|
92
|
-
paddingVertical: 4,
|
|
93
|
-
},
|
|
94
|
-
compactWarningText: {
|
|
95
|
-
color: theme.colors.warningForeground,
|
|
96
|
-
fontSize: 14,
|
|
97
|
-
fontWeight: "600",
|
|
98
|
-
},
|
|
99
|
-
});
|
|
100
|
-
}
|
|
@@ -3,8 +3,10 @@ export { ContentMetadataForm } from "./content-metadata-form";
|
|
|
3
3
|
|
|
4
4
|
// Display components
|
|
5
5
|
export { ContentRights } from "./content-rights";
|
|
6
|
+
export { ContentWarningBadge } from "./content-warning-badge";
|
|
6
7
|
export { ContentWarnings } from "./content-warnings";
|
|
7
8
|
|
|
8
9
|
export type { ContentRightsProps } from "./content-rights";
|
|
9
10
|
|
|
11
|
+
export type { ContentWarningBadgeProps } from "./content-warning-badge";
|
|
10
12
|
export type { ContentWarningsProps } from "./content-warnings";
|