@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.
- package/dist/components/content-metadata/content-metadata-form.js +467 -0
- package/dist/components/content-metadata/content-rights.js +78 -0
- package/dist/components/content-metadata/content-warnings.js +68 -0
- package/dist/components/content-metadata/index.js +11 -0
- package/dist/components/mobile-player/player.js +4 -0
- package/dist/components/mobile-player/ui/report-modal.js +3 -2
- package/dist/components/ui/checkbox.js +87 -0
- package/dist/components/ui/dialog.js +188 -83
- package/dist/components/ui/primitives/input.js +13 -1
- package/dist/components/ui/primitives/modal.js +2 -2
- package/dist/components/ui/select.js +89 -0
- package/dist/components/ui/textarea.js +23 -4
- package/dist/components/ui/toast.js +464 -114
- package/dist/components/ui/tooltip.js +103 -0
- package/dist/index.js +2 -0
- package/dist/lib/metadata-constants.js +157 -0
- package/dist/lib/theme/theme.js +5 -3
- package/dist/streamplace-provider/index.js +14 -4
- package/dist/streamplace-store/content-metadata-actions.js +124 -0
- package/dist/streamplace-store/streamplace-store.js +22 -5
- package/dist/streamplace-store/user.js +67 -7
- package/node-compile-cache/v22.15.0-x64-efe9a9df-0/37be0eec +0 -0
- package/package.json +3 -3
- package/src/components/content-metadata/content-metadata-form.tsx +893 -0
- package/src/components/content-metadata/content-rights.tsx +104 -0
- package/src/components/content-metadata/content-warnings.tsx +100 -0
- package/src/components/content-metadata/index.tsx +10 -0
- package/src/components/mobile-player/player.tsx +5 -0
- package/src/components/mobile-player/ui/report-modal.tsx +13 -7
- package/src/components/ui/checkbox.tsx +147 -0
- package/src/components/ui/dialog.tsx +319 -99
- package/src/components/ui/primitives/input.tsx +19 -2
- package/src/components/ui/primitives/modal.tsx +4 -2
- package/src/components/ui/select.tsx +175 -0
- package/src/components/ui/textarea.tsx +47 -29
- package/src/components/ui/toast.tsx +785 -179
- package/src/components/ui/tooltip.tsx +131 -0
- package/src/index.tsx +3 -0
- package/src/lib/metadata-constants.ts +180 -0
- package/src/lib/theme/theme.tsx +10 -6
- package/src/streamplace-provider/index.tsx +20 -2
- package/src/streamplace-store/content-metadata-actions.tsx +145 -0
- package/src/streamplace-store/streamplace-store.tsx +41 -4
- package/src/streamplace-store/user.tsx +71 -7
- 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
|
-
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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 ?
|
|
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
|
-
</
|
|
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
|
-
</
|
|
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
|
+
}
|