@umituz/react-native-ai-generation-content 1.14.0 → 1.15.1

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.
Files changed (37) hide show
  1. package/package.json +2 -2
  2. package/src/features/audio-generation/index.ts +0 -1
  3. package/src/features/colorization/index.ts +0 -1
  4. package/src/features/face-swap/index.ts +0 -1
  5. package/src/features/future-prediction/index.ts +0 -1
  6. package/src/features/image-captioning/index.ts +0 -1
  7. package/src/features/inpainting/index.ts +0 -1
  8. package/src/features/photo-restoration/index.ts +0 -1
  9. package/src/features/sketch-to-image/index.ts +0 -1
  10. package/src/features/style-transfer/index.ts +0 -1
  11. package/src/features/text-to-image/index.ts +0 -1
  12. package/src/features/text-to-video/index.ts +0 -1
  13. package/src/features/upscaling/index.ts +0 -1
  14. package/src/index.ts +24 -0
  15. package/src/presentation/components/index.ts +1 -0
  16. package/src/presentation/components/photo-step/PhotoStep.tsx +96 -0
  17. package/src/presentation/components/photo-step/index.ts +2 -0
  18. package/src/presentation/hooks/base/index.ts +9 -0
  19. package/src/presentation/hooks/base/types.ts +47 -0
  20. package/src/presentation/hooks/base/use-dual-image-feature.ts +178 -0
  21. package/src/presentation/hooks/base/use-image-with-prompt-feature.ts +170 -0
  22. package/src/presentation/hooks/base/use-single-image-feature.ts +154 -0
  23. package/src/presentation/hooks/index.ts +9 -0
  24. package/src/presentation/hooks/useGenerationFlow.ts +315 -0
  25. package/src/presentation/types/flow-config.types.ts +246 -0
  26. package/src/features/audio-generation/presentation/hooks.ts +0 -39
  27. package/src/features/colorization/presentation/hooks.ts +0 -39
  28. package/src/features/face-swap/presentation/hooks.ts +0 -41
  29. package/src/features/future-prediction/presentation/hooks.ts +0 -39
  30. package/src/features/image-captioning/presentation/hooks.ts +0 -39
  31. package/src/features/inpainting/presentation/hooks.ts +0 -39
  32. package/src/features/photo-restoration/presentation/hooks.ts +0 -39
  33. package/src/features/sketch-to-image/presentation/hooks.ts +0 -39
  34. package/src/features/style-transfer/presentation/hooks.ts +0 -39
  35. package/src/features/text-to-image/presentation/hooks.ts +0 -39
  36. package/src/features/text-to-video/presentation/hooks.ts +0 -39
  37. package/src/features/upscaling/presentation/hooks.ts +0 -39
