@umituz/react-native-ai-generation-content 1.26.68 → 1.26.70
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/domains/generation/wizard/presentation/components/GenericWizardFlow.tsx +4 -20
- package/src/domains/generation/wizard/presentation/hooks/useWizardGeneration.ts +8 -40
- package/src/presentation/hooks/generation/orchestrator.ts +31 -185
- package/src/presentation/hooks/generation/types.ts +4 -61
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-ai-generation-content",
|
|
3
|
-
"version": "1.26.
|
|
3
|
+
"version": "1.26.70",
|
|
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",
|
|
@@ -1,15 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Generic Wizard Flow Component
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* Works for:
|
|
6
|
-
* - Couple features (romantic-kiss, ai-hug, etc.)
|
|
7
|
-
* - Face swap
|
|
8
|
-
* - Image-to-video
|
|
9
|
-
* - Text-to-video
|
|
10
|
-
* - ANY future feature!
|
|
11
|
-
*
|
|
12
|
-
* NO feature-specific code here - everything driven by configuration!
|
|
3
|
+
* Config-driven wizard for AI generation features
|
|
13
4
|
*/
|
|
14
5
|
|
|
15
6
|
import React, { useMemo, useCallback, useEffect, useRef, useState } from "react";
|
|
@@ -28,6 +19,8 @@ import { validateScenario } from "../utilities/validateScenario";
|
|
|
28
19
|
import { WizardStepRenderer } from "./WizardStepRenderer";
|
|
29
20
|
import { StarRatingPicker } from "../../../../result-preview/presentation/components/StarRatingPicker";
|
|
30
21
|
|
|
22
|
+
declare const __DEV__: boolean;
|
|
23
|
+
|
|
31
24
|
export interface GenericWizardFlowProps {
|
|
32
25
|
readonly featureConfig: WizardFeatureConfig;
|
|
33
26
|
readonly scenario?: WizardScenarioData;
|
|
@@ -38,9 +31,6 @@ export interface GenericWizardFlowProps {
|
|
|
38
31
|
readonly onGenerationComplete?: (result: unknown) => void;
|
|
39
32
|
readonly onGenerationError?: (error: string) => void;
|
|
40
33
|
readonly onCreditsExhausted?: () => void;
|
|
41
|
-
/** Auth check - orchestrator will check auth BEFORE credits */
|
|
42
|
-
readonly isAuthenticated?: () => boolean;
|
|
43
|
-
readonly onAuthRequired?: () => void;
|
|
44
34
|
readonly onRate?: (creationId: string, rating: number, description: string) => Promise<boolean>;
|
|
45
35
|
readonly onBack?: () => void;
|
|
46
36
|
readonly onTryAgain?: () => void;
|
|
@@ -61,8 +51,6 @@ export const GenericWizardFlow: React.FC<GenericWizardFlowProps> = ({
|
|
|
61
51
|
onGenerationComplete,
|
|
62
52
|
onGenerationError,
|
|
63
53
|
onCreditsExhausted,
|
|
64
|
-
isAuthenticated,
|
|
65
|
-
onAuthRequired,
|
|
66
54
|
onRate,
|
|
67
55
|
onBack,
|
|
68
56
|
onTryAgain,
|
|
@@ -130,8 +118,6 @@ export const GenericWizardFlow: React.FC<GenericWizardFlowProps> = ({
|
|
|
130
118
|
onError: onGenerationError,
|
|
131
119
|
onProgressChange: handleProgressChange,
|
|
132
120
|
onCreditsExhausted,
|
|
133
|
-
isAuthenticated,
|
|
134
|
-
onAuthRequired,
|
|
135
121
|
});
|
|
136
122
|
|
|
137
123
|
useEffect(() => {
|
|
@@ -174,9 +160,7 @@ export const GenericWizardFlow: React.FC<GenericWizardFlowProps> = ({
|
|
|
174
160
|
const handleSubmitRating = useCallback(async (rating: number, description: string) => {
|
|
175
161
|
if (!currentCreation?.id || !onRate) return;
|
|
176
162
|
const success = await onRate(currentCreation.id, rating, description);
|
|
177
|
-
if (success)
|
|
178
|
-
setHasRated(true);
|
|
179
|
-
}
|
|
163
|
+
if (success) setHasRated(true);
|
|
180
164
|
setShowRatingPicker(false);
|
|
181
165
|
}, [currentCreation, onRate]);
|
|
182
166
|
|
|
@@ -10,17 +10,13 @@ import { createWizardStrategy, buildWizardInput } from "../../infrastructure/str
|
|
|
10
10
|
|
|
11
11
|
declare const __DEV__: boolean;
|
|
12
12
|
|
|
13
|
-
// ============================================================================
|
|
14
|
-
// Types
|
|
15
|
-
// ============================================================================
|
|
16
|
-
|
|
17
13
|
export type WizardOutputType = "image" | "video";
|
|
18
14
|
|
|
19
15
|
export interface WizardScenarioData {
|
|
20
16
|
readonly id: string;
|
|
21
17
|
readonly aiPrompt: string;
|
|
22
18
|
readonly outputType?: WizardOutputType;
|
|
23
|
-
readonly model?: string;
|
|
19
|
+
readonly model?: string;
|
|
24
20
|
readonly title?: string;
|
|
25
21
|
readonly description?: string;
|
|
26
22
|
[key: string]: unknown;
|
|
@@ -36,9 +32,6 @@ export interface UseWizardGenerationProps {
|
|
|
36
32
|
readonly onError?: (error: string) => void;
|
|
37
33
|
readonly onProgressChange?: (progress: number) => void;
|
|
38
34
|
readonly onCreditsExhausted?: () => void;
|
|
39
|
-
/** Auth check - if user not authenticated, onAuthRequired will be called */
|
|
40
|
-
readonly isAuthenticated?: () => boolean;
|
|
41
|
-
readonly onAuthRequired?: () => void;
|
|
42
35
|
}
|
|
43
36
|
|
|
44
37
|
export interface UseWizardGenerationReturn {
|
|
@@ -46,10 +39,6 @@ export interface UseWizardGenerationReturn {
|
|
|
46
39
|
readonly progress: number;
|
|
47
40
|
}
|
|
48
41
|
|
|
49
|
-
// ============================================================================
|
|
50
|
-
// Hook
|
|
51
|
-
// ============================================================================
|
|
52
|
-
|
|
53
42
|
export const useWizardGeneration = (
|
|
54
43
|
props: UseWizardGenerationProps,
|
|
55
44
|
): UseWizardGenerationReturn => {
|
|
@@ -63,24 +52,19 @@ export const useWizardGeneration = (
|
|
|
63
52
|
onError,
|
|
64
53
|
onProgressChange,
|
|
65
54
|
onCreditsExhausted,
|
|
66
|
-
isAuthenticated,
|
|
67
|
-
onAuthRequired,
|
|
68
55
|
} = props;
|
|
69
56
|
|
|
70
57
|
const hasStarted = useRef(false);
|
|
71
58
|
|
|
72
|
-
// Log output type on mount
|
|
73
59
|
useEffect(() => {
|
|
74
60
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
75
61
|
console.log("[useWizardGeneration] Initialized", {
|
|
76
62
|
scenarioId: scenario.id,
|
|
77
63
|
outputType: scenario.outputType || "video",
|
|
78
|
-
hasOutputType: !!scenario.outputType,
|
|
79
64
|
});
|
|
80
65
|
}
|
|
81
66
|
}, [scenario.id, scenario.outputType]);
|
|
82
67
|
|
|
83
|
-
// Create strategy using factory
|
|
84
68
|
const strategy = useMemo(() => {
|
|
85
69
|
return createWizardStrategy({
|
|
86
70
|
scenario,
|
|
@@ -89,7 +73,6 @@ export const useWizardGeneration = (
|
|
|
89
73
|
});
|
|
90
74
|
}, [scenario, wizardData]);
|
|
91
75
|
|
|
92
|
-
// Use orchestrator with strategy
|
|
93
76
|
const { generate, isGenerating, progress } = useGenerationOrchestrator(
|
|
94
77
|
strategy,
|
|
95
78
|
{
|
|
@@ -102,10 +85,6 @@ export const useWizardGeneration = (
|
|
|
102
85
|
unknown: "An error occurred",
|
|
103
86
|
},
|
|
104
87
|
onCreditsExhausted,
|
|
105
|
-
// Auth config - check auth BEFORE credits
|
|
106
|
-
auth: isAuthenticated && onAuthRequired
|
|
107
|
-
? { isAuthenticated, onAuthRequired }
|
|
108
|
-
: undefined,
|
|
109
88
|
onSuccess: (result) => {
|
|
110
89
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
111
90
|
console.log("[useWizardGeneration] Success");
|
|
@@ -121,14 +100,10 @@ export const useWizardGeneration = (
|
|
|
121
100
|
},
|
|
122
101
|
);
|
|
123
102
|
|
|
124
|
-
// Sync progress to parent
|
|
125
103
|
useEffect(() => {
|
|
126
|
-
if (onProgressChange)
|
|
127
|
-
onProgressChange(progress);
|
|
128
|
-
}
|
|
104
|
+
if (onProgressChange) onProgressChange(progress);
|
|
129
105
|
}, [progress, onProgressChange]);
|
|
130
106
|
|
|
131
|
-
// Auto-start generation when entering generating step
|
|
132
107
|
useEffect(() => {
|
|
133
108
|
if (isGeneratingStep && !hasStarted.current && !isGenerating) {
|
|
134
109
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
@@ -138,37 +113,30 @@ export const useWizardGeneration = (
|
|
|
138
113
|
});
|
|
139
114
|
}
|
|
140
115
|
|
|
141
|
-
|
|
116
|
+
hasStarted.current = true;
|
|
117
|
+
|
|
142
118
|
buildWizardInput(wizardData, scenario)
|
|
143
119
|
.then((input) => {
|
|
144
120
|
if (!input) {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
console.error("[useWizardGeneration]", error);
|
|
148
|
-
}
|
|
149
|
-
onError?.(error);
|
|
121
|
+
hasStarted.current = false;
|
|
122
|
+
onError?.("Failed to build generation input");
|
|
150
123
|
return;
|
|
151
124
|
}
|
|
152
|
-
|
|
153
125
|
generate(input);
|
|
154
|
-
hasStarted.current = true;
|
|
155
126
|
})
|
|
156
127
|
.catch((error) => {
|
|
157
128
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
158
129
|
console.error("[useWizardGeneration] Input build error:", error);
|
|
159
130
|
}
|
|
131
|
+
hasStarted.current = false;
|
|
160
132
|
onError?.(error.message || "Failed to prepare generation");
|
|
161
133
|
});
|
|
162
134
|
}
|
|
163
135
|
|
|
164
|
-
// Reset hasStarted when leaving generating step
|
|
165
136
|
if (!isGeneratingStep && hasStarted.current) {
|
|
166
137
|
hasStarted.current = false;
|
|
167
138
|
}
|
|
168
139
|
}, [isGeneratingStep, scenario, wizardData, isGenerating, generate, onError]);
|
|
169
140
|
|
|
170
|
-
return {
|
|
171
|
-
isGenerating,
|
|
172
|
-
progress,
|
|
173
|
-
};
|
|
141
|
+
return { isGenerating, progress };
|
|
174
142
|
};
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Generation Orchestrator
|
|
3
|
-
* Feature-agnostic hook for AI generation with
|
|
4
|
-
* -
|
|
3
|
+
* Feature-agnostic hook for AI generation with:
|
|
4
|
+
* - Network check (via design system's useOfflineStore)
|
|
5
5
|
* - Content moderation (optional)
|
|
6
|
-
* - Credit management
|
|
7
|
-
* - Error handling
|
|
8
|
-
* - Alert display
|
|
6
|
+
* - Credit management (via subscription package)
|
|
7
|
+
* - Error handling & alerts
|
|
9
8
|
* - Progress tracking
|
|
10
|
-
* - Lifecycle management
|
|
9
|
+
* - Lifecycle management
|
|
10
|
+
*
|
|
11
|
+
* NOTE: Auth is handled by useFeatureGate before generation starts
|
|
11
12
|
*/
|
|
12
13
|
|
|
13
14
|
import { useState, useCallback, useRef, useEffect } from "react";
|
|
@@ -45,7 +46,6 @@ export const useGenerationOrchestrator = <TInput, TResult>(
|
|
|
45
46
|
onCreditsExhausted,
|
|
46
47
|
onSuccess,
|
|
47
48
|
onError,
|
|
48
|
-
auth,
|
|
49
49
|
moderation,
|
|
50
50
|
credits,
|
|
51
51
|
lifecycle,
|
|
@@ -54,7 +54,6 @@ export const useGenerationOrchestrator = <TInput, TResult>(
|
|
|
54
54
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
55
55
|
console.log("[Orchestrator] 🎬 Hook initialized:", {
|
|
56
56
|
userId,
|
|
57
|
-
hasAuth: !!auth,
|
|
58
57
|
hasModeration: !!moderation,
|
|
59
58
|
hasCreditsCallbacks: !!credits,
|
|
60
59
|
hasLifecycle: !!lifecycle,
|
|
@@ -72,84 +71,48 @@ export const useGenerationOrchestrator = <TInput, TResult>(
|
|
|
72
71
|
const { showError, showSuccess } = useAlert();
|
|
73
72
|
const creditHook = useDeductCredit({ userId, onCreditsExhausted });
|
|
74
73
|
|
|
75
|
-
// Wrap credit hook to match expected interface
|
|
76
74
|
const defaultCredits = {
|
|
77
75
|
checkCredits: creditHook.checkCredits,
|
|
78
76
|
deductCredit: async (amount: number): Promise<boolean> => {
|
|
79
77
|
return creditHook.deductCredit(amount);
|
|
80
|
-
}
|
|
78
|
+
},
|
|
81
79
|
};
|
|
82
80
|
|
|
83
|
-
// Use provided credit callbacks or default to useDeductCredit hook
|
|
84
81
|
const checkCredits = credits?.checkCredits ?? defaultCredits.checkCredits;
|
|
85
82
|
const deductCredit = credits?.deductCredits ?? defaultCredits.deductCredit;
|
|
86
83
|
const handleCreditsExhausted = credits?.onCreditsExhausted ?? onCreditsExhausted;
|
|
87
84
|
|
|
88
|
-
// Cleanup on unmount
|
|
89
85
|
useEffect(() => {
|
|
90
86
|
isMountedRef.current = true;
|
|
91
87
|
return () => {
|
|
92
88
|
isMountedRef.current = false;
|
|
93
|
-
if (completeTimeoutRef.current)
|
|
94
|
-
|
|
95
|
-
completeTimeoutRef.current = null;
|
|
96
|
-
}
|
|
97
|
-
if (resetTimeoutRef.current) {
|
|
98
|
-
clearTimeout(resetTimeoutRef.current);
|
|
99
|
-
resetTimeoutRef.current = null;
|
|
100
|
-
}
|
|
89
|
+
if (completeTimeoutRef.current) clearTimeout(completeTimeoutRef.current);
|
|
90
|
+
if (resetTimeoutRef.current) clearTimeout(resetTimeoutRef.current);
|
|
101
91
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
102
|
-
console.log("[Orchestrator] 🧹 Cleanup
|
|
92
|
+
console.log("[Orchestrator] 🧹 Cleanup");
|
|
103
93
|
}
|
|
104
94
|
};
|
|
105
95
|
}, []);
|
|
106
96
|
|
|
107
|
-
// Handle lifecycle completion
|
|
108
97
|
const handleLifecycleComplete = useCallback(
|
|
109
98
|
(status: "success" | "error", result?: TResult, error?: GenerationError) => {
|
|
110
99
|
if (!lifecycle?.onComplete) return;
|
|
111
100
|
|
|
112
101
|
const delay = lifecycle.completeDelay ?? DEFAULT_COMPLETE_DELAY;
|
|
113
102
|
|
|
114
|
-
if (
|
|
115
|
-
console.log("[Orchestrator] ⏱️ Scheduling lifecycle.onComplete:", { status, delay });
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Clear any existing timeout
|
|
119
|
-
if (completeTimeoutRef.current) {
|
|
120
|
-
clearTimeout(completeTimeoutRef.current);
|
|
121
|
-
}
|
|
103
|
+
if (completeTimeoutRef.current) clearTimeout(completeTimeoutRef.current);
|
|
122
104
|
|
|
123
105
|
completeTimeoutRef.current = setTimeout(() => {
|
|
124
|
-
if (!isMountedRef.current)
|
|
125
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
126
|
-
console.log("[Orchestrator] ⚠️ Component unmounted, skipping onComplete");
|
|
127
|
-
}
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
132
|
-
console.log("[Orchestrator] 📍 Calling lifecycle.onComplete:", status);
|
|
133
|
-
}
|
|
106
|
+
if (!isMountedRef.current) return;
|
|
134
107
|
|
|
135
108
|
lifecycle.onComplete?.(status, result, error);
|
|
136
109
|
|
|
137
|
-
// Auto-reset if configured
|
|
138
110
|
if (lifecycle.autoReset) {
|
|
139
111
|
const resetDelay = lifecycle.resetDelay ?? DEFAULT_RESET_DELAY;
|
|
140
|
-
if (
|
|
141
|
-
console.log("[Orchestrator] 🔄 Scheduling auto-reset in:", resetDelay);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (resetTimeoutRef.current) {
|
|
145
|
-
clearTimeout(resetTimeoutRef.current);
|
|
146
|
-
}
|
|
112
|
+
if (resetTimeoutRef.current) clearTimeout(resetTimeoutRef.current);
|
|
147
113
|
|
|
148
114
|
resetTimeoutRef.current = setTimeout(() => {
|
|
149
115
|
if (isMountedRef.current) {
|
|
150
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
151
|
-
console.log("[Orchestrator] 🔄 Auto-reset triggered");
|
|
152
|
-
}
|
|
153
116
|
setState(INITIAL_STATE);
|
|
154
117
|
isGeneratingRef.current = false;
|
|
155
118
|
}
|
|
@@ -160,74 +123,41 @@ export const useGenerationOrchestrator = <TInput, TResult>(
|
|
|
160
123
|
[lifecycle],
|
|
161
124
|
);
|
|
162
125
|
|
|
163
|
-
// Core execution logic (after all checks pass)
|
|
164
126
|
const executeGeneration = useCallback(
|
|
165
127
|
async (input: TInput) => {
|
|
166
128
|
const creditCost = strategy.getCreditCost();
|
|
167
129
|
|
|
168
130
|
setState((prev) => ({ ...prev, status: "generating", progress: 10 }));
|
|
169
131
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
170
|
-
console.log("[Orchestrator] 🎨 Starting
|
|
132
|
+
console.log("[Orchestrator] 🎨 Starting generation");
|
|
171
133
|
}
|
|
172
134
|
|
|
173
135
|
const result = await strategy.execute(input, (progress) => {
|
|
174
|
-
if (
|
|
175
|
-
console.log("[Orchestrator] 📊 Progress update:", progress);
|
|
176
|
-
}
|
|
177
|
-
if (isMountedRef.current) {
|
|
178
|
-
setState((prev) => ({ ...prev, progress }));
|
|
179
|
-
}
|
|
136
|
+
if (isMountedRef.current) setState((prev) => ({ ...prev, progress }));
|
|
180
137
|
});
|
|
181
138
|
|
|
182
|
-
if (
|
|
183
|
-
console.log("[Orchestrator] ✅ strategy.execute() completed");
|
|
184
|
-
}
|
|
139
|
+
if (isMountedRef.current) setState((prev) => ({ ...prev, progress: 70 }));
|
|
185
140
|
|
|
186
|
-
if (isMountedRef.current) {
|
|
187
|
-
setState((prev) => ({ ...prev, progress: 70 }));
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Save result
|
|
191
141
|
if (strategy.save && userId) {
|
|
192
|
-
if (isMountedRef.current) {
|
|
193
|
-
setState((prev) => ({ ...prev, status: "saving" }));
|
|
194
|
-
}
|
|
195
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
196
|
-
console.log("[Orchestrator] 💾 Saving result...");
|
|
197
|
-
}
|
|
142
|
+
if (isMountedRef.current) setState((prev) => ({ ...prev, status: "saving" }));
|
|
198
143
|
try {
|
|
199
144
|
await strategy.save(result, userId);
|
|
200
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
201
|
-
console.log("[Orchestrator] ✅ Save completed");
|
|
202
|
-
}
|
|
203
145
|
} catch (saveErr) {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
146
|
+
throw createGenerationError(
|
|
147
|
+
"save",
|
|
148
|
+
"Failed to save",
|
|
149
|
+
saveErr instanceof Error ? saveErr : undefined,
|
|
150
|
+
);
|
|
208
151
|
}
|
|
209
152
|
}
|
|
210
153
|
|
|
211
|
-
if (isMountedRef.current) {
|
|
212
|
-
setState((prev) => ({ ...prev, progress: 90 }));
|
|
213
|
-
}
|
|
154
|
+
if (isMountedRef.current) setState((prev) => ({ ...prev, progress: 90 }));
|
|
214
155
|
|
|
215
|
-
// Deduct credit
|
|
216
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
217
|
-
console.log("[Orchestrator] 💳 Deducting credit:", creditCost);
|
|
218
|
-
}
|
|
219
156
|
const creditDeducted = await deductCredit(creditCost);
|
|
220
157
|
if (!creditDeducted) {
|
|
221
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
222
|
-
console.log("[Orchestrator] ❌ Credit deduction failed");
|
|
223
|
-
}
|
|
224
158
|
throw createGenerationError("credits", "Failed to deduct credits");
|
|
225
159
|
}
|
|
226
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
227
|
-
console.log("[Orchestrator] ✅ Credit deducted successfully");
|
|
228
|
-
}
|
|
229
160
|
|
|
230
|
-
// Success
|
|
231
161
|
if (isMountedRef.current) {
|
|
232
162
|
setState({ status: "success", isGenerating: false, progress: 100, result, error: null });
|
|
233
163
|
}
|
|
@@ -236,14 +166,8 @@ export const useGenerationOrchestrator = <TInput, TResult>(
|
|
|
236
166
|
console.log("[Orchestrator] 🎉 Generation SUCCESS");
|
|
237
167
|
}
|
|
238
168
|
|
|
239
|
-
if (alertMessages.success)
|
|
240
|
-
void showSuccess("Success", alertMessages.success);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// Call onSuccess callback
|
|
169
|
+
if (alertMessages.success) void showSuccess("Success", alertMessages.success);
|
|
244
170
|
onSuccess?.(result);
|
|
245
|
-
|
|
246
|
-
// Handle lifecycle completion
|
|
247
171
|
handleLifecycleComplete("success", result);
|
|
248
172
|
|
|
249
173
|
return result;
|
|
@@ -255,105 +179,41 @@ export const useGenerationOrchestrator = <TInput, TResult>(
|
|
|
255
179
|
async (input: TInput) => {
|
|
256
180
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
257
181
|
console.log("[Orchestrator] 🚀 generate() called");
|
|
258
|
-
console.log("[Orchestrator] Input:", JSON.stringify(input, null, 2).slice(0, 500));
|
|
259
182
|
}
|
|
260
183
|
|
|
261
|
-
if (isGeneratingRef.current)
|
|
262
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
263
|
-
console.log("[Orchestrator] ⚠️ Already generating, skipping");
|
|
264
|
-
}
|
|
265
|
-
return;
|
|
266
|
-
}
|
|
184
|
+
if (isGeneratingRef.current) return;
|
|
267
185
|
|
|
268
186
|
isGeneratingRef.current = true;
|
|
269
187
|
pendingInputRef.current = input;
|
|
270
188
|
setState({ ...INITIAL_STATE, status: "checking", isGenerating: true });
|
|
271
189
|
|
|
272
190
|
try {
|
|
273
|
-
// 1. Auth check (optional)
|
|
274
|
-
if (auth) {
|
|
275
|
-
setState((prev) => ({ ...prev, status: "authenticating" }));
|
|
276
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
277
|
-
console.log("[Orchestrator] 🔐 Checking authentication...");
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
if (!auth.isAuthenticated()) {
|
|
281
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
282
|
-
console.log("[Orchestrator] ❌ Not authenticated");
|
|
283
|
-
}
|
|
284
|
-
isGeneratingRef.current = false;
|
|
285
|
-
setState(INITIAL_STATE);
|
|
286
|
-
auth.onAuthRequired?.();
|
|
287
|
-
return;
|
|
288
|
-
}
|
|
289
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
290
|
-
console.log("[Orchestrator] ✅ Authentication passed");
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// 2. Network check
|
|
295
191
|
if (!offlineStore.isOnline) {
|
|
296
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
297
|
-
console.log("[Orchestrator] ❌ Network check failed - offline");
|
|
298
|
-
}
|
|
299
192
|
throw createGenerationError("network", "No internet connection");
|
|
300
193
|
}
|
|
301
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
302
|
-
console.log("[Orchestrator] ✅ Network check passed");
|
|
303
|
-
}
|
|
304
194
|
|
|
305
|
-
// 3. Credit check
|
|
306
195
|
const creditCost = strategy.getCreditCost();
|
|
307
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
308
|
-
console.log("[Orchestrator] 💳 Credit cost:", creditCost);
|
|
309
|
-
}
|
|
310
|
-
|
|
311
196
|
const hasCredits = await checkCredits(creditCost);
|
|
312
197
|
if (!hasCredits) {
|
|
313
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
314
|
-
console.log("[Orchestrator] ❌ No credits, opening paywall");
|
|
315
|
-
}
|
|
316
198
|
isGeneratingRef.current = false;
|
|
317
199
|
setState(INITIAL_STATE);
|
|
318
200
|
handleCreditsExhausted?.();
|
|
319
201
|
return;
|
|
320
202
|
}
|
|
321
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
322
|
-
console.log("[Orchestrator] ✅ Credit check passed");
|
|
323
|
-
}
|
|
324
203
|
|
|
325
|
-
// 4. Content moderation (optional)
|
|
326
204
|
if (moderation) {
|
|
327
205
|
setState((prev) => ({ ...prev, status: "moderating" }));
|
|
328
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
329
|
-
console.log("[Orchestrator] 🛡️ Checking content moderation...");
|
|
330
|
-
}
|
|
331
|
-
|
|
332
206
|
const moderationResult = await moderation.checkContent(input);
|
|
333
|
-
if (!moderationResult.allowed && moderationResult.warnings.length > 0) {
|
|
334
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
335
|
-
console.log("[Orchestrator] ⚠️ Moderation warnings:", moderationResult.warnings);
|
|
336
|
-
}
|
|
337
207
|
|
|
208
|
+
if (!moderationResult.allowed && moderationResult.warnings.length > 0) {
|
|
338
209
|
if (moderation.onShowWarning) {
|
|
339
|
-
// Show warning and let user decide
|
|
340
210
|
moderation.onShowWarning(
|
|
341
211
|
moderationResult.warnings,
|
|
342
212
|
() => {
|
|
343
|
-
// User cancelled
|
|
344
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
345
|
-
console.log("[Orchestrator] User cancelled after moderation warning");
|
|
346
|
-
}
|
|
347
213
|
isGeneratingRef.current = false;
|
|
348
|
-
if (isMountedRef.current)
|
|
349
|
-
setState(INITIAL_STATE);
|
|
350
|
-
}
|
|
214
|
+
if (isMountedRef.current) setState(INITIAL_STATE);
|
|
351
215
|
},
|
|
352
216
|
async () => {
|
|
353
|
-
// User continued - execute generation
|
|
354
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
355
|
-
console.log("[Orchestrator] User continued after moderation warning");
|
|
356
|
-
}
|
|
357
217
|
try {
|
|
358
218
|
await executeGeneration(input);
|
|
359
219
|
} catch (err) {
|
|
@@ -369,41 +229,30 @@ export const useGenerationOrchestrator = <TInput, TResult>(
|
|
|
369
229
|
}
|
|
370
230
|
},
|
|
371
231
|
);
|
|
372
|
-
return;
|
|
232
|
+
return;
|
|
373
233
|
}
|
|
374
|
-
// No warning handler - block the request
|
|
375
234
|
throw createGenerationError("policy", "Content policy violation");
|
|
376
235
|
}
|
|
377
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
378
|
-
console.log("[Orchestrator] ✅ Moderation passed");
|
|
379
|
-
}
|
|
380
236
|
}
|
|
381
237
|
|
|
382
|
-
// 5. Execute generation
|
|
383
238
|
return await executeGeneration(input);
|
|
384
239
|
} catch (err) {
|
|
385
240
|
const error = parseError(err);
|
|
386
241
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
387
|
-
console.log("[Orchestrator] ❌
|
|
242
|
+
console.log("[Orchestrator] ❌ Error:", error);
|
|
388
243
|
}
|
|
389
244
|
if (isMountedRef.current) {
|
|
390
245
|
setState({ status: "error", isGenerating: false, progress: 0, result: null, error });
|
|
391
246
|
}
|
|
392
247
|
void showError("Error", getAlertMessage(error, alertMessages));
|
|
393
248
|
onError?.(error);
|
|
394
|
-
|
|
395
|
-
// Handle lifecycle completion for errors
|
|
396
249
|
handleLifecycleComplete("error", undefined, error);
|
|
397
|
-
throw error;
|
|
250
|
+
throw error;
|
|
398
251
|
} finally {
|
|
399
252
|
isGeneratingRef.current = false;
|
|
400
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
401
|
-
console.log("[Orchestrator] 🏁 generate() finished");
|
|
402
|
-
}
|
|
403
253
|
}
|
|
404
254
|
},
|
|
405
255
|
[
|
|
406
|
-
auth,
|
|
407
256
|
moderation,
|
|
408
257
|
strategy,
|
|
409
258
|
alertMessages,
|
|
@@ -418,9 +267,6 @@ export const useGenerationOrchestrator = <TInput, TResult>(
|
|
|
418
267
|
);
|
|
419
268
|
|
|
420
269
|
const reset = useCallback(() => {
|
|
421
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
422
|
-
console.log("[Orchestrator] 🔄 reset() called");
|
|
423
|
-
}
|
|
424
270
|
setState(INITIAL_STATE);
|
|
425
271
|
isGeneratingRef.current = false;
|
|
426
272
|
}, []);
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
export type OrchestratorStatus =
|
|
7
7
|
| "idle"
|
|
8
8
|
| "checking"
|
|
9
|
-
| "authenticating"
|
|
10
9
|
| "moderating"
|
|
11
10
|
| "generating"
|
|
12
11
|
| "saving"
|
|
@@ -14,14 +13,8 @@ export type OrchestratorStatus =
|
|
|
14
13
|
| "error";
|
|
15
14
|
|
|
16
15
|
export interface GenerationStrategy<TInput, TResult> {
|
|
17
|
-
|
|
18
|
-
execute: (
|
|
19
|
-
input: TInput,
|
|
20
|
-
onProgress?: (progress: number) => void,
|
|
21
|
-
) => Promise<TResult>;
|
|
22
|
-
/** Credit cost for this generation */
|
|
16
|
+
execute: (input: TInput, onProgress?: (progress: number) => void) => Promise<TResult>;
|
|
23
17
|
getCreditCost: () => number;
|
|
24
|
-
/** Optional: Save result to storage */
|
|
25
18
|
save?: (result: TResult, userId: string) => Promise<void>;
|
|
26
19
|
}
|
|
27
20
|
|
|
@@ -32,61 +25,28 @@ export interface AlertMessages {
|
|
|
32
25
|
creditFailed: string;
|
|
33
26
|
unknown: string;
|
|
34
27
|
success?: string;
|
|
35
|
-
authRequired?: string;
|
|
36
28
|
}
|
|
37
29
|
|
|
38
|
-
/** Content moderation result */
|
|
39
30
|
export interface ModerationResult {
|
|
40
31
|
allowed: boolean;
|
|
41
32
|
warnings: string[];
|
|
42
33
|
}
|
|
43
34
|
|
|
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
35
|
export interface ModerationCallbacks {
|
|
54
|
-
/** Check content for policy violations */
|
|
55
36
|
checkContent: (input: unknown) => Promise<ModerationResult>;
|
|
56
|
-
|
|
57
|
-
onShowWarning?: (
|
|
58
|
-
warnings: string[],
|
|
59
|
-
onCancel: () => void,
|
|
60
|
-
onContinue: () => void,
|
|
61
|
-
) => void;
|
|
37
|
+
onShowWarning?: (warnings: string[], onCancel: () => void, onContinue: () => void) => void;
|
|
62
38
|
}
|
|
63
39
|
|
|
64
|
-
/** Credit callbacks for features that manage credits via callbacks */
|
|
65
40
|
export interface CreditCallbacks {
|
|
66
|
-
/** Check if user can afford the cost */
|
|
67
41
|
checkCredits: (cost: number) => Promise<boolean>;
|
|
68
|
-
/** Deduct credits after successful generation - returns true if successful */
|
|
69
42
|
deductCredits: (cost: number) => Promise<boolean>;
|
|
70
|
-
/** Called when credits are exhausted */
|
|
71
43
|
onCreditsExhausted?: () => void;
|
|
72
44
|
}
|
|
73
45
|
|
|
74
|
-
/**
|
|
75
|
-
* Lifecycle configuration for generation flow
|
|
76
|
-
* Centralizes post-generation behavior (navigation, cleanup, etc.)
|
|
77
|
-
*/
|
|
78
46
|
export interface LifecycleConfig {
|
|
79
|
-
|
|
80
|
-
onComplete?: (
|
|
81
|
-
status: "success" | "error",
|
|
82
|
-
result?: unknown,
|
|
83
|
-
error?: GenerationError,
|
|
84
|
-
) => void;
|
|
85
|
-
/** Delay before calling onComplete (ms) - allows UI to show success state */
|
|
47
|
+
onComplete?: (status: "success" | "error", result?: unknown, error?: GenerationError) => void;
|
|
86
48
|
completeDelay?: number;
|
|
87
|
-
/** Auto-reset state after completion */
|
|
88
49
|
autoReset?: boolean;
|
|
89
|
-
/** Delay before auto-reset (ms) */
|
|
90
50
|
resetDelay?: number;
|
|
91
51
|
}
|
|
92
52
|
|
|
@@ -96,13 +56,8 @@ export interface GenerationConfig {
|
|
|
96
56
|
onCreditsExhausted?: () => void;
|
|
97
57
|
onSuccess?: (result: unknown) => void;
|
|
98
58
|
onError?: (error: GenerationError) => void;
|
|
99
|
-
/** Optional auth callbacks - if not provided, auth is skipped */
|
|
100
|
-
auth?: AuthCallbacks;
|
|
101
|
-
/** Optional moderation callbacks - if not provided, moderation is skipped */
|
|
102
59
|
moderation?: ModerationCallbacks;
|
|
103
|
-
/** Optional credit callbacks - if provided, overrides default useDeductCredit */
|
|
104
60
|
credits?: CreditCallbacks;
|
|
105
|
-
/** Lifecycle configuration for post-generation behavior */
|
|
106
61
|
lifecycle?: LifecycleConfig;
|
|
107
62
|
}
|
|
108
63
|
|
|
@@ -120,12 +75,7 @@ export interface GenerationError {
|
|
|
120
75
|
originalError?: Error;
|
|
121
76
|
}
|
|
122
77
|
|
|
123
|
-
export type GenerationErrorType =
|
|
124
|
-
| "network"
|
|
125
|
-
| "credits"
|
|
126
|
-
| "policy"
|
|
127
|
-
| "save"
|
|
128
|
-
| "unknown";
|
|
78
|
+
export type GenerationErrorType = "network" | "credits" | "policy" | "save" | "unknown";
|
|
129
79
|
|
|
130
80
|
export interface UseGenerationOrchestratorReturn<TInput, TResult> {
|
|
131
81
|
generate: (input: TInput) => Promise<TResult | void>;
|
|
@@ -137,22 +87,15 @@ export interface UseGenerationOrchestratorReturn<TInput, TResult> {
|
|
|
137
87
|
error: GenerationError | null;
|
|
138
88
|
}
|
|
139
89
|
|
|
140
|
-
/**
|
|
141
|
-
* Generation error UI configuration
|
|
142
|
-
*/
|
|
143
90
|
export interface GenerationErrorConfig {
|
|
144
91
|
readonly showCreditInfo?: boolean;
|
|
145
92
|
readonly iconName?: string;
|
|
146
93
|
readonly iconSize?: number;
|
|
147
94
|
}
|
|
148
95
|
|
|
149
|
-
/**
|
|
150
|
-
* Generation error UI translations
|
|
151
|
-
*/
|
|
152
96
|
export interface GenerationErrorTranslations {
|
|
153
97
|
readonly title: string;
|
|
154
98
|
readonly tryAgain: string;
|
|
155
99
|
readonly chooseAnother: string;
|
|
156
100
|
readonly noCreditCharged: string;
|
|
157
101
|
}
|
|
158
|
-
|