@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.51",
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",
@@ -131,6 +131,7 @@ export const WizardStepRenderer: React.FC<WizardStepRendererProps> = ({
131
131
 
132
132
  return (
133
133
  <GenericPhotoUploadScreen
134
+ stepId={step.id}
134
135
  translations={{
135
136
  title: t(titleKey),
136
137
  subtitle: t(subtitleKey),
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * Generic Photo Upload State Hook
3
3
  * Manages photo upload state for wizard steps
4
- * NO feature-specific logic - works for ANY photo upload
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
- // Sync state with initialImage prop when it changes
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 maxFileSizeMB = config?.maxFileSizeMB ?? DEFAULT_MAX_FILE_SIZE_MB;
57
+ const clearImage = useCallback(() => {
58
+ setImage(null);
59
+ }, []);
51
60
 
52
61
  const handlePickImage = useCallback(async () => {
53
62
  try {
54
- // Request permission
55
- const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
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: 0.8,
67
+ quality: MediaQuality.MEDIUM,
68
+ maxFileSizeMB,
67
69
  });
68
70
 
69
- if (result.canceled) {
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
- const selectedAsset = result.assets[0];
74
- if (!selectedAsset) {
87
+ if (result.canceled || !result.assets || result.assets.length === 0) {
75
88
  return;
76
89
  }
77
90
 
78
- // Check file size
79
- const fileSize = selectedAsset.fileSize || 0;
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: "Smile" },
91
- { key: "photoUpload.tips.goodLighting", icon: "Sun" },
92
- { key: "photoUpload.tips.recentPhoto", icon: "Clock" },
93
- { key: "photoUpload.tips.noFilters", icon: "Image" },
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="ChevronRight"
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="Lightbulb"
158
+ headerIcon="bulb-outline"
156
159
  />
157
160
  </View>
158
161
  )}