@umituz/react-native-ai-generation-content 1.26.51 → 1.26.53
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-generation-content",
|
|
3
|
-
"version": "1.26.
|
|
3
|
+
"version": "1.26.53",
|
|
4
4
|
"description": "Provider-agnostic AI generation orchestration for React Native with result preview components",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Generic Photo Upload State Hook
|
|
3
3
|
* Manages photo upload state for wizard steps
|
|
4
|
-
*
|
|
4
|
+
* Uses design system's useMedia hook for media picking with built-in validation
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { useState, useCallback, useEffect } from "react";
|
|
8
|
-
import * as ImagePicker from "expo-image-picker";
|
|
9
8
|
import { Alert } from "react-native";
|
|
9
|
+
import { useMedia, MediaValidationError, MediaQuality, MEDIA_CONSTANTS } from "@umituz/react-native-design-system";
|
|
10
10
|
import type { UploadedImage } from "../../../../../presentation/hooks/generation/useAIGenerateState";
|
|
11
11
|
|
|
12
12
|
export interface PhotoUploadConfig {
|
|
@@ -18,72 +18,78 @@ export interface PhotoUploadTranslations {
|
|
|
18
18
|
readonly maxFileSize: string;
|
|
19
19
|
readonly error: string;
|
|
20
20
|
readonly uploadFailed: string;
|
|
21
|
+
readonly permissionDenied?: string;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
export interface UsePhotoUploadStateProps {
|
|
24
25
|
readonly config?: PhotoUploadConfig;
|
|
25
26
|
readonly translations: PhotoUploadTranslations;
|
|
26
27
|
readonly initialImage?: UploadedImage;
|
|
28
|
+
readonly stepId?: string;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
export interface UsePhotoUploadStateReturn {
|
|
30
32
|
readonly image: UploadedImage | null;
|
|
31
33
|
readonly handlePickImage: () => Promise<void>;
|
|
32
34
|
readonly canContinue: boolean;
|
|
35
|
+
readonly clearImage: () => void;
|
|
33
36
|
}
|
|
34
37
|
|
|
35
|
-
const DEFAULT_MAX_FILE_SIZE_MB = 10;
|
|
36
|
-
|
|
37
38
|
export const usePhotoUploadState = ({
|
|
38
39
|
config,
|
|
39
40
|
translations,
|
|
40
41
|
initialImage,
|
|
42
|
+
stepId,
|
|
41
43
|
}: UsePhotoUploadStateProps): UsePhotoUploadStateReturn => {
|
|
42
44
|
const [image, setImage] = useState<UploadedImage | null>(initialImage || null);
|
|
45
|
+
const { pickImage, isLoading } = useMedia();
|
|
46
|
+
|
|
47
|
+
const maxFileSizeMB = config?.maxFileSizeMB ?? MEDIA_CONSTANTS.MAX_IMAGE_SIZE_MB;
|
|
43
48
|
|
|
44
|
-
//
|
|
45
|
-
// This handles cases where the same component is reused for different steps
|
|
49
|
+
// Reset state when stepId changes (new step = new photo)
|
|
46
50
|
useEffect(() => {
|
|
51
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
52
|
+
console.log("[usePhotoUploadState] Step changed, resetting image", { stepId, hasInitialImage: !!initialImage });
|
|
53
|
+
}
|
|
47
54
|
setImage(initialImage || null);
|
|
48
|
-
}, [initialImage]);
|
|
55
|
+
}, [stepId, initialImage]);
|
|
49
56
|
|
|
50
|
-
const
|
|
57
|
+
const clearImage = useCallback(() => {
|
|
58
|
+
setImage(null);
|
|
59
|
+
}, []);
|
|
51
60
|
|
|
52
61
|
const handlePickImage = useCallback(async () => {
|
|
53
62
|
try {
|
|
54
|
-
//
|
|
55
|
-
const
|
|
56
|
-
if (status !== "granted") {
|
|
57
|
-
Alert.alert(translations.error, "Permission to access media library is required");
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Pick image
|
|
62
|
-
const result = await ImagePicker.launchImageLibraryAsync({
|
|
63
|
-
mediaTypes: ImagePicker.MediaTypeOptions.Images,
|
|
63
|
+
// Design system handles validation with maxFileSizeMB
|
|
64
|
+
const result = await pickImage({
|
|
64
65
|
allowsEditing: true,
|
|
65
66
|
aspect: [1, 1],
|
|
66
|
-
quality:
|
|
67
|
+
quality: MediaQuality.MEDIUM,
|
|
68
|
+
maxFileSizeMB,
|
|
67
69
|
});
|
|
68
70
|
|
|
69
|
-
|
|
71
|
+
// Handle validation errors from design system
|
|
72
|
+
if (result.error) {
|
|
73
|
+
if (result.error === MediaValidationError.FILE_TOO_LARGE) {
|
|
74
|
+
Alert.alert(
|
|
75
|
+
translations.fileTooLarge,
|
|
76
|
+
translations.maxFileSize.replace("{size}", maxFileSizeMB.toString()),
|
|
77
|
+
);
|
|
78
|
+
} else if (result.error === MediaValidationError.PERMISSION_DENIED) {
|
|
79
|
+
Alert.alert(
|
|
80
|
+
translations.error,
|
|
81
|
+
translations.permissionDenied || "Permission to access media library is required",
|
|
82
|
+
);
|
|
83
|
+
}
|
|
70
84
|
return;
|
|
71
85
|
}
|
|
72
86
|
|
|
73
|
-
|
|
74
|
-
if (!selectedAsset) {
|
|
87
|
+
if (result.canceled || !result.assets || result.assets.length === 0) {
|
|
75
88
|
return;
|
|
76
89
|
}
|
|
77
90
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const fileSizeMB = fileSize / (1024 * 1024);
|
|
81
|
-
|
|
82
|
-
if (fileSizeMB > maxFileSizeMB) {
|
|
83
|
-
Alert.alert(
|
|
84
|
-
translations.fileTooLarge,
|
|
85
|
-
translations.maxFileSize.replace("{size}", maxFileSizeMB.toString()),
|
|
86
|
-
);
|
|
91
|
+
const selectedAsset = result.assets[0];
|
|
92
|
+
if (!selectedAsset) {
|
|
87
93
|
return;
|
|
88
94
|
}
|
|
89
95
|
|
|
@@ -93,12 +99,13 @@ export const usePhotoUploadState = ({
|
|
|
93
99
|
previewUrl: selectedAsset.uri,
|
|
94
100
|
width: selectedAsset.width,
|
|
95
101
|
height: selectedAsset.height,
|
|
96
|
-
fileSize,
|
|
102
|
+
fileSize: selectedAsset.fileSize,
|
|
97
103
|
};
|
|
98
104
|
|
|
99
105
|
setImage(uploadedImage);
|
|
100
106
|
|
|
101
107
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
108
|
+
const fileSizeMB = (selectedAsset.fileSize || 0) / (1024 * 1024);
|
|
102
109
|
console.log("[usePhotoUploadState] Image selected", {
|
|
103
110
|
width: uploadedImage.width,
|
|
104
111
|
height: uploadedImage.height,
|
|
@@ -111,13 +118,14 @@ export const usePhotoUploadState = ({
|
|
|
111
118
|
}
|
|
112
119
|
Alert.alert(translations.error, translations.uploadFailed);
|
|
113
120
|
}
|
|
114
|
-
}, [maxFileSizeMB, translations]);
|
|
121
|
+
}, [pickImage, maxFileSizeMB, translations]);
|
|
115
122
|
|
|
116
|
-
const canContinue = image !== null;
|
|
123
|
+
const canContinue = image !== null && !isLoading;
|
|
117
124
|
|
|
118
125
|
return {
|
|
119
126
|
image,
|
|
120
127
|
handlePickImage,
|
|
121
128
|
canContinue,
|
|
129
|
+
clearImage,
|
|
122
130
|
};
|
|
123
131
|
};
|
|
@@ -48,6 +48,7 @@ export interface PhotoUploadScreenProps {
|
|
|
48
48
|
readonly onBack: () => void;
|
|
49
49
|
readonly onContinue: (image: UploadedImage) => void;
|
|
50
50
|
readonly existingImage?: UploadedImage | null;
|
|
51
|
+
readonly stepId?: string;
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
const DEFAULT_CONFIG: PhotoUploadScreenConfig = {
|
|
@@ -62,6 +63,7 @@ export const GenericPhotoUploadScreen: React.FC<PhotoUploadScreenProps> = ({
|
|
|
62
63
|
onBack,
|
|
63
64
|
onContinue,
|
|
64
65
|
existingImage,
|
|
66
|
+
stepId,
|
|
65
67
|
}) => {
|
|
66
68
|
const tokens = useAppDesignTokens();
|
|
67
69
|
|
|
@@ -74,6 +76,7 @@ export const GenericPhotoUploadScreen: React.FC<PhotoUploadScreenProps> = ({
|
|
|
74
76
|
uploadFailed: translations.uploadFailed,
|
|
75
77
|
},
|
|
76
78
|
initialImage: existingImage || undefined,
|
|
79
|
+
stepId,
|
|
77
80
|
});
|
|
78
81
|
|
|
79
82
|
const handleContinuePress = () => {
|
|
@@ -87,10 +90,10 @@ export const GenericPhotoUploadScreen: React.FC<PhotoUploadScreenProps> = ({
|
|
|
87
90
|
// Build photo tips items from translations
|
|
88
91
|
const photoTipsItems: InfoGridItem[] = useMemo(() => {
|
|
89
92
|
const tipKeys = [
|
|
90
|
-
{ key: "photoUpload.tips.clearFace", icon: "
|
|
91
|
-
{ key: "photoUpload.tips.goodLighting", icon: "
|
|
92
|
-
{ key: "photoUpload.tips.recentPhoto", icon: "
|
|
93
|
-
{ key: "photoUpload.tips.noFilters", icon: "
|
|
93
|
+
{ key: "photoUpload.tips.clearFace", icon: "happy-outline" },
|
|
94
|
+
{ key: "photoUpload.tips.goodLighting", icon: "sunny-outline" },
|
|
95
|
+
{ key: "photoUpload.tips.recentPhoto", icon: "time-outline" },
|
|
96
|
+
{ key: "photoUpload.tips.noFilters", icon: "image-outline" },
|
|
94
97
|
];
|
|
95
98
|
return tipKeys.map(({ key, icon }) => ({
|
|
96
99
|
text: t(key),
|
|
@@ -126,7 +129,7 @@ export const GenericPhotoUploadScreen: React.FC<PhotoUploadScreenProps> = ({
|
|
|
126
129
|
{translations.continue}
|
|
127
130
|
</AtomicText>
|
|
128
131
|
<AtomicIcon
|
|
129
|
-
name="
|
|
132
|
+
name="chevron-forward-outline"
|
|
130
133
|
size="sm"
|
|
131
134
|
color={canContinue && image ? "onPrimary" : "textSecondary"}
|
|
132
135
|
/>
|
|
@@ -152,7 +155,7 @@ export const GenericPhotoUploadScreen: React.FC<PhotoUploadScreenProps> = ({
|
|
|
152
155
|
items={photoTipsItems}
|
|
153
156
|
columns={2}
|
|
154
157
|
title={t("photoUpload.tips.title")}
|
|
155
|
-
headerIcon="
|
|
158
|
+
headerIcon="bulb-outline"
|
|
156
159
|
/>
|
|
157
160
|
</View>
|
|
158
161
|
)}
|