@umituz/react-native-ai-generation-content 1.90.1 → 1.90.3

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.
Files changed (152) hide show
  1. package/package.json +1 -4
  2. package/src/domain/interfaces/app-services-auth.interface.ts +27 -0
  3. package/src/domain/interfaces/app-services-composite.interface.ts +29 -0
  4. package/src/domain/interfaces/app-services-optional.interface.ts +42 -0
  5. package/src/domain/interfaces/app-services.interface.ts +0 -79
  6. package/src/domains/background/infrastructure/services/job-poller-index.ts +7 -0
  7. package/src/domains/background/infrastructure/services/job-poller-utils.ts +130 -0
  8. package/src/domains/background/infrastructure/utils/polling-interval.util.ts +1 -1
  9. package/src/domains/background/presentation/hooks/use-background-generation.ts +1 -1
  10. package/src/domains/content-moderation/infrastructure/services/content-moderation.service.ts +1 -1
  11. package/src/domains/content-moderation/infrastructure/services/moderators/image.moderator.ts +34 -8
  12. package/src/domains/content-moderation/infrastructure/services/moderators/text.moderator.ts +15 -4
  13. package/src/domains/content-moderation/infrastructure/services/moderators/video.moderator.ts +34 -8
  14. package/src/domains/content-moderation/infrastructure/services/moderators/voice.moderator.ts +19 -8
  15. package/src/domains/creations/domain/types/creation-categories.constants.ts +57 -0
  16. package/src/domains/creations/domain/types/creation-categories.helpers.ts +67 -0
  17. package/src/domains/creations/domain/types/creation-categories.ts +5 -111
  18. package/src/domains/creations/presentation/hooks/creation-validators.ts +31 -29
  19. package/src/domains/creations/presentation/hooks/job-poller-index.ts +10 -0
  20. package/src/domains/creations/presentation/hooks/job-poller-utils.filters.ts +34 -0
  21. package/src/domains/creations/presentation/hooks/job-poller-utils.logger.ts +76 -0
  22. package/src/domains/creations/presentation/hooks/job-poller-utils.stale-handlers.ts +52 -0
  23. package/src/domains/creations/presentation/hooks/job-poller-utils.ts +8 -0
  24. package/src/domains/creations/presentation/screens/CreationsGalleryScreen.tsx +1 -1
  25. package/src/domains/creations/presentation-exports.ts +2 -2
  26. package/src/domains/face-detection/domain/entities/FaceDetection.ts +4 -3
  27. package/src/domains/face-detection/presentation/hooks/useFaceDetection.ts +24 -21
  28. package/src/domains/generation/infrastructure/couple-generation-builder/builder-couple-preparation.ts +58 -0
  29. package/src/domains/generation/infrastructure/couple-generation-builder/builder-couple-prompt.ts +69 -0
  30. package/src/domains/generation/infrastructure/couple-generation-builder/builder-couple-resolution.ts +77 -0
  31. package/src/domains/generation/infrastructure/couple-generation-builder/builder-couple.ts +54 -0
  32. package/src/domains/generation/infrastructure/couple-generation-builder/builder-index.ts +8 -0
  33. package/src/domains/generation/infrastructure/couple-generation-builder/builder-scenario.ts +113 -0
  34. package/src/domains/generation/infrastructure/couple-generation-builder/builder.ts +7 -0
  35. package/src/domains/generation/infrastructure/couple-generation-builder/index.ts +20 -0
  36. package/src/domains/generation/infrastructure/couple-generation-builder/types.ts +44 -0
  37. package/src/domains/generation/infrastructure/couple-generation-builder/utils/builder-end-logger.ts +18 -0
  38. package/src/domains/generation/infrastructure/couple-generation-builder/utils/builder-start-logger.ts +57 -0
  39. package/src/domains/generation/infrastructure/couple-generation-builder/utils/builder-step-logger.ts +106 -0
  40. package/src/domains/generation/infrastructure/couple-generation-builder/utils/index.ts +8 -0
  41. package/src/domains/generation/infrastructure/couple-generation-builder/utils/types.ts +49 -0
  42. package/src/domains/generation/infrastructure/couple-generation-builder/utils.ts +8 -0
  43. package/src/domains/generation/infrastructure/flow/flow-store-actions.ts +105 -0
  44. package/src/domains/generation/infrastructure/flow/flow-store-initial-state.ts +26 -0
  45. package/src/domains/generation/infrastructure/flow/useFlowStore.ts +4 -116
  46. package/src/domains/generation/presentation/useAIGeneration.hook.ts +1 -1
  47. package/src/domains/generation/wizard/infrastructure/strategies/image-generation-strategy-index.ts +7 -0
  48. package/src/domains/generation/wizard/infrastructure/strategies/image-generation.executor.ts +2 -12
  49. package/src/domains/generation/wizard/infrastructure/strategies/image-generation.executor.types.ts +11 -0
  50. package/src/domains/generation/wizard/infrastructure/strategies/image-generation.executor.utils.ts +12 -0
  51. package/src/domains/generation/wizard/infrastructure/strategies/image-generation.strategy.ts +1 -220
  52. package/src/domains/generation/wizard/infrastructure/strategies/image-input-builder.ts +66 -0
  53. package/src/domains/generation/wizard/infrastructure/strategies/image-input-extraction.ts +88 -0
  54. package/src/domains/generation/wizard/infrastructure/strategies/image-input-prompt-builder.ts +75 -0
  55. package/src/domains/generation/wizard/infrastructure/strategies/image-input-style-enhancements.ts +35 -0
  56. package/src/domains/generation/wizard/infrastructure/strategies/image-strategy-factory.ts +42 -0
  57. package/src/domains/generation/wizard/infrastructure/strategies/video-generation-executor-index.ts +11 -0
  58. package/src/domains/generation/wizard/infrastructure/strategies/video-generation-executor.ts +76 -0
  59. package/src/domains/generation/wizard/infrastructure/strategies/video-generation-input-builder.ts +46 -0
  60. package/src/domains/generation/wizard/infrastructure/strategies/video-generation-result-types.ts +17 -0
  61. package/src/domains/generation/wizard/infrastructure/strategies/video-generation-submission.ts +62 -0
  62. package/src/domains/generation/wizard/infrastructure/strategies/video-generation.audio-extractor.ts +27 -0
  63. package/src/domains/generation/wizard/infrastructure/strategies/video-generation.executor.ts +2 -175
  64. package/src/domains/generation/wizard/infrastructure/strategies/video-generation.input-builder.ts +90 -0
  65. package/src/domains/generation/wizard/infrastructure/strategies/video-generation.strategy.ts +3 -108
  66. package/src/domains/generation/wizard/infrastructure/strategies/video-generation.types.ts +0 -129
  67. package/src/domains/generation/wizard/infrastructure/strategies/video-generation.validation.ts +136 -0
  68. package/src/domains/generation/wizard/presentation/hooks/photo-upload/index.ts +39 -0
  69. package/src/domains/generation/wizard/presentation/hooks/photo-upload/types.ts +37 -0
  70. package/src/domains/generation/wizard/presentation/hooks/photo-upload/usePhotoUploadStateLogic.ts +142 -0
  71. package/src/domains/generation/wizard/presentation/hooks/use-video-queue-utils.ts +103 -0
  72. package/src/domains/generation/wizard/presentation/hooks/usePhotoBlockingGeneration.handlers.ts +97 -0
  73. package/src/domains/generation/wizard/presentation/hooks/usePhotoBlockingGeneration.saver.ts +54 -0
  74. package/src/domains/generation/wizard/presentation/hooks/usePhotoBlockingGeneration.ts +22 -87
  75. package/src/domains/generation/wizard/presentation/hooks/usePhotoUploadState.ts +8 -177
  76. package/src/domains/generation/wizard/presentation/hooks/useVideoQueueGeneration.ts +1 -295
  77. package/src/domains/generation/wizard/presentation/hooks/useWizardGeneration.ts +1 -1
  78. package/src/domains/generation/wizard/presentation/hooks/video-queue/index.ts +82 -0
  79. package/src/domains/generation/wizard/presentation/hooks/video-queue/useVideoQueueGenerationCallbacks.ts +120 -0
  80. package/src/domains/generation/wizard/presentation/hooks/video-queue/useVideoQueueGenerationPolling.ts +76 -0
  81. package/src/domains/generation/wizard/presentation/hooks/video-queue/useVideoQueueGenerationRefs.ts +65 -0
  82. package/src/domains/generation/wizard/presentation/hooks/video-queue/useVideoQueueGenerationStart.ts +123 -0
  83. package/src/domains/generation/wizard/presentation/hooks/video-queue-index.ts +9 -0
  84. package/src/domains/image-to-video/domain/types/image-to-video-state.types.ts +11 -4
  85. package/src/domains/text-to-image/domain/types/text-to-image.types.ts +44 -22
  86. package/src/domains/text-to-video/domain/types/request.types.ts +33 -9
  87. package/src/domains/text-to-video/domain/types/state.types.ts +29 -9
  88. package/src/domains/text-to-video/presentation/hooks/useTextToVideoForm.handlers.ts +44 -0
  89. package/src/domains/text-to-video/presentation/hooks/useTextToVideoForm.ts +5 -51
  90. package/src/domains/text-to-video/presentation/hooks/useTextToVideoForm.types.ts +33 -0
  91. package/src/infrastructure/services/generation-orchestrator.service.ts +2 -2
  92. package/src/infrastructure/utils/couple-input-context.ts +13 -0
  93. package/src/infrastructure/utils/couple-input-index.ts +8 -0
  94. package/src/infrastructure/utils/couple-input-refiner.ts +101 -0
  95. package/src/infrastructure/utils/couple-input-resolver.ts +71 -0
  96. package/src/infrastructure/utils/couple-input-types.ts +14 -0
  97. package/src/infrastructure/utils/couple-input.util.ts +3 -176
  98. package/src/infrastructure/utils/photo-generation/photo-preparation.util.ts +1 -1
  99. package/src/infrastructure/validation/base-validator.ts +1 -27
  100. package/src/infrastructure/validation/base-validator.types.ts +32 -0
  101. package/src/presentation/hooks/generation/index.ts +1 -1
  102. package/src/presentation/hooks/generation/orchestrator-abort-logs.ts +48 -0
  103. package/src/presentation/hooks/generation/orchestrator-execution-logs.ts +67 -0
  104. package/src/presentation/hooks/generation/orchestrator-index.ts +14 -0
  105. package/src/presentation/hooks/generation/orchestrator-start-logs.ts +65 -0
  106. package/src/presentation/hooks/generation/orchestrator-state-utils.ts +17 -0
  107. package/src/presentation/hooks/generation/orchestrator-types.ts +55 -0
  108. package/src/presentation/hooks/generation/orchestrator-utils-index.ts +29 -0
  109. package/src/presentation/hooks/generation/orchestrator-utils.ts +25 -0
  110. package/src/presentation/hooks/generation/useDualImageGeneration.ts +1 -1
  111. package/src/presentation/hooks/generation/useImageGeneration.ts +1 -1
  112. package/src/presentation/hooks/generation/useVideoGeneration.ts +1 -1
  113. package/src/shared/hooks/factories/generation-hook-index.ts +12 -0
  114. package/src/shared/hooks/factories/generation-hook-types.ts +47 -0
  115. package/src/shared/hooks/factories/generation-hook-utils.ts +94 -0
  116. package/src/shared/hooks/factories/index.ts +1 -1
  117. package/src/shared/index.ts +1 -1
  118. package/src/shared/utils/calculations/aspect-ratio-calculations.ts +30 -0
  119. package/src/shared/utils/calculations/base64-calculations.ts +26 -0
  120. package/src/shared/utils/calculations/confidence-calculations.ts +21 -0
  121. package/src/shared/utils/calculations/cost-calculations-index.ts +43 -0
  122. package/src/shared/utils/calculations/cost-calculations.ts +25 -0
  123. package/src/shared/utils/calculations/credit-calculations.ts +37 -0
  124. package/src/shared/utils/calculations/index.ts +46 -0
  125. package/src/shared/utils/calculations/math-utilities.ts +32 -0
  126. package/src/shared/utils/calculations/memory-calculations.ts +33 -0
  127. package/src/shared/utils/calculations/pagination-calculations.ts +38 -0
  128. package/src/shared/utils/calculations/percentage-calculations.ts +33 -0
  129. package/src/shared/utils/calculations/time-calculations.ts +99 -0
  130. package/src/shared/utils/credit.ts +1 -1
  131. package/src/shared-kernel/application/hooks/index.ts +8 -0
  132. package/src/shared-kernel/application/hooks/use-feature-state.ts +107 -0
  133. package/src/shared-kernel/application/hooks/use-generation-handler.ts +110 -0
  134. package/src/shared-kernel/base-types/base-callbacks.types.ts +73 -0
  135. package/src/shared-kernel/base-types/base-feature-state.types.ts +77 -0
  136. package/src/shared-kernel/base-types/base-generation.types.ts +69 -0
  137. package/src/shared-kernel/base-types/index.ts +30 -0
  138. package/src/shared-kernel/domain/base-generation-strategy.ts +146 -0
  139. package/src/shared-kernel/domain/index.ts +7 -0
  140. package/src/shared-kernel/index.ts +17 -0
  141. package/src/shared-kernel/infrastructure/validation/common-validators.ts +126 -0
  142. package/src/shared-kernel/infrastructure/validation/common-validators.types.ts +33 -0
  143. package/src/shared-kernel/infrastructure/validation/error-handler.ts +52 -0
  144. package/src/shared-kernel/infrastructure/validation/error-handler.types.ts +38 -0
  145. package/src/shared-kernel/infrastructure/validation/error-handler.utils.ts +79 -0
  146. package/src/shared-kernel/infrastructure/validation/index.ts +28 -0
  147. package/src/domains/background/infrastructure/services/job-poller.service.ts +0 -234
  148. package/src/domains/creations/presentation/hooks/useProcessingJobsPoller.ts +0 -256
  149. package/src/domains/generation/infrastructure/couple-generation-builder.ts +0 -374
  150. package/src/presentation/hooks/generation/orchestrator.ts +0 -276
  151. package/src/shared/hooks/factories/createGenerationHook.ts +0 -253
  152. package/src/shared/utils/calculations.util.ts +0 -366
