@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-ai-generation-content",
3
- "version": "1.19.7",
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("[CoupleFuture] Starting Nano Banana generation");
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("[CoupleFuture] Prompt:", enhancedPrompt);
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("[CoupleFuture] Status:", status.status);
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("[CoupleFuture] Raw result:", JSON.stringify(result, null, 2));
84
- console.log("[CoupleFuture] Result keys:", result ? Object.keys(result as object) : "null");
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("[CoupleFuture] Parsed data:", JSON.stringify(data, null, 2));
93
- console.log("[CoupleFuture] Image URL:", imageUrl);
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("[CoupleFuture] Error:", message);
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
- * Handles couple future wizard flow logic
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 { useCoupleFutureGeneration } from "./useCoupleFutureGeneration";
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
- CoupleFutureFlowConfig,
13
- CoupleFutureFlowState,
14
- CoupleFutureFlowActions,
15
- CoupleFutureFlowProps,
16
- } from "./coupleFutureFlow.types";
17
-
18
- export type {
19
- CoupleFutureFlowConfig,
20
- CoupleFutureFlowState,
21
- CoupleFutureFlowActions,
22
- CoupleFutureFlowProps,
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, userId } = props;
87
+ const { config, state, actions, generationConfig, alertMessages, userId } = props;
88
+ const { processResult, buildCreation, onCreditsExhausted } = props;
30
89
  const hasStarted = useRef(false);
31
-
32
- const { generate, isGenerating, progress } =
33
- useCoupleFutureGeneration<TResult>({
34
- userId,
35
- onCreditsExhausted,
36
- onSuccess: (result) => {
37
- actions.generationSuccess(result);
38
- InteractionManager.runAfterInteractions(() => {
39
- setTimeout(() => actions.onNavigateToHistory(), 300);
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
- onError: actions.generationError,
43
- processResult: processResult as never,
44
- buildCreation: buildCreation as never,
45
- alertMessages,
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
- const { step, isProcessing, selectedFeature, selectedScenarioId, selectedScenarioData } = state;
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
- }, [step, isProcessing, state, config, generationConfig, generate]);
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
- actions.setStep(config.steps.COUPLE_FEATURE_SELECTOR);
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
- } else {
97
- actions.setStep(config.steps.PARTNER_A);
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
- const targetStep =
112
- selectedScenarioId === config.customScenarioId
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
- actions.setStep(config.steps.PARTNER_A);
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
- config;
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 (isGeneratingRef.current) return;
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
- // Open paywall instead of showing error
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
- };