@@ -0,0 +1,154 @@
1
+ /**
2
+ * useSingleImageFeature Hook
3
+ * Provider-agnostic hook for single image processing features
4
+ * App provides processRequest callback with their AI provider
5
+ */
6
+
7
+ import { useCallback, useState } from "react";
8
+ import type {
9
+ BaseFeatureState,
10
+ BaseFeatureActions,
11
+ FeatureProcessResult,
12
+ OnProgressCallback,
13
+ OnSelectImageCallback,
14
+ OnSaveCallback,
15
+ } from "./types";
16
+
17
+ /**
18
+ * Request passed to processRequest callback
19
+ */
20
+ export interface SingleImageProcessRequest {
21
+ readonly imageUri: string;
22
+ readonly onProgress: OnProgressCallback;
23
+ }
24
+
25
+ /**
26
+ * Configuration for single image feature
27
+ */
28
+ export interface UseSingleImageFeatureConfig {
29
+ readonly onSelectImage: OnSelectImageCallback;
30
+ readonly processRequest: (
31
+ request: SingleImageProcessRequest
32
+ ) => Promise<FeatureProcessResult>;
33
+ readonly onSave?: OnSaveCallback;
34
+ readonly onError?: (error: string) => void;
35
+ readonly onSuccess?: (url: string) => void;
36
+ }
37
+
38
+ /**
39
+ * State for single image feature
40
+ */
41
+ export interface SingleImageFeatureState extends BaseFeatureState {
42
+ readonly imageUri: string | null;
43
+ }
44
+
45
+ /**
46
+ * Return type for single image feature hook
47
+ */
48
+ export interface UseSingleImageFeatureReturn
49
+ extends SingleImageFeatureState,
50
+ BaseFeatureActions {
51
+ readonly selectImage: () => Promise<void>;
52
+ readonly process: () => Promise<void>;
53
+ readonly save: () => Promise<void>;
54
+ }
55
+
56
+ export function useSingleImageFeature(
57
+ config: UseSingleImageFeatureConfig
58
+ ): UseSingleImageFeatureReturn {
59
+ const [imageUri, setImageUri] = useState<string | null>(null);
60
+ const [processedUrl, setProcessedUrl] = useState<string | null>(null);
61
+ const [isProcessing, setIsProcessing] = useState(false);
62
+ const [progress, setProgress] = useState(0);
63
+ const [error, setError] = useState<string | null>(null);
64
+
65
+ const selectImage = useCallback(async (): Promise<void> => {
66
+ try {
67
+ const uri = await config.onSelectImage();
68
+ if (uri) {
69
+ setImageUri(uri);
70
+ setError(null);
71
+ setProcessedUrl(null);
72
+ }
73
+ } catch (err) {
74
+ const message = err instanceof Error ? err.message : "error.selectImage";
75
+ setError(message);
76
+ config.onError?.(message);
77
+ }
78
+ }, [config]);
79
+
80
+ const process = useCallback(async (): Promise<void> => {
81
+ if (!imageUri) {
82
+ const message = "error.noImage";
83
+ setError(message);
84
+ config.onError?.(message);
85
+ return;
86
+ }
87
+
88
+ setIsProcessing(true);
89
+ setProgress(0);
90
+ setError(null);
91
+
92
+ try {
93
+ const result = await config.processRequest({
94
+ imageUri,
95
+ onProgress: setProgress,
96
+ });
97
+
98
+ if (result.success && result.outputUrl) {
99
+ setProcessedUrl(result.outputUrl);
100
+ config.onSuccess?.(result.outputUrl);
101
+ } else {
102
+ const message = result.error || "error.processing";
103
+ setError(message);
104
+ config.onError?.(message);
105
+ }
106
+ } catch (err) {
107
+ const message = err instanceof Error ? err.message : "error.processing";
108
+ setError(message);
109
+ config.onError?.(message);
110
+ } finally {
111
+ setIsProcessing(false);
112
+ setProgress(0);
113
+ }
114
+ }, [imageUri, config]);
115
+
116
+ const save = useCallback(async (): Promise<void> => {
117
+ if (!processedUrl || !config.onSave) {
118
+ return;
119
+ }
120
+
121
+ try {
122
+ await config.onSave(processedUrl);
123
+ } catch (err) {
124
+ const message = err instanceof Error ? err.message : "error.save";
125
+ setError(message);
126
+ config.onError?.(message);
127
+ }
128
+ }, [processedUrl, config]);
129
+
130
+ const reset = useCallback((): void => {
131
+ setImageUri(null);
132
+ setProcessedUrl(null);
133
+ setIsProcessing(false);
134
+ setProgress(0);
135
+ setError(null);
136
+ }, []);
137
+
138
+ const clearError = useCallback((): void => {
139
+ setError(null);
140
+ }, []);
141
+
142
+ return {
143
+ imageUri,
144
+ processedUrl,
145
+ isProcessing,
146
+ progress,
147
+ error,
148
+ selectImage,
149
+ process,
150
+ save,
151
+ reset,
152
+ clearError,
153
+ };
154
+ }
@@ -2,6 +2,9 @@
2
2
  * Presentation Hooks
3
3
  */
4
4
 
5
+ // Base Feature Hooks (Provider-Agnostic)
6
+ export * from "./base";
7
+
5
8
  export { useGeneration } from "./use-generation";