@@ -2,298 +2,4 @@
2
2
  * Video Queue Generation Hook
3
3
  */
4
4
 
5
- import { useEffect, useRef, useCallback, useState } from "react";
6
-
7
- import { pollQueueStatus } from "./videoQueuePoller";
8
- import {
9
- DEFAULT_POLL_INTERVAL_MS,
10
- DEFAULT_MAX_POLL_TIME_MS,
11
- } from "../../../../../infrastructure/constants/polling.constants";
12
- import type { GenerationUrls } from "./generation-result.utils";
13
- import type {
14
- UseVideoQueueGenerationProps,
15
- UseVideoQueueGenerationReturn,
16
- } from "./use-video-queue-generation.types";
17
-
18
- export function useVideoQueueGeneration(props: UseVideoQueueGenerationProps): UseVideoQueueGenerationReturn {
19
- const { userId, scenario, persistence, strategy, creditCost, deductCredits, onSuccess, onError } = props;
20
-
21
- const creationIdRef = useRef<string | null>(null);
22
- const requestIdRef = useRef<string | null>(null);
23
- const modelRef = useRef<string | null>(null);
24
- const pollingRef = useRef<ReturnType<typeof setInterval> | null>(null);
25
- const isGeneratingRef = useRef(false);
26
- const isPollingRef = useRef(false);
27
- const consecutiveErrorsRef = useRef(0);
28
- const pollStartTimeRef = useRef<number | null>(null);
29
- const [isGenerating, setIsGenerating] = useState(false);
30
-
31
- const clearPolling = useCallback(() => {
32
- if (pollingRef.current) {
33
- clearInterval(pollingRef.current);
34
- pollingRef.current = null;
35
- }
36
- }, []);
37
-
38
- useEffect(() => {
39
- return () => {
40
- clearPolling();
41
- isGeneratingRef.current = false;
42
- isPollingRef.current = false;
43
- consecutiveErrorsRef.current = 0;
44
- pollStartTimeRef.current = null;
45
- // NOTE: Do NOT null creationIdRef/requestIdRef/modelRef here.
46
- // In-flight poll callbacks may still resolve after unmount and need
47
- // these refs to properly save the completed generation to Firestore.
48
- // They are cleaned up by resetRefs() after handleComplete/handleError.
49
- setIsGenerating(false);
50
- };
51
- }, [clearPolling]);
52
-
53
- const resetRefs = useCallback(() => {
54
- clearPolling();
55
- creationIdRef.current = null;
56
- requestIdRef.current = null;
57
- modelRef.current = null;
58
- isGeneratingRef.current = false;
59
- isPollingRef.current = false;
60
- consecutiveErrorsRef.current = 0;
61
- pollStartTimeRef.current = null;
62
- setIsGenerating(false);
63
- }, [clearPolling]);
64
-
65
- const handleComplete = useCallback(
66
- async (urls: GenerationUrls) => {
67
- clearPolling();
68
-
69
- const creationId = creationIdRef.current;
70
- const uri = (urls.videoUrl || urls.imageUrl) ?? "";
71
-
72
- if (typeof __DEV__ !== "undefined" && __DEV__) {
73
- console.log("[VideoQueue] ✅ handleComplete called", {
74
- creationId,
75
- userId,
76
- hasVideoUrl: !!urls.videoUrl,
77
- hasImageUrl: !!urls.imageUrl,
78
- hasOnSuccess: !!onSuccess
79
- });
80
- }
81
-
82
- if (!creationId || !userId || !uri || uri.trim() === "") {
83
- if (typeof __DEV__ !== "undefined" && __DEV__) {
84
- console.error("[VideoQueue] ❌ Invalid completion data:", { creationId, userId, uri });
85
- }
86
- resetRefs();
87
- onError?.("Invalid completion data - no valid URL received");
88
- return;
89
- }
90
-
91
- let persistenceSucceeded = true;
92
- if (creationId && userId) {
93
- try {
94
- await persistence.updateToCompleted(userId, creationId, {
95
- uri,
96
- imageUrl: urls.imageUrl,
97
- videoUrl: urls.videoUrl,
98
- thumbnailUrl: urls.thumbnailUrl,
99
- generationStartedAt: pollStartTimeRef.current ?? undefined,
100
- });
101
- if (typeof __DEV__ !== "undefined" && __DEV__) {
102
- console.log("[VideoQueue] ✅ Updated completion status in Firestore");
103
- }
104
- } catch (error) {
105
- persistenceSucceeded = false;
106
- if (typeof __DEV__ !== "undefined" && __DEV__) {
107
- console.error("[VideoQueue] ❌ Failed to update completion status:", error);
108
- }
109
- }
110
- }
111
-
112
- resetRefs();
113
-
114
- // Deduct credits after successful generation
115
- if (deductCredits && creditCost) {
116
- await deductCredits(creditCost).catch((err) => {
117
- if (typeof __DEV__ !== "undefined" && __DEV__) {
118
- console.error("[VideoQueue] deductCredits error:", err);
119
- }
120
- });
121
- }
122
-
123
- if (typeof __DEV__ !== "undefined" && __DEV__) {
124
- console.log("[VideoQueue] 🎯 Calling onSuccess callback now...", { persistenceSucceeded });
125
- }
126
- onSuccess?.(urls);
127
-
128
- if (typeof __DEV__ !== "undefined" && __DEV__) {
129
- console.log("[VideoQueue] ✅ onSuccess callback completed");
130
- }
131
- },
132
- [userId, persistence, deductCredits, creditCost, onSuccess, onError, resetRefs, clearPolling],
133
- );
134
-
135
- const handleError = useCallback(
136
- async (errorMsg: string) => {
137
- clearPolling();
138
- const creationId = creationIdRef.current;
139
- if (creationId && userId) {
140
- try {
141
- await persistence.updateToFailed(userId, creationId, errorMsg);
142
- } catch (error) {
143
- if (typeof __DEV__ !== "undefined" && __DEV__) {
144
- console.error("[VideoQueue] Failed to update error status:", error);
145
- }
146
- }
147
- }
148
- resetRefs();
149
- onError?.(errorMsg);
150
- },
151
- [userId, persistence, onError, resetRefs, clearPolling],
152
- );
153
-
154
- // Use a ref to hold the latest handleComplete/handleError to avoid stale closures
155
- // in the setInterval callback
156
- const handleCompleteRef = useRef(handleComplete);
157
- const handleErrorRef = useRef(handleError);
158
- useEffect(() => { handleCompleteRef.current = handleComplete; }, [handleComplete]);
159
- useEffect(() => { handleErrorRef.current = handleError; }, [handleError]);
160
-
161
- const pollStatus = useCallback(async () => {
162
- const requestId = requestIdRef.current;
163
- const model = modelRef.current;
164
- if (!requestId || !model) return;
165
-
166
- // Check max poll time
167
- if (pollStartTimeRef.current !== null) {
168
- const elapsed = Date.now() - pollStartTimeRef.current;
169
- if (elapsed >= DEFAULT_MAX_POLL_TIME_MS) {
170
- if (typeof __DEV__ !== "undefined" && __DEV__) {
171
- console.warn("[VideoQueue] ⏰ Max poll time exceeded, aborting");
172
- }
173
- await handleErrorRef.current("Generation timed out. Please try again.");
174
- return;
175
- }
176
- }
177
-
178
- try {
179
- await pollQueueStatus({
180
- requestId,
181
- model,
182
- isPollingRef,
183
- pollingRef,
184
- consecutiveErrorsRef,
185
- onComplete: handleCompleteRef.current,
186
- onError: handleErrorRef.current,
187
- });
188
- } catch (error) {
189
- if (typeof __DEV__ !== "undefined" && __DEV__) {
190
- console.error("[VideoQueue] Unexpected poll error:", error);
191
- }
192
- }
193
- }, []);
194
-
195
- // Keep a stable ref to pollStatus for the setInterval closure
196
- const pollStatusRef = useRef(pollStatus);
197
- useEffect(() => { pollStatusRef.current = pollStatus; }, [pollStatus]);
198
-
199
- const startGeneration = useCallback(
200
- async (input: unknown, prompt: string) => {
201
- if (!strategy.submitToQueue) {
202
- onError?.("Queue submission not available");
203
- return;
204
- }
205
- if (isGeneratingRef.current) return;
206
-
207
- isGeneratingRef.current = true;
208
- setIsGenerating(true);
209
-
210
- let creationId: string | null = null;
211
- if (userId && prompt) {
212
- try {
213
- const inputData = input as Record<string, unknown>;
214
- const duration = typeof inputData?.duration === "number" ? inputData.duration : undefined;
215
- const resolution = typeof inputData?.resolution === "string" ? inputData.resolution : undefined;
216
- const aspectRatio = typeof inputData?.aspectRatio === "string" ? inputData.aspectRatio : undefined;
217
-
218
- const result = await persistence.saveAsProcessing(userId, {
219
- scenarioId: scenario.id,
220
- scenarioTitle: scenario.title || scenario.id,
221
- prompt,
222
- duration,
223
- resolution,
224
- creditCost,
225
- aspectRatio,
226
- provider: "fal",
227
- outputType: scenario.outputType,
228
- });
229
- creationId = result.creationId;
230
- creationIdRef.current = creationId;
231
- // Record the actual DB-level start time for accurate durationMs
232
- pollStartTimeRef.current = result.startedAt.getTime();
233
- } catch (error) {
234
- if (typeof __DEV__ !== "undefined" && __DEV__) {
235
- console.error("[VideoQueue] Failed to save processing creation:", error);
236
- }
237
- }
238
- }
239
-
240
- let queueResult;
241
- try {
242
- queueResult = await strategy.submitToQueue(input);
243
- } catch (error) {
244
- if (creationId && userId) {
245
- try {
246
- await persistence.updateToFailed(userId, creationId, error instanceof Error ? error.message : "Queue submission failed");
247
- } catch (persistError) {
248
- if (typeof __DEV__ !== "undefined" && __DEV__) {
249
- console.error("[VideoQueue] Failed to persist submission error:", persistError);
250
- }
251
- }
252
- }
253
- isGeneratingRef.current = false;
254
- setIsGenerating(false);
255
- onError?.(error instanceof Error ? error.message : "Queue submission failed");
256
- return;
257
- }
258
-
259
- if (!queueResult.success || !queueResult.requestId || !queueResult.model) {
260
- if (creationId && userId) {
261
- try {
262
- await persistence.updateToFailed(userId, creationId, queueResult.error || "Queue submission failed");
263
- } catch (persistError) {
264
- if (typeof __DEV__ !== "undefined" && __DEV__) {
265
- console.error("[VideoQueue] Failed to persist queue failure:", persistError);
266
- }
267
- }
268
- }
269
- isGeneratingRef.current = false;
270
- setIsGenerating(false);
271
- onError?.(queueResult.error || "Queue submission failed");
272
- return;
273
- }
274
-
275
- requestIdRef.current = queueResult.requestId;
276
- modelRef.current = queueResult.model;
277
-
278
- if (creationId && userId && queueResult.requestId && queueResult.model) {
279
- try {
280
- await persistence.updateRequestId(userId, creationId, queueResult.requestId, queueResult.model);
281
- } catch (error) {
282
- if (typeof __DEV__ !== "undefined" && __DEV__) {
283
- console.error("[VideoQueue] Failed to update request ID:", error);
284
- }
285
- }
286
- }
287
-
288
- // Start polling: use DB-level startedAt if available, otherwise fallback to now
289
- if (pollStartTimeRef.current === null) {
290
- pollStartTimeRef.current = Date.now();
291
- }
292
- pollingRef.current = setInterval(() => void pollStatusRef.current(), DEFAULT_POLL_INTERVAL_MS);
293
- void pollStatusRef.current();
294
- },
295
- [userId, scenario, persistence, strategy, creditCost, onError],
296
- );
297
-
298
- return { isGenerating, startGeneration };
299
- }
5
+ export { useVideoQueueGeneration } from "./video-queue";
@@ -6,7 +6,7 @@
6
6
  import { useEffect, useReducer, useMemo, useRef } from "react";
