@umituz/react-native-ai-generation-content 1.17.310 → 1.18.0
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 +1 -1
- package/src/features/anime-selfie/domain/types/anime-selfie.types.ts +1 -0
- package/src/features/anime-selfie/presentation/hooks/useAnimeSelfieFeature.ts +31 -126
- package/src/features/hd-touch-up/domain/types/hd-touch-up.types.ts +1 -0
- package/src/features/hd-touch-up/presentation/hooks/useHDTouchUpFeature.ts +25 -105
- package/src/features/image-to-image/presentation/hooks/useDualImageFeature.ts +111 -75
- package/src/features/image-to-image/presentation/hooks/useImageWithPromptFeature.ts +115 -84
- package/src/features/image-to-image/presentation/hooks/useSingleImageFeature.ts +93 -69
- package/src/features/photo-restoration/domain/types/photo-restore.types.ts +1 -0
- package/src/features/photo-restoration/presentation/hooks/usePhotoRestoreFeature.ts +25 -121
- package/src/features/remove-object/presentation/hooks/useRemoveObjectFeature.ts +125 -79
- package/src/features/shared/dual-image-video/presentation/hooks/useDualImageVideoFeature.ts +106 -64
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* useRemoveObjectFeature Hook
|
|
3
3
|
* Manages remove object feature state and actions
|
|
4
|
+
* Uses centralized orchestrator for credit/error handling
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
|
-
import { useState, useCallback, useRef } from "react";
|
|
7
|
+
import { useState, useCallback, useRef, useMemo } from "react";
|
|
7
8
|
import { generateUUID } from "@umituz/react-native-design-system";
|
|
8
9
|
import { executeImageFeature } from "../../../../infrastructure/services";
|
|
10
|
+
import {
|
|
11
|
+
useGenerationOrchestrator,
|
|
12
|
+
type GenerationStrategy,
|
|
13
|
+
type AlertMessages,
|
|
14
|
+
} from "../../../../presentation/hooks/generation";
|
|
9
15
|
import type {
|
|
10
|
-
RemoveObjectFeatureState,
|
|
11
16
|
RemoveObjectFeatureConfig,
|
|
12
17
|
RemoveObjectResult,
|
|
13
18
|
} from "../../domain/types";
|
|
@@ -20,7 +25,23 @@ export interface UseRemoveObjectFeatureProps {
|
|
|
20
25
|
onBeforeProcess?: () => Promise<boolean>;
|
|
21
26
|
}
|
|
22
27
|
|
|
23
|
-
export interface
|
|
28
|
+
export interface UseRemoveObjectFeatureOptions {
|
|
29
|
+
/** Alert messages for error handling */
|
|
30
|
+
alertMessages?: AlertMessages;
|
|
31
|
+
/** User ID for credit operations */
|
|
32
|
+
userId?: string;
|
|
33
|
+
/** Callback when credits are exhausted */
|
|
34
|
+
onCreditsExhausted?: () => void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface UseRemoveObjectFeatureReturn {
|
|
38
|
+
imageUri: string | null;
|
|
39
|
+
maskUri: string | null;
|
|
40
|
+
prompt: string;
|
|
41
|
+
processedUrl: string | null;
|
|
42
|
+
isProcessing: boolean;
|
|
43
|
+
progress: number;
|
|
44
|
+
error: string | null;
|
|
24
45
|
selectImage: () => Promise<void>;
|
|
25
46
|
selectMask: () => Promise<void>;
|
|
26
47
|
setPrompt: (prompt: string) => void;
|
|
@@ -29,33 +50,85 @@ export interface UseRemoveObjectFeatureReturn extends RemoveObjectFeatureState {
|
|
|
29
50
|
reset: () => void;
|
|
30
51
|
}
|
|
31
52
|
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
progress: 0,
|
|
39
|
-
error: null,
|
|
53
|
+
const DEFAULT_ALERT_MESSAGES: AlertMessages = {
|
|
54
|
+
networkError: "No internet connection. Please check your network.",
|
|
55
|
+
policyViolation: "Content not allowed. Please try a different image.",
|
|
56
|
+
saveFailed: "Failed to save result. Please try again.",
|
|
57
|
+
creditFailed: "Credit operation failed. Please try again.",
|
|
58
|
+
unknown: "An error occurred. Please try again.",
|
|
40
59
|
};
|
|
41
60
|
|
|
61
|
+
interface RemoveObjectInput {
|
|
62
|
+
imageBase64: string;
|
|
63
|
+
maskBase64?: string;
|
|
64
|
+
prompt?: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
42
67
|
export function useRemoveObjectFeature(
|
|
43
68
|
props: UseRemoveObjectFeatureProps,
|
|
69
|
+
options?: UseRemoveObjectFeatureOptions,
|
|
44
70
|
): UseRemoveObjectFeatureReturn {
|
|
45
71
|
const { config, onSelectImage, onSelectMask, onSaveImage, onBeforeProcess } = props;
|
|
46
|
-
|
|
72
|
+
|
|
73
|
+
// UI state (separate from orchestrator state)
|
|
74
|
+
const [imageUri, setImageUri] = useState<string | null>(null);
|
|
75
|
+
const [maskUri, setMaskUri] = useState<string | null>(null);
|
|
76
|
+
const [prompt, setPromptState] = useState("");
|
|
77
|
+
const [imageError, setImageError] = useState<string | null>(null);
|
|
47
78
|
const creationIdRef = useRef<string | null>(null);
|
|
48
79
|
|
|
80
|
+
// Create strategy for orchestrator
|
|
81
|
+
const strategy: GenerationStrategy<RemoveObjectInput, string> = useMemo(
|
|
82
|
+
() => ({
|
|
83
|
+
execute: async (input, onProgress) => {
|
|
84
|
+
const result = await executeImageFeature(
|
|
85
|
+
"remove-object",
|
|
86
|
+
{
|
|
87
|
+
imageBase64: input.imageBase64,
|
|
88
|
+
targetImageBase64: input.maskBase64,
|
|
89
|
+
prompt: input.prompt,
|
|
90
|
+
},
|
|
91
|
+
{ extractResult: config.extractResult, onProgress },
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
if (!result.success || !result.imageUrl) {
|
|
95
|
+
throw new Error(result.error || "Processing failed");
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Notify completion with creationId
|
|
99
|
+
const creationId = creationIdRef.current;
|
|
100
|
+
if (creationId) {
|
|
101
|
+
config.onProcessingComplete?.({ ...result, creationId } as RemoveObjectResult & { creationId?: string });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return result.imageUrl;
|
|
105
|
+
},
|
|
106
|
+
getCreditCost: () => config.creditCost || 1,
|
|
107
|
+
}),
|
|
108
|
+
[config],
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
// Use orchestrator for generation
|
|
112
|
+
const orchestrator = useGenerationOrchestrator(strategy, {
|
|
113
|
+
userId: options?.userId,
|
|
114
|
+
alertMessages: options?.alertMessages || DEFAULT_ALERT_MESSAGES,
|
|
115
|
+
onCreditsExhausted: options?.onCreditsExhausted,
|
|
116
|
+
onError: (error) => {
|
|
117
|
+
config.onError?.(error.message, creationIdRef.current ?? undefined);
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
|
|
49
121
|
const selectImage = useCallback(async () => {
|
|
50
122
|
try {
|
|
51
123
|
const uri = await onSelectImage();
|
|
52
124
|
if (uri) {
|
|
53
|
-
|
|
125
|
+
setImageUri(uri);
|
|
126
|
+
setImageError(null);
|
|
54
127
|
config.onImageSelect?.(uri);
|
|
55
128
|
}
|
|
56
129
|
} catch (error) {
|
|
57
130
|
const message = error instanceof Error ? error.message : String(error);
|
|
58
|
-
|
|
131
|
+
setImageError(message);
|
|
59
132
|
}
|
|
60
133
|
}, [onSelectImage, config]);
|
|
61
134
|
|
|
@@ -65,25 +138,23 @@ export function useRemoveObjectFeature(
|
|
|
65
138
|
try {
|
|
66
139
|
const uri = await onSelectMask();
|
|
67
140
|
if (uri) {
|
|
68
|
-
|
|
141
|
+
setMaskUri(uri);
|
|
142
|
+
setImageError(null);
|
|
69
143
|
config.onMaskSelect?.(uri);
|
|
70
144
|
}
|
|
71
145
|
} catch (error) {
|
|
72
146
|
const message = error instanceof Error ? error.message : String(error);
|
|
73
|
-
|
|
147
|
+
setImageError(message);
|
|
74
148
|
}
|
|
75
149
|
}, [onSelectMask, config]);
|
|
76
150
|
|
|
77
|
-
const setPrompt = useCallback((
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const handleProgress = useCallback((progress: number) => {
|
|
82
|
-
setState((prev) => ({ ...prev, progress }));
|
|
151
|
+
const setPrompt = useCallback((newPrompt: string) => {
|
|
152
|
+
setPromptState(newPrompt);
|
|
153
|
+
setImageError(null);
|
|
83
154
|
}, []);
|
|
84
155
|
|
|
85
156
|
const process = useCallback(async () => {
|
|
86
|
-
if (!
|
|
157
|
+
if (!imageUri) return;
|
|
87
158
|
|
|
88
159
|
if (onBeforeProcess) {
|
|
89
160
|
const canProceed = await onBeforeProcess();
|
|
@@ -93,78 +164,53 @@ export function useRemoveObjectFeature(
|
|
|
93
164
|
const creationId = generateUUID();
|
|
94
165
|
creationIdRef.current = creationId;
|
|
95
166
|
|
|
96
|
-
|
|
97
|
-
...prev,
|
|
98
|
-
isProcessing: true,
|
|
99
|
-
progress: 0,
|
|
100
|
-
error: null,
|
|
101
|
-
}));
|
|
102
|
-
|
|
103
|
-
config.onProcessingStart?.({ creationId, imageUri: state.imageUri });
|
|
167
|
+
config.onProcessingStart?.({ creationId, imageUri });
|
|
104
168
|
|
|
105
169
|
try {
|
|
106
|
-
const imageBase64 = await config.prepareImage(
|
|
107
|
-
const maskBase64 =
|
|
108
|
-
? await config.prepareImage(
|
|
170
|
+
const imageBase64 = await config.prepareImage(imageUri);
|
|
171
|
+
const maskBase64 = maskUri
|
|
172
|
+
? await config.prepareImage(maskUri)
|
|
109
173
|
: undefined;
|
|
110
174
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
prompt: state.prompt || undefined,
|
|
117
|
-
},
|
|
118
|
-
{ extractResult: config.extractResult, onProgress: handleProgress },
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
if (result.success && result.imageUrl) {
|
|
122
|
-
setState((prev) => ({
|
|
123
|
-
...prev,
|
|
124
|
-
isProcessing: false,
|
|
125
|
-
processedUrl: result.imageUrl!,
|
|
126
|
-
progress: 100,
|
|
127
|
-
}));
|
|
128
|
-
config.onProcessingComplete?.({ ...result, creationId } as RemoveObjectResult & { creationId?: string });
|
|
129
|
-
} else {
|
|
130
|
-
const errorMessage = result.error || "Processing failed";
|
|
131
|
-
setState((prev) => ({
|
|
132
|
-
...prev,
|
|
133
|
-
isProcessing: false,
|
|
134
|
-
error: errorMessage,
|
|
135
|
-
progress: 0,
|
|
136
|
-
}));
|
|
137
|
-
config.onError?.(errorMessage, creationId);
|
|
138
|
-
}
|
|
175
|
+
await orchestrator.generate({
|
|
176
|
+
imageBase64,
|
|
177
|
+
maskBase64,
|
|
178
|
+
prompt: prompt || undefined,
|
|
179
|
+
});
|
|
139
180
|
} catch (error) {
|
|
140
|
-
|
|
141
|
-
setState((prev) => ({
|
|
142
|
-
...prev,
|
|
143
|
-
isProcessing: false,
|
|
144
|
-
error: message,
|
|
145
|
-
progress: 0,
|
|
146
|
-
}));
|
|
147
|
-
config.onError?.(message, creationIdRef.current ?? undefined);
|
|
181
|
+
// Error already handled by orchestrator
|
|
148
182
|
}
|
|
149
|
-
}, [
|
|
183
|
+
}, [imageUri, maskUri, prompt, config, onBeforeProcess, orchestrator]);
|
|
150
184
|
|
|
151
185
|
const save = useCallback(async () => {
|
|
152
|
-
if (!
|
|
186
|
+
if (!orchestrator.result) return;
|
|
153
187
|
|
|
154
188
|
try {
|
|
155
|
-
await onSaveImage(
|
|
189
|
+
await onSaveImage(orchestrator.result);
|
|
156
190
|
} catch (error) {
|
|
157
191
|
const message = error instanceof Error ? error.message : String(error);
|
|
158
|
-
|
|
192
|
+
setImageError(message);
|
|
159
193
|
}
|
|
160
|
-
}, [
|
|
194
|
+
}, [orchestrator.result, onSaveImage]);
|
|
161
195
|
|
|
162
196
|
const reset = useCallback(() => {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
197
|
+
setImageUri(null);
|
|
198
|
+
setMaskUri(null);
|
|
199
|
+
setPromptState("");
|
|
200
|
+
setImageError(null);
|
|
201
|
+
creationIdRef.current = null;
|
|
202
|
+
orchestrator.reset();
|
|
203
|
+
}, [orchestrator]);
|
|
204
|
+
|
|
205
|
+
// Combine states for backward compatibility
|
|
166
206
|
return {
|
|
167
|
-
|
|
207
|
+
imageUri,
|
|
208
|
+
maskUri,
|
|
209
|
+
prompt,
|
|
210
|
+
processedUrl: orchestrator.result,
|
|
211
|
+
isProcessing: orchestrator.isGenerating,
|
|
212
|
+
progress: orchestrator.progress,
|
|
213
|
+
error: orchestrator.error?.message || imageError,
|
|
168
214
|
selectImage,
|
|
169
215
|
selectMask,
|
|
170
216
|
setPrompt,
|
|
@@ -2,43 +2,104 @@
|
|
|
2
2
|
* useDualImageVideoFeature Hook
|
|
3
3
|
* Base hook for video features that take two images (ai-hug, ai-kiss, etc.)
|
|
4
4
|
* DRY: Consolidates common logic from useAIHugFeature and useAIKissFeature
|
|
5
|
+
* Uses centralized orchestrator for credit/error handling
|
|
5
6
|
*/
|
|
6
7
|
|
|
7
|
-
import { useState, useCallback, useRef } from "react";
|
|
8
|
+
import { useState, useCallback, useRef, useMemo } from "react";
|
|
8
9
|
import { executeVideoFeature } from "../../../../../infrastructure/services";
|
|
9
10
|
import { generateCreationId } from "../../../../../domains/creations/domain/utils";
|
|
11
|
+
import {
|
|
12
|
+
useGenerationOrchestrator,
|
|
13
|
+
type GenerationStrategy,
|
|
14
|
+
type AlertMessages,
|
|
15
|
+
} from "../../../../../presentation/hooks/generation";
|
|
10
16
|
import type {
|
|
11
|
-
DualImageVideoFeatureState,
|
|
12
17
|
UseDualImageVideoFeatureProps,
|
|
13
18
|
UseDualImageVideoFeatureReturn,
|
|
14
19
|
} from "../../domain/types/dual-image-video.types";
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
export interface DualImageVideoFeatureOptions {
|
|
22
|
+
/** Alert messages for error handling */
|
|
23
|
+
alertMessages?: AlertMessages;
|
|
24
|
+
/** User ID for credit operations */
|
|
25
|
+
userId?: string;
|
|
26
|
+
/** Callback when credits are exhausted */
|
|
27
|
+
onCreditsExhausted?: () => void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const DEFAULT_ALERT_MESSAGES: AlertMessages = {
|
|
31
|
+
networkError: "No internet connection. Please check your network.",
|
|
32
|
+
policyViolation: "Content not allowed. Please try different images.",
|
|
33
|
+
saveFailed: "Failed to save result. Please try again.",
|
|
34
|
+
creditFailed: "Credit operation failed. Please try again.",
|
|
35
|
+
unknown: "An error occurred. Please try again.",
|
|
23
36
|
};
|
|
24
37
|
|
|
38
|
+
interface DualImageVideoInput {
|
|
39
|
+
sourceImageBase64: string;
|
|
40
|
+
targetImageBase64: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
25
43
|
export function useDualImageVideoFeature(
|
|
26
44
|
props: UseDualImageVideoFeatureProps,
|
|
45
|
+
options?: DualImageVideoFeatureOptions,
|
|
27
46
|
): UseDualImageVideoFeatureReturn {
|
|
28
47
|
const { featureType, config, onSelectSourceImage, onSelectTargetImage, onSaveVideo, onBeforeProcess } = props;
|
|
29
|
-
|
|
48
|
+
|
|
49
|
+
// Image selection state (separate from orchestrator state)
|
|
50
|
+
const [sourceImageUri, setSourceImageUri] = useState<string | null>(null);
|
|
51
|
+
const [targetImageUri, setTargetImageUri] = useState<string | null>(null);
|
|
52
|
+
const [imageError, setImageError] = useState<string | null>(null);
|
|
30
53
|
const currentCreationIdRef = useRef<string | null>(null);
|
|
31
54
|
|
|
55
|
+
// Create strategy for orchestrator
|
|
56
|
+
const strategy: GenerationStrategy<DualImageVideoInput, string> = useMemo(
|
|
57
|
+
() => ({
|
|
58
|
+
execute: async (input, onProgress) => {
|
|
59
|
+
const result = await executeVideoFeature(
|
|
60
|
+
featureType,
|
|
61
|
+
{ sourceImageBase64: input.sourceImageBase64, targetImageBase64: input.targetImageBase64 },
|
|
62
|
+
{ extractResult: config.extractResult, onProgress },
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
if (!result.success || !result.videoUrl) {
|
|
66
|
+
throw new Error(result.error || "Processing failed");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Notify completion with creationId
|
|
70
|
+
const creationId = currentCreationIdRef.current;
|
|
71
|
+
if (creationId) {
|
|
72
|
+
config.onProcessingComplete?.({ success: true, videoUrl: result.videoUrl, creationId });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return result.videoUrl;
|
|
76
|
+
},
|
|
77
|
+
getCreditCost: () => config.creditCost || 1,
|
|
78
|
+
}),
|
|
79
|
+
[featureType, config],
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// Use orchestrator for generation
|
|
83
|
+
const orchestrator = useGenerationOrchestrator(strategy, {
|
|
84
|
+
userId: options?.userId,
|
|
85
|
+
alertMessages: options?.alertMessages || DEFAULT_ALERT_MESSAGES,
|
|
86
|
+
onCreditsExhausted: options?.onCreditsExhausted,
|
|
87
|
+
onError: (error) => {
|
|
88
|
+
config.onError?.(error.message, currentCreationIdRef.current ?? undefined);
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
|
|
32
92
|
const selectSourceImage = useCallback(async () => {
|
|
33
93
|
try {
|
|
34
94
|
const uri = await onSelectSourceImage();
|
|
35
95
|
if (uri) {
|
|
36
|
-
|
|
96
|
+
setSourceImageUri(uri);
|
|
97
|
+
setImageError(null);
|
|
37
98
|
config.onSourceImageSelect?.(uri);
|
|
38
99
|
}
|
|
39
100
|
} catch (error) {
|
|
40
101
|
const message = error instanceof Error ? error.message : String(error);
|
|
41
|
-
|
|
102
|
+
setImageError(message);
|
|
42
103
|
}
|
|
43
104
|
}, [onSelectSourceImage, config]);
|
|
44
105
|
|
|
@@ -46,21 +107,18 @@ export function useDualImageVideoFeature(
|
|
|
46
107
|
try {
|
|
47
108
|
const uri = await onSelectTargetImage();
|
|
48
109
|
if (uri) {
|
|
49
|
-
|
|
110
|
+
setTargetImageUri(uri);
|
|
111
|
+
setImageError(null);
|
|
50
112
|
config.onTargetImageSelect?.(uri);
|
|
51
113
|
}
|
|
52
114
|
} catch (error) {
|
|
53
115
|
const message = error instanceof Error ? error.message : String(error);
|
|
54
|
-
|
|
116
|
+
setImageError(message);
|
|
55
117
|
}
|
|
56
118
|
}, [onSelectTargetImage, config]);
|
|
57
119
|
|
|
58
|
-
const handleProgress = useCallback((progress: number) => {
|
|
59
|
-
setState((prev) => ({ ...prev, progress }));
|
|
60
|
-
}, []);
|
|
61
|
-
|
|
62
120
|
const process = useCallback(async () => {
|
|
63
|
-
if (!
|
|
121
|
+
if (!sourceImageUri || !targetImageUri) return;
|
|
64
122
|
|
|
65
123
|
if (onBeforeProcess) {
|
|
66
124
|
const canProceed = await onBeforeProcess();
|
|
@@ -71,69 +129,53 @@ export function useDualImageVideoFeature(
|
|
|
71
129
|
const creationId = generateCreationId();
|
|
72
130
|
currentCreationIdRef.current = creationId;
|
|
73
131
|
|
|
74
|
-
setState((prev) => ({
|
|
75
|
-
...prev,
|
|
76
|
-
isProcessing: true,
|
|
77
|
-
progress: 0,
|
|
78
|
-
error: null,
|
|
79
|
-
}));
|
|
80
|
-
|
|
81
132
|
// Notify start with creationId for Firestore creation
|
|
82
133
|
config.onProcessingStart?.({
|
|
83
134
|
creationId,
|
|
84
135
|
featureType,
|
|
85
|
-
sourceImageUri
|
|
86
|
-
targetImageUri
|
|
136
|
+
sourceImageUri,
|
|
137
|
+
targetImageUri,
|
|
87
138
|
});
|
|
88
139
|
|
|
89
|
-
|
|
90
|
-
|
|
140
|
+
try {
|
|
141
|
+
const [sourceImageBase64, targetImageBase64] = await Promise.all([
|
|
142
|
+
config.prepareImage(sourceImageUri),
|
|
143
|
+
config.prepareImage(targetImageUri),
|
|
144
|
+
]);
|
|
91
145
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
{ extractResult: config.extractResult, onProgress: handleProgress },
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
if (result.success && result.videoUrl) {
|
|
99
|
-
setState((prev) => ({
|
|
100
|
-
...prev,
|
|
101
|
-
isProcessing: false,
|
|
102
|
-
processedVideoUrl: result.videoUrl!,
|
|
103
|
-
progress: 100,
|
|
104
|
-
}));
|
|
105
|
-
// Notify completion with creationId and videoUrl for Firestore update
|
|
106
|
-
config.onProcessingComplete?.({ success: true, videoUrl: result.videoUrl, creationId });
|
|
107
|
-
} else {
|
|
108
|
-
const errorMessage = result.error || "Processing failed";
|
|
109
|
-
setState((prev) => ({
|
|
110
|
-
...prev,
|
|
111
|
-
isProcessing: false,
|
|
112
|
-
error: errorMessage,
|
|
113
|
-
progress: 0,
|
|
114
|
-
}));
|
|
115
|
-
// Notify error with creationId for Firestore update to "failed"
|
|
116
|
-
config.onError?.(errorMessage, creationId);
|
|
146
|
+
await orchestrator.generate({ sourceImageBase64, targetImageBase64 });
|
|
147
|
+
} catch (error) {
|
|
148
|
+
// Error already handled by orchestrator
|
|
117
149
|
}
|
|
118
|
-
}, [
|
|
150
|
+
}, [sourceImageUri, targetImageUri, featureType, config, onBeforeProcess, orchestrator]);
|
|
119
151
|
|
|
120
152
|
const save = useCallback(async () => {
|
|
121
|
-
if (!
|
|
153
|
+
if (!orchestrator.result) return;
|
|
122
154
|
|
|
123
155
|
try {
|
|
124
|
-
await onSaveVideo(
|
|
156
|
+
await onSaveVideo(orchestrator.result);
|
|
125
157
|
} catch (error) {
|
|
126
158
|
const message = error instanceof Error ? error.message : String(error);
|
|
127
|
-
|
|
159
|
+
setImageError(message);
|
|
128
160
|
}
|
|
129
|
-
}, [
|
|
161
|
+
}, [orchestrator.result, onSaveVideo]);
|
|
130
162
|
|
|
131
163
|
const reset = useCallback(() => {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
164
|
+
setSourceImageUri(null);
|
|
165
|
+
setTargetImageUri(null);
|
|
166
|
+
setImageError(null);
|
|
167
|
+
currentCreationIdRef.current = null;
|
|
168
|
+
orchestrator.reset();
|
|
169
|
+
}, [orchestrator]);
|
|
170
|
+
|
|
171
|
+
// Combine states for backward compatibility
|
|
135
172
|
return {
|
|
136
|
-
|
|
173
|
+
sourceImageUri,
|
|
174
|
+
targetImageUri,
|
|
175
|
+
processedVideoUrl: orchestrator.result,
|
|
176
|
+
isProcessing: orchestrator.isGenerating,
|
|
177
|
+
progress: orchestrator.progress,
|
|
178
|
+
error: orchestrator.error?.message || imageError,
|
|
137
179
|
selectSourceImage,
|
|
138
180
|
selectTargetImage,
|
|
139
181
|
process,
|