6
9
  export type {
7
10
  UseGenerationOptions,
@@ -34,3 +37,9 @@ export type {
34
37
  PhotoGenerationState,
35
38
  PhotoGenerationStatus,
36
39
  } from "./photo-generation.types";
40
+
41
+ export { useGenerationFlow } from "./useGenerationFlow";
42
+ export type {
43
+ UseGenerationFlowOptions,
44
+ UseGenerationFlowReturn,
45
+ } from "./useGenerationFlow";
@@ -0,0 +1,315 @@
1
+ /**
2
+ * useGenerationFlow Hook
3
+ * Manages step-by-step generation flow state and navigation
4
+ *
5
+ * @package @umituz/react-native-ai-generation-content
6
+ */
7
+
8
+ import { useState, useCallback, useMemo } from "react";
9
+ import type {
10
+ GenerationFlowConfig,
11
+ GenerationFlowState,
12
+ PhotoStepData,
13
+ PhotoStepConfig,
14
+ } from "../types/flow-config.types";
15
+
16
+ export interface UseGenerationFlowOptions {
17
+ /** Flow configuration */
18
+ config: GenerationFlowConfig;
19
+ /** Callback when flow is complete */
20
+ onComplete?: (state: GenerationFlowState) => void;
21
+ /** Callback when step changes */
22
+ onStepChange?: (stepIndex: number, stepConfig: PhotoStepConfig) => void;
23
+ }
24
+
25
+ export interface UseGenerationFlowReturn {
26
+ /** Current flow state */
27
+ state: GenerationFlowState;
28
+ /** Current step configuration */
29
+ currentStepConfig: PhotoStepConfig | null;
30
+ /** Current step data */
31
+ currentStepData: PhotoStepData | null;
32
+ /** Whether can go to next step */
33
+ canGoNext: boolean;
34
+ /** Whether can go to previous step */
35
+ canGoBack: boolean;
36
+ /** Go to next step */
37
+ goNext: () => void;
38
+ /** Go to previous step */
39
+ goBack: () => void;
40
+ /** Update current step photo */
41
+ updatePhoto: (imageUri: string, previewUrl?: string) => void;
42
+ /** Update current step name */
43
+ updateName: (name: string) => void;
44
+ /** Update current step validation */
45
+ updateValidation: (isValid: boolean) => void;
46
+ /** Update text input */
47
+ updateTextInput: (text: string) => void;
48
+ /** Reset flow */
49
+ reset: () => void;
50
+ /** Complete flow */
51
+ complete: () => void;
52
+ }
53
+
54
+ /**
55
+ * Hook to manage generation flow state
56
+ */
57
+ export const useGenerationFlow = ({
58
+ config,
59
+ onComplete,
60
+ onStepChange,
61
+ }: UseGenerationFlowOptions): UseGenerationFlowReturn => {
62
+ // Initialize state
63
+ const [state, setState] = useState<GenerationFlowState>(() => ({
64
+ currentStepIndex: 0,
65
+ photoSteps: config.photoSteps.map((step) => ({
66
+ id: step.id,
67
+ imageUri: null,
68
+ previewUrl: undefined,
69
+ name: undefined,
70
+ isValid: undefined,
71
+ validationStatus: "pending",
72
+ })),
73
+ textInput: config.textInputStep
74
+ ? {
75
+ id: config.textInputStep.id,
76
+ text: "",
77
+ isValid: false,
78
+ }
79
+ : undefined,
80
+ isComplete: false,
81
+ isProcessing: false,
82
+ }));
83
+
84
+ // Get current step configuration
85
+ const currentStepConfig = useMemo(() => {
86
+ if (state.currentStepIndex >= config.photoSteps.length) {
87
+ return null;
88
+ }
89
+ return config.photoSteps[state.currentStepIndex];
90
+ }, [state.currentStepIndex, config.photoSteps]);
91
+
92
+ // Get current step data
93
+ const currentStepData = useMemo(() => {
94
+ if (state.currentStepIndex >= state.photoSteps.length) {
95
+ return null;
96
+ }
97
+ return state.photoSteps[state.currentStepIndex];
98
+ }, [state.currentStepIndex, state.photoSteps]);
99
+
100
+ // Check if current step is valid
101
+ const isCurrentStepValid = useMemo(() => {
102
+ if (!currentStepData || !currentStepConfig) {
103
+ return false;
104
+ }
105
+
106
+ // Check photo
107
+ if (!currentStepData.imageUri) {
108
+ return false;
109
+ }
110
+
111
+ // Check name if required
112
+ if (currentStepConfig.requireNameInput && !currentStepData.name) {
113
+ return false;
114
+ }
115
+
116
+ // Check validation if enabled
117
+ if (currentStepConfig.enableValidation && !currentStepData.isValid) {
118
+ return false;
119
+ }
120
+
121
+ return true;
122
+ }, [currentStepData, currentStepConfig]);
123
+
124
+ // Can go to next step
125
+ const canGoNext = useMemo(() => {
126
+ return isCurrentStepValid && !state.isProcessing;
127
+ }, [isCurrentStepValid, state.isProcessing]);
128
+
129
+ // Can go back
130
+ const canGoBack = useMemo(() => {
131
+ return (
132
+ state.currentStepIndex > 0 &&
133
+ config.behavior?.allowBack !== false &&
134
+ !state.isProcessing
135
+ );
136
+ }, [state.currentStepIndex, config.behavior?.allowBack, state.isProcessing]);
137
+
138
+ // Go to next step
139
+ const goNext = useCallback(() => {
140
+ if (!canGoNext) return;
141
+
142
+ setState((prev) => {
143
+ const nextIndex = prev.currentStepIndex + 1;
144
+
145
+ // If we've completed all photo steps, mark as complete
146
+ if (nextIndex >= config.photoSteps.length) {
147
+ const newState = {
148
+ ...prev,
149
+ isComplete: true,
150
+ };
151
+ onComplete?.(newState);
152
+ return newState;
153
+ }
154
+
155
+ // Move to next step
156
+ const newState = {
157
+ ...prev,
158
+ currentStepIndex: nextIndex,
159
+ };
160
+
161
+ // Notify step change
162
+ onStepChange?.(nextIndex, config.photoSteps[nextIndex]);
163
+
164
+ return newState;
165
+ });
166
+ }, [canGoNext, config.photoSteps, onComplete, onStepChange]);
167
+
168
+ // Go to previous step
169
+ const goBack = useCallback(() => {
170
+ if (!canGoBack) return;
171
+
172
+ setState((prev) => {
173
+ const prevIndex = prev.currentStepIndex - 1;
174
+ const newState = {
175
+ ...prev,
176
+ currentStepIndex: prevIndex,
177
+ isComplete: false,
178
+ };
179
+
180
+ // Notify step change
181
+ onStepChange?.(prevIndex, config.photoSteps[prevIndex]);
182
+
183
+ return newState;
184
+ });
185
+ }, [canGoBack, config.photoSteps, onStepChange]);
186
+
187
+ // Update photo
188
+ const updatePhoto = useCallback(
189
+ (imageUri: string, previewUrl?: string) => {
190
+ setState((prev) => {
191
+ const newPhotoSteps = [...prev.photoSteps];
192
+ newPhotoSteps[prev.currentStepIndex] = {
193
+ ...newPhotoSteps[prev.currentStepIndex],
194
+ imageUri,
195
+ previewUrl,
196
+ validationStatus: "pending",
197
+ };
198
+
199
+ return {
200
+ ...prev,
201
+ photoSteps: newPhotoSteps,
202
+ };
203
+ });
204
+ },
205
+ [],
206
+ );
207
+
208
+ // Update name
209
+ const updateName = useCallback((name: string) => {
210
+ setState((prev) => {
211
+ const newPhotoSteps = [...prev.photoSteps];
212
+ newPhotoSteps[prev.currentStepIndex] = {
213
+ ...newPhotoSteps[prev.currentStepIndex],
214
+ name,
215
+ };
216
+
217
+ return {
218
+ ...prev,
219
+ photoSteps: newPhotoSteps,
220
+ };
221
+ });
222
+ }, []);
223
+
224
+ // Update validation
225
+ const updateValidation = useCallback((isValid: boolean) => {
226
+ setState((prev) => {
227
+ const newPhotoSteps = [...prev.photoSteps];
228
+ newPhotoSteps[prev.currentStepIndex] = {
229
+ ...newPhotoSteps[prev.currentStepIndex],
230
+ isValid,
231
+ validationStatus: isValid ? "valid" : "invalid",
232
+ };
233
+
234
+ return {
235
+ ...prev,
236
+ photoSteps: newPhotoSteps,
237
+ };
238
+ });
239
+ }, []);
240
+
241
+ // Update text input
242
+ const updateTextInput = useCallback(
243
+ (text: string) => {
244
+ setState((prev) => {
245
+ if (!prev.textInput) return prev;
246
+
247
+ const minLength = config.textInputStep?.minLength ?? 0;
248
+ const maxLength = config.textInputStep?.maxLength ?? Infinity;
249
+ const isValid = text.length >= minLength && text.length <= maxLength;
250
+
251
+ return {
252
+ ...prev,
253
+ textInput: {
254
+ ...prev.textInput,
255
+ text,
256
+ isValid,
257
+ },
258
+ };
259
+ });
260
+ },
261
+ [config.textInputStep],
262
+ );
263
+
264
+ // Reset flow
265
+ const reset = useCallback(() => {
266
+ setState({
267
+ currentStepIndex: 0,
268
+ photoSteps: config.photoSteps.map((step) => ({
269
+ id: step.id,
270
+ imageUri: null,
271
+ previewUrl: undefined,
272
+ name: undefined,
273
+ isValid: undefined,
274
+ validationStatus: "pending",
275
+ })),
276
+ textInput: config.textInputStep
277
+ ? {
278
+ id: config.textInputStep.id,
279
+ text: "",
280
+ isValid: false,
281
+ }
282
+ : undefined,
283
+ isComplete: false,
284
+ isProcessing: false,
285
+ });
286
+ }, [config.photoSteps, config.textInputStep]);
287
+
288
+ // Complete flow
289
+ const complete = useCallback(() => {
290
+ setState((prev) => {
291
+ const newState = {
292
+ ...prev,
293
+ isComplete: true,
294
+ };
295
+ onComplete?.(newState);
296
+ return newState;
297
+ });
298
+ }, [onComplete]);
299
+
300
+ return {
301
+ state,
302
+ currentStepConfig,
303
+ currentStepData,
304
+ canGoNext,
305
+ canGoBack,
306
+ goNext,
307
+ goBack,
308
+ updatePhoto,
309
+ updateName,
310
+ updateValidation,
311
+ updateTextInput,
312
+ reset,
313
+ complete,
314
+ };
315
+ };