@umituz/react-native-ai-generation-content 1.19.7 → 1.19.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 +1 -1
- package/src/features/couple-future/index.ts +0 -2
- package/src/features/couple-future/infrastructure/executor.ts +81 -11
- package/src/features/couple-future/presentation/hooks/useCoupleFutureFlow.ts +136 -71
- package/src/presentation/hooks/generation/orchestrator.ts +85 -8
- package/src/features/couple-future/presentation/hooks/coupleFutureFlow.types.ts +0 -71
- package/src/features/couple-future/presentation/hooks/useCoupleFutureGeneration.ts +0 -99
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-ai-generation-content",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.9",
|
|
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",
|
|
@@ -14,8 +14,6 @@ export type {
|
|
|
14
14
|
CoupleFeatureSelection,
|
|
15
15
|
} from "./domain/types";
|
|
16
16
|
export { COUPLE_FUTURE_DEFAULTS } from "./domain/types";
|
|
17
|
-
export { useCoupleFutureGeneration } from "./presentation/hooks/useCoupleFutureGeneration";
|
|
18
|
-
export type { CoupleFutureConfig as UseCoupleFutureGenerationConfig } from "./presentation/hooks/useCoupleFutureGeneration";
|
|
19
17
|
export { useCoupleFutureFlow } from "./presentation/hooks/useCoupleFutureFlow";
|
|
20
18
|
export type {
|
|
21
19
|
CoupleFutureFlowConfig,
|
|
@@ -18,23 +18,51 @@ export async function executeCoupleFuture(
|
|
|
18
18
|
input: CoupleFutureInput,
|
|
19
19
|
config?: CoupleFutureConfig,
|
|
20
20
|
): Promise<CoupleFutureResult> {
|
|
21
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
22
|
+
console.log("[Executor] ========== COUPLE FUTURE START ==========");
|
|
23
|
+
console.log("[Executor] Input received:", {
|
|
24
|
+
hasPartnerA: !!input.partnerABase64,
|
|
25
|
+
partnerALength: input.partnerABase64?.length || 0,
|
|
26
|
+
hasPartnerB: !!input.partnerBBase64,
|
|
27
|
+
partnerBLength: input.partnerBBase64?.length || 0,
|
|
28
|
+
promptLength: input.prompt?.length || 0,
|
|
29
|
+
prompt: input.prompt?.slice(0, 300),
|
|
30
|
+
});
|
|
31
|
+
console.log("[Executor] Config:", {
|
|
32
|
+
hasOnProgress: !!config?.onProgress,
|
|
33
|
+
timeoutMs: config?.timeoutMs,
|
|
34
|
+
aspectRatio: config?.aspectRatio,
|
|
35
|
+
outputFormat: config?.outputFormat,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
21
39
|
const provider = providerRegistry.getActiveProvider();
|
|
22
40
|
|
|
23
41
|
if (!provider) {
|
|
42
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
43
|
+
console.log("[Executor] ❌ No AI provider configured");
|
|
44
|
+
}
|
|
24
45
|
return { success: false, error: "No AI provider configured" };
|
|
25
46
|
}
|
|
26
47
|
|
|
48
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
49
|
+
console.log("[Executor] ✅ Provider found:", provider.constructor.name);
|
|
50
|
+
}
|
|
51
|
+
|
|
27
52
|
if (!provider.isInitialized()) {
|
|
53
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
54
|
+
console.log("[Executor] ❌ AI provider not initialized");
|
|
55
|
+
}
|
|
28
56
|
return { success: false, error: "AI provider not initialized" };
|
|
29
57
|
}
|
|
30
58
|
|
|
31
|
-
const { onProgress, timeoutMs, aspectRatio, outputFormat } = config ?? {};
|
|
32
|
-
let lastStatus = "";
|
|
33
|
-
|
|
34
59
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
35
|
-
console.log("[
|
|
60
|
+
console.log("[Executor] ✅ Provider initialized");
|
|
36
61
|
}
|
|
37
62
|
|
|
63
|
+
const { onProgress, timeoutMs, aspectRatio, outputFormat } = config ?? {};
|
|
64
|
+
let lastStatus = "";
|
|
65
|
+
|
|
38
66
|
try {
|
|
39
67
|
onProgress?.(5);
|
|
40
68
|
|
|
@@ -42,7 +70,18 @@ export async function executeCoupleFuture(
|
|
|
42
70
|
.filter(Boolean)
|
|
43
71
|
.map(formatBase64);
|
|
44
72
|
|
|
73
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
74
|
+
console.log("[Executor] 🖼️ Image URLs prepared:", {
|
|
75
|
+
count: imageUrls.length,
|
|
76
|
+
url1Length: imageUrls[0]?.length || 0,
|
|
77
|
+
url2Length: imageUrls[1]?.length || 0,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
45
81
|
if (imageUrls.length < 2) {
|
|
82
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
83
|
+
console.log("[Executor] ❌ Two reference images required, got:", imageUrls.length);
|
|
84
|
+
}
|
|
46
85
|
return { success: false, error: "Two reference images required" };
|
|
47
86
|
}
|
|
48
87
|
|
|
@@ -59,7 +98,16 @@ export async function executeCoupleFuture(
|
|
|
59
98
|
};
|
|
60
99
|
|
|
61
100
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
62
|
-
console.log("[
|
|
101
|
+
console.log("[Executor] 📤 FAL AI Request:");
|
|
102
|
+
console.log("[Executor] Model:", COUPLE_FUTURE_DEFAULTS.model);
|
|
103
|
+
console.log("[Executor] Prompt:", enhancedPrompt.slice(0, 400));
|
|
104
|
+
console.log("[Executor] Aspect ratio:", modelInput.aspect_ratio);
|
|
105
|
+
console.log("[Executor] Output format:", modelInput.output_format);
|
|
106
|
+
console.log("[Executor] Timeout:", timeoutMs ?? COUPLE_FUTURE_DEFAULTS.timeoutMs);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
110
|
+
console.log("[Executor] 📡 Calling provider.subscribe()...");
|
|
63
111
|
}
|
|
64
112
|
|
|
65
113
|
const result = await provider.subscribe(COUPLE_FUTURE_DEFAULTS.model, modelInput, {
|
|
@@ -69,7 +117,10 @@ export async function executeCoupleFuture(
|
|
|
69
117
|
lastStatus = status.status;
|
|
70
118
|
|
|
71
119
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
72
|
-
console.log("[
|
|
120
|
+
console.log("[Executor] 📊 Queue status update:", {
|
|
121
|
+
status: status.status,
|
|
122
|
+
logs: status.logs?.slice(-3),
|
|
123
|
+
});
|
|
73
124
|
}
|
|
74
125
|
|
|
75
126
|
if (status.status === "IN_QUEUE") onProgress?.(20);
|
|
@@ -80,8 +131,10 @@ export async function executeCoupleFuture(
|
|
|
80
131
|
onProgress?.(90);
|
|
81
132
|
|
|
82
133
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
83
|
-
console.log("[
|
|
84
|
-
console.log("[
|
|
134
|
+
console.log("[Executor] 📥 FAL AI Response received");
|
|
135
|
+
console.log("[Executor] Raw result type:", typeof result);
|
|
136
|
+
console.log("[Executor] Raw result keys:", result ? Object.keys(result as object) : "null");
|
|
137
|
+
console.log("[Executor] Raw result:", JSON.stringify(result, null, 2).slice(0, 1000));
|
|
85
138
|
}
|
|
86
139
|
|
|
87
140
|
const rawResult = result as Record<string, unknown>;
|
|
@@ -89,21 +142,38 @@ export async function executeCoupleFuture(
|
|
|
89
142
|
const imageUrl = data?.images?.[0]?.url;
|
|
90
143
|
|
|
91
144
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
92
|
-
console.log("[
|
|
93
|
-
console.log("[
|
|
145
|
+
console.log("[Executor] 🔍 Parsed response:");
|
|
146
|
+
console.log("[Executor] Has data:", !!data);
|
|
147
|
+
console.log("[Executor] Images array length:", data?.images?.length || 0);
|
|
148
|
+
console.log("[Executor] First image URL:", imageUrl?.slice(0, 100));
|
|
94
149
|
}
|
|
95
150
|
|
|
96
151
|
onProgress?.(100);
|
|
97
152
|
|
|
98
153
|
if (!imageUrl) {
|
|
154
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
155
|
+
console.log("[Executor] ❌ No image URL in response");
|
|
156
|
+
console.log("[Executor] ========== COUPLE FUTURE END (FAILED) ==========");
|
|
157
|
+
}
|
|
99
158
|
return { success: false, error: "No image generated" };
|
|
100
159
|
}
|
|
101
160
|
|
|
161
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
162
|
+
console.log("[Executor] ✅ Generation successful!");
|
|
163
|
+
console.log("[Executor] Image URL:", imageUrl.slice(0, 100));
|
|
164
|
+
console.log("[Executor] ========== COUPLE FUTURE END (SUCCESS) ==========");
|
|
165
|
+
}
|
|
166
|
+
|
|
102
167
|
return { success: true, imageUrl };
|
|
103
168
|
} catch (error) {
|
|
104
169
|
const message = error instanceof Error ? error.message : "Generation failed";
|
|
105
170
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
106
|
-
console.error("[
|
|
171
|
+
console.error("[Executor] ❌ Exception caught:", {
|
|
172
|
+
message,
|
|
173
|
+
name: error instanceof Error ? error.name : "Unknown",
|
|
174
|
+
stack: error instanceof Error ? error.stack?.slice(0, 500) : undefined,
|
|
175
|
+
});
|
|
176
|
+
console.log("[Executor] ========== COUPLE FUTURE END (ERROR) ==========");
|
|
107
177
|
}
|
|
108
178
|
return { success: false, error: message };
|
|
109
179
|
}
|
|
@@ -1,58 +1,144 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* useCoupleFutureFlow Hook
|
|
3
|
-
*
|
|
3
|
+
* Optimized: Merged flow + generation logic
|
|
4
|
+
* Uses centralized orchestrator directly
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
|
-
import { useCallback, useEffect, useRef } from "react";
|
|
7
|
+
import { useCallback, useEffect, useRef, useMemo } from "react";
|
|
7
8
|
import { InteractionManager } from "react-native";
|
|
8
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
useGenerationOrchestrator,
|
|
11
|
+
type GenerationStrategy,
|
|
12
|
+
type AlertMessages,
|
|
13
|
+
} from "../../../../presentation/hooks/generation";
|
|
14
|
+
import { executeCoupleFuture } from "../../infrastructure/executor";
|
|
9
15
|
import { buildGenerationInputFromConfig } from "../../infrastructure/generationUtils";
|
|
16
|
+
import { createCreationsRepository } from "../../../../domains/creations/infrastructure/adapters";
|
|
10
17
|
import type { UploadedImage } from "../../../partner-upload/domain/types";
|
|
11
|
-
import type {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
import type { CoupleFutureInput } from "../../domain/types";
|
|
19
|
+
import type { Creation } from "../../../../domains/creations/domain/entities/Creation";
|
|
20
|
+
|
|
21
|
+
declare const __DEV__: boolean;
|
|
22
|
+
|
|
23
|
+
export interface CoupleFutureFlowConfig<TStep, TScenarioId> {
|
|
24
|
+
steps: {
|
|
25
|
+
SCENARIO: TStep;
|
|
26
|
+
SCENARIO_PREVIEW: TStep;
|
|
27
|
+
COUPLE_FEATURE_SELECTOR: TStep;
|
|
28
|
+
TEXT_INPUT: TStep;
|
|
29
|
+
PARTNER_A: TStep;
|
|
30
|
+
PARTNER_B: TStep;
|
|
31
|
+
GENERATING: TStep;
|
|
32
|
+
};
|
|
33
|
+
customScenarioId: TScenarioId;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface CoupleFutureFlowState<TStep, TScenarioId> {
|
|
37
|
+
step: TStep;
|
|
38
|
+
selectedScenarioId: TScenarioId | null;
|
|
39
|
+
selectedFeature: string | null;
|
|
40
|
+
partnerA: unknown;
|
|
41
|
+
partnerB: unknown;
|
|
42
|
+
partnerAName: string;
|
|
43
|
+
partnerBName: string;
|
|
44
|
+
customPrompt: string | null;
|
|
45
|
+
visualStyle: string | null;
|
|
46
|
+
selection: unknown;
|
|
47
|
+
isProcessing: boolean;
|
|
48
|
+
scenarioConfig: unknown;
|
|
49
|
+
selectedScenarioData: { requiresPhoto?: boolean } | null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface CoupleFutureFlowActions<TStep, TScenarioId, TResult> {
|
|
53
|
+
setStep: (step: TStep) => void;
|
|
54
|
+
selectScenario: (id: TScenarioId) => void;
|
|
55
|
+
setPartnerA: (image: unknown) => void;
|
|
56
|
+
setPartnerAName: (name: string) => void;
|
|
57
|
+
setPartnerB: (image: unknown) => void;
|
|
58
|
+
setPartnerBName: (name: string) => void;
|
|
59
|
+
setCustomPrompt: (prompt: string) => void;
|
|
60
|
+
setVisualStyle: (style: string) => void;
|
|
61
|
+
startGeneration: () => void;
|
|
62
|
+
generationSuccess: (result: TResult) => void;
|
|
63
|
+
generationError: (error: string) => void;
|
|
64
|
+
requireFeature: (callback: () => void) => void;
|
|
65
|
+
onNavigateToHistory: () => void;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface CoupleFutureFlowProps<TStep, TScenarioId, TResult> {
|
|
69
|
+
userId?: string;
|
|
70
|
+
config: CoupleFutureFlowConfig<TStep, TScenarioId>;
|
|
71
|
+
state: CoupleFutureFlowState<TStep, TScenarioId>;
|
|
72
|
+
actions: CoupleFutureFlowActions<TStep, TScenarioId, TResult>;
|
|
73
|
+
generationConfig: {
|
|
74
|
+
visualStyleModifiers: Record<string, string>;
|
|
75
|
+
defaultPartnerAName: string;
|
|
76
|
+
defaultPartnerBName: string;
|
|
77
|
+
};
|
|
78
|
+
alertMessages: AlertMessages;
|
|
79
|
+
processResult: (imageUrl: string, input: CoupleFutureInput) => TResult;
|
|
80
|
+
buildCreation: (result: TResult, input: CoupleFutureInput) => Creation | null;
|
|
81
|
+
onCreditsExhausted: () => void;
|
|
82
|
+
}
|
|
24
83
|
|
|
25
84
|
export const useCoupleFutureFlow = <TStep, TScenarioId, TResult>(
|
|
26
85
|
props: CoupleFutureFlowProps<TStep, TScenarioId, TResult>,
|
|
27
86
|
) => {
|
|
28
|
-
const { config, state, actions, generationConfig, alertMessages } = props;
|
|
29
|
-
const { processResult, buildCreation, onCreditsExhausted
|
|
87
|
+
const { config, state, actions, generationConfig, alertMessages, userId } = props;
|
|
88
|
+
const { processResult, buildCreation, onCreditsExhausted } = props;
|
|
30
89
|
const hasStarted = useRef(false);
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
90
|
+
const lastInputRef = useRef<CoupleFutureInput | null>(null);
|
|
91
|
+
const repository = useMemo(() => createCreationsRepository("creations"), []);
|
|
92
|
+
|
|
93
|
+
// Strategy for orchestrator - directly uses executor
|
|
94
|
+
const strategy: GenerationStrategy<CoupleFutureInput, TResult> = useMemo(
|
|
95
|
+
() => ({
|
|
96
|
+
execute: async (input, onProgress) => {
|
|
97
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
98
|
+
console.log("[CoupleFutureFlow] 🎯 Executing generation...");
|
|
99
|
+
}
|
|
100
|
+
lastInputRef.current = input;
|
|
101
|
+
const result = await executeCoupleFuture(
|
|
102
|
+
{ partnerABase64: input.partnerABase64, partnerBBase64: input.partnerBBase64, prompt: input.prompt },
|
|
103
|
+
{ onProgress },
|
|
104
|
+
);
|
|
105
|
+
if (!result.success || !result.imageUrl) throw new Error(result.error || "Generation failed");
|
|
106
|
+
return processResult(result.imageUrl, input);
|
|
41
107
|
},
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
108
|
+
getCreditCost: () => 1,
|
|
109
|
+
save: async (result, uid) => {
|
|
110
|
+
const input = lastInputRef.current;
|
|
111
|
+
if (!input) return;
|
|
112
|
+
const creation = buildCreation(result, input);
|
|
113
|
+
if (creation) await repository.create(uid, creation);
|
|
114
|
+
},
|
|
115
|
+
}),
|
|
116
|
+
[processResult, buildCreation, repository],
|
|
117
|
+
);
|
|
47
118
|
|
|
48
|
-
|
|
119
|
+
// Use orchestrator directly
|
|
120
|
+
const { generate, isGenerating, progress } = useGenerationOrchestrator(strategy, {
|
|
121
|
+
userId,
|
|
122
|
+
alertMessages,
|
|
123
|
+
onCreditsExhausted,
|
|
124
|
+
onSuccess: (result) => {
|
|
125
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) console.log("[CoupleFutureFlow] 🎉 Success");
|
|
126
|
+
actions.generationSuccess(result as TResult);
|
|
127
|
+
InteractionManager.runAfterInteractions(() => setTimeout(() => actions.onNavigateToHistory(), 300));
|
|
128
|
+
},
|
|
129
|
+
onError: (err) => {
|
|
130
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) console.log("[CoupleFutureFlow] ❌ Error:", err.message);
|
|
131
|
+
actions.generationError(err.message);
|
|
132
|
+
},
|
|
133
|
+
});
|
|
49
134
|
|
|
135
|
+
// Trigger generation when step changes to GENERATING
|
|
50
136
|
useEffect(() => {
|
|
51
|
-
if (step !== config.steps.GENERATING) {
|
|
137
|
+
if (state.step !== config.steps.GENERATING) {
|
|
52
138
|
hasStarted.current = false;
|
|
53
139
|
return;
|
|
54
140
|
}
|
|
55
|
-
if (!isProcessing || hasStarted.current) return;
|
|
141
|
+
if (!state.isProcessing || hasStarted.current) return;
|
|
56
142
|
hasStarted.current = true;
|
|
57
143
|
|
|
58
144
|
const input = buildGenerationInputFromConfig({
|
|
@@ -70,8 +156,9 @@ export const useCoupleFutureFlow = <TStep, TScenarioId, TResult>(
|
|
|
70
156
|
customScenarioId: config.customScenarioId as string,
|
|
71
157
|
});
|
|
72
158
|
if (input) generate(input);
|
|
73
|
-
}, [
|
|
159
|
+
}, [state, config, generationConfig, generate]);
|
|
74
160
|
|
|
161
|
+
// Handlers
|
|
75
162
|
const handleScenarioSelect = useCallback(
|
|
76
163
|
(id: string) => {
|
|
77
164
|
actions.selectScenario(id as TScenarioId);
|
|
@@ -80,23 +167,14 @@ export const useCoupleFutureFlow = <TStep, TScenarioId, TResult>(
|
|
|
80
167
|
[actions, config.steps.SCENARIO_PREVIEW],
|
|
81
168
|
);
|
|
82
169
|
|
|
83
|
-
const handleScenarioPreviewBack = useCallback(
|
|
84
|
-
() => actions.setStep(config.steps.SCENARIO),
|
|
85
|
-
[actions, config.steps.SCENARIO],
|
|
86
|
-
);
|
|
170
|
+
const handleScenarioPreviewBack = useCallback(() => actions.setStep(config.steps.SCENARIO), [actions, config.steps.SCENARIO]);
|
|
87
171
|
|
|
88
172
|
const handleScenarioPreviewContinue = useCallback(() => {
|
|
89
|
-
if (selectedFeature)
|
|
90
|
-
|
|
91
|
-
} else if (
|
|
92
|
-
selectedScenarioId === config.customScenarioId ||
|
|
93
|
-
selectedScenarioData?.requiresPhoto === false
|
|
94
|
-
) {
|
|
173
|
+
if (state.selectedFeature) actions.setStep(config.steps.COUPLE_FEATURE_SELECTOR);
|
|
174
|
+
else if (state.selectedScenarioId === config.customScenarioId || state.selectedScenarioData?.requiresPhoto === false)
|
|
95
175
|
actions.setStep(config.steps.TEXT_INPUT);
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
}, [actions, config, selectedFeature, selectedScenarioId, selectedScenarioData]);
|
|
176
|
+
else actions.setStep(config.steps.PARTNER_A);
|
|
177
|
+
}, [actions, config, state.selectedFeature, state.selectedScenarioId, state.selectedScenarioData]);
|
|
100
178
|
|
|
101
179
|
const handlePartnerAContinue = useCallback(
|
|
102
180
|
(image: UploadedImage, name: string) => {
|
|
@@ -108,12 +186,8 @@ export const useCoupleFutureFlow = <TStep, TScenarioId, TResult>(
|
|
|
108
186
|
);
|
|
109
187
|
|
|
110
188
|
const handlePartnerABack = useCallback(() => {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
? config.steps.TEXT_INPUT
|
|
114
|
-
: config.steps.SCENARIO_PREVIEW;
|
|
115
|
-
actions.setStep(targetStep);
|
|
116
|
-
}, [actions, config, selectedScenarioId]);
|
|
189
|
+
actions.setStep(state.selectedScenarioId === config.customScenarioId ? config.steps.TEXT_INPUT : config.steps.SCENARIO_PREVIEW);
|
|
190
|
+
}, [actions, config, state.selectedScenarioId]);
|
|
117
191
|
|
|
118
192
|
const handlePartnerBContinue = useCallback(
|
|
119
193
|
(image: UploadedImage, name: string) => {
|
|
@@ -126,28 +200,19 @@ export const useCoupleFutureFlow = <TStep, TScenarioId, TResult>(
|
|
|
126
200
|
[actions],
|
|
127
201
|
);
|
|
128
202
|
|
|
129
|
-
const handlePartnerBBack = useCallback(
|
|
130
|
-
() => actions.setStep(config.steps.PARTNER_A),
|
|
131
|
-
[actions, config.steps.PARTNER_A],
|
|
132
|
-
);
|
|
203
|
+
const handlePartnerBBack = useCallback(() => actions.setStep(config.steps.PARTNER_A), [actions, config.steps.PARTNER_A]);
|
|
133
204
|
|
|
134
205
|
const handleMagicPromptContinue = useCallback(
|
|
135
206
|
(prompt: string, style: string) => {
|
|
136
207
|
actions.setCustomPrompt(prompt);
|
|
137
208
|
actions.setVisualStyle(style);
|
|
138
|
-
if (selectedScenarioId === config.customScenarioId)
|
|
139
|
-
|
|
140
|
-
} else {
|
|
141
|
-
actions.startGeneration();
|
|
142
|
-
}
|
|
209
|
+
if (state.selectedScenarioId === config.customScenarioId) actions.setStep(config.steps.PARTNER_A);
|
|
210
|
+
else actions.startGeneration();
|
|
143
211
|
},
|
|
144
|
-
[actions, config, selectedScenarioId],
|
|
212
|
+
[actions, config, state.selectedScenarioId],
|
|
145
213
|
);
|
|
146
214
|
|
|
147
|
-
const handleMagicPromptBack = useCallback(
|
|
148
|
-
() => actions.setStep(config.steps.SCENARIO_PREVIEW),
|
|
149
|
-
[actions, config.steps.SCENARIO_PREVIEW],
|
|
150
|
-
);
|
|
215
|
+
const handleMagicPromptBack = useCallback(() => actions.setStep(config.steps.SCENARIO_PREVIEW), [actions, config.steps.SCENARIO_PREVIEW]);
|
|
151
216
|
|
|
152
217
|
return {
|
|
153
218
|
isGenerating,
|
|
@@ -18,6 +18,8 @@ import type {
|
|
|
18
18
|
UseGenerationOrchestratorReturn,
|
|
19
19
|
} from "./types";
|
|
20
20
|
|
|
21
|
+
declare const __DEV__: boolean;
|
|
22
|
+
|
|
21
23
|
const INITIAL_STATE = {
|
|
22
24
|
status: "idle" as const,
|
|
23
25
|
isGenerating: false,
|
|
@@ -30,53 +32,109 @@ export const useGenerationOrchestrator = <TInput, TResult>(
|
|
|
30
32
|
strategy: GenerationStrategy<TInput, TResult>,
|
|
31
33
|
config: GenerationConfig,
|
|
32
34
|
): UseGenerationOrchestratorReturn<TInput, TResult> => {
|
|
33
|
-
const { userId, alertMessages, onCreditsExhausted, onSuccess, onError } =
|
|
34
|
-
|
|
35
|
+
const { userId, alertMessages, onCreditsExhausted, onSuccess, onError } = config;
|
|
36
|
+
|
|
37
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
38
|
+
console.log("[Orchestrator] Hook initialized:", { userId, hasAlertMessages: !!alertMessages });
|
|
39
|
+
}
|
|
35
40
|
|
|
36
41
|
const [state, setState] = useState<GenerationState<TResult>>(INITIAL_STATE);
|
|
37
42
|
const isGeneratingRef = useRef(false);
|
|
38
43
|
const offlineStore = useOfflineStore();
|
|
39
44
|
const { showError, showSuccess } = useAlert();
|
|
40
|
-
const { checkCredits, deductCredit } = useDeductCredit({
|
|
41
|
-
userId,
|
|
42
|
-
onCreditsExhausted,
|
|
43
|
-
});
|
|
45
|
+
const { checkCredits, deductCredit } = useDeductCredit({ userId, onCreditsExhausted });
|
|
44
46
|
|
|
45
47
|
const generate = useCallback(
|
|
46
48
|
async (input: TInput) => {
|
|
47
|
-
if (
|
|
49
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
50
|
+
console.log("[Orchestrator] 🚀 generate() called");
|
|
51
|
+
console.log("[Orchestrator] Input:", JSON.stringify(input, null, 2).slice(0, 500));
|
|
52
|
+
console.log("[Orchestrator] isGeneratingRef:", isGeneratingRef.current);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (isGeneratingRef.current) {
|
|
56
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
57
|
+
console.log("[Orchestrator] ⚠️ Already generating, skipping");
|
|
58
|
+
}
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
48
61
|
|
|
49
62
|
isGeneratingRef.current = true;
|
|
50
63
|
setState({ ...INITIAL_STATE, status: "checking", isGenerating: true });
|
|
51
64
|
|
|
65
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
66
|
+
console.log("[Orchestrator] State set to: checking, isGenerating: true");
|
|
67
|
+
}
|
|
68
|
+
|
|
52
69
|
try {
|
|
70
|
+
// Network check
|
|
53
71
|
if (!offlineStore.isOnline) {
|
|
72
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
73
|
+
console.log("[Orchestrator] ❌ Network check failed - offline");
|
|
74
|
+
}
|
|
54
75
|
throw createGenerationError("network", "No internet connection");
|
|
55
76
|
}
|
|
56
77
|
|
|
78
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
79
|
+
console.log("[Orchestrator] ✅ Network check passed");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Credit check
|
|
57
83
|
const creditCost = strategy.getCreditCost();
|
|
84
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
85
|
+
console.log("[Orchestrator] 💳 Credit cost:", creditCost);
|
|
86
|
+
}
|
|
87
|
+
|
|
58
88
|
const hasCredits = await checkCredits(creditCost);
|
|
89
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
90
|
+
console.log("[Orchestrator] 💳 Has credits:", hasCredits);
|
|
91
|
+
}
|
|
92
|
+
|
|
59
93
|
if (!hasCredits) {
|
|
60
|
-
|
|
94
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
95
|
+
console.log("[Orchestrator] ❌ No credits, opening paywall");
|
|
96
|
+
}
|
|
61
97
|
isGeneratingRef.current = false;
|
|
62
98
|
setState(INITIAL_STATE);
|
|
63
99
|
onCreditsExhausted?.();
|
|
64
100
|
return;
|
|
65
101
|
}
|
|
66
102
|
|
|
103
|
+
// Start generation
|
|
67
104
|
setState((prev) => ({ ...prev, status: "generating", progress: 10 }));
|
|
105
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
106
|
+
console.log("[Orchestrator] 🎨 Starting strategy.execute()");
|
|
107
|
+
}
|
|
68
108
|
|
|
69
109
|
const result = await strategy.execute(input, (progress) => {
|
|
110
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
111
|
+
console.log("[Orchestrator] 📊 Progress update:", progress);
|
|
112
|
+
}
|
|
70
113
|
setState((prev) => ({ ...prev, progress }));
|
|
71
114
|
});
|
|
72
115
|
|
|
116
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
117
|
+
console.log("[Orchestrator] ✅ strategy.execute() completed");
|
|
118
|
+
console.log("[Orchestrator] Result type:", typeof result);
|
|
119
|
+
}
|
|
120
|
+
|
|
73
121
|
setState((prev) => ({ ...prev, progress: 70 }));
|
|
74
122
|
|
|
123
|
+
// Save result
|
|
75
124
|
if (strategy.save && userId) {
|
|
76
125
|
setState((prev) => ({ ...prev, status: "saving" }));
|
|
126
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
127
|
+
console.log("[Orchestrator] 💾 Saving result...");
|
|
128
|
+
}
|
|
77
129
|
try {
|
|
78
130
|
await strategy.save(result, userId);
|
|
131
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
132
|
+
console.log("[Orchestrator] ✅ Save completed");
|
|
133
|
+
}
|
|
79
134
|
} catch (saveErr) {
|
|
135
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
136
|
+
console.log("[Orchestrator] ❌ Save failed:", saveErr);
|
|
137
|
+
}
|
|
80
138
|
throw createGenerationError(
|
|
81
139
|
"save",
|
|
82
140
|
"Failed to save",
|
|
@@ -87,8 +145,13 @@ export const useGenerationOrchestrator = <TInput, TResult>(
|
|
|
87
145
|
|
|
88
146
|
setState((prev) => ({ ...prev, progress: 90 }));
|
|
89
147
|
|
|
148
|
+
// Deduct credit
|
|
149
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
150
|
+
console.log("[Orchestrator] 💳 Deducting credit:", creditCost);
|
|
151
|
+
}
|
|
90
152
|
await deductCredit(creditCost);
|
|
91
153
|
|
|
154
|
+
// Success
|
|
92
155
|
setState({
|
|
93
156
|
status: "success",
|
|
94
157
|
isGenerating: false,
|
|
@@ -97,6 +160,10 @@ export const useGenerationOrchestrator = <TInput, TResult>(
|
|
|
97
160
|
error: null,
|
|
98
161
|
});
|
|
99
162
|
|
|
163
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
164
|
+
console.log("[Orchestrator] 🎉 Generation SUCCESS");
|
|
165
|
+
}
|
|
166
|
+
|
|
100
167
|
if (alertMessages.success) {
|
|
101
168
|
void showSuccess("Success", alertMessages.success);
|
|
102
169
|
}
|
|
@@ -105,6 +172,10 @@ export const useGenerationOrchestrator = <TInput, TResult>(
|
|
|
105
172
|
} catch (err) {
|
|
106
173
|
const error = parseError(err);
|
|
107
174
|
|
|
175
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
176
|
+
console.log("[Orchestrator] ❌ Generation ERROR:", error);
|
|
177
|
+
}
|
|
178
|
+
|
|
108
179
|
setState({
|
|
109
180
|
status: "error",
|
|
110
181
|
isGenerating: false,
|
|
@@ -117,6 +188,9 @@ export const useGenerationOrchestrator = <TInput, TResult>(
|
|
|
117
188
|
onError?.(error);
|
|
118
189
|
} finally {
|
|
119
190
|
isGeneratingRef.current = false;
|
|
191
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
192
|
+
console.log("[Orchestrator] 🏁 generate() finished, isGeneratingRef reset to false");
|
|
193
|
+
}
|
|
120
194
|
}
|
|
121
195
|
},
|
|
122
196
|
[
|
|
@@ -134,6 +208,9 @@ export const useGenerationOrchestrator = <TInput, TResult>(
|
|
|
134
208
|
);
|
|
135
209
|
|
|
136
210
|
const reset = useCallback(() => {
|
|
211
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
212
|
+
console.log("[Orchestrator] 🔄 reset() called");
|
|
213
|
+
}
|
|
137
214
|
setState(INITIAL_STATE);
|
|
138
215
|
isGeneratingRef.current = false;
|
|
139
216
|
}, []);
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Couple Future Flow Types
|
|
3
|
-
* Type definitions for couple future wizard flow
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export interface CoupleFutureFlowConfig<TStep, TScenarioId> {
|
|
7
|
-
steps: {
|
|
8
|
-
SCENARIO: TStep;
|
|
9
|
-
SCENARIO_PREVIEW: TStep;
|
|
10
|
-
COUPLE_FEATURE_SELECTOR: TStep;
|
|
11
|
-
TEXT_INPUT: TStep;
|
|
12
|
-
PARTNER_A: TStep;
|
|
13
|
-
PARTNER_B: TStep;
|
|
14
|
-
GENERATING: TStep;
|
|
15
|
-
};
|
|
16
|
-
customScenarioId: TScenarioId;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface CoupleFutureFlowState<TStep, TScenarioId> {
|
|
20
|
-
step: TStep;
|
|
21
|
-
selectedScenarioId: TScenarioId | null;
|
|
22
|
-
selectedFeature: string | null;
|
|
23
|
-
partnerA: unknown;
|
|
24
|
-
partnerB: unknown;
|
|
25
|
-
partnerAName: string;
|
|
26
|
-
partnerBName: string;
|
|
27
|
-
customPrompt: string | null;
|
|
28
|
-
visualStyle: string | null;
|
|
29
|
-
selection: unknown;
|
|
30
|
-
isProcessing: boolean;
|
|
31
|
-
scenarioConfig: unknown;
|
|
32
|
-
selectedScenarioData: { requiresPhoto?: boolean } | null;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface CoupleFutureFlowActions<TStep, TScenarioId, TResult> {
|
|
36
|
-
setStep: (step: TStep) => void;
|
|
37
|
-
selectScenario: (id: TScenarioId) => void;
|
|
38
|
-
setPartnerA: (image: unknown) => void;
|
|
39
|
-
setPartnerAName: (name: string) => void;
|
|
40
|
-
setPartnerB: (image: unknown) => void;
|
|
41
|
-
setPartnerBName: (name: string) => void;
|
|
42
|
-
setCustomPrompt: (prompt: string) => void;
|
|
43
|
-
setVisualStyle: (style: string) => void;
|
|
44
|
-
startGeneration: () => void;
|
|
45
|
-
generationSuccess: (result: TResult) => void;
|
|
46
|
-
generationError: (error: string) => void;
|
|
47
|
-
requireFeature: (callback: () => void) => void;
|
|
48
|
-
onNavigateToHistory: () => void;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export interface CoupleFutureFlowProps<TStep, TScenarioId, TResult> {
|
|
52
|
-
userId?: string;
|
|
53
|
-
config: CoupleFutureFlowConfig<TStep, TScenarioId>;
|
|
54
|
-
state: CoupleFutureFlowState<TStep, TScenarioId>;
|
|
55
|
-
actions: CoupleFutureFlowActions<TStep, TScenarioId, TResult>;
|
|
56
|
-
generationConfig: {
|
|
57
|
-
visualStyleModifiers: Record<string, string>;
|
|
58
|
-
defaultPartnerAName: string;
|
|
59
|
-
defaultPartnerBName: string;
|
|
60
|
-
};
|
|
61
|
-
alertMessages: {
|
|
62
|
-
networkError: string;
|
|
63
|
-
policyViolation: string;
|
|
64
|
-
saveFailed: string;
|
|
65
|
-
creditFailed: string;
|
|
66
|
-
unknown: string;
|
|
67
|
-
};
|
|
68
|
-
processResult: (imageUrl: string, input: unknown) => TResult;
|
|
69
|
-
buildCreation: (result: TResult, input: unknown) => unknown;
|
|
70
|
-
onCreditsExhausted: () => void;
|
|
71
|
-
}
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useCoupleFutureGeneration Hook
|
|
3
|
-
* Couple future generation using centralized orchestrator
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { useMemo, useCallback, useRef } from "react";
|
|
7
|
-
import {
|
|
8
|
-
useGenerationOrchestrator,
|
|
9
|
-
type GenerationStrategy,
|
|
10
|
-
type AlertMessages,
|
|
11
|
-
} from "../../../../presentation/hooks/generation";
|
|
12
|
-
import { executeCoupleFuture } from "../../infrastructure/executor";
|
|
13
|
-
import type { CoupleFutureInput } from "../../domain/types";
|
|
14
|
-
import { createCreationsRepository } from "../../../../domains/creations/infrastructure/adapters";
|
|
15
|
-
import type { Creation } from "../../../../domains/creations/domain/entities/Creation";
|
|
16
|
-
|
|
17
|
-
export interface CoupleFutureConfig<TResult> {
|
|
18
|
-
userId: string | undefined;
|
|
19
|
-
processResult: (imageUrl: string, input: CoupleFutureInput) => TResult;
|
|
20
|
-
buildCreation?: (result: TResult, input: CoupleFutureInput) => Creation | null;
|
|
21
|
-
onCreditsExhausted?: () => void;
|
|
22
|
-
onSuccess?: (result: TResult) => void;
|
|
23
|
-
onError?: (error: string) => void;
|
|
24
|
-
alertMessages: AlertMessages;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export const useCoupleFutureGeneration = <TResult>(
|
|
28
|
-
config: CoupleFutureConfig<TResult>,
|
|
29
|
-
) => {
|
|
30
|
-
const {
|
|
31
|
-
userId,
|
|
32
|
-
processResult,
|
|
33
|
-
buildCreation,
|
|
34
|
-
onCreditsExhausted,
|
|
35
|
-
onSuccess,
|
|
36
|
-
onError,
|
|
37
|
-
alertMessages,
|
|
38
|
-
} = config;
|
|
39
|
-
|
|
40
|
-
const repository = useMemo(
|
|
41
|
-
() => createCreationsRepository("creations"),
|
|
42
|
-
[],
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
// Store input for use in save callback
|
|
46
|
-
const lastInputRef = useRef<CoupleFutureInput | null>(null);
|
|
47
|
-
|
|
48
|
-
const strategy: GenerationStrategy<CoupleFutureInput, TResult> = useMemo(
|
|
49
|
-
() => ({
|
|
50
|
-
execute: async (input, onProgress) => {
|
|
51
|
-
// Store input for save callback
|
|
52
|
-
lastInputRef.current = input;
|
|
53
|
-
|
|
54
|
-
const result = await executeCoupleFuture(
|
|
55
|
-
{
|
|
56
|
-
partnerABase64: input.partnerABase64,
|
|
57
|
-
partnerBBase64: input.partnerBBase64,
|
|
58
|
-
prompt: input.prompt,
|
|
59
|
-
},
|
|
60
|
-
{ onProgress },
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
if (!result.success || !result.imageUrl) {
|
|
64
|
-
throw new Error(result.error || "Generation failed");
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return processResult(result.imageUrl, input);
|
|
68
|
-
},
|
|
69
|
-
getCreditCost: () => 1,
|
|
70
|
-
save: buildCreation
|
|
71
|
-
? async (result, uid) => {
|
|
72
|
-
const input = lastInputRef.current;
|
|
73
|
-
if (input) {
|
|
74
|
-
const creation = buildCreation(result, input);
|
|
75
|
-
if (creation) {
|
|
76
|
-
await repository.create(uid, creation);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
: undefined,
|
|
81
|
-
}),
|
|
82
|
-
[processResult, buildCreation, repository],
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
const handleError = useCallback(
|
|
86
|
-
(error: { message: string }) => {
|
|
87
|
-
onError?.(error.message);
|
|
88
|
-
},
|
|
89
|
-
[onError],
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
return useGenerationOrchestrator(strategy, {
|
|
93
|
-
userId,
|
|
94
|
-
alertMessages,
|
|
95
|
-
onCreditsExhausted,
|
|
96
|
-
onSuccess: onSuccess as (result: unknown) => void,
|
|
97
|
-
onError: handleError,
|
|
98
|
-
});
|
|
99
|
-
};
|