@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-ai-generation-content",
3
- "version": "1.82.6",
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({ type: "COMPLETE" });
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
- if (pollingRef.current) {
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
- await pollQueueStatus({
133
- requestId,
134
- model,
135
- isPollingRef,
136
- pollingRef,
137
- consecutiveErrorsRef,
138
- onComplete: handleComplete,
139
- onError: handleError,
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
- await persistence.updateToFailed(userId, creationId, queueResult.error || "Queue submission failed");
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
- return await handleModeration({
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
- isGeneratingRef.current = false;
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
  },