@umituz/react-native-ai-generation-content 1.12.8 → 1.12.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 +4 -6
- package/src/domains/creations/domain/value-objects/CreationsConfig.ts +1 -1
- package/src/domains/creations/presentation/components/CreationsGalleryEmptyState.tsx +77 -0
- package/src/domains/creations/presentation/components/FilterBottomSheet.tsx +1 -2
- package/src/domains/creations/presentation/components/index.ts +1 -0
- package/src/domains/creations/presentation/screens/CreationsGalleryScreen.tsx +14 -63
- package/src/domains/feature-background/presentation/components/GenerateButton.tsx +1 -1
- package/src/domains/feature-background/presentation/components/ImagePicker.tsx +2 -2
- package/src/domains/feature-background/presentation/components/ModeSelector.tsx +1 -1
- package/src/domains/feature-background/presentation/components/ResultDisplay.tsx +2 -2
- package/src/presentation/components/result/ResultActions.tsx +3 -3
- package/src/presentation/components/result/ResultHeader.tsx +1 -1
- package/src/presentation/components/result/ResultImageCard.tsx +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-ai-generation-content",
|
|
3
|
-
"version": "1.12.
|
|
3
|
+
"version": "1.12.10",
|
|
4
4
|
"description": "Provider-agnostic AI generation orchestration for React Native",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"src"
|
|
17
17
|
],
|
|
18
18
|
"scripts": {
|
|
19
|
-
"typecheck": "
|
|
20
|
-
"lint": "
|
|
19
|
+
"typecheck": "tsc --noEmit",
|
|
20
|
+
"lint": "eslint src --ext .ts,.tsx --max-warnings 0",
|
|
21
21
|
"lint:fix": "eslint src --ext .ts,.tsx --fix"
|
|
22
22
|
},
|
|
23
23
|
"keywords": [
|
|
@@ -41,7 +41,6 @@
|
|
|
41
41
|
"@react-navigation/native": ">=6.0.0",
|
|
42
42
|
"@tanstack/react-query": ">=5.0.0",
|
|
43
43
|
"@umituz/react-native-animation": "latest",
|
|
44
|
-
"@umituz/react-native-bottom-sheet": "latest",
|
|
45
44
|
"@umituz/react-native-design-system": "latest",
|
|
46
45
|
"@umituz/react-native-firebase": "latest",
|
|
47
46
|
"@umituz/react-native-image": "latest",
|
|
@@ -62,7 +61,6 @@
|
|
|
62
61
|
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
|
63
62
|
"@typescript-eslint/parser": "^7.0.0",
|
|
64
63
|
"@umituz/react-native-animation": "latest",
|
|
65
|
-
"@umituz/react-native-bottom-sheet": "latest",
|
|
66
64
|
"@umituz/react-native-design-system": "latest",
|
|
67
65
|
"@umituz/react-native-firebase": "latest",
|
|
68
66
|
"@umituz/react-native-image": "latest",
|
|
@@ -73,7 +71,7 @@
|
|
|
73
71
|
"firebase": "^11.1.0",
|
|
74
72
|
"react": "19.1.0",
|
|
75
73
|
"react-native": "0.81.5",
|
|
76
|
-
"react-native-safe-area-context": "^
|
|
74
|
+
"react-native-safe-area-context": "^5.6.2",
|
|
77
75
|
"typescript": "^5.3.0",
|
|
78
76
|
"zustand": "^5.0.2"
|
|
79
77
|
},
|
|
@@ -41,7 +41,7 @@ export type PathBuilder = (userId: string) => string[];
|
|
|
41
41
|
*/
|
|
42
42
|
export type DocumentMapper = (id: string, data: CreationDocument) => Creation;
|
|
43
43
|
|
|
44
|
-
import type { FilterCategory } from "@umituz/react-native-
|
|
44
|
+
import type { FilterCategory } from "@umituz/react-native-design-system";
|
|
45
45
|
|
|
46
46
|
export interface CreationsConfig {
|
|
47
47
|
readonly collectionName: string;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import React, { useMemo } from "react";
|
|
2
|
+
import { View, StyleSheet, ActivityIndicator } from "react-native";
|
|
3
|
+
import { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
4
|
+
import { EmptyState } from "./EmptyState";
|
|
5
|
+
import type { CreationsConfig } from "../../domain/value-objects/CreationsConfig";
|
|
6
|
+
|
|
7
|
+
interface CreationsGalleryEmptyStateProps {
|
|
8
|
+
readonly isLoading: boolean;
|
|
9
|
+
readonly hasCreations: boolean;
|
|
10
|
+
readonly config: CreationsConfig;
|
|
11
|
+
readonly t: (key: string) => string;
|
|
12
|
+
readonly emptyActionLabel?: string;
|
|
13
|
+
readonly onEmptyAction?: () => void;
|
|
14
|
+
readonly clearFilters: () => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const CreationsGalleryEmptyState: React.FC<CreationsGalleryEmptyStateProps> = ({
|
|
18
|
+
isLoading,
|
|
19
|
+
hasCreations,
|
|
20
|
+
config,
|
|
21
|
+
t,
|
|
22
|
+
emptyActionLabel,
|
|
23
|
+
onEmptyAction,
|
|
24
|
+
clearFilters,
|
|
25
|
+
}) => {
|
|
26
|
+
const tokens = useAppDesignTokens();
|
|
27
|
+
const styles = useStyles(tokens);
|
|
28
|
+
|
|
29
|
+
return useMemo(() => {
|
|
30
|
+
// 1. Loading State
|
|
31
|
+
if (isLoading && !hasCreations) {
|
|
32
|
+
return (
|
|
33
|
+
<View style={styles.centerContainer}>
|
|
34
|
+
<ActivityIndicator size="large" color={tokens.colors.primary} />
|
|
35
|
+
</View>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 2. System Empty State (User has NO creations at all)
|
|
40
|
+
// We check 'hasCreations' which represents the full list presence
|
|
41
|
+
if (!hasCreations) {
|
|
42
|
+
return (
|
|
43
|
+
<View style={styles.centerContainer}>
|
|
44
|
+
<EmptyState
|
|
45
|
+
title={t(config.translations.empty)}
|
|
46
|
+
description={t(config.translations.emptyDescription)}
|
|
47
|
+
actionLabel={emptyActionLabel}
|
|
48
|
+
onAction={onEmptyAction}
|
|
49
|
+
/>
|
|
50
|
+
</View>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 3. Filter Empty State (User has creations, but filter returns none)
|
|
55
|
+
// This component is rendered when the list is empty, but hasCreations is true.
|
|
56
|
+
return (
|
|
57
|
+
<View style={styles.centerContainer}>
|
|
58
|
+
<EmptyState
|
|
59
|
+
title={t("common.no_results") || "No results"}
|
|
60
|
+
description={t("common.no_results_description") || "Try changing your filters"}
|
|
61
|
+
actionLabel={t("common.clear_all") || "Clear All"}
|
|
62
|
+
onAction={clearFilters}
|
|
63
|
+
/>
|
|
64
|
+
</View>
|
|
65
|
+
);
|
|
66
|
+
}, [isLoading, hasCreations, config, t, emptyActionLabel, onEmptyAction, clearFilters, styles.centerContainer, tokens.colors.primary]);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const useStyles = (tokens: any) => StyleSheet.create({
|
|
70
|
+
centerContainer: {
|
|
71
|
+
flex: 1,
|
|
72
|
+
justifyContent: 'center',
|
|
73
|
+
alignItems: 'center',
|
|
74
|
+
minHeight: 400,
|
|
75
|
+
paddingHorizontal: tokens.spacing.xl
|
|
76
|
+
},
|
|
77
|
+
});
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React, { forwardRef, useCallback, useMemo } from 'react';
|
|
2
2
|
import { View, StyleSheet, TouchableOpacity, ScrollView } from 'react-native';
|
|
3
|
-
import { BottomSheetModal, BottomSheetModalRef } from '@umituz/react-native-
|
|
4
|
-
import { useAppDesignTokens, AtomicText, AtomicIcon } from '@umituz/react-native-design-system';
|
|
3
|
+
import { BottomSheetModal, type BottomSheetModalRef, useAppDesignTokens, AtomicText, AtomicIcon } from '@umituz/react-native-design-system';
|
|
5
4
|
|
|
6
5
|
export interface FilterOption {
|
|
7
6
|
id: string;
|
|
@@ -10,6 +10,7 @@ export { CreationCard } from "./CreationCard";
|
|
|
10
10
|
export { CreationThumbnail } from "./CreationThumbnail";
|
|
11
11
|
export { CreationImageViewer } from "./CreationImageViewer";
|
|
12
12
|
export { CreationsGrid } from "./CreationsGrid";
|
|
13
|
+
export { CreationsGalleryEmptyState } from "./CreationsGalleryEmptyState";
|
|
13
14
|
export { FilterBottomSheet, type FilterCategory, type FilterOption } from "./FilterBottomSheet";
|
|
14
15
|
|
|
15
16
|
// Detail Components
|
|
@@ -7,9 +7,8 @@ import { useFocusEffect } from "@react-navigation/native";
|
|
|
7
7
|
import { useCreations } from "../hooks/useCreations";
|
|
8
8
|
import { useDeleteCreation } from "../hooks/useDeleteCreation";
|
|
9
9
|
import { useCreationsFilter } from "../hooks/useCreationsFilter";
|
|
10
|
-
import { useAlert, AlertMode } from "@umituz/react-native-design-system";
|
|
11
|
-
import {
|
|
12
|
-
import { GalleryHeader, EmptyState, CreationsGrid, FilterBottomSheet, CreationImageViewer, type FilterCategory } from "../components";
|
|
10
|
+
import { useAlert, AlertMode, type BottomSheetModalRef, type FilterCategory } from "@umituz/react-native-design-system";
|
|
11
|
+
import { GalleryHeader, CreationsGrid, FilterBottomSheet, CreationImageViewer, CreationsGalleryEmptyState } from "../components";
|
|
13
12
|
import { getTranslatedTypes, getFilterCategoriesFromConfig } from "../utils/filterUtils";
|
|
14
13
|
import type { Creation } from "../../domain/entities/Creation";
|
|
15
14
|
import type { CreationsConfig } from "../../domain/value-objects/CreationsConfig";
|
|
@@ -91,59 +90,8 @@ export function CreationsGalleryScreen({
|
|
|
91
90
|
setSelectedCreation(creation);
|
|
92
91
|
}, []);
|
|
93
92
|
|
|
94
|
-
const styles = useStyles(tokens);
|
|
95
|
-
|
|
96
|
-
// Define empty state content based on state
|
|
97
|
-
const renderEmptyComponent = useMemo(() => {
|
|
98
|
-
// 1. Loading State
|
|
99
|
-
if (isLoading && (!creations || creations?.length === 0)) {
|
|
100
|
-
return (
|
|
101
|
-
<View style={styles.centerContainer}>
|
|
102
|
-
<ActivityIndicator size="large" color={tokens.colors.primary} />
|
|
103
|
-
</View>
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// 2. System Empty State (User has NO creations at all)
|
|
108
|
-
// We check 'creations' (the full list)
|
|
109
|
-
if (!creations || creations?.length === 0) {
|
|
110
|
-
return (
|
|
111
|
-
<View style={styles.centerContainer}>
|
|
112
|
-
<EmptyState
|
|
113
|
-
title={t(config.translations.empty)}
|
|
114
|
-
description={t(config.translations.emptyDescription)}
|
|
115
|
-
actionLabel={emptyActionLabel}
|
|
116
|
-
onAction={onEmptyAction}
|
|
117
|
-
/>
|
|
118
|
-
</View>
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// 3. Filter Empty State (User has creations, but filter returns none)
|
|
123
|
-
// We check 'filtered' (the displayed list)
|
|
124
|
-
return (
|
|
125
|
-
<View style={styles.centerContainer}>
|
|
126
|
-
<EmptyState
|
|
127
|
-
title={t("common.no_results") || "No results"}
|
|
128
|
-
description={t("common.no_results_description") || "Try changing your filters"}
|
|
129
|
-
actionLabel={t("common.clear_all") || "Clear All"}
|
|
130
|
-
onAction={clearFilters}
|
|
131
|
-
/>
|
|
132
|
-
</View>
|
|
133
|
-
);
|
|
134
|
-
}, [isLoading, creations, config, t, emptyActionLabel, onEmptyAction, clearFilters, styles.centerContainer, tokens.colors.primary]);
|
|
135
93
|
|
|
136
|
-
|
|
137
|
-
return (
|
|
138
|
-
<CreationDetailScreen
|
|
139
|
-
creation={selectedCreation}
|
|
140
|
-
onClose={() => setSelectedCreation(null)}
|
|
141
|
-
onShare={handleShare}
|
|
142
|
-
onDelete={handleDelete}
|
|
143
|
-
t={t}
|
|
144
|
-
/>
|
|
145
|
-
);
|
|
146
|
-
}
|
|
94
|
+
const styles = useStyles(tokens);
|
|
147
95
|
|
|
148
96
|
return (
|
|
149
97
|
<View style={styles.container}>
|
|
@@ -181,7 +129,17 @@ export function CreationsGalleryScreen({
|
|
|
181
129
|
onShare={handleShare}
|
|
182
130
|
onDelete={handleDelete}
|
|
183
131
|
contentContainerStyle={{ paddingBottom: insets.bottom + tokens.spacing.xl }}
|
|
184
|
-
ListEmptyComponent={
|
|
132
|
+
ListEmptyComponent={
|
|
133
|
+
<CreationsGalleryEmptyState
|
|
134
|
+
isLoading={isLoading}
|
|
135
|
+
hasCreations={!!creations && creations.length > 0}
|
|
136
|
+
config={config}
|
|
137
|
+
t={t}
|
|
138
|
+
emptyActionLabel={emptyActionLabel}
|
|
139
|
+
onEmptyAction={onEmptyAction}
|
|
140
|
+
clearFilters={clearFilters}
|
|
141
|
+
/>
|
|
142
|
+
}
|
|
185
143
|
/>
|
|
186
144
|
|
|
187
145
|
<CreationImageViewer
|
|
@@ -209,11 +167,4 @@ export function CreationsGalleryScreen({
|
|
|
209
167
|
|
|
210
168
|
const useStyles = (tokens: any) => StyleSheet.create({
|
|
211
169
|
container: { flex: 1, backgroundColor: tokens.colors.background },
|
|
212
|
-
centerContainer: {
|
|
213
|
-
flex: 1,
|
|
214
|
-
justifyContent: 'center',
|
|
215
|
-
alignItems: 'center',
|
|
216
|
-
minHeight: 400,
|
|
217
|
-
paddingHorizontal: tokens.spacing.xl
|
|
218
|
-
},
|
|
219
170
|
});
|
|
@@ -44,7 +44,7 @@ export const ImagePicker: React.FC<ImagePickerProps> = memo(
|
|
|
44
44
|
>
|
|
45
45
|
<AtomicIcon
|
|
46
46
|
name="image-plus"
|
|
47
|
-
size=
|
|
47
|
+
size="md"
|
|
48
48
|
color="onPrimary"
|
|
49
49
|
/>
|
|
50
50
|
</View>
|
|
@@ -65,7 +65,7 @@ export const ImagePicker: React.FC<ImagePickerProps> = memo(
|
|
|
65
65
|
>
|
|
66
66
|
<AtomicIcon
|
|
67
67
|
name="upload"
|
|
68
|
-
size=
|
|
68
|
+
size="lg"
|
|
69
69
|
color="primary"
|
|
70
70
|
/>
|
|
71
71
|
</View>
|
|
@@ -47,7 +47,7 @@ export const ResultDisplay: React.FC<ResultDisplayProps> = memo(
|
|
|
47
47
|
>
|
|
48
48
|
<AtomicIcon
|
|
49
49
|
name="refresh-cw"
|
|
50
|
-
size=
|
|
50
|
+
size="md"
|
|
51
51
|
color="onSurface"
|
|
52
52
|
/>
|
|
53
53
|
<AtomicText
|
|
@@ -67,7 +67,7 @@ export const ResultDisplay: React.FC<ResultDisplayProps> = memo(
|
|
|
67
67
|
>
|
|
68
68
|
<AtomicIcon
|
|
69
69
|
name="download"
|
|
70
|
-
size=
|
|
70
|
+
size="md"
|
|
71
71
|
color="onPrimary"
|
|
72
72
|
/>
|
|
73
73
|
<AtomicText
|
|
@@ -40,7 +40,7 @@ export const ResultActions: React.FC<ResultActionsProps> = ({
|
|
|
40
40
|
<View style={styles.container}>
|
|
41
41
|
{onRetry && (
|
|
42
42
|
<TouchableOpacity style={styles.retryButton} onPress={onRetry}>
|
|
43
|
-
<AtomicIcon name="refresh" size=
|
|
43
|
+
<AtomicIcon name="refresh" size="sm" customColor={tokens.colors.primary} />
|
|
44
44
|
<AtomicText style={styles.retryText}>{translations.retry}</AtomicText>
|
|
45
45
|
</TouchableOpacity>
|
|
46
46
|
)}
|
|
@@ -54,7 +54,7 @@ export const ResultActions: React.FC<ResultActionsProps> = ({
|
|
|
54
54
|
>
|
|
55
55
|
<AtomicIcon
|
|
56
56
|
name={isSharing ? "hourglass" : "share-social"}
|
|
57
|
-
size=
|
|
57
|
+
size="md"
|
|
58
58
|
customColor="#fff"
|
|
59
59
|
/>
|
|
60
60
|
<AtomicText style={styles.shareText}>
|
|
@@ -71,7 +71,7 @@ export const ResultActions: React.FC<ResultActionsProps> = ({
|
|
|
71
71
|
>
|
|
72
72
|
<AtomicIcon
|
|
73
73
|
name={isSaving ? "hourglass" : "download"}
|
|
74
|
-
size=
|
|
74
|
+
size="md"
|
|
75
75
|
customColor={tokens.colors.primary}
|
|
76
76
|
/>
|
|
77
77
|
<AtomicText style={styles.saveText}>{translations.save}</AtomicText>
|
|
@@ -29,7 +29,7 @@ export const ResultHeader: React.FC<ResultHeaderProps> = ({ title, date }) => {
|
|
|
29
29
|
<View style={styles.badge}>
|
|
30
30
|
<AtomicIcon
|
|
31
31
|
name="calendar-outline"
|
|
32
|
-
size=
|
|
32
|
+
size="sm"
|
|
33
33
|
customColor={tokens.colors.primary}
|
|
34
34
|
/>
|
|
35
35
|
<AtomicText style={styles.dateText}>{date}</AtomicText>
|
|
@@ -28,7 +28,7 @@ export const ResultImageCard: React.FC<ResultImageCardProps> = ({
|
|
|
28
28
|
<View style={styles.frame}>
|
|
29
29
|
<Image source={{ uri: imageUrl }} style={styles.image} resizeMode="cover" />
|
|
30
30
|
<View style={styles.badge}>
|
|
31
|
-
<AtomicIcon name="sparkles" size=
|
|
31
|
+
<AtomicIcon name="sparkles" size="xs" customColor="#fff" />
|
|
32
32
|
<AtomicText style={styles.badgeText}>{badgeText}</AtomicText>
|
|
33
33
|
</View>
|
|
34
34
|
</View>
|