@streamplace/components 0.7.35 → 0.8.3

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 (45) hide show
  1. package/dist/components/content-metadata/content-metadata-form.js +467 -0
  2. package/dist/components/content-metadata/content-rights.js +78 -0
  3. package/dist/components/content-metadata/content-warnings.js +68 -0
  4. package/dist/components/content-metadata/index.js +11 -0
  5. package/dist/components/mobile-player/player.js +4 -0
  6. package/dist/components/mobile-player/ui/report-modal.js +3 -2
  7. package/dist/components/ui/checkbox.js +87 -0
  8. package/dist/components/ui/dialog.js +188 -83
  9. package/dist/components/ui/primitives/input.js +13 -1
  10. package/dist/components/ui/primitives/modal.js +2 -2
  11. package/dist/components/ui/select.js +89 -0
  12. package/dist/components/ui/textarea.js +23 -4
  13. package/dist/components/ui/toast.js +464 -114
  14. package/dist/components/ui/tooltip.js +103 -0
  15. package/dist/index.js +2 -0
  16. package/dist/lib/metadata-constants.js +157 -0
  17. package/dist/lib/theme/theme.js +5 -3
  18. package/dist/streamplace-provider/index.js +14 -4
  19. package/dist/streamplace-store/content-metadata-actions.js +124 -0
  20. package/dist/streamplace-store/streamplace-store.js +22 -5
  21. package/dist/streamplace-store/user.js +67 -7
  22. package/node-compile-cache/v22.15.0-x64-efe9a9df-0/37be0eec +0 -0
  23. package/package.json +3 -3
  24. package/src/components/content-metadata/content-metadata-form.tsx +893 -0
  25. package/src/components/content-metadata/content-rights.tsx +104 -0
  26. package/src/components/content-metadata/content-warnings.tsx +100 -0
  27. package/src/components/content-metadata/index.tsx +10 -0
  28. package/src/components/mobile-player/player.tsx +5 -0
  29. package/src/components/mobile-player/ui/report-modal.tsx +13 -7
  30. package/src/components/ui/checkbox.tsx +147 -0
  31. package/src/components/ui/dialog.tsx +319 -99
  32. package/src/components/ui/primitives/input.tsx +19 -2
  33. package/src/components/ui/primitives/modal.tsx +4 -2
  34. package/src/components/ui/select.tsx +175 -0
  35. package/src/components/ui/textarea.tsx +47 -29
  36. package/src/components/ui/toast.tsx +785 -179
  37. package/src/components/ui/tooltip.tsx +131 -0
  38. package/src/index.tsx +3 -0
  39. package/src/lib/metadata-constants.ts +180 -0
  40. package/src/lib/theme/theme.tsx +10 -6
  41. package/src/streamplace-provider/index.tsx +20 -2
  42. package/src/streamplace-store/content-metadata-actions.tsx +145 -0
  43. package/src/streamplace-store/streamplace-store.tsx +41 -4
  44. package/src/streamplace-store/user.tsx +71 -7
  45. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,104 @@