7
7
  import { createWizardStrategy } from "../../infrastructure/strategies";
8
8
  import { createCreationPersistence } from "../../infrastructure/utils/creation-persistence.util";
9
- import { useVideoQueueGeneration } from "./useVideoQueueGeneration";
9
+ import { useVideoQueueGeneration } from "./video-queue-index";
10
10
  import { usePhotoBlockingGeneration } from "./usePhotoBlockingGeneration";
11
11
  import { generationReducer, INITIAL_STATE } from "./generationStateMachine";
12
12
  import { executeWizardGeneration } from "./generationExecutor";
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Video Queue Generation Hook
3
+ */
4
+
5
+ import { useEffect } from "react";
6
+ import type { GenerationUrls } from "../generation-result.utils";
7
+ import type {
8
+ UseVideoQueueGenerationProps,
9
+ UseVideoQueueGenerationReturn,
10
+ } from "../use-video-queue-generation.types";
11
+ import {
12
+ createClearPolling,
13
+ createResetRefs,
14
+ } from "./use-video-queue-utils";
15
+ import { useVideoQueueGenerationRefs, useVideoQueueGenerationState } from "./useVideoQueueGenerationRefs";
16
+ import { useCompletionHandler, useErrorHandler } from "./useVideoQueueGenerationCallbacks";
17
+ import { usePollStatus, useCallbackRefs } from "./useVideoQueueGenerationPolling";
18
+ import { useStartGeneration } from "./useVideoQueueGenerationStart";
19
+
20
+ export function useVideoQueueGeneration(props: UseVideoQueueGenerationProps): UseVideoQueueGenerationReturn {
21
+ const refs = useVideoQueueGenerationRefs();
22
+ const state = useVideoQueueGenerationState();
23
+
24
+ const clearPolling = (() => {
25
+ const { pollingRef } = refs;
26
+ return () => {
27
+ if (pollingRef.current) {
28
+ clearInterval(pollingRef.current);
29
+ pollingRef.current = null;
30
+ }
31
+ };
32
+ })();
33
+
34
+ useEffect(() => {
35
+ return () => {
36
+ clearPolling();
37
+ refs.isGeneratingRef.current = false;
38
+ refs.isPollingRef.current = false;
39
+ refs.consecutiveErrorsRef.current = 0;
40
+ refs.pollStartTimeRef.current = null;
41
+ // NOTE: Do NOT null creationIdRef/requestIdRef/modelRef here.
42
+ // In-flight poll callbacks may still resolve after unmount and need
43
+ // these refs to properly save the completed generation to Firestore.
44
+ // They are cleaned up by resetRefs() after handleComplete/handleError.
45
+ state.setIsGenerating(false);
46
+ };
47
+ }, [clearPolling, refs, state]);
48
+
49
+ const resetRefs = (() => {
50
+ const {
51
+ creationIdRef,
52
+ requestIdRef,
53
+ modelRef,
54
+ isGeneratingRef,
55
+ isPollingRef,
56
+ consecutiveErrorsRef,
57
+ pollStartTimeRef,
58
+ } = refs;
59
+ const { setIsGenerating } = state;
60
+
61
+ return () => {
62
+ creationIdRef.current = null;
63
+ requestIdRef.current = null;
64
+ modelRef.current = null;
65
+ isGeneratingRef.current = false;
66
+ isPollingRef.current = false;
67
+ consecutiveErrorsRef.current = 0;
68
+ pollStartTimeRef.current = null;
69
+ setIsGenerating(false);
70
+ };
71
+ })();
72
+
73
+ const handleComplete = useCompletionHandler(props, refs, state, clearPolling, resetRefs);
74
+ const handleError = useErrorHandler(props, refs, clearPolling, resetRefs);
75
+ const pollStatus = usePollStatus(refs);
76
+ const startGeneration = useStartGeneration(props, refs, state, clearPolling);
77
+
78
+ // Sync callback refs
79
+ useCallbackRefs(handleComplete, handleError, pollStatus, refs);
80
+
81
+ return { isGenerating: state.isGenerating, startGeneration };
82
+ }
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Video Queue Generation Hook - Completion and Error Handlers
3
+ */
4
+
5
+ import { useCallback } from "react";
6
+ import type { GenerationUrls } from "../../generation-result.utils";
7
+ import type {
8
+ UseVideoQueueGenerationProps,
9
+ } from "../use-video-queue-generation.types";
10
+ import {
11
+ logCompletion,
12
+ logError,
13
+ validateCompletionData,
14
+ } from "./use-video-queue-utils";
15
+ import type { VideoQueueRefs, VideoQueueState } from "./useVideoQueueGenerationRefs";
16
+
17
+ /**
18
+ * Create completion handler callback
19
+ */
20
+ export function useCompletionHandler(
21
+ props: UseVideoQueueGenerationProps,
22
+ refs: VideoQueueRefs,
23
+ state: VideoQueueState,
24
+ clearPolling: () => void,
25
+ resetRefs: () => void,
26
+ ) {
27
+ const { userId, persistence, strategy, creditCost, deductCredits, onSuccess, onError } = props;
28
+ const { setIsGenerating } = state;
29
+
30
+ return useCallback(
31
+ async (urls: GenerationUrls) => {
32
+ clearPolling();
33
+
34
+ const creationId = refs.creationIdRef.current;
35
+ const uri = (urls.videoUrl || urls.imageUrl) ?? "";
36
+
37
+ logCompletion(creationId, userId, urls, !!onSuccess);
38
+
39
+ if (!validateCompletionData(creationId, userId, uri)) {
40
+ logError("Invalid completion data:", { creationId, userId, uri });
41
+ resetRefs();
42
+ onError?.("Invalid completion data - no valid URL received");
43
+ return;
44
+ }
45
+
46
+ let persistenceSucceeded = true;
47
+ if (creationId && userId) {
48
+ try {
49
+ await persistence.updateToCompleted(userId, creationId, {
50
+ uri,
51
+ imageUrl: urls.imageUrl,
52
+ videoUrl: urls.videoUrl,
53
+ thumbnailUrl: urls.thumbnailUrl,
54
+ generationStartedAt: refs.pollStartTimeRef.current ?? undefined,
55
+ });
56
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
57
+ console.log("[VideoQueue] ✅ Updated completion status in Firestore");
58
+ }
59
+ } catch (error) {
60
+ persistenceSucceeded = false;
61
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
62
+ console.error("[VideoQueue] ❌ Failed to update completion status:", error);
63
+ }
64
+ }
65
+ }
66
+
67
+ resetRefs();
68
+
69
+ // Deduct credits after successful generation
70
+ if (deductCredits && creditCost) {
71
+ await deductCredits(creditCost).catch((err) => {
72
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
73
+ console.error("[VideoQueue] deductCredits error:", err);
74
+ }
75
+ });
76
+ }
77
+
78
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
79
+ console.log("[VideoQueue] 🎯 Calling onSuccess callback now...", { persistenceSucceeded });
80
+ }
81
+ onSuccess?.(urls);
82
+
83
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
84
+ console.log("[VideoQueue] ✅ onSuccess callback completed");
85
+ }
86
+ },
87
+ [userId, persistence, deductCredits, creditCost, onSuccess, onError, resetRefs, clearPolling, refs],
88
+ );
89
+ }
90
+
91
+ /**
92
+ * Create error handler callback
93
+ */
94
+ export function useErrorHandler(
95
+ props: UseVideoQueueGenerationProps,
96
+ refs: VideoQueueRefs,
97
+ clearPolling: () => void,
98
+ resetRefs: () => void,
99
+ ) {
100
+ const { userId, persistence, onError } = props;
101
+
102
+ return useCallback(
103
+ async (errorMsg: string) => {
104
+ clearPolling();
105
+ const creationId = refs.creationIdRef.current;
106
+ if (creationId && userId) {
107
+ try {
108
+ await persistence.updateToFailed(userId, creationId, errorMsg);
109
+ } catch (error) {
110
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
111
+ console.error("[VideoQueue] Failed to update error status:", error);
112
+ }
113
+ }
114
+ }
115
+ resetRefs();
116
+ onError?.(errorMsg);
117
+ },
118
+ [userId, persistence, onError, resetRefs, clearPolling, refs],
119
+ );
120
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Video Queue Generation Hook - Polling Logic
3
+ */
4
+
5
+ import { useCallback, useEffect } from "react";
6
+ import { pollQueueStatus } from "../videoQueuePoller";
7
+ import {
8
+ DEFAULT_POLL_INTERVAL_MS,
9
+ DEFAULT_MAX_POLL_TIME_MS,
10
+ } from "../../../../../infrastructure/constants/polling.constants";
11
+ import type { VideoQueueRefs } from "./useVideoQueueGenerationRefs";
12
+
13
+ /**
14
+ * Create poll status callback
15
+ */
16
+ export function usePollStatus(
17
+ refs: VideoQueueRefs,
18
+ ): () => Promise<void> {
19
+ const { requestIdRef, modelRef, isPollingRef, pollingRef, consecutiveErrorsRef, pollStartTimeRef, handleCompleteRef, handleErrorRef } = refs;
20
+
21
+ return useCallback(async () => {
22
+ const requestId = requestIdRef.current;
23
+ const model = modelRef.current;
24
+ if (!requestId || !model) return;
25
+
26
+ // Check max poll time
27
+ if (pollStartTimeRef.current !== null) {
28
+ const elapsed = Date.now() - pollStartTimeRef.current;
29
+ if (elapsed >= DEFAULT_MAX_POLL_TIME_MS) {
30
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
31
+ console.warn("[VideoQueue] ⏰ Max poll time exceeded, aborting");
32
+ }
33
+ await handleErrorRef.current("Generation timed out. Please try again.");
34
+ return;
35
+ }
36
+ }
37
+
38
+ try {
39
+ await pollQueueStatus({
40
+ requestId,
41
+ model,
42
+ isPollingRef,
43
+ pollingRef,
44
+ consecutiveErrorsRef,
45
+ onComplete: handleCompleteRef.current,
46
+ onError: handleErrorRef.current,
47
+ });
48
+ } catch (error) {
49
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
50
+ console.error("[VideoQueue] Unexpected poll error:", error);
51
+ }
52
+ }
53
+ }, [requestIdRef, modelRef, isPollingRef, pollingRef, consecutiveErrorsRef, pollStartTimeRef, handleCompleteRef, handleErrorRef]);
54
+ }
55
+
56
+ /**
57
+ * Hook to sync callback refs
58
+ */
59
+ export function useCallbackRefs(
60
+ handleComplete: (urls: import("../../generation-result.utils").GenerationUrls) => Promise<void>,
61
+ handleError: (errorMsg: string) => Promise<void>,
62
+ pollStatus: () => Promise<void>,
63
+ refs: VideoQueueRefs,
64
+ ) {
65
+ useEffect(() => {
66
+ refs.handleCompleteRef.current = handleComplete;
67
+ }, [handleComplete, refs]);
68
+
69
+ useEffect(() => {
70
+ refs.handleErrorRef.current = handleError;
71
+ }, [handleError, refs]);
72
+
73
+ useEffect(() => {
74
+ refs.pollStatusRef.current = pollStatus;
75
+ }, [pollStatus, refs]);
76
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Video Queue Generation Hook - Refs and State Management
3
+ */
4
+
5
+ import { useRef, useState } from "react";
6
+
7
+ export interface VideoQueueRefs {
8
+ readonly creationIdRef: React.MutableRefObject<string | null>;
9
+ readonly requestIdRef: React.MutableRefObject<string | null>;
10
+ readonly modelRef: React.MutableRefObject<string | null>;
11
+ readonly pollingRef: React.MutableRefObject<ReturnType<typeof setInterval> | null>;
12
+ readonly isGeneratingRef: React.MutableRefObject<boolean>;
13
+ readonly isPollingRef: React.MutableRefObject<boolean>;
14
+ readonly consecutiveErrorsRef: React.MutableRefObject<number>;
15
+ readonly pollStartTimeRef: React.MutableRefObject<number | null>;
16
+ readonly handleCompleteRef: React.MutableRefObject<(urls: import("../generation-result.utils").GenerationUrls) => Promise<void>>;
17
+ readonly handleErrorRef: React.MutableRefObject<(errorMsg: string) => Promise<void>>;
18
+ readonly pollStatusRef: React.MutableRefObject<() => Promise<void>>;
19
+ }
20
+
21
+ export interface VideoQueueState {
22
+ readonly isGenerating: boolean;
23
+ readonly setIsGenerating: React.Dispatch<React.SetStateAction<boolean>>;
24
+ }
25
+
26
+ /**
27
+ * Initialize refs for video queue generation
28
+ */
29
+ export function useVideoQueueGenerationRefs(): VideoQueueRefs {
30
+ const creationIdRef = useRef<string | null>(null);
31
+ const requestIdRef = useRef<string | null>(null);
32
+ const modelRef = useRef<string | null>(null);
33
+ const pollingRef = useRef<ReturnType<typeof setInterval> | null>(null);
34
+ const isGeneratingRef = useRef(false);
35
+ const isPollingRef = useRef(false);
36
+ const consecutiveErrorsRef = useRef(0);
37
+ const pollStartTimeRef = useRef<number | null>(null);
38
+
39
+ // Placeholders for callback refs - will be set by the hook
40
+ const handleCompleteRef = useRef<VideoQueueRefs["handleCompleteRef"]["current"]>(async () => {});
41
+ const handleErrorRef = useRef<VideoQueueRefs["handleErrorRef"]["current"]>(async () => {});
42
+ const pollStatusRef = useRef<VideoQueueRefs["pollStatusRef"]["current"]>(async () => {});
43
+
44
+ return {
45
+ creationIdRef,
46
+ requestIdRef,
47
+ modelRef,
48
+ pollingRef,
49
+ isGeneratingRef,
50
+ isPollingRef,
51
+ consecutiveErrorsRef,
52
+ pollStartTimeRef,
53
+ handleCompleteRef,
54
+ handleErrorRef,
55
+ pollStatusRef,
56
+ };
57
+ }
58
+
59
+ /**
60
+ * Initialize state for video queue generation
61
+ */
62
+ export function useVideoQueueGenerationState(): VideoQueueState {
63
+ const [isGenerating, setIsGenerating] = useState(false);
64
+ return { isGenerating, setIsGenerating };
65
+ }