@umituz/react-native-ai-creations 1.2.8 → 1.2.10
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-ai-creations",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.10",
|
|
4
4
|
"description": "AI-generated creations gallery with filtering, sharing, and management for React Native apps",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -27,32 +27,32 @@
|
|
|
27
27
|
"type": "git",
|
|
28
28
|
"url": "https://github.com/umituz/react-native-ai-creations"
|
|
29
29
|
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@umituz/react-native-filter": "latest",
|
|
32
|
+
"@umituz/react-native-image": "latest",
|
|
33
|
+
"@umituz/react-native-bottom-sheet": "latest",
|
|
34
|
+
"@umituz/react-native-alert": "latest"
|
|
35
|
+
},
|
|
30
36
|
"peerDependencies": {
|
|
31
37
|
"@tanstack/react-query": ">=5.0.0",
|
|
32
38
|
"@umituz/react-native-design-system": "latest",
|
|
33
39
|
"@umituz/react-native-firestore": "latest",
|
|
34
|
-
"@umituz/react-native-image": "latest",
|
|
35
40
|
"@umituz/react-native-sharing": "latest",
|
|
36
41
|
"firebase": ">=11.0.0",
|
|
37
42
|
"react": ">=19.1.0",
|
|
38
43
|
"react-native": ">=0.81.5",
|
|
39
|
-
"react-native-safe-area-context": ">=5.0.0"
|
|
40
|
-
"@umituz/react-native-filter": "latest",
|
|
41
|
-
"@umituz/react-native-bottom-sheet": "latest"
|
|
44
|
+
"react-native-safe-area-context": ">=5.0.0"
|
|
42
45
|
},
|
|
43
46
|
"devDependencies": {
|
|
44
47
|
"@tanstack/react-query": "^5.62.16",
|
|
45
48
|
"@types/react": "^19.0.0",
|
|
46
49
|
"@umituz/react-native-design-system": "latest",
|
|
47
|
-
"@umituz/react-native-image": "latest",
|
|
48
50
|
"@umituz/react-native-sharing": "latest",
|
|
49
51
|
"@umituz/react-native-firestore": "latest",
|
|
50
52
|
"firebase": "^11.0.0",
|
|
51
53
|
"react": "19.1.0",
|
|
52
54
|
"react-native": "0.81.5",
|
|
53
55
|
"react-native-safe-area-context": "^5.6.0",
|
|
54
|
-
"@umituz/react-native-filter": "latest",
|
|
55
|
-
"@umituz/react-native-bottom-sheet": "latest",
|
|
56
56
|
"typescript": "^5.3.3"
|
|
57
57
|
},
|
|
58
58
|
"publishConfig": {
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View, TouchableOpacity, StyleSheet } from 'react-native';
|
|
3
|
+
import { AtomicText, AtomicIcon, useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
4
|
+
|
|
5
|
+
interface GalleryHeaderProps {
|
|
6
|
+
readonly title: string;
|
|
7
|
+
readonly count: number;
|
|
8
|
+
readonly countLabel: string;
|
|
9
|
+
readonly isFiltered: boolean;
|
|
10
|
+
readonly onFilterPress: () => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const GalleryHeader: React.FC<GalleryHeaderProps> = ({
|
|
14
|
+
title,
|
|
15
|
+
count,
|
|
16
|
+
countLabel,
|
|
17
|
+
isFiltered,
|
|
18
|
+
onFilterPress
|
|
19
|
+
}) => {
|
|
20
|
+
const tokens = useAppDesignTokens();
|
|
21
|
+
const styles = useStyles(tokens);
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<View style={styles.headerArea}>
|
|
25
|
+
<View>
|
|
26
|
+
<AtomicText style={styles.title}>{title}</AtomicText>
|
|
27
|
+
<AtomicText style={styles.subtitle}>
|
|
28
|
+
{count} {countLabel}
|
|
29
|
+
</AtomicText>
|
|
30
|
+
</View>
|
|
31
|
+
<TouchableOpacity
|
|
32
|
+
onPress={onFilterPress}
|
|
33
|
+
style={[styles.filterButton, isFiltered && styles.filterButtonActive]}
|
|
34
|
+
>
|
|
35
|
+
<AtomicIcon
|
|
36
|
+
name="ListFilter"
|
|
37
|
+
size="sm"
|
|
38
|
+
color={isFiltered ? "primary" : "secondary"}
|
|
39
|
+
/>
|
|
40
|
+
<AtomicText style={[styles.filterText, { color: isFiltered ? tokens.colors.primary : tokens.colors.textSecondary }]}>
|
|
41
|
+
Filter
|
|
42
|
+
</AtomicText>
|
|
43
|
+
{isFiltered && (
|
|
44
|
+
<View style={styles.badge} />
|
|
45
|
+
)}
|
|
46
|
+
</TouchableOpacity>
|
|
47
|
+
</View>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const useStyles = (tokens: any) => StyleSheet.create({
|
|
52
|
+
headerArea: {
|
|
53
|
+
flexDirection: "row",
|
|
54
|
+
alignItems: "center",
|
|
55
|
+
justifyContent: 'space-between',
|
|
56
|
+
paddingHorizontal: tokens.spacing.md,
|
|
57
|
+
paddingVertical: tokens.spacing.sm,
|
|
58
|
+
marginBottom: tokens.spacing.sm,
|
|
59
|
+
},
|
|
60
|
+
title: {
|
|
61
|
+
fontSize: 20,
|
|
62
|
+
fontWeight: "700",
|
|
63
|
+
color: tokens.colors.textPrimary,
|
|
64
|
+
marginBottom: 4,
|
|
65
|
+
},
|
|
66
|
+
subtitle: {
|
|
67
|
+
fontSize: 14,
|
|
68
|
+
color: tokens.colors.textSecondary,
|
|
69
|
+
opacity: 0.6
|
|
70
|
+
},
|
|
71
|
+
filterButton: {
|
|
72
|
+
flexDirection: 'row',
|
|
73
|
+
alignItems: 'center',
|
|
74
|
+
gap: tokens.spacing.xs,
|
|
75
|
+
paddingVertical: tokens.spacing.xs,
|
|
76
|
+
paddingHorizontal: tokens.spacing.md,
|
|
77
|
+
borderRadius: 999,
|
|
78
|
+
backgroundColor: tokens.colors.surfaceVariant,
|
|
79
|
+
borderWidth: 1,
|
|
80
|
+
borderColor: 'transparent',
|
|
81
|
+
},
|
|
82
|
+
filterButtonActive: {
|
|
83
|
+
backgroundColor: tokens.colors.primary + "15",
|
|
84
|
+
borderColor: tokens.colors.primary + "30",
|
|
85
|
+
},
|
|
86
|
+
filterText: {
|
|
87
|
+
fontSize: 14,
|
|
88
|
+
fontWeight: "500",
|
|
89
|
+
},
|
|
90
|
+
badge: {
|
|
91
|
+
width: 8,
|
|
92
|
+
height: 8,
|
|
93
|
+
borderRadius: 4,
|
|
94
|
+
backgroundColor: tokens.colors.primary,
|
|
95
|
+
position: 'absolute',
|
|
96
|
+
top: 6,
|
|
97
|
+
right: 6,
|
|
98
|
+
},
|
|
99
|
+
});
|
|
@@ -8,10 +8,6 @@ import type { Creation } from "../../domain/entities/Creation";
|
|
|
8
8
|
|
|
9
9
|
const ALL_FILTER = "all";
|
|
10
10
|
|
|
11
|
-
interface UseCreationsFilterProps {
|
|
12
|
-
readonly creations: Creation[] | undefined;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
11
|
import { FilterUtils } from "@umituz/react-native-filter";
|
|
16
12
|
|
|
17
13
|
interface UseCreationsFilterProps {
|
|
@@ -20,6 +20,9 @@ import { EmptyState } from "../components/EmptyState";
|
|
|
20
20
|
import { FilterBottomSheet, type FilterCategory } from "@umituz/react-native-filter";
|
|
21
21
|
import { BottomSheetModalRef } from "@umituz/react-native-bottom-sheet";
|
|
22
22
|
import { AtomicIcon, AtomicText } from "@umituz/react-native-design-system";
|
|
23
|
+
import { GalleryHeader } from "../components/GalleryHeader";
|
|
24
|
+
|
|
25
|
+
import { useAlert } from "@umituz/react-native-alert";
|
|
23
26
|
|
|
24
27
|
interface CreationsGalleryScreenProps {
|
|
25
28
|
readonly userId: string | null;
|
|
@@ -41,6 +44,7 @@ export function CreationsGalleryScreen({
|
|
|
41
44
|
const tokens = useAppDesignTokens();
|
|
42
45
|
const insets = useSafeAreaInsets();
|
|
43
46
|
const { share } = useSharing();
|
|
47
|
+
const alert = useAlert();
|
|
44
48
|
const [viewerVisible, setViewerVisible] = useState(false);
|
|
45
49
|
const [viewerIndex, setViewerIndex] = useState(0);
|
|
46
50
|
const filterSheetRef = React.useRef<BottomSheetModalRef>(null);
|
|
@@ -60,13 +64,13 @@ export function CreationsGalleryScreen({
|
|
|
60
64
|
// Add dynamic types category if types exist
|
|
61
65
|
if (config.types.length > 0) {
|
|
62
66
|
categories.push({
|
|
63
|
-
id: 'type',
|
|
67
|
+
id: 'type',
|
|
64
68
|
title: t('creations.category') || 'Category',
|
|
65
69
|
multiSelect: false,
|
|
66
70
|
options: config.types.map(t => ({
|
|
67
71
|
id: t.id,
|
|
68
|
-
label: t.labelKey,
|
|
69
|
-
icon: 'Image',
|
|
72
|
+
label: t.labelKey,
|
|
73
|
+
icon: 'Image',
|
|
70
74
|
}))
|
|
71
75
|
});
|
|
72
76
|
}
|
|
@@ -105,20 +109,28 @@ export function CreationsGalleryScreen({
|
|
|
105
109
|
|
|
106
110
|
const handleDelete = useCallback(
|
|
107
111
|
(creation: Creation) => {
|
|
108
|
-
|
|
109
|
-
t(config.translations.deleteTitle),
|
|
110
|
-
t(config.translations.deleteMessage),
|
|
111
|
-
|
|
112
|
-
|
|
112
|
+
alert.show({
|
|
113
|
+
title: t(config.translations.deleteTitle),
|
|
114
|
+
message: t(config.translations.deleteMessage),
|
|
115
|
+
type: 'warning' as any,
|
|
116
|
+
actions: [
|
|
117
|
+
{
|
|
118
|
+
id: 'cancel',
|
|
119
|
+
label: t("common.cancel"),
|
|
120
|
+
style: 'secondary',
|
|
121
|
+
onPress: () => { }
|
|
122
|
+
},
|
|
113
123
|
{
|
|
114
|
-
|
|
115
|
-
|
|
124
|
+
id: 'delete',
|
|
125
|
+
label: t("common.delete"),
|
|
126
|
+
style: 'destructive',
|
|
127
|
+
variant: 'danger',
|
|
116
128
|
onPress: () => deleteMutation.mutate(creation.id),
|
|
117
129
|
},
|
|
118
|
-
]
|
|
119
|
-
);
|
|
130
|
+
]
|
|
131
|
+
});
|
|
120
132
|
},
|
|
121
|
-
[t, config.translations, deleteMutation],
|
|
133
|
+
[t, config.translations, deleteMutation, alert],
|
|
122
134
|
);
|
|
123
135
|
|
|
124
136
|
const handleImageChange = useCallback(
|
|
@@ -138,38 +150,6 @@ export function CreationsGalleryScreen({
|
|
|
138
150
|
flex: 1,
|
|
139
151
|
backgroundColor: tokens.colors.backgroundPrimary,
|
|
140
152
|
},
|
|
141
|
-
headerArea: {
|
|
142
|
-
flexDirection: "row",
|
|
143
|
-
alignItems: "center",
|
|
144
|
-
justifyContent: 'space-between',
|
|
145
|
-
paddingHorizontal: tokens.spacing.md,
|
|
146
|
-
paddingVertical: tokens.spacing.sm,
|
|
147
|
-
marginBottom: tokens.spacing.sm,
|
|
148
|
-
},
|
|
149
|
-
filterButton: {
|
|
150
|
-
flexDirection: 'row',
|
|
151
|
-
alignItems: 'center',
|
|
152
|
-
gap: tokens.spacing.xs,
|
|
153
|
-
paddingVertical: tokens.spacing.xs,
|
|
154
|
-
paddingHorizontal: tokens.spacing.md,
|
|
155
|
-
borderRadius: (tokens as any).borders?.radius?.full || 999,
|
|
156
|
-
backgroundColor: tokens.colors.surfaceVariant,
|
|
157
|
-
borderWidth: 1,
|
|
158
|
-
borderColor: 'transparent',
|
|
159
|
-
},
|
|
160
|
-
filterButtonActive: {
|
|
161
|
-
backgroundColor: tokens.colors.primary + "15",
|
|
162
|
-
borderColor: tokens.colors.primary + "30",
|
|
163
|
-
},
|
|
164
|
-
badge: {
|
|
165
|
-
width: 8,
|
|
166
|
-
height: 8,
|
|
167
|
-
borderRadius: 4,
|
|
168
|
-
backgroundColor: tokens.colors.primary,
|
|
169
|
-
position: 'absolute',
|
|
170
|
-
top: 6,
|
|
171
|
-
right: 6,
|
|
172
|
-
},
|
|
173
153
|
list: {
|
|
174
154
|
paddingHorizontal: tokens.spacing.md,
|
|
175
155
|
paddingBottom: insets.bottom + tokens.spacing.xl,
|
|
@@ -210,22 +190,14 @@ export function CreationsGalleryScreen({
|
|
|
210
190
|
|
|
211
191
|
return (
|
|
212
192
|
<View style={styles.container}>
|
|
213
|
-
<
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
>
|
|
222
|
-
<AtomicIcon name="ListFilter" size="sm" color={isFiltered ? "primary" : "secondary"} />
|
|
223
|
-
<AtomicText type="labelMedium" color={isFiltered ? "primary" : "secondary"}>Filter</AtomicText>
|
|
224
|
-
{isFiltered && (
|
|
225
|
-
<View style={styles.badge} />
|
|
226
|
-
)}
|
|
227
|
-
</TouchableOpacity>
|
|
228
|
-
</View>
|
|
193
|
+
<GalleryHeader
|
|
194
|
+
title={t(config.translations.title) || 'My Creations'}
|
|
195
|
+
count={filtered.length}
|
|
196
|
+
countLabel={t(config.translations.photoCount) || 'photos'}
|
|
197
|
+
isFiltered={isFiltered}
|
|
198
|
+
onFilterPress={() => filterSheetRef.current?.present()}
|
|
199
|
+
/>
|
|
200
|
+
|
|
229
201
|
<FlatList
|
|
230
202
|
data={filtered}
|
|
231
203
|
renderItem={renderItem}
|