@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.
Files changed (29) hide show
  1. package/dist/components/content-metadata/content-metadata-form.js +6 -22
  2. package/dist/components/content-metadata/content-rights.js +18 -47
  3. package/dist/components/content-metadata/content-warning-badge.js +42 -0
  4. package/dist/components/content-metadata/content-warnings.js +15 -48
  5. package/dist/components/content-metadata/index.js +3 -1
  6. package/dist/components/mobile-player/ui/viewer-context-menu.js +17 -11
  7. package/dist/components/ui/checkbox.js +6 -18
  8. package/dist/components/ui/dropdown.js +16 -11
  9. package/dist/components/ui/index.js +2 -0
  10. package/dist/components/ui/primitives/text.js +19 -4
  11. package/dist/components/ui/select.js +44 -75
  12. package/dist/hooks/useLivestreamInfo.js +1 -1
  13. package/dist/streamplace-store/stream.js +11 -9
  14. package/node-compile-cache/v22.15.0-x64-efe9a9df-0/37be0eec +0 -0
  15. package/package.json +2 -2
  16. package/src/components/content-metadata/content-metadata-form.tsx +9 -49
  17. package/src/components/content-metadata/content-rights.tsx +31 -56
  18. package/src/components/content-metadata/content-warning-badge.tsx +94 -0
  19. package/src/components/content-metadata/content-warnings.tsx +46 -58
  20. package/src/components/content-metadata/index.tsx +2 -0
  21. package/src/components/mobile-player/ui/viewer-context-menu.tsx +33 -1
  22. package/src/components/ui/checkbox.tsx +23 -21
  23. package/src/components/ui/dropdown.tsx +60 -40
  24. package/src/components/ui/index.ts +2 -0
  25. package/src/components/ui/primitives/text.tsx +24 -4
  26. package/src/components/ui/select.tsx +97 -125
  27. package/src/hooks/useLivestreamInfo.ts +1 -1
  28. package/src/streamplace-store/stream.tsx +16 -10
  29. 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
- const handleSelect = (itemValue) => {
15
- onValueChange(itemValue);
16
- setIsOpen(false);
17
- };
18
- const styles = createStyles(theme, disabled);
19
- return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)(react_native_1.TouchableOpacity, { ref: ref, style: [styles.container, style], onPress: () => !disabled && setIsOpen(true), disabled: disabled, children: [(0, jsx_runtime_1.jsx)(text_1.Text, { style: styles.value, 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)(react_native_1.Modal, { visible: isOpen, transparent: true, animationType: "fade", onRequestClose: () => setIsOpen(false), children: (0, jsx_runtime_1.jsx)(react_native_1.TouchableOpacity, { style: styles.overlay, activeOpacity: 1, onPress: () => setIsOpen(false), children: (0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.dropdown, children: (0, jsx_runtime_1.jsx)(react_native_1.FlatList, { data: items, keyExtractor: (item) => item.value, renderItem: ({ item }) => ((0, jsx_runtime_1.jsxs)(react_native_1.TouchableOpacity, { style: [
20
- styles.item,
21
- item.value === value && styles.selectedItem,
22
- ], onPress: () => handleSelect(item.value), children: [(0, jsx_runtime_1.jsx)(text_1.Text, { style: [
23
- styles.itemText,
24
- item.value === value ? styles.selectedItemText : {},
25
- ], children: item.label }), item.description && ((0, jsx_runtime_1.jsx)(text_1.Text, { style: styles.itemDescription, children: item.description }))] })), style: styles.list }) }) }) })] }));
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
- }
@@ -22,7 +22,7 @@ function useLivestreamInfo(url) {
22
22
  // Create the livestream record with title and custom url if available
23
23
  await createStreamRecord({
24
24
  title,
25
- customUrl: url || null,
25
+ canonicalUrl: url || undefined,
26
26
  });
27
27
  }
28
28
  }
@@ -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, customUrl, }) => {
100
- if (!submitPost) {
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: finalUrl,
183
+ url: thisUrl,
185
184
  createdAt: new Date().toISOString(),
186
185
  // would match up with e.g. https://stream.place/iame.li
187
- canonicalUrl: `${finalUrl}/${profile.data.handle}`,
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",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@streamplace/components",
3
- "version": "0.8.5",
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": "baa713714aac33e98c399f92ce12268a1aac3ef6"
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
- style={[
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
- style={[
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
- style={[
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, View } from "react-native";
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
- export interface ContentRightsProps {
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<any, ContentRightsProps>(
19
- ({ contentRights }, ref) => {
20
- const { theme } = useTheme();
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(`© ${contentRights.copyrightYear.toString()}`);
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 (contentRights.creditLine) {
53
- elements.push(contentRights.creditLine);
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
- <View ref={ref} style={styles.compactContainer}>
58
- <Text style={styles.compactText}>{elements.join(" • ")}</Text>
59
- </View>
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 { StyleSheet, View } from "react-native";
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<any, ContentWarningsProps>(
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 ref={ref} style={styles.compactContainer}>
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 key={index} style={styles.compactWarning}>
31
- <Text style={styles.compactWarningText}>
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={styles.container}>
42
- <Text style={styles.title}>Content Warnings</Text>
43
- <View style={styles.warningsContainer}>
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 key={index} style={styles.warning}>
46
- <Text style={styles.warningText}>{getWarningLabel(warning)}</Text>
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";