@umituz/react-native-ai-generation-content 1.82.6 → 1.82.8
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/creations/presentation/hooks/useProcessingJobsPoller.ts +7 -3
- package/src/domains/generation/wizard/presentation/hooks/generationExecutor.ts +6 -1
- package/src/domains/generation/wizard/presentation/hooks/useVideoQueueGeneration.ts +64 -23
- package/src/presentation/hooks/generation/orchestrator.ts +14 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-ai-generation-content",
|
|
3
|
-
"version": "1.82.
|
|
3
|
+
"version": "1.82.8",
|
|
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",
|
|
@@ -85,7 +85,8 @@ export function useProcessingJobsPoller(
|
|
|
85
85
|
try {
|
|
86
86
|
await repository.update(userId, creation.id, {
|
|
87
87
|
status: CREATION_STATUS.FAILED,
|
|
88
|
-
metadata: { error: "Generation timed out" },
|
|
88
|
+
metadata: { ...creation.metadata, error: "Generation timed out" },
|
|
89
|
+
completedAt: new Date(),
|
|
89
90
|
});
|
|
90
91
|
} catch (e) {
|
|
91
92
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
@@ -129,7 +130,8 @@ export function useProcessingJobsPoller(
|
|
|
129
130
|
}
|
|
130
131
|
await repository.update(userId, creation.id, {
|
|
131
132
|
status: CREATION_STATUS.FAILED,
|
|
132
|
-
metadata: { error: "No valid result URL received" },
|
|
133
|
+
metadata: { ...creation.metadata, error: "No valid result URL received" },
|
|
134
|
+
completedAt: new Date(),
|
|
133
135
|
});
|
|
134
136
|
return;
|
|
135
137
|
}
|
|
@@ -143,6 +145,7 @@ export function useProcessingJobsPoller(
|
|
|
143
145
|
status: CREATION_STATUS.COMPLETED,
|
|
144
146
|
uri,
|
|
145
147
|
output,
|
|
148
|
+
completedAt: new Date(),
|
|
146
149
|
});
|
|
147
150
|
} else if (status.status === QUEUE_STATUS.FAILED) {
|
|
148
151
|
if (typeof __DEV__ !== "undefined" && __DEV__) console.log("[ProcessingJobsPoller] Failed:", creation.id);
|
|
@@ -151,7 +154,8 @@ export function useProcessingJobsPoller(
|
|
|
151
154
|
|
|
152
155
|
await repository.update(userId, creation.id, {
|
|
153
156
|
status: CREATION_STATUS.FAILED,
|
|
154
|
-
metadata: { error: "Generation failed" },
|
|
157
|
+
metadata: { ...creation.metadata, error: "Generation failed" },
|
|
158
|
+
completedAt: new Date(),
|
|
155
159
|
});
|
|
156
160
|
}
|
|
157
161
|
} catch (error) {
|
|
@@ -53,7 +53,12 @@ export const executeWizardGeneration = async (params: ExecuteGenerationParams):
|
|
|
53
53
|
await generationFn(input, prompt);
|
|
54
54
|
|
|
55
55
|
if (isMountedRef.current) {
|
|
56
|
-
dispatch
|
|
56
|
+
// For video queue mode, don't dispatch COMPLETE — the queue polling handles
|
|
57
|
+
// completion separately via onSuccess. The function above just submitted to queue.
|
|
58
|
+
// For photo (blocking) mode, the generation is truly done here.
|
|
59
|
+
if (!isVideoMode) {
|
|
60
|
+
dispatch({ type: "COMPLETE" });
|
|
61
|
+
}
|
|
57
62
|
}
|
|
58
63
|
} catch (error: unknown) {
|
|
59
64
|
if (!isMountedRef.current) return;
|
|
@@ -25,13 +25,16 @@ export function useVideoQueueGeneration(props: UseVideoQueueGenerationProps): Us
|
|
|
25
25
|
const consecutiveErrorsRef = useRef(0);
|
|
26
26
|
const [isGenerating, setIsGenerating] = useState(false);
|
|
27
27
|
|
|
28
|
+
const clearPolling = useCallback(() => {
|
|
29
|
+
if (pollingRef.current) {
|
|
30
|
+
clearInterval(pollingRef.current);
|
|
31
|
+
pollingRef.current = null;
|
|
32
|
+
}
|
|
33
|
+
}, []);
|
|
34
|
+
|
|
28
35
|
useEffect(() => {
|
|
29
36
|
return () => {
|
|
30
|
-
|
|
31
|
-
clearInterval(pollingRef.current);
|
|
32
|
-
pollingRef.current = null;
|
|
33
|
-
}
|
|
34
|
-
// Reset all refs on unmount
|
|
37
|
+
clearPolling();
|
|
35
38
|
isGeneratingRef.current = false;
|
|
36
39
|
isPollingRef.current = false;
|
|
37
40
|
consecutiveErrorsRef.current = 0;
|
|
@@ -40,9 +43,10 @@ export function useVideoQueueGeneration(props: UseVideoQueueGenerationProps): Us
|
|
|
40
43
|
modelRef.current = null;
|
|
41
44
|
setIsGenerating(false);
|
|
42
45
|
};
|
|
43
|
-
}, []);
|
|
46
|
+
}, [clearPolling]);
|
|
44
47
|
|
|
45
48
|
const resetRefs = useCallback(() => {
|
|
49
|
+
clearPolling();
|
|
46
50
|
creationIdRef.current = null;
|
|
47
51
|
requestIdRef.current = null;
|
|
48
52
|
modelRef.current = null;
|
|
@@ -50,10 +54,13 @@ export function useVideoQueueGeneration(props: UseVideoQueueGenerationProps): Us
|
|
|
50
54
|
isPollingRef.current = false;
|
|
51
55
|
consecutiveErrorsRef.current = 0;
|
|
52
56
|
setIsGenerating(false);
|
|
53
|
-
}, []);
|
|
57
|
+
}, [clearPolling]);
|
|
54
58
|
|
|
55
59
|
const handleComplete = useCallback(
|
|
56
60
|
async (urls: GenerationUrls) => {
|
|
61
|
+
// Stop polling immediately on completion
|
|
62
|
+
clearPolling();
|
|
63
|
+
|
|
57
64
|
const creationId = creationIdRef.current;
|
|
58
65
|
const uri = (urls.videoUrl || urls.imageUrl) ?? "";
|
|
59
66
|
|
|
@@ -72,9 +79,12 @@ export function useVideoQueueGeneration(props: UseVideoQueueGenerationProps): Us
|
|
|
72
79
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
73
80
|
console.error("[VideoQueue] ❌ Invalid completion data:", { creationId, userId, uri });
|
|
74
81
|
}
|
|
82
|
+
resetRefs();
|
|
83
|
+
onError?.("Invalid completion data - no valid URL received");
|
|
75
84
|
return;
|
|
76
85
|
}
|
|
77
86
|
|
|
87
|
+
let persistenceSucceeded = true;
|
|
78
88
|
if (creationId && userId) {
|
|
79
89
|
try {
|
|
80
90
|
await persistence.updateToCompleted(userId, creationId, {
|
|
@@ -87,27 +97,32 @@ export function useVideoQueueGeneration(props: UseVideoQueueGenerationProps): Us
|
|
|
87
97
|
console.log("[VideoQueue] ✅ Updated completion status in Firestore");
|
|
88
98
|
}
|
|
89
99
|
} catch (error) {
|
|
100
|
+
persistenceSucceeded = false;
|
|
90
101
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
91
102
|
console.error("[VideoQueue] ❌ Failed to update completion status:", error);
|
|
92
103
|
}
|
|
93
104
|
}
|
|
94
105
|
}
|
|
95
106
|
|
|
107
|
+
resetRefs();
|
|
108
|
+
|
|
109
|
+
// Still call onSuccess even if persistence failed - the generation itself succeeded
|
|
110
|
+
// The video/image URL is valid, user should still see the result
|
|
96
111
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
97
|
-
console.log("[VideoQueue] 🎯 Calling onSuccess callback now...");
|
|
112
|
+
console.log("[VideoQueue] 🎯 Calling onSuccess callback now...", { persistenceSucceeded });
|
|
98
113
|
}
|
|
99
|
-
resetRefs();
|
|
100
114
|
onSuccess?.(urls);
|
|
101
115
|
|
|
102
116
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
103
117
|
console.log("[VideoQueue] ✅ onSuccess callback completed");
|
|
104
118
|
}
|
|
105
119
|
},
|
|
106
|
-
[userId, persistence, onSuccess, resetRefs],
|
|
120
|
+
[userId, persistence, onSuccess, onError, resetRefs, clearPolling],
|
|
107
121
|
);
|
|
108
122
|
|
|
109
123
|
const handleError = useCallback(
|
|
110
124
|
async (errorMsg: string) => {
|
|
125
|
+
clearPolling();
|
|
111
126
|
const creationId = creationIdRef.current;
|
|
112
127
|
if (creationId && userId) {
|
|
113
128
|
try {
|
|
@@ -121,7 +136,7 @@ export function useVideoQueueGeneration(props: UseVideoQueueGenerationProps): Us
|
|
|
121
136
|
resetRefs();
|
|
122
137
|
onError?.(errorMsg);
|
|
123
138
|
},
|
|
124
|
-
[userId, persistence, onError, resetRefs],
|
|
139
|
+
[userId, persistence, onError, resetRefs, clearPolling],
|
|
125
140
|
);
|
|
126
141
|
|
|
127
142
|
const pollStatus = useCallback(async () => {
|
|
@@ -129,15 +144,21 @@ export function useVideoQueueGeneration(props: UseVideoQueueGenerationProps): Us
|
|
|
129
144
|
const model = modelRef.current;
|
|
130
145
|
if (!requestId || !model) return;
|
|
131
146
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
147
|
+
try {
|
|
148
|
+
await pollQueueStatus({
|
|
149
|
+
requestId,
|
|
150
|
+
model,
|
|
151
|
+
isPollingRef,
|
|
152
|
+
pollingRef,
|
|
153
|
+
consecutiveErrorsRef,
|
|
154
|
+
onComplete: handleComplete,
|
|
155
|
+
onError: handleError,
|
|
156
|
+
});
|
|
157
|
+
} catch (error) {
|
|
158
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
159
|
+
console.error("[VideoQueue] Unexpected poll error:", error);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
141
162
|
}, [handleComplete, handleError]);
|
|
142
163
|
|
|
143
164
|
const startGeneration = useCallback(
|
|
@@ -176,14 +197,34 @@ export function useVideoQueueGeneration(props: UseVideoQueueGenerationProps): Us
|
|
|
176
197
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
177
198
|
console.error("[VideoQueue] Failed to save processing creation:", error);
|
|
178
199
|
}
|
|
200
|
+
// Continue without creation tracking - generation can still proceed
|
|
201
|
+
// The video will be generated but won't appear in gallery history
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
let queueResult;
|
|
206
|
+
try {
|
|
207
|
+
queueResult = await strategy.submitToQueue(input);
|
|
208
|
+
} catch (error) {
|
|
209
|
+
// Queue submission threw - reset state and report error
|
|
210
|
+
if (creationId && userId) {
|
|
211
|
+
try {
|
|
212
|
+
await persistence.updateToFailed(userId, creationId, error instanceof Error ? error.message : "Queue submission failed");
|
|
213
|
+
} catch { /* best effort */ }
|
|
179
214
|
}
|
|
215
|
+
isGeneratingRef.current = false;
|
|
216
|
+
setIsGenerating(false);
|
|
217
|
+
onError?.(error instanceof Error ? error.message : "Queue submission failed");
|
|
218
|
+
return;
|
|
180
219
|
}
|
|
181
220
|
|
|
182
|
-
const queueResult = await strategy.submitToQueue(input);
|
|
183
221
|
if (!queueResult.success || !queueResult.requestId || !queueResult.model) {
|
|
184
222
|
if (creationId && userId) {
|
|
185
|
-
|
|
223
|
+
try {
|
|
224
|
+
await persistence.updateToFailed(userId, creationId, queueResult.error || "Queue submission failed");
|
|
225
|
+
} catch { /* best effort */ }
|
|
186
226
|
}
|
|
227
|
+
isGeneratingRef.current = false;
|
|
187
228
|
setIsGenerating(false);
|
|
188
229
|
onError?.(queueResult.error || "Queue submission failed");
|
|
189
230
|
return;
|
|
@@ -205,7 +246,7 @@ export function useVideoQueueGeneration(props: UseVideoQueueGenerationProps): Us
|
|
|
205
246
|
pollingRef.current = setInterval(() => void pollStatus(), DEFAULT_POLL_INTERVAL_MS);
|
|
206
247
|
void pollStatus();
|
|
207
248
|
},
|
|
208
|
-
[userId, scenario, persistence, strategy, pollStatus, onError],
|
|
249
|
+
[userId, scenario, persistence, strategy, creditCost, pollStatus, onError],
|
|
209
250
|
);
|
|
210
251
|
|
|
211
252
|
return { isGenerating, startGeneration };
|
|
@@ -136,6 +136,7 @@ export const useGenerationOrchestrator = <TInput, TResult>(
|
|
|
136
136
|
// Create new AbortController for this generation
|
|
137
137
|
abortControllerRef.current = new AbortController();
|
|
138
138
|
isGeneratingRef.current = true;
|
|
139
|
+
let moderationPending = false;
|
|
139
140
|
setState({ ...INITIAL_STATE, status: "checking", isGenerating: true });
|
|
140
141
|
|
|
141
142
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
@@ -167,7 +168,7 @@ export const useGenerationOrchestrator = <TInput, TResult>(
|
|
|
167
168
|
console.log("[Orchestrator] Starting moderation check");
|
|
168
169
|
}
|
|
169
170
|
|
|
170
|
-
|
|
171
|
+
const result = await handleModeration({
|
|
171
172
|
input,
|
|
172
173
|
moderation,
|
|
173
174
|
alertMessages,
|
|
@@ -180,6 +181,14 @@ export const useGenerationOrchestrator = <TInput, TResult>(
|
|
|
180
181
|
onError,
|
|
181
182
|
handleLifecycleComplete,
|
|
182
183
|
});
|
|
184
|
+
|
|
185
|
+
// If handleModeration returned undefined, the warning dialog was shown.
|
|
186
|
+
// The dialog's proceed/cancel callbacks now own isGeneratingRef — don't reset in finally.
|
|
187
|
+
if (result === undefined && moderation) {
|
|
188
|
+
moderationPending = true;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return result;
|
|
183
192
|
} catch (err) {
|
|
184
193
|
// Don't show error if aborted
|
|
185
194
|
if (abortControllerRef.current?.signal.aborted) {
|
|
@@ -197,7 +206,10 @@ export const useGenerationOrchestrator = <TInput, TResult>(
|
|
|
197
206
|
handleLifecycleComplete("error", undefined, error);
|
|
198
207
|
throw error;
|
|
199
208
|
} finally {
|
|
200
|
-
|
|
209
|
+
// Only reset if moderation dialog did not take ownership
|
|
210
|
+
if (!moderationPending) {
|
|
211
|
+
isGeneratingRef.current = false;
|
|
212
|
+
}
|
|
201
213
|
abortControllerRef.current = null;
|
|
202
214
|
}
|
|
203
215
|
},
|