1
+ import { forwardRef } from "react";
2
+ import { StyleSheet, View } from "react-native";
3
+ import { LICENSE_URL_LABELS } from "../../lib/metadata-constants";
4
+ import { useTheme } from "../../lib/theme/theme";
5
+ import { Text } from "../ui/text";
6
+
7
+ export interface ContentRightsProps {
8
+ contentRights: {
9
+ creator?: string;
10
+ copyrightNotice?: string;
11
+ copyrightYear?: string | number;
12
+ license?: string;
13
+ creditLine?: string;
14
+ };
15
+ compact?: boolean;
16
+ }
17
+
18
+ export const ContentRights = forwardRef<any, ContentRightsProps>(
19
+ ({ contentRights }, ref) => {
20
+ const { theme } = useTheme();
21
+
22
+ if (!contentRights || Object.keys(contentRights).length === 0) {
23
+ return null;
24
+ }
25
+
26
+ const styles = createStyles(theme);
27
+
28
+ const formatLicense = (license: string) => {
29
+ return LICENSE_URL_LABELS[license] || license;
30
+ };
31
+
32
+ // Display rights in bottom metadata view
33
+ const elements: string[] = [];
34
+
35
+ // TODO: Map DID to handle creator
36
+ // if (contentRights.creator) {
37
+ // elements.push(`Creator: ${contentRights.creator}`);
38
+ // }
39
+
40
+ if (contentRights.copyrightYear) {
41
+ elements.push(`© ${contentRights.copyrightYear.toString()}`);
42
+ }
43
+
44
+ if (contentRights.license) {
45
+ elements.push(formatLicense(contentRights.license));
46
+ }
47
+
48
+ if (contentRights.copyrightNotice) {
49
+ elements.push(contentRights.copyrightNotice);
50
+ }
51
+
52
+ if (contentRights.creditLine) {
53
+ elements.push(contentRights.creditLine);
54
+ }
55
+
56
+ return (
57
+ <View ref={ref} style={styles.compactContainer}>
58
+ <Text style={styles.compactText}>{elements.join(" • ")}</Text>
59
+ </View>
60
+ );
61
+ },
62
+ );
63
+
64
+ 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,100 @@
1
+ import { forwardRef } from "react";
2
+ import { StyleSheet, View } from "react-native";
3
+ import { C2PA_WARNING_LABELS } from "../../lib/metadata-constants";
4
+ import { useTheme } from "../../lib/theme/theme";
5
+ import { Text } from "../ui/text";
6
+
7
+ export interface ContentWarningsProps {
8
+ warnings: string[];
9
+ compact?: boolean;
10
+ }
11
+
12
+ export const ContentWarnings = forwardRef<any, ContentWarningsProps>(
13
+ ({ warnings, compact = false }, ref) => {
14
+ const { theme } = useTheme();
15
+
16
+ if (!warnings || warnings.length === 0) {
17
+ return null;
18
+ }
19
+
20
+ const styles = createStyles(theme, compact);
21
+
22
+ const getWarningLabel = (warning: string): string => {
23
+ return C2PA_WARNING_LABELS[warning] || warning;
24
+ };
25
+
26
+ if (compact) {
27
+ return (
28
+ <View ref={ref} style={styles.compactContainer}>
29
+ {warnings.map((warning, index) => (
30
+ <View key={index} style={styles.compactWarning}>
31
+ <Text style={styles.compactWarningText}>
32
+ {getWarningLabel(warning)}
33
+ </Text>
34
+ </View>
35
+ ))}
36
+ </View>
37
+ );
38
+ }
39
+
40
+ return (
41
+ <View ref={ref} style={styles.container}>
42
+ <Text style={styles.title}>Content Warnings</Text>
43
+ <View style={styles.warningsContainer}>
44
+ {warnings.map((warning, index) => (
45
+ <View key={index} style={styles.warning}>
46
+ <Text style={styles.warningText}>{getWarningLabel(warning)}</Text>
47
+ </View>
48
+ ))}
49
+ </View>
50
+ </View>
51
+ );
52
+ },
53
+ );
54
+
55
+ 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
+ }
@@ -0,0 +1,10 @@
1
+ // Main form component
2
+ export { ContentMetadataForm } from "./content-metadata-form";
3
+
4
+ // Display components
5
+ export { ContentRights } from "./content-rights";
6
+ export { ContentWarnings } from "./content-warnings";
7
+
8
+ export type { ContentRightsProps } from "./content-rights";
9
+
10
+ export type { ContentWarningsProps } from "./content-warnings";
@@ -23,6 +23,7 @@ export function Player(
23
23
  const clearControlsTimeout = usePlayerStore((x) => x.clearControlsTimeout);
24
24
 
25
25
  const setReportingURL = usePlayerStore((x) => x.setReportingURL);
26
+ const setEmbedded = usePlayerStore((x) => x.setEmbedded);
26
27
 
27
28
  const reportModalOpen = usePlayerStore((x) => x.reportModalOpen);
28
29
  const setReportModalOpen = usePlayerStore((x) => x.setReportModalOpen);
@@ -32,6 +33,10 @@ export function Player(
32
33
  setReportingURL(props.reportingURL ?? null);
33
34
  }, [props.reportingURL]);
