@umituz/react-native-ai-generation-content 1.19.8 → 1.20.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-ai-generation-content",
3
- "version": "1.19.8",
3
+ "version": "1.20.1",
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,
@@ -1,105 +1,146 @@
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";
18
+ import type { CoupleFutureInput } from "../../domain/types";
19
+ import type { Creation } from "../../../../domains/creations/domain/entities/Creation";
17
20
 
18
21
  declare const __DEV__: boolean;
19
22
 
20
- export type {
21
- CoupleFutureFlowConfig,
22
- CoupleFutureFlowState,
23
- CoupleFutureFlowActions,
24
- CoupleFutureFlowProps,
25
- };
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
+ }
26
83
 
27
84
  export const useCoupleFutureFlow = <TStep, TScenarioId, TResult>(
28
85
  props: CoupleFutureFlowProps<TStep, TScenarioId, TResult>,
29
86
  ) => {
30
- const { config, state, actions, generationConfig, alertMessages } = props;
31
- const { processResult, buildCreation, onCreditsExhausted, userId } = props;
87
+ const { config, state, actions, generationConfig, alertMessages, userId } = props;
88
+ const { processResult, buildCreation, onCreditsExhausted } = props;
32
89
  const hasStarted = useRef(false);
90
+ const lastInputRef = useRef<CoupleFutureInput | null>(null);
91
+ const repository = useMemo(() => createCreationsRepository("creations"), []);
33
92
 
34
- if (typeof __DEV__ !== "undefined" && __DEV__) {
35
- console.log("[CoupleFutureFlow] Hook initialized:", { userId, step: state.step });
36
- }
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);
107
+ },
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
+ );
37
118
 
