@umituz/react-native-ai-generation-content 1.12.8 → 1.12.9
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 -4
- package/src/debug-props.ts +10 -0
- package/src/domains/creations/presentation/components/CreationsGalleryEmptyState.tsx +77 -0
- package/src/domains/creations/presentation/components/index.ts +1 -0
- package/src/domains/creations/presentation/screens/CreationsGalleryScreen.tsx +13 -61
- 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.9",
|
|
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": [
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"firebase": "^11.1.0",
|
|
74
74
|
"react": "19.1.0",
|
|
75
75
|
"react-native": "0.81.5",
|
|
76
|
-
"react-native-safe-area-context": "^
|
|
76
|
+
"react-native-safe-area-context": "^5.6.2",
|
|
77
77
|
"typescript": "^5.3.0",
|
|
78
78
|
"zustand": "^5.0.2"
|
|
79
79
|
},
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
|
|
2
|
+
import { AtomicTextProps, AtomicIconProps } from "@umituz/react-native-design-system";
|
|
3
|
+
|
|
4
|
+
const textProps: AtomicTextProps = {
|
|
5
|
+
// This helps us see what is required/optional in IDE or tsc output
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const iconProps: AtomicIconProps = {
|
|
9
|
+
name: 'check',
|
|
10
|
+
};
|
|
@@ -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
|
+
});
|
|
@@ -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
|
|
@@ -9,7 +9,7 @@ import { useDeleteCreation } from "../hooks/useDeleteCreation";
|
|
|
9
9
|
import { useCreationsFilter } from "../hooks/useCreationsFilter";
|
|
10
10
|
import { useAlert, AlertMode } from "@umituz/react-native-design-system";
|
|
11
11
|
import { BottomSheetModalRef } from "@umituz/react-native-bottom-sheet";
|
|
12
|
-
import { GalleryHeader,
|
|
12
|
+
import { GalleryHeader, CreationsGrid, FilterBottomSheet, CreationImageViewer, type FilterCategory, CreationsGalleryEmptyState } from "../components";
|
|
13
13
|
import { getTranslatedTypes, getFilterCategoriesFromConfig } from "../utils/filterUtils";
|
|
14
14
|
import type { Creation } from "../../domain/entities/Creation";
|
|
15
15
|
import type { CreationsConfig } from "../../domain/value-objects/CreationsConfig";
|
|
@@ -91,59 +91,8 @@ export function CreationsGalleryScreen({
|
|
|
91
91
|
setSelectedCreation(creation);
|
|
92
92
|
}, []);
|
|
93
93
|
|
|
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
94
|
|
|
136
|
-
|
|
137
|
-
return (
|
|
138
|
-
<CreationDetailScreen
|
|
139
|
-
creation={selectedCreation}
|
|
140
|
-
onClose={() => setSelectedCreation(null)}
|
|
141
|
-
onShare={handleShare}
|
|
142
|
-
onDelete={handleDelete}
|
|
143
|
-
t={t}
|
|
144
|
-
/>
|
|
145
|
-
);
|
|
146
|
-
}
|
|
95
|
+
const styles = useStyles(tokens);
|
|
147
96
|
|
|
148
97
|
return (
|
|
149
98
|
<View style={styles.container}>
|
|
@@ -181,7 +130,17 @@ export function CreationsGalleryScreen({
|
|
|
181
130
|
onShare={handleShare}
|
|
182
131
|
onDelete={handleDelete}
|
|
183
132
|
contentContainerStyle={{ paddingBottom: insets.bottom + tokens.spacing.xl }}
|
|
184
|
-
ListEmptyComponent={
|
|
133
|
+
ListEmptyComponent={
|
|
134
|
+
<CreationsGalleryEmptyState
|
|
135
|
+
isLoading={isLoading}
|
|
136
|
+
hasCreations={!!creations && creations.length > 0}
|
|
137
|
+
config={config}
|
|
138
|
+
t={t}
|
|
139
|
+
emptyActionLabel={emptyActionLabel}
|
|
140
|
+
onEmptyAction={onEmptyAction}
|
|
141
|
+
clearFilters={clearFilters}
|
|
142
|
+
/>
|
|
143
|
+
}
|
|
185
144
|
/>
|
|
186
145
|
|
|
187
146
|
<CreationImageViewer
|
|
@@ -209,11 +168,4 @@ export function CreationsGalleryScreen({
|
|
|
209
168
|
|
|
210
169
|
const useStyles = (tokens: any) => StyleSheet.create({
|
|
211
170
|
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
171
|
});
|
|
@@ -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>
|