34
35
 
36
+ useEffect(() => {
37
+ setEmbedded(props.embedded ?? false);
38
+ }, [props.embedded]);
39
+
35
40
  // Will call back every few seconds to send health updates
36
41
  usePlayerStatus();
37
42
 
@@ -9,11 +9,11 @@ import { zero } from "../../..";
9
9
  import { useSubmitReport } from "../../../livestream-store";
10
10
  import {
11
11
  Button,
12
- Dialog,
13
12
  DialogFooter,
14
- ModalContent,
13
+ ResponsiveDialog,
15
14
  Text,
16
15
  Textarea,
16
+ useTheme,
17
17
  } from "../../ui";
18
18
 
19
19
  // AT Protocol moderation reason types with proper labels
@@ -72,6 +72,8 @@ export const ReportModal: React.FC<ReportModalProps> = ({
72
72
  const [isSubmitting, setIsSubmitting] = useState(false);
73
73
  const [submitError, setSubmitError] = useState<string | null>(null);
74
74
 
75
+ const { theme } = useTheme();
76
+
75
77
  const submitReport = useSubmitReport();
76
78
 
77
79
  const handleCancel = () => {
@@ -107,7 +109,7 @@ export const ReportModal: React.FC<ReportModalProps> = ({
107
109
  };
108
110
 
109
111
  return (
110
- <Dialog
112
+ <ResponsiveDialog
111
113
  open={open}
112
114
  onOpenChange={onOpenChange}
113
115
  title={title}
@@ -118,7 +120,7 @@ export const ReportModal: React.FC<ReportModalProps> = ({
118
120
  dismissible={false}
119
121
  position="center"
120
122
  >
121
- <ModalContent style={[zero.pb[2]]}>
123
+ <View style={[zero.pb[2]]}>
122
124
  {REPORT_REASONS.map((reason) => (
123
125
  <TouchableOpacity
124
126
  key={reason.value}
@@ -136,7 +138,11 @@ export const ReportModal: React.FC<ReportModalProps> = ({
136
138
  ]}
137
139
  >
138
140
  <View>
139
- {selectedReason === reason.value ? <CheckCircle /> : <Circle />}
141
+ {selectedReason === reason.value ? (
142
+ <CheckCircle color={theme.colors.foreground} />
143
+ ) : (
144
+ <Circle color={theme.colors.foreground} />
145
+ )}
140
146
  </View>
141
147
  <View
142
148
  style={[zero.layout.flex.column, zero.gap.all[1], zero.flex[1]]}
@@ -164,7 +170,7 @@ export const ReportModal: React.FC<ReportModalProps> = ({
164
170
  </Text>
165
171
  )}
166
172
  </View>
167
- </ModalContent>
173
+ </View>
168
174
  <DialogFooter>
169
175
  <Button
170
176
  variant="secondary"
@@ -188,7 +194,7 @@ export const ReportModal: React.FC<ReportModalProps> = ({
188
194
  )}
189
195
  </Button>
190
196
  </DialogFooter>
191
- </Dialog>
197
+ </ResponsiveDialog>
192
198
  );
193
199
  };
194
200
 
@@ -0,0 +1,147 @@
1
+ import { Check } from "lucide-react-native";
2
+ import { forwardRef } from "react";
3
+ import { StyleSheet, TouchableOpacity, View } from "react-native";
4
+ import { useTheme } from "../../lib/theme/theme";
5
+ import { Text } from "./text";
6
+
7
+ export interface CheckboxProps {
8
+ checked: boolean;
9
+ onCheckedChange: (checked: boolean) => void;
10
+ disabled?: boolean;
11
+ size?: "sm" | "md" | "lg";
12
+ label?: string;
13
+ description?: string;
14
+ style?: any;
15
+ }
16
+
17
+ export const Checkbox = forwardRef<any, CheckboxProps>(
18
+ (
19
+ {
20
+ checked,
21
+ onCheckedChange,
22
+ disabled = false,
23
+ size = "md",
24
+ label,
25
+ description,
26
+ style,
27
+ ...props
28
+ },
29
+ ref,
30
+ ) => {
31
+ const { theme } = useTheme();
32
+
33
+ const handlePress = () => {
34
+ if (!disabled) {
35
+ onCheckedChange(!checked);
36
+ }
37
+ };
38
+
39
+ const styles = createStyles(theme, size, disabled, checked);
40
+
41
+ return (
42
+ <TouchableOpacity
43
+ ref={ref}
44
+ style={[styles.container, style]}
45
+ onPress={handlePress}
46
+ disabled={disabled}
47
+ {...props}
48
+ >
49
+ <View style={styles.checkbox}>
50
+ {checked && (
51
+ <Check
52
+ size={size === "sm" ? 12 : size === "lg" ? 18 : 14}
53
+ color={
54
+ disabled
55
+ ? theme.colors.textDisabled
56
+ : theme.colors.primaryForeground
57
+ }
58
+ />
59
+ )}
60
+ </View>
61
+ {(label || description) && (
62
+ <View style={styles.content}>
63
+ {label && <Text style={styles.label}>{label}</Text>}
64
+ {description && (
65
+ <Text style={styles.description}>{description}</Text>
66
+ )}
67
+ </View>
68
+ )}
69
+ </TouchableOpacity>
70
+ );
71
+ },
72
+ );
73
+
74
+ Checkbox.displayName = "Checkbox";
75
+
76
+ function createStyles(
77
+ theme: any,
78
+ size: string,
79
+ disabled: boolean,
80
+ checked: boolean,
81
+ ) {
82
+ const sizeStyles = {
83
+ sm: {
84
+ checkboxSize: 16,
85
+ borderRadius: 2,
86
+ padding: theme.spacing[1],
87
+ gap: theme.spacing[1],
88
+ },
89
+ md: {
90
+ checkboxSize: 20,
91
+ borderRadius: 4,
92
+ padding: theme.spacing[1],
93
+ gap: theme.spacing[2],
94
+ },
95
+ lg: {
96
+ checkboxSize: 24,
97
+ borderRadius: 6,
98
+ padding: theme.spacing[2],
99
+ gap: theme.spacing[3],
100
+ },
101
+ };
102
+
103
+ const currentSize = sizeStyles[size as keyof typeof sizeStyles];
104
+
105
+ return StyleSheet.create({
106
+ container: {
107
+ flexDirection: "row",
108
+ alignItems: "flex-start",
109
+ opacity: disabled ? 0.5 : 1,
110
+ },
111
+ checkbox: {
112
+ width: currentSize.checkboxSize,
113
+ height: currentSize.checkboxSize,
114
+ borderWidth: 1.5,
115
+ borderColor: disabled
116
+ ? theme.colors.border
117
+ : checked
118
+ ? theme.colors.primary
119
+ : theme.colors.border,
120
+ borderRadius: currentSize.borderRadius,
121
+ backgroundColor: disabled
122
+ ? theme.colors.muted
123
+ : checked
124
+ ? theme.colors.primary
125
+ : "transparent",
126
+ alignItems: "center",
127
+ justifyContent: "center",
128
+ },
129
+ content: {
130
+ flex: 1,
131
+ paddingTop: currentSize.padding * 0.5,
132
+ paddingLeft: theme.spacing[2],
133
+ },
134
+ label: {
135
+ fontSize: size === "sm" ? 14 : size === "lg" ? 18 : 16,
136
+ fontWeight: "500",
137
+ color: disabled ? theme.colors.textDisabled : theme.colors.text,
138
+ lineHeight: size === "sm" ? 18 : size === "lg" ? 22 : 20,
139
+ },
140
+ description: {
141
+ fontSize: size === "sm" ? 12 : size === "lg" ? 16 : 14,
142
+ color: disabled ? theme.colors.textDisabled : theme.colors.textMuted,
143
+ marginTop: theme.spacing[1],
144
+ lineHeight: size === "sm" ? 16 : size === "lg" ? 20 : 18,
145
+ },
146
+ });
147
+ }