38
- const { generate, isGenerating, progress } = useCoupleFutureGeneration<TResult>({
119
+ // Use orchestrator directly
120
+ const { generate, isGenerating, progress } = useGenerationOrchestrator(strategy, {
39
121
  userId,
122
+ alertMessages,
40
123
  onCreditsExhausted,
41
124
  onSuccess: (result) => {
42
- if (typeof __DEV__ !== "undefined" && __DEV__) {
43
- console.log("[CoupleFutureFlow] 🎉 onSuccess callback triggered");
44
- }
45
- actions.generationSuccess(result);
46
- InteractionManager.runAfterInteractions(() => {
47
- if (typeof __DEV__ !== "undefined" && __DEV__) {
48
- console.log("[CoupleFutureFlow] 🔄 Navigating to History in 300ms...");
49
- }
50
- setTimeout(() => actions.onNavigateToHistory(), 300);
51
- });
125
+ if (typeof __DEV__ !== "undefined" && __DEV__) console.log("[CoupleFutureFlow] 🎉 Success");
126
+ actions.generationSuccess(result as TResult);
127
+ InteractionManager.runAfterInteractions(() => setTimeout(() => actions.onNavigateToHistory(), 300));
52
128
  },
53
- onError: (error) => {
54
- if (typeof __DEV__ !== "undefined" && __DEV__) {
55
- console.log("[CoupleFutureFlow] ❌ onError callback:", error);
56
- }
57
- actions.generationError(error);
129
+ onError: (err) => {
130
+ if (typeof __DEV__ !== "undefined" && __DEV__) console.log("[CoupleFutureFlow] ❌ Error:", err.message);
131
+ actions.generationError(err.message);
58
132
  },
59
- processResult: processResult as never,
60
- buildCreation: buildCreation as never,
61
- alertMessages,
62
133
  });
63
134
 
64
- const { step, isProcessing, selectedFeature, selectedScenarioId, selectedScenarioData } = state;
65
-
135
+ // Trigger generation when step changes to GENERATING
66
136
  useEffect(() => {
67
- if (typeof __DEV__ !== "undefined" && __DEV__) {
68
- console.log("[CoupleFutureFlow] useEffect triggered:", {
69
- step,
70
- isProcessing,
71
- hasStarted: hasStarted.current,
72
- targetStep: config.steps.GENERATING,
73
- });
74
- }
75
-
76
- if (step !== config.steps.GENERATING) {
137
+ if (state.step !== config.steps.GENERATING) {
77
138
  hasStarted.current = false;
78
139
  return;
79
140
  }
80
- if (!isProcessing || hasStarted.current) {
81
- if (typeof __DEV__ !== "undefined" && __DEV__) {
82
- console.log("[CoupleFutureFlow] ⚠️ Skipping generation:", {
83
- reason: !isProcessing ? "isProcessing=false" : "already started",
84
- });
85
- }
86
- return;
87
- }
141
+ if (!state.isProcessing || hasStarted.current) return;
88
142
  hasStarted.current = true;
89
143
 
90
- if (typeof __DEV__ !== "undefined" && __DEV__) {
91
- console.log("[CoupleFutureFlow] 🚀 Building generation input...");
92
- console.log("[CoupleFutureFlow] State:", {
93
- hasPartnerA: !!state.partnerA,
94
- hasPartnerB: !!state.partnerB,
95
- partnerAName: state.partnerAName,
96
- partnerBName: state.partnerBName,
97
- hasScenarioConfig: !!state.scenarioConfig,
98
- customPrompt: state.customPrompt?.slice(0, 100),
99
- visualStyle: state.visualStyle,
100
- });
101
- }
102
-
103
144
  const input = buildGenerationInputFromConfig({
104
145
  partnerA: state.partnerA as never,
105
146
  partnerB: state.partnerB as never,
@@ -114,56 +155,29 @@ export const useCoupleFutureFlow = <TStep, TScenarioId, TResult>(
114
155
  visualStyles: generationConfig.visualStyleModifiers,
115
156
  customScenarioId: config.customScenarioId as string,
116
157
  });
158
+ if (input) generate(input);
159
+ }, [state, config, generationConfig, generate]);
117
160
 
118
- if (typeof __DEV__ !== "undefined" && __DEV__) {
119
- console.log("[CoupleFutureFlow] 📦 Generation input built:", {
120
- hasInput: !!input,
121
- prompt: input?.prompt?.slice(0, 200),
122
- });
123
- }
124
-
125
- if (input) {
126
- if (typeof __DEV__ !== "undefined" && __DEV__) {
127
- console.log("[CoupleFutureFlow] 🚀 Calling generate()...");
128
- }
129
- generate(input);
130
- }
131
- }, [step, isProcessing, state, config, generationConfig, generate]);
132
-
161
+ // Handlers
133
162
  const handleScenarioSelect = useCallback(
134
163
  (id: string) => {
135
- if (typeof __DEV__ !== "undefined" && __DEV__) {
136
- console.log("[CoupleFutureFlow] handleScenarioSelect:", id);
137
- }
138
164
  actions.selectScenario(id as TScenarioId);
139
165
  actions.setStep(config.steps.SCENARIO_PREVIEW);
140
166
  },
141
167
  [actions, config.steps.SCENARIO_PREVIEW],
142
168
  );
143
169
 
144
- const handleScenarioPreviewBack = useCallback(
145
- () => actions.setStep(config.steps.SCENARIO),
146
- [actions, config.steps.SCENARIO],
147
- );
170
+ const handleScenarioPreviewBack = useCallback(() => actions.setStep(config.steps.SCENARIO), [actions, config.steps.SCENARIO]);
148
171
 
149
172
  const handleScenarioPreviewContinue = useCallback(() => {
150
- if (selectedFeature) {
151
- actions.setStep(config.steps.COUPLE_FEATURE_SELECTOR);
152
- } else if (
153
- selectedScenarioId === config.customScenarioId ||
154
- selectedScenarioData?.requiresPhoto === false
155
- ) {
173
+ if (state.selectedFeature) actions.setStep(config.steps.COUPLE_FEATURE_SELECTOR);
174
+ else if (state.selectedScenarioId === config.customScenarioId || state.selectedScenarioData?.requiresPhoto === false)
156
175
  actions.setStep(config.steps.TEXT_INPUT);
157
- } else {
158
- actions.setStep(config.steps.PARTNER_A);
159
- }
160
- }, [actions, config, selectedFeature, selectedScenarioId, selectedScenarioData]);
176
+ else actions.setStep(config.steps.PARTNER_A);
177
+ }, [actions, config, state.selectedFeature, state.selectedScenarioId, state.selectedScenarioData]);
161
178
 
162
179
  const handlePartnerAContinue = useCallback(
163
180
  (image: UploadedImage, name: string) => {
164
- if (typeof __DEV__ !== "undefined" && __DEV__) {
165
- console.log("[CoupleFutureFlow] handlePartnerAContinue:", { hasImage: !!image, name });
166
- }
167
181
  actions.setPartnerA(image);
168
182
  actions.setPartnerAName(name);
169
183
  actions.setStep(config.steps.PARTNER_B);
@@ -172,23 +186,12 @@ export const useCoupleFutureFlow = <TStep, TScenarioId, TResult>(
172
186
  );
173
187
 
174
188
  const handlePartnerABack = useCallback(() => {
175
- const targetStep =
176
- selectedScenarioId === config.customScenarioId
177
- ? config.steps.TEXT_INPUT
178
- : config.steps.SCENARIO_PREVIEW;
179
- actions.setStep(targetStep);
180
- }, [actions, config, selectedScenarioId]);
189
+ actions.setStep(state.selectedScenarioId === config.customScenarioId ? config.steps.TEXT_INPUT : config.steps.SCENARIO_PREVIEW);
190
+ }, [actions, config, state.selectedScenarioId]);
181
191
 
182
192
  const handlePartnerBContinue = useCallback(
183
193
  (image: UploadedImage, name: string) => {
184
- if (typeof __DEV__ !== "undefined" && __DEV__) {
185
- console.log("[CoupleFutureFlow] handlePartnerBContinue:", { hasImage: !!image, name });
186
- console.log("[CoupleFutureFlow] Calling requireFeature()...");
187
- }
188
194
  actions.requireFeature(() => {
189
- if (typeof __DEV__ !== "undefined" && __DEV__) {
190
- console.log("[CoupleFutureFlow] 🔓 Feature gate passed, starting generation");
191
- }
192
195
  actions.setPartnerB(image);
193
196
  actions.setPartnerBName(name);
194
197
  actions.startGeneration();
@@ -197,34 +200,19 @@ export const useCoupleFutureFlow = <TStep, TScenarioId, TResult>(
197
200
  [actions],
198
201
  );
199
202
 
200
- const handlePartnerBBack = useCallback(
201
- () => actions.setStep(config.steps.PARTNER_A),
202
- [actions, config.steps.PARTNER_A],
203
- );
203
+ const handlePartnerBBack = useCallback(() => actions.setStep(config.steps.PARTNER_A), [actions, config.steps.PARTNER_A]);
204
204
 
205
205
  const handleMagicPromptContinue = useCallback(
206
206
  (prompt: string, style: string) => {
207
- if (typeof __DEV__ !== "undefined" && __DEV__) {
208
- console.log("[CoupleFutureFlow] handleMagicPromptContinue:", {
209
- promptLength: prompt.length,
210
- style,
211
- });
212
- }
213
207
  actions.setCustomPrompt(prompt);
214
208
  actions.setVisualStyle(style);
215
- if (selectedScenarioId === config.customScenarioId) {
216
- actions.setStep(config.steps.PARTNER_A);
217
- } else {
218
- actions.startGeneration();
219
- }
209
+ if (state.selectedScenarioId === config.customScenarioId) actions.setStep(config.steps.PARTNER_A);
210
+ else actions.startGeneration();
220
211
  },
221
- [actions, config, selectedScenarioId],
212
+ [actions, config, state.selectedScenarioId],
222
213
  );
223
214
 
224
- const handleMagicPromptBack = useCallback(
225
- () => actions.setStep(config.steps.SCENARIO_PREVIEW),
226
- [actions, config.steps.SCENARIO_PREVIEW],
227
- );
215
+ const handleMagicPromptBack = useCallback(() => actions.setStep(config.steps.SCENARIO_PREVIEW), [actions, config.steps.SCENARIO_PREVIEW]);
228
216
 
229
217
  return {
230
218
  isGenerating,
@@ -1,9 +1,14 @@
1
1
  /**
2
2
  * Text-to-Image Generation Hook
3
- * Orchestrates generation with app-provided callbacks
3
+ * Uses centralized orchestrator for auth, credits, and error handling
4
4
  */
5
5
 
6
- import { useState, useCallback } from "react";
6
+ import { useCallback, useMemo } from "react";
7
+ import {
8
+ useGenerationOrchestrator,
9
+ type GenerationStrategy,
10
+ type AlertMessages,
11
+ } from "../../../../presentation/hooks/generation";
7
12
  import type {
8
13
  TextToImageFormState,
9
14
  TextToImageCallbacks,
@@ -11,91 +16,95 @@ import type {
11
16
  TextToImageGenerationRequest,
12
17
  } from "../../domain/types";
13
18
 
14
- export interface GenerationState {
15
- isGenerating: boolean;
16
- progress: number;
17
- error: string | null;
18
- }
19
+ declare const __DEV__: boolean;
19
20
 
20
21
  export interface UseGenerationOptions {
21
22
  formState: TextToImageFormState;
22
23
  callbacks: TextToImageCallbacks;
24
+ userId?: string;
23
25
  onPromptCleared?: () => void;
24
26
  }
25
27
 
28
+ export interface GenerationState {
29
+ isGenerating: boolean;
30
+ progress: number;
31
+ error: string | null;
32
+ }
33
+
26
34
  export interface UseGenerationReturn {
27
35
  generationState: GenerationState;
28
36
  totalCost: number;
29
37
  handleGenerate: () => Promise<TextToImageGenerationResult | null>;
30
38
  }
31
39
 
32
- const initialState: GenerationState = {
33
- isGenerating: false,
34
- progress: 0,
35
- error: null,
40
+ const DEFAULT_ALERT_MESSAGES: AlertMessages = {
41
+ networkError: "No internet connection. Please check your network.",
42
+ policyViolation: "Content not allowed. Please try again.",
43
+ saveFailed: "Failed to save. Please try again.",
44
+ creditFailed: "Credit operation failed. Please try again.",
45
+ unknown: "An error occurred. Please try again.",
46
+ authRequired: "Please sign in to continue.",
36
47
  };
37
48
 
38
- declare const __DEV__: boolean;
39
-
40
49
  export function useGeneration(options: UseGenerationOptions): UseGenerationReturn {
41
- const { formState, callbacks, onPromptCleared } = options;
42
- const [generationState, setGenerationState] = useState<GenerationState>(initialState);
50
+ const { formState, callbacks, userId, onPromptCleared } = options;
43
51
 
44
52
  const totalCost = callbacks.calculateCost(formState.numImages, formState.selectedModel);
45
53
 
46
- const handleGenerate = useCallback(async (): Promise<TextToImageGenerationResult | null> => {
47
- if (typeof __DEV__ !== "undefined" && __DEV__) {
48
-
49
- console.log("[TextToImage] handleGenerate called");
50
- }
51
-
52
- const trimmedPrompt = formState.prompt.trim();
53
-
54
- if (!trimmedPrompt) {
54
+ // Build strategy for orchestrator
55
+ const strategy: GenerationStrategy<TextToImageGenerationRequest, string[]> = useMemo(
56
+ () => ({
57
+ execute: async (request) => {
58
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
59
+ console.log("[TextToImage] Executing generation:", JSON.stringify(request, null, 2));
60
+ }
61
+ const result = await callbacks.executeGeneration(request);
62
+ if (result.success === false) {
63
+ throw new Error(result.error);
64
+ }
65
+ return result.imageUrls;
66
+ },
67
+ getCreditCost: () => totalCost,
68
+ }),
69
+ [callbacks, totalCost],
70
+ );
71
+
72
+ // Use orchestrator with auth support
73
+ const { generate, isGenerating, progress, error } = useGenerationOrchestrator(strategy, {
74
+ userId,
75
+ alertMessages: DEFAULT_ALERT_MESSAGES,
76
+ onCreditsExhausted: () => callbacks.onCreditsRequired?.(totalCost),
77
+ auth: {
78
+ isAuthenticated: callbacks.isAuthenticated,
79
+ onAuthRequired: callbacks.onAuthRequired,
80
+ },
81
+ onSuccess: (result) => {
82
+ const imageUrls = result as string[];
55
83
  if (typeof __DEV__ !== "undefined" && __DEV__) {
56
-
57
- console.log("[TextToImage] No prompt provided");
84
+ console.log("[TextToImage] Success! Generated", imageUrls.length, "image(s)");
58
85
  }
59
- setGenerationState((prev) => ({ ...prev, error: "Prompt is required" }));
60
- return null;
61
- }
62
-
63
- const isAuth = callbacks.isAuthenticated();
64
- if (typeof __DEV__ !== "undefined" && __DEV__) {
65
-
66
- console.log("[TextToImage] isAuthenticated:", isAuth);
67
- }
68
-
69
- if (!isAuth) {
86
+ callbacks.onSuccess?.(imageUrls);
87
+ onPromptCleared?.();
88
+ },
89
+ onError: (err) => {
70
90
  if (typeof __DEV__ !== "undefined" && __DEV__) {
71
-
72
- console.log("[TextToImage] Auth required - calling onAuthRequired");
91
+ console.log("[TextToImage] Error:", err.message);
73
92
  }
74
- callbacks.onAuthRequired?.();
75
- return null;
76
- }
93
+ callbacks.onError?.(err.message);
94
+ },
95
+ });
77
96
 
78
- const affordable = callbacks.canAfford(totalCost);
79
- if (typeof __DEV__ !== "undefined" && __DEV__) {
80
-
81
- console.log("[TextToImage] canAfford:", affordable, "totalCost:", totalCost);
82
- }
97
+ const handleGenerate = useCallback(async (): Promise<TextToImageGenerationResult | null> => {
98
+ const trimmedPrompt = formState.prompt.trim();
83
99
 
84
- if (!affordable) {
100
+ if (!trimmedPrompt) {
85
101
  if (typeof __DEV__ !== "undefined" && __DEV__) {
86
-
87
- console.log("[TextToImage] Credits required - calling onCreditsRequired");
102
+ console.log("[TextToImage] No prompt provided");
88
103
  }
89
- callbacks.onCreditsRequired?.(totalCost);
90
- return null;
104
+ callbacks.onError?.("Prompt is required");
105
+ return { success: false, error: "Prompt is required" };
91
106
  }
92
107
 
93
- if (typeof __DEV__ !== "undefined" && __DEV__) {
94
-
95
- console.log("[TextToImage] Starting generation...");
96
- }
97
- setGenerationState({ isGenerating: true, progress: 0, error: null });
98
-
99
108
  const request: TextToImageGenerationRequest = {
100
109
  prompt: trimmedPrompt,
101
110
  model: formState.selectedModel ?? undefined,
@@ -109,51 +118,22 @@ export function useGeneration(options: UseGenerationOptions): UseGenerationRetur
109
118
  };
110
119
 
111
120
  if (typeof __DEV__ !== "undefined" && __DEV__) {
112
-
113
- console.log("[TextToImage] Request:", JSON.stringify(request, null, 2));
114
- }
115
-
116
- try {
117
- const result = await callbacks.executeGeneration(request);
118
- if (typeof __DEV__ !== "undefined" && __DEV__) {
119
- const logResult = {
120
- success: result.success,
121
- imageCount: result.success ? result.imageUrls?.length : 0,
122
- error: result.success === false ? result.error : undefined,
123
- };
124
-
125
- console.log("[TextToImage] Result:", JSON.stringify(logResult));
126
- }
127
-
128
- if (result.success === true) {
129
- if (typeof __DEV__ !== "undefined" && __DEV__) {
130
-
131
- console.log("[TextToImage] Success! Generated", result.imageUrls?.length, "image(s)");
132
- }
133
- callbacks.onSuccess?.(result.imageUrls);
134
- onPromptCleared?.();
135
- setGenerationState({ isGenerating: false, progress: 100, error: null });
136
- } else {
137
- if (typeof __DEV__ !== "undefined" && __DEV__) {
138
-
139
- console.log("[TextToImage] Generation failed:", result.error);
140
- }
141
- setGenerationState({ isGenerating: false, progress: 0, error: result.error });
142
- callbacks.onError?.(result.error);
143
- }
144
-
145
- return result;
146
- } catch (error) {
147
- const message = error instanceof Error ? error.message : String(error);
148
- if (typeof __DEV__ !== "undefined" && __DEV__) {
149
-
150
- console.error("[TextToImage] Exception:", message);
151
- }
152
- setGenerationState({ isGenerating: false, progress: 0, error: message });
153
- callbacks.onError?.(message);
154
- return null;
121
+ console.log("[TextToImage] Starting generation...");
155
122
  }
156
- }, [formState, callbacks, totalCost, onPromptCleared]);
157
123
 
158
- return { generationState, totalCost, handleGenerate };
124
+ await generate(request);
125
+
126
+ // Return result based on orchestrator state
127
+ return null; // Result handled via callbacks
128
+ }, [formState, generate, callbacks]);
129
+
130
+ return {
131
+ generationState: {
132
+ isGenerating,
133
+ progress,
134
+ error: error?.message || null,
135
+ },
136
+ totalCost,
137
+ handleGenerate,
138
+ };
159
139
  }
@@ -20,6 +20,10 @@ export type {
20
20
  GenerationErrorType,
21
21
  AlertMessages,
22
22
  UseGenerationOrchestratorReturn,
23
+ AuthCallbacks,
24
+ ModerationCallbacks,
25
+ ModerationResult,
26
+ CreditCallbacks,
23
27
  } from "./types";
24
28
 
25
29
  export type {
@@ -1,6 +1,8 @@
1
1
  /**
2
2
  * Generation Orchestrator
3
3
  * Feature-agnostic hook for AI generation with centralized:
4
+ * - Auth checking (optional)
5
+ * - Content moderation (optional)
4
6
  * - Credit management
5
7
  * - Error handling
6
8
  * - Alert display
@@ -32,24 +34,100 @@ export const useGenerationOrchestrator = <TInput, TResult>(
32
34
  strategy: GenerationStrategy<TInput, TResult>,
33
35
  config: GenerationConfig,
34
36
  ): UseGenerationOrchestratorReturn<TInput, TResult> => {
35
- const { userId, alertMessages, onCreditsExhausted, onSuccess, onError } = config;
37
+ const { userId, alertMessages, onCreditsExhausted, onSuccess, onError, auth, moderation, credits } = config;
36
38
 
37
39
  if (typeof __DEV__ !== "undefined" && __DEV__) {
38
- console.log("[Orchestrator] Hook initialized:", { userId, hasAlertMessages: !!alertMessages });
40
+ console.log("[Orchestrator] Hook initialized:", {
41
+ userId,
42
+ hasAuth: !!auth,
43
+ hasModeration: !!moderation,
44
+ hasCreditsCallbacks: !!credits,
45
+ });
39
46
  }
40
47
 
41
48
  const [state, setState] = useState<GenerationState<TResult>>(INITIAL_STATE);
42
49
  const isGeneratingRef = useRef(false);
50
+ const pendingInputRef = useRef<TInput | null>(null);
43
51
  const offlineStore = useOfflineStore();
44
52
  const { showError, showSuccess } = useAlert();
45
- const { checkCredits, deductCredit } = useDeductCredit({ userId, onCreditsExhausted });
53
+ const defaultCredits = useDeductCredit({ userId, onCreditsExhausted });
54
+
55
+ // Use provided credit callbacks or default to useDeductCredit hook
56
+ const checkCredits = credits?.checkCredits ?? defaultCredits.checkCredits;
57
+ const deductCredit = credits?.deductCredits ?? defaultCredits.deductCredit;
58
+ const handleCreditsExhausted = credits?.onCreditsExhausted ?? onCreditsExhausted;
59
+
60
+ // Core execution logic (after all checks pass)
61
+ const executeGeneration = useCallback(
62
+ async (input: TInput) => {
63
+ const creditCost = strategy.getCreditCost();
64
+
65
+ setState((prev) => ({ ...prev, status: "generating", progress: 10 }));
66
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
67
+ console.log("[Orchestrator] 🎨 Starting strategy.execute()");
68
+ }
69
+
70
+ const result = await strategy.execute(input, (progress) => {
71
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
72
+ console.log("[Orchestrator] 📊 Progress update:", progress);
73
+ }
74
+ setState((prev) => ({ ...prev, progress }));
75
+ });
76
+
77
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
78
+ console.log("[Orchestrator] ✅ strategy.execute() completed");
79
+ }
80
+
81
+ setState((prev) => ({ ...prev, progress: 70 }));
82
+
83
+ // Save result
84
+ if (strategy.save && userId) {
85
+ setState((prev) => ({ ...prev, status: "saving" }));
86
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
87
+ console.log("[Orchestrator] 💾 Saving result...");
88
+ }
89
+ try {
90
+ await strategy.save(result, userId);
91
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
92
+ console.log("[Orchestrator] ✅ Save completed");
93
+ }
94
+ } catch (saveErr) {
95
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
96
+ console.log("[Orchestrator] ❌ Save failed:", saveErr);
97
+ }
98
+ throw createGenerationError("save", "Failed to save", saveErr instanceof Error ? saveErr : undefined);
99
+ }
100
+ }
101
+
102
+ setState((prev) => ({ ...prev, progress: 90 }));
103
+
104
+ // Deduct credit
105
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
106
+ console.log("[Orchestrator] 💳 Deducting credit:", creditCost);
107
+ }
108
+ await deductCredit(creditCost);
109
+
110
+ // Success
111
+ setState({ status: "success", isGenerating: false, progress: 100, result, error: null });
112
+
113
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
114
+ console.log("[Orchestrator] 🎉 Generation SUCCESS");
115
+ }
116
+
117
+ if (alertMessages.success) {
118
+ void showSuccess("Success", alertMessages.success);
119
+ }
120
+
121
+ onSuccess?.(result);
122
+ },
123
+ [strategy, userId, alertMessages, deductCredit, showSuccess, onSuccess],
124
+ );
46
125
 
47
126
  const generate = useCallback(
48
127
  async (input: TInput) => {
49
128
  if (typeof __DEV__ !== "undefined" && __DEV__) {
50
129
  console.log("[Orchestrator] 🚀 generate() called");
51
130
  console.log("[Orchestrator] Input:", JSON.stringify(input, null, 2).slice(0, 500));
52
- console.log("[Orchestrator] isGeneratingRef:", isGeneratingRef.current);
53
131
  }
54
132
 
55
133
  if (isGeneratingRef.current) {
@@ -60,149 +138,141 @@ export const useGenerationOrchestrator = <TInput, TResult>(
60
138
  }
61
139
 
62
140
  isGeneratingRef.current = true;
141
+ pendingInputRef.current = input;
63
142
  setState({ ...INITIAL_STATE, status: "checking", isGenerating: true });
64
143
 
65
- if (typeof __DEV__ !== "undefined" && __DEV__) {
66
- console.log("[Orchestrator] State set to: checking, isGenerating: true");
67
- }
68
-
69
144
  try {
70
- // Network check
145
+ // 1. Auth check (optional)
146
+ if (auth) {
147
+ setState((prev) => ({ ...prev, status: "authenticating" }));
148
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
149
+ console.log("[Orchestrator] 🔐 Checking authentication...");
150
+ }
151
+
152
+ if (!auth.isAuthenticated()) {
153
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
154
+ console.log("[Orchestrator] ❌ Not authenticated");
155
+ }
156
+ isGeneratingRef.current = false;
157
+ setState(INITIAL_STATE);
158
+ auth.onAuthRequired?.();
159
+ return;
160
+ }
161
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
162
+ console.log("[Orchestrator] ✅ Authentication passed");
163
+ }
164
+ }
165
+
166
+ // 2. Network check
71
167
  if (!offlineStore.isOnline) {
72
168
  if (typeof __DEV__ !== "undefined" && __DEV__) {
73
169
  console.log("[Orchestrator] ❌ Network check failed - offline");
74
170
  }
75
171
  throw createGenerationError("network", "No internet connection");
76
172
  }
77
-
78
173
  if (typeof __DEV__ !== "undefined" && __DEV__) {
79
174
  console.log("[Orchestrator] ✅ Network check passed");
80
175
  }
81
176
 
82
- // Credit check
177
+ // 3. Credit check
83
178
  const creditCost = strategy.getCreditCost();
84
179
  if (typeof __DEV__ !== "undefined" && __DEV__) {
85
180
  console.log("[Orchestrator] 💳 Credit cost:", creditCost);
86
181
  }
87
182
 
88
183
  const hasCredits = await checkCredits(creditCost);
89
- if (typeof __DEV__ !== "undefined" && __DEV__) {
90
- console.log("[Orchestrator] 💳 Has credits:", hasCredits);
91
- }
92
-
93
184
  if (!hasCredits) {
94
185
  if (typeof __DEV__ !== "undefined" && __DEV__) {
95
186
  console.log("[Orchestrator] ❌ No credits, opening paywall");
96
187
  }
97
188
  isGeneratingRef.current = false;
98
189
  setState(INITIAL_STATE);
99
- onCreditsExhausted?.();
190
+ handleCreditsExhausted?.();
100
191
  return;
101
192
  }
102
-
103
- // Start generation
104
- setState((prev) => ({ ...prev, status: "generating", progress: 10 }));
105
193
  if (typeof __DEV__ !== "undefined" && __DEV__) {
106
- console.log("[Orchestrator] 🎨 Starting strategy.execute()");
194
+ console.log("[Orchestrator] Credit check passed");
107
195
  }
108
196
 
109
- const result = await strategy.execute(input, (progress) => {
197
+ // 4. Content moderation (optional)
198
+ if (moderation) {
199
+ setState((prev) => ({ ...prev, status: "moderating" }));
110
200
  if (typeof __DEV__ !== "undefined" && __DEV__) {
111
- console.log("[Orchestrator] 📊 Progress update:", progress);
201
+ console.log("[Orchestrator] 🛡️ Checking content moderation...");
112
202
  }
113
- setState((prev) => ({ ...prev, progress }));
114
- });
115
203
 
116
- if (typeof __DEV__ !== "undefined" && __DEV__) {
117
- console.log("[Orchestrator] strategy.execute() completed");
118
- console.log("[Orchestrator] Result type:", typeof result);
119
- }
120
-
121
- setState((prev) => ({ ...prev, progress: 70 }));
122
-
123
- // Save result
124
- if (strategy.save && userId) {
125
- setState((prev) => ({ ...prev, status: "saving" }));
126
- if (typeof __DEV__ !== "undefined" && __DEV__) {
127
- console.log("[Orchestrator] 💾 Saving result...");
128
- }
129
- try {
130
- await strategy.save(result, userId);
204
+ const moderationResult = await moderation.checkContent(input);
205
+ if (!moderationResult.allowed && moderationResult.warnings.length > 0) {
131
206
  if (typeof __DEV__ !== "undefined" && __DEV__) {
132
- console.log("[Orchestrator] Save completed");
207
+ console.log("[Orchestrator] ⚠️ Moderation warnings:", moderationResult.warnings);
133
208
  }
134
- } catch (saveErr) {
135
- if (typeof __DEV__ !== "undefined" && __DEV__) {
136
- console.log("[Orchestrator] Save failed:", saveErr);
209
+
210
+ if (moderation.onShowWarning) {
211
+ // Show warning and let user decide
212
+ moderation.onShowWarning(
213
+ moderationResult.warnings,
214
+ () => {
215
+ // User cancelled
216
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
217
+ console.log("[Orchestrator] User cancelled after moderation warning");
218
+ }
219
+ isGeneratingRef.current = false;
220
+ setState(INITIAL_STATE);
221
+ },
222
+ async () => {
223
+ // User continued - execute generation
224
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
225
+ console.log("[Orchestrator] User continued after moderation warning");
226
+ }
227
+ try {
228
+ await executeGeneration(input);
229
+ } catch (err) {
230
+ const error = parseError(err);
231
+ setState({ status: "error", isGenerating: false, progress: 0, result: null, error });
232
+ void showError("Error", getAlertMessage(error, alertMessages));
233
+ onError?.(error);
234
+ } finally {
235
+ isGeneratingRef.current = false;
236
+ }
237
+ },
238
+ );
239
+ return; // Exit here - callback will handle the rest
137
240
  }
138
- throw createGenerationError(
139
- "save",
140
- "Failed to save",
141
- saveErr instanceof Error ? saveErr : undefined,
142
- );
241
+ // No warning handler - block the request
242
+ throw createGenerationError("policy", "Content policy violation");
243
+ }
244
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
245
+ console.log("[Orchestrator] ✅ Moderation passed");
143
246
  }
144
247
  }
145
248
 
146
- setState((prev) => ({ ...prev, progress: 90 }));
147
-
148
- // Deduct credit
149
- if (typeof __DEV__ !== "undefined" && __DEV__) {
150
- console.log("[Orchestrator] 💳 Deducting credit:", creditCost);
151
- }
152
- await deductCredit(creditCost);
153
-
154
- // Success
155
- setState({
156
- status: "success",
157
- isGenerating: false,
158
- progress: 100,
159
- result,
160
- error: null,
161
- });
162
-
163
- if (typeof __DEV__ !== "undefined" && __DEV__) {
164
- console.log("[Orchestrator] 🎉 Generation SUCCESS");
165
- }
166
-
167
- if (alertMessages.success) {
168
- void showSuccess("Success", alertMessages.success);
169
- }
170
-
171
- onSuccess?.(result);
249
+ // 5. Execute generation
250
+ await executeGeneration(input);
172
251
  } catch (err) {
173
252
  const error = parseError(err);
174
-
175
253
  if (typeof __DEV__ !== "undefined" && __DEV__) {
176
254
  console.log("[Orchestrator] ❌ Generation ERROR:", error);
177
255
  }
178
-
179
- setState({
180
- status: "error",
181
- isGenerating: false,
182
- progress: 0,
183
- result: null,
184
- error,
185
- });
186
-
256
+ setState({ status: "error", isGenerating: false, progress: 0, result: null, error });
187
257
  void showError("Error", getAlertMessage(error, alertMessages));
188
258
  onError?.(error);
189
259
  } finally {
190
260
  isGeneratingRef.current = false;
191
261
  if (typeof __DEV__ !== "undefined" && __DEV__) {
192
- console.log("[Orchestrator] 🏁 generate() finished, isGeneratingRef reset to false");
262
+ console.log("[Orchestrator] 🏁 generate() finished");
193
263
  }
194
264
  }
195
265
  },
196
266
  [
267
+ auth,
268
+ moderation,
197
269
  strategy,
198
- userId,
199
270
  alertMessages,
200
271
  offlineStore.isOnline,
201
272
  checkCredits,
202
- deductCredit,
273
+ handleCreditsExhausted,
274
+ executeGeneration,
203
275
  showError,
204
- showSuccess,
205
- onSuccess,
206
276
  onError,
207
277
  ],
208
278
  );
@@ -6,6 +6,8 @@
6
6
  export type OrchestratorStatus =
7
7
  | "idle"
8
8
  | "checking"
9
+ | "authenticating"
10
+ | "moderating"
9
11
  | "generating"
10
12
  | "saving"
11
13
  | "success"
@@ -30,6 +32,43 @@ export interface AlertMessages {
30
32
  creditFailed: string;
31
33
  unknown: string;
32
34
  success?: string;
35
+ authRequired?: string;
36
+ }
37
+
38
+ /** Content moderation result */
39
+ export interface ModerationResult {
40
+ allowed: boolean;
41
+ warnings: string[];
42
+ }
43
+
44
+ /** Auth callbacks for features that need authentication */
45
+ export interface AuthCallbacks {
46
+ /** Check if user is authenticated */
47
+ isAuthenticated: () => boolean;
48
+ /** Called when auth is required */
49
+ onAuthRequired?: () => void;
50
+ }
51
+
52
+ /** Moderation callbacks for content filtering */
53
+ export interface ModerationCallbacks {
54
+ /** Check content for policy violations */
55
+ checkContent: (input: unknown) => Promise<ModerationResult>;
56
+ /** Show moderation warning with continue/cancel options */
57
+ onShowWarning?: (
58
+ warnings: string[],
59
+ onCancel: () => void,
60
+ onContinue: () => void,
61
+ ) => void;
62
+ }
63
+
64
+ /** Credit callbacks for features that manage credits via callbacks */
65
+ export interface CreditCallbacks {
66
+ /** Check if user can afford the cost */
67
+ checkCredits: (cost: number) => Promise<boolean>;
68
+ /** Deduct credits after successful generation */
69
+ deductCredits: (cost: number) => Promise<void>;
70
+ /** Called when credits are exhausted */
71
+ onCreditsExhausted?: () => void;
33
72
  }
34
73
 
35
74
  export interface GenerationConfig {
@@ -38,6 +77,12 @@ export interface GenerationConfig {
38
77
  onCreditsExhausted?: () => void;
39
78
  onSuccess?: (result: unknown) => void;
40
79
  onError?: (error: GenerationError) => void;
80
+ /** Optional auth callbacks - if not provided, auth is skipped */
81
+ auth?: AuthCallbacks;
82
+ /** Optional moderation callbacks - if not provided, moderation is skipped */
83
+ moderation?: ModerationCallbacks;
84
+ /** Optional credit callbacks - if provided, overrides default useDeductCredit */
85
+ credits?: CreditCallbacks;
41
86
  }
42
87
 
43
88
  export interface GenerationState<TResult> {
@@ -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,141 +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
- declare const __DEV__: boolean;
18
-
19
- export interface CoupleFutureConfig<TResult> {
20
- userId: string | undefined;
21
- processResult: (imageUrl: string, input: CoupleFutureInput) => TResult;
22
- buildCreation?: (result: TResult, input: CoupleFutureInput) => Creation | null;
23
- onCreditsExhausted?: () => void;
24
- onSuccess?: (result: TResult) => void;
25
- onError?: (error: string) => void;
26
- alertMessages: AlertMessages;
27
- }
28
-
29
- export const useCoupleFutureGeneration = <TResult>(config: CoupleFutureConfig<TResult>) => {
30
- const { userId, processResult, buildCreation, onCreditsExhausted, onSuccess, onError, alertMessages } = config;
31
-
32
- if (typeof __DEV__ !== "undefined" && __DEV__) {
33
- console.log("[CoupleFutureGeneration] Hook initialized:", {
34
- userId,
35
- hasBuildCreation: !!buildCreation,
36
- hasOnSuccess: !!onSuccess,
37
- });
38
- }
39
-
40
- const repository = useMemo(() => createCreationsRepository("creations"), []);
41
- const lastInputRef = useRef<CoupleFutureInput | null>(null);
42
-
43
- const strategy: GenerationStrategy<CoupleFutureInput, TResult> = useMemo(
44
- () => ({
45
- execute: async (input, onProgress) => {
46
- if (typeof __DEV__ !== "undefined" && __DEV__) {
47
- console.log("[CoupleFutureGeneration] 🎯 strategy.execute() START");
48
- console.log("[CoupleFutureGeneration] Input prompt:", input.prompt?.slice(0, 200));
49
- console.log("[CoupleFutureGeneration] Has partnerA:", !!input.partnerABase64);
50
- console.log("[CoupleFutureGeneration] Has partnerB:", !!input.partnerBBase64);
51
- }
52
-
53
- lastInputRef.current = input;
54
-
55
- if (typeof __DEV__ !== "undefined" && __DEV__) {
56
- console.log("[CoupleFutureGeneration] 📡 Calling executeCoupleFuture()...");
57
- }
58
-
59
- const result = await executeCoupleFuture(
60
- {
61
- partnerABase64: input.partnerABase64,
62
- partnerBBase64: input.partnerBBase64,
63
- prompt: input.prompt,
64
- },
65
- { onProgress },
66
- );
67
-
68
- if (typeof __DEV__ !== "undefined" && __DEV__) {
69
- console.log("[CoupleFutureGeneration] 📡 executeCoupleFuture() returned:", {
70
- success: result.success,
71
- hasImageUrl: !!result.imageUrl,
72
- error: result.error,
73
- });
74
- }
75
-
76
- if (!result.success || !result.imageUrl) {
77
- if (typeof __DEV__ !== "undefined" && __DEV__) {
78
- console.log("[CoupleFutureGeneration] ❌ Generation failed:", result.error);
79
- }
80
- throw new Error(result.error || "Generation failed");
81
- }
82
-
83
- if (typeof __DEV__ !== "undefined" && __DEV__) {
84
- console.log("[CoupleFutureGeneration] ✅ Processing result with processResult()");
85
- }
86
-
87
- return processResult(result.imageUrl, input);
88
- },
89
- getCreditCost: () => 1,
90
- save: buildCreation
91
- ? async (result, uid) => {
92
- if (typeof __DEV__ !== "undefined" && __DEV__) {
93
- console.log("[CoupleFutureGeneration] 💾 strategy.save() START");
94
- }
95
- const input = lastInputRef.current;
96
- if (input) {
97
- const creation = buildCreation(result, input);
98
- if (creation) {
99
- if (typeof __DEV__ !== "undefined" && __DEV__) {
100
- console.log("[CoupleFutureGeneration] 💾 Saving creation to repository...");
101
- }
102
- await repository.create(uid, creation);
103
- if (typeof __DEV__ !== "undefined" && __DEV__) {
104
- console.log("[CoupleFutureGeneration] ✅ Creation saved");
105
- }
106
- }
107
- }
108
- }
109
- : undefined,
110
- }),
111
- [processResult, buildCreation, repository],
112
- );
113
-
114
- const handleError = useCallback(
115
- (error: { message: string }) => {
116
- if (typeof __DEV__ !== "undefined" && __DEV__) {
117
- console.log("[CoupleFutureGeneration] ❌ handleError:", error.message);
118
- }
119
- onError?.(error.message);
120
- },
121
- [onError],
122
- );
123
-
124
- const handleSuccess = useCallback(
125
- (result: unknown) => {
126
- if (typeof __DEV__ !== "undefined" && __DEV__) {
127
- console.log("[CoupleFutureGeneration] 🎉 handleSuccess called");
128
- }
129
- onSuccess?.(result as TResult);
130
- },
131
- [onSuccess],
132
- );
133
-
134
- return useGenerationOrchestrator(strategy, {
135
- userId,
136
- alertMessages,
137
- onCreditsExhausted,
138
- onSuccess: handleSuccess,
139
- onError: handleError,
140
- });
141
- };