@umituz/react-native-ai-generation-content 1.90.2 → 1.90.4

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 (171) hide show
  1. package/package.json +3 -3
  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/domain/interfaces/index.ts +3 -0
  7. package/src/domains/background/infrastructure/services/job-poller-index.ts +7 -0
  8. package/src/domains/background/infrastructure/services/job-poller-utils.ts +127 -0
  9. package/src/domains/background/infrastructure/services/job-poller.service.ts +85 -140
  10. package/src/domains/background/infrastructure/utils/polling-interval.util.ts +1 -1
  11. package/src/domains/background/presentation/hooks/use-background-generation.ts +1 -1
  12. package/src/domains/content-moderation/index.ts +7 -13
  13. package/src/domains/content-moderation/infrastructure/services/content-moderation.service.ts +1 -1
  14. package/src/domains/content-moderation/infrastructure/services/moderators/image.moderator.ts +34 -8
  15. package/src/domains/content-moderation/infrastructure/services/moderators/text.moderator.ts +15 -4
  16. package/src/domains/content-moderation/infrastructure/services/moderators/video.moderator.ts +34 -8
  17. package/src/domains/content-moderation/infrastructure/services/moderators/voice.moderator.ts +19 -8
  18. package/src/domains/content-moderation/infrastructure/services/pattern-matcher.service.ts +1 -2
  19. package/src/domains/creations/domain/types/creation-categories.constants.ts +57 -0
  20. package/src/domains/creations/domain/types/creation-categories.helpers.ts +67 -0
  21. package/src/domains/creations/domain/types/creation-categories.ts +7 -114
  22. package/src/domains/creations/domain/utils/creation-display.util.ts +1 -1
  23. package/src/domains/creations/domain/utils/status-helpers.ts +1 -1
  24. package/src/domains/creations/presentation/hooks/creation-validators.ts +31 -29
  25. package/src/domains/creations/presentation/hooks/job-poller-index.ts +10 -0
  26. package/src/domains/creations/presentation/hooks/job-poller-utils.filters.ts +34 -0
  27. package/src/domains/creations/presentation/hooks/job-poller-utils.logger.ts +76 -0
  28. package/src/domains/creations/presentation/hooks/job-poller-utils.stale-handlers.ts +52 -0
  29. package/src/domains/creations/presentation/hooks/job-poller-utils.ts +8 -0
  30. package/src/domains/creations/presentation/hooks/useCreations.ts +1 -1
  31. package/src/domains/creations/presentation/hooks/useProcessingJobsPoller.ts +18 -235
  32. package/src/domains/creations/presentation/screens/CreationsGalleryScreen.tsx +1 -2
  33. package/src/domains/creations/presentation-exports.ts +2 -2
  34. package/src/domains/face-detection/domain/entities/FaceDetection.ts +4 -3
  35. package/src/domains/face-detection/presentation/hooks/useFaceDetection.ts +24 -21
  36. package/src/domains/generation/infrastructure/appearance-analysis/index.ts +5 -0
  37. package/src/domains/generation/infrastructure/couple-generation-builder/builder-couple-preparation.ts +58 -0
  38. package/src/domains/generation/infrastructure/couple-generation-builder/builder-couple-prompt.ts +69 -0
  39. package/src/domains/generation/infrastructure/couple-generation-builder/builder-couple-resolution.ts +77 -0
  40. package/src/domains/generation/infrastructure/couple-generation-builder/builder-couple.ts +54 -0
  41. package/src/domains/generation/infrastructure/couple-generation-builder/builder-index.ts +8 -0
  42. package/src/domains/generation/infrastructure/couple-generation-builder/builder-scenario.ts +112 -0
  43. package/src/domains/generation/infrastructure/couple-generation-builder/builder.ts +7 -0
  44. package/src/domains/generation/infrastructure/couple-generation-builder/index.ts +20 -0
  45. package/src/domains/generation/infrastructure/couple-generation-builder/types.ts +44 -0
  46. package/src/domains/generation/infrastructure/couple-generation-builder/utils/builder-end-logger.ts +18 -0
  47. package/src/domains/generation/infrastructure/couple-generation-builder/utils/builder-start-logger.ts +57 -0
  48. package/src/domains/generation/infrastructure/couple-generation-builder/utils/builder-step-logger.ts +106 -0
  49. package/src/domains/generation/infrastructure/couple-generation-builder/utils/index.ts +8 -0
  50. package/src/domains/generation/infrastructure/couple-generation-builder/utils/types.ts +49 -0
  51. package/src/domains/generation/infrastructure/couple-generation-builder/utils.ts +8 -0
  52. package/src/domains/generation/infrastructure/flow/flow-store-actions.ts +105 -0
  53. package/src/domains/generation/infrastructure/flow/flow-store-initial-state.ts +26 -0
  54. package/src/domains/generation/infrastructure/flow/useFlowStore.ts +4 -116
  55. package/src/domains/generation/presentation/useAIGeneration.hook.ts +1 -1
  56. package/src/domains/generation/wizard/infrastructure/strategies/image-generation-strategy-index.ts +7 -0
  57. package/src/domains/generation/wizard/infrastructure/strategies/image-generation.executor.ts +2 -12
  58. package/src/domains/generation/wizard/infrastructure/strategies/image-generation.executor.types.ts +11 -0
  59. package/src/domains/generation/wizard/infrastructure/strategies/image-generation.executor.utils.ts +12 -0
  60. package/src/domains/generation/wizard/infrastructure/strategies/image-generation.strategy.ts +1 -220
  61. package/src/domains/generation/wizard/infrastructure/strategies/image-input-builder.ts +66 -0
  62. package/src/domains/generation/wizard/infrastructure/strategies/image-input-extraction.ts +88 -0
  63. package/src/domains/generation/wizard/infrastructure/strategies/image-input-prompt-builder.ts +74 -0
  64. package/src/domains/generation/wizard/infrastructure/strategies/image-input-style-enhancements.ts +35 -0
  65. package/src/domains/generation/wizard/infrastructure/strategies/image-strategy-factory.ts +41 -0
  66. package/src/domains/generation/wizard/infrastructure/strategies/video-generation-executor-index.ts +10 -0
  67. package/src/domains/generation/wizard/infrastructure/strategies/video-generation-executor.ts +76 -0
  68. package/src/domains/generation/wizard/infrastructure/strategies/video-generation-input-builder.ts +46 -0
  69. package/src/domains/generation/wizard/infrastructure/strategies/video-generation-result-types.ts +17 -0
  70. package/src/domains/generation/wizard/infrastructure/strategies/video-generation-submission.ts +61 -0
  71. package/src/domains/generation/wizard/infrastructure/strategies/video-generation.audio-extractor.ts +27 -0
  72. package/src/domains/generation/wizard/infrastructure/strategies/video-generation.executor.ts +2 -176
  73. package/src/domains/generation/wizard/infrastructure/strategies/video-generation.input-builder.ts +90 -0
  74. package/src/domains/generation/wizard/infrastructure/strategies/video-generation.strategy.ts +3 -108
  75. package/src/domains/generation/wizard/infrastructure/strategies/video-generation.types.ts +0 -130
  76. package/src/domains/generation/wizard/infrastructure/strategies/video-generation.validation.ts +136 -0
  77. package/src/domains/generation/wizard/presentation/hooks/photo-upload/index.ts +40 -0
  78. package/src/domains/generation/wizard/presentation/hooks/photo-upload/types.ts +37 -0
  79. package/src/domains/generation/wizard/presentation/hooks/photo-upload/usePhotoUploadStateLogic.ts +142 -0
  80. package/src/domains/generation/wizard/presentation/hooks/use-video-queue-utils.ts +102 -0
  81. package/src/domains/generation/wizard/presentation/hooks/usePhotoBlockingGeneration.handlers.ts +97 -0
  82. package/src/domains/generation/wizard/presentation/hooks/usePhotoBlockingGeneration.saver.ts +54 -0
  83. package/src/domains/generation/wizard/presentation/hooks/usePhotoBlockingGeneration.ts +22 -87
  84. package/src/domains/generation/wizard/presentation/hooks/usePhotoUploadState.ts +8 -177
  85. package/src/domains/generation/wizard/presentation/hooks/useVideoQueueGeneration.ts +1 -295
  86. package/src/domains/generation/wizard/presentation/hooks/useWizardGeneration.ts +1 -1
  87. package/src/domains/generation/wizard/presentation/hooks/video-queue/index.ts +77 -0
  88. package/src/domains/generation/wizard/presentation/hooks/video-queue/use-video-queue-utils.ts +123 -0
  89. package/src/domains/generation/wizard/presentation/hooks/video-queue/useVideoQueueGenerationCallbacks.ts +119 -0
  90. package/src/domains/generation/wizard/presentation/hooks/video-queue/useVideoQueueGenerationPolling.ts +75 -0
  91. package/src/domains/generation/wizard/presentation/hooks/video-queue/useVideoQueueGenerationRefs.ts +65 -0
  92. package/src/domains/generation/wizard/presentation/hooks/video-queue/useVideoQueueGenerationStart.ts +123 -0
  93. package/src/domains/generation/wizard/presentation/hooks/video-queue-index.ts +9 -0
  94. package/src/domains/image-to-video/domain/types/image-to-video-state.types.ts +11 -4
  95. package/src/domains/text-to-image/domain/constants/index.ts +5 -6
  96. package/src/domains/text-to-image/domain/types/text-to-image.types.ts +43 -22
  97. package/src/domains/text-to-video/domain/types/request.types.ts +32 -9
  98. package/src/domains/text-to-video/domain/types/state.types.ts +22 -22
  99. package/src/domains/text-to-video/presentation/hooks/useTextToVideoForm.handlers.ts +44 -0
  100. package/src/domains/text-to-video/presentation/hooks/useTextToVideoForm.ts +5 -51
  101. package/src/domains/text-to-video/presentation/hooks/useTextToVideoForm.types.ts +33 -0
  102. package/src/exports/features.ts +1 -1
  103. package/src/infrastructure/services/generation-orchestrator.service.ts +2 -2
  104. package/src/infrastructure/utils/couple-input-context.ts +13 -0
  105. package/src/infrastructure/utils/couple-input-index.ts +9 -0
  106. package/src/infrastructure/utils/couple-input-photorealistic.ts +40 -0
  107. package/src/infrastructure/utils/couple-input-refiner.ts +101 -0
  108. package/src/infrastructure/utils/couple-input-resolver.ts +71 -0
  109. package/src/infrastructure/utils/couple-input-types.ts +11 -0
  110. package/src/infrastructure/utils/couple-input.util.ts +3 -176
  111. package/src/infrastructure/utils/photo-generation/photo-preparation.util.ts +1 -1
  112. package/src/infrastructure/validation/base-validator.ts +3 -26
  113. package/src/infrastructure/validation/base-validator.types.ts +32 -0
  114. package/src/presentation/hooks/generation/index.ts +1 -1
  115. package/src/presentation/hooks/generation/orchestrator-abort-logs.ts +48 -0
  116. package/src/presentation/hooks/generation/orchestrator-execution-logs.ts +67 -0
  117. package/src/presentation/hooks/generation/orchestrator-index.ts +14 -0
  118. package/src/presentation/hooks/generation/orchestrator-start-logs.ts +65 -0
  119. package/src/presentation/hooks/generation/orchestrator-state-utils.ts +17 -0
  120. package/src/presentation/hooks/generation/orchestrator-types.ts +55 -0
  121. package/src/presentation/hooks/generation/orchestrator-utils-index.ts +29 -0
  122. package/src/presentation/hooks/generation/orchestrator-utils.ts +25 -0
  123. package/src/presentation/hooks/generation/useDualImageGeneration.ts +1 -1
  124. package/src/presentation/hooks/generation/useImageGeneration.ts +1 -1
  125. package/src/presentation/hooks/generation/useVideoGeneration.ts +1 -1
  126. package/src/shared/hooks/factories/generation-hook-index.ts +12 -0
  127. package/src/shared/hooks/factories/generation-hook-types.ts +47 -0
  128. package/src/shared/hooks/factories/generation-hook-utils.ts +94 -0
  129. package/src/shared/hooks/factories/index.ts +1 -1
  130. package/src/shared/index.ts +1 -1
  131. package/src/shared/utils/calculations/aspect-ratio-calculations.ts +30 -0
  132. package/src/shared/utils/calculations/base64-calculations.ts +26 -0
  133. package/src/shared/utils/calculations/confidence-calculations.ts +21 -0
  134. package/src/shared/utils/calculations/cost-calculations-index.ts +43 -0
  135. package/src/shared/utils/calculations/cost-calculations.ts +25 -0
  136. package/src/shared/utils/calculations/credit-calculations.ts +37 -0
  137. package/src/shared/utils/calculations/index.ts +46 -0
  138. package/src/shared/utils/calculations/math-utilities.ts +32 -0
  139. package/src/shared/utils/calculations/memory-calculations.ts +33 -0
  140. package/src/shared/utils/calculations/pagination-calculations.ts +38 -0
  141. package/src/shared/utils/calculations/percentage-calculations.ts +33 -0
  142. package/src/shared/utils/calculations/time-calculations.ts +99 -0
  143. package/src/shared/utils/credit.ts +1 -1
  144. package/src/shared-kernel/application/hooks/index.ts +8 -0
  145. package/src/shared-kernel/application/hooks/use-feature-state.ts +106 -0
  146. package/src/shared-kernel/application/hooks/use-generation-handler.ts +110 -0
  147. package/src/shared-kernel/base-types/base-callbacks.types.ts +73 -0
  148. package/src/shared-kernel/base-types/base-feature-state.types.ts +77 -0
  149. package/src/shared-kernel/base-types/base-generation.types.ts +69 -0
  150. package/src/shared-kernel/base-types/index.ts +30 -0
  151. package/src/shared-kernel/domain/base-generation-strategy.ts +146 -0
  152. package/src/shared-kernel/domain/index.ts +7 -0
  153. package/src/shared-kernel/index.ts +17 -0
  154. package/src/shared-kernel/infrastructure/validation/common-validators.ts +126 -0
  155. package/src/shared-kernel/infrastructure/validation/common-validators.types.ts +33 -0
  156. package/src/shared-kernel/infrastructure/validation/error-handler.ts +52 -0
  157. package/src/shared-kernel/infrastructure/validation/error-handler.types.ts +38 -0
  158. package/src/shared-kernel/infrastructure/validation/error-handler.utils.ts +79 -0
  159. package/src/shared-kernel/infrastructure/validation/index.ts +70 -0
  160. package/src/domains/content-moderation/infrastructure/services/index.ts +0 -8
  161. package/src/domains/creations/domain/constants/index.ts +0 -12
  162. package/src/domains/creations/domain/utils/index.ts +0 -12
  163. package/src/domains/generation/infrastructure/couple-generation-builder.ts +0 -374
  164. package/src/domains/image-to-video/domain/index.ts +0 -2
  165. package/src/domains/image-to-video/infrastructure/index.ts +0 -1
  166. package/src/domains/image-to-video/presentation/index.ts +0 -5
  167. package/src/domains/text-to-video/domain/index.ts +0 -1
  168. package/src/domains/text-to-video/presentation/index.ts +0 -7
  169. package/src/presentation/hooks/generation/orchestrator.ts +0 -276
  170. package/src/shared/hooks/factories/createGenerationHook.ts +0 -253
  171. package/src/shared/utils/calculations.util.ts +0 -366
@@ -4,180 +4,11 @@
4
4
  * Uses design system's useMedia hook for media picking with built-in validation
5
5
  */
6
6
 
7
- import { useState, useCallback, useEffect, useRef } from "react";
8
-
9
- import { useMedia, MediaQuality, MediaValidationError, MEDIA_CONSTANTS } from "@umituz/react-native-design-system/media";
10
- import type { UploadedImage } from "../../../../../presentation/hooks/generation/useAIGenerateState";
11
-
12
- export interface PhotoUploadConfig {
13
- readonly maxFileSizeMB?: number;
14
- }
15
-
16
- export interface PhotoUploadTranslations {
17
- readonly fileTooLarge: string;
18
- readonly maxFileSize: string;
19
- readonly error: string;
20
- readonly uploadFailed: string;
21
- readonly permissionDenied?: string;
22
- }
23
-
24
- export interface PhotoUploadError {
25
- readonly title: string;
26
- readonly message: string;
27
- }
28
-
29
- export interface UsePhotoUploadStateProps {
30
- readonly config?: PhotoUploadConfig;
31
- readonly translations: PhotoUploadTranslations;
32
- readonly initialImage?: UploadedImage;
33
- readonly stepId?: string;
34
- readonly onError?: (error: PhotoUploadError) => void;
35
- }
36
-
37
- export interface UsePhotoUploadStateReturn {
38
- readonly image: UploadedImage | null;
39
- readonly handlePickImage: () => Promise<void>;
40
- readonly canContinue: boolean;
41
- readonly clearImage: () => void;
42
- }
43
-
44
- export const usePhotoUploadState = ({
45
- config,
46
- translations,
47
- initialImage,
48
- stepId,
49
- onError,
50
- }: UsePhotoUploadStateProps): UsePhotoUploadStateReturn => {
51
- const [image, setImage] = useState<UploadedImage | null>(initialImage || null);
52
- const { pickImage, isLoading } = useMedia();
53
- const timeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
54
-
55
- // Use refs to avoid effect re-runs on callback changes
56
- const onErrorRef = useRef(onError);
57
- const translationsRef = useRef(translations);
58
-
59
- useEffect(() => {
60
- onErrorRef.current = onError;
61
- translationsRef.current = translations;
62
- }, [onError, translations]);
63
-
64
- const maxFileSizeMB = config?.maxFileSizeMB ?? MEDIA_CONSTANTS.MAX_IMAGE_SIZE_MB;
65
-
66
- useEffect(() => {
67
- if (typeof __DEV__ !== "undefined" && __DEV__) {
68
- console.log("[usePhotoUploadState] Step changed, resetting image", { stepId, hasInitialImage: !!initialImage });
69
- }
70
- setImage(initialImage || null);
71
- }, [stepId, initialImage]);
72
-
73
- useEffect(() => {
74
- // Clear any existing timeout first
75
- if (timeoutRef.current) {
76
- clearTimeout(timeoutRef.current);
77
- timeoutRef.current = undefined;
78
- }
79
-
80
- if (isLoading) {
81
- timeoutRef.current = setTimeout(() => {
82
- if (typeof __DEV__ !== "undefined" && __DEV__) {
83
- console.warn("[usePhotoUploadState] Image picker timeout - possible stuck state (DEV warning only)");
84
- }
85
- // NOTE: Do NOT call onError here — the picker may still complete successfully.
86
- // Showing a modal alert while the picker is open blocks the UI.
87
- }, 30000);
88
- }
89
-
90
- return () => {
91
- if (timeoutRef.current) {
92
- clearTimeout(timeoutRef.current);
93
- timeoutRef.current = undefined;
94
- }
95
- };
96
- }, [isLoading]);
97
-
98
- const clearImage = useCallback(() => {
99
- setImage(null);
100
- }, []);
101
-
102
- const handlePickImage = useCallback(async () => {
103
- try {
104
- if (typeof __DEV__ !== "undefined" && __DEV__) {
105
- console.log("[usePhotoUploadState] Starting image pick");
106
- }
107
-
108
- const result = await pickImage({
109
- allowsEditing: true,
110
- aspect: [1, 1],
111
- quality: MediaQuality.MEDIUM,
112
- maxFileSizeMB,
113
- });
114
-
115
- if (result.error) {
116
- if (typeof __DEV__ !== "undefined" && __DEV__) {
117
- console.log("[usePhotoUploadState] Validation error", result.error);
118
- }
119
-
120
- if (result.error === MediaValidationError.FILE_TOO_LARGE) {
121
- onErrorRef.current?.({
122
- title: translationsRef.current.fileTooLarge,
123
- message: translationsRef.current.maxFileSize.replace("{size}", maxFileSizeMB.toString()),
124
- });
125
- } else if (result.error === MediaValidationError.PERMISSION_DENIED) {
126
- onErrorRef.current?.({
127
- title: translationsRef.current.error,
128
- message: translationsRef.current.permissionDenied ?? "Permission to access media library is required",
129
- });
130
- }
131
- return;
132
- }
133
-
134
- if (result.canceled || !result.assets || result.assets.length === 0) {
135
- if (typeof __DEV__ !== "undefined" && __DEV__) {
136
- console.log("[usePhotoUploadState] Image pick canceled");
137
- }
138
- return;
139
- }
140
-
141
- const selectedAsset = result.assets[0];
142
- if (!selectedAsset) {
143
- return;
144
- }
145
-
146
- const uploadedImage: UploadedImage = {
147
- uri: selectedAsset.uri,
148
- previewUrl: selectedAsset.uri,
149
- width: selectedAsset.width,
150
- height: selectedAsset.height,
151
- fileSize: selectedAsset.fileSize,
152
- };
153
-
154
- setImage(uploadedImage);
155
-
156
- if (typeof __DEV__ !== "undefined" && __DEV__) {
157
- const fileSizeMB = (selectedAsset.fileSize ?? 0) / (1024 * 1024);
158
- console.log("[usePhotoUploadState] Image selected", {
159
- width: uploadedImage.width,
160
- height: uploadedImage.height,
161
- fileSizeMB: fileSizeMB.toFixed(2),
162
- });
163
- }
164
- } catch (error) {
165
- if (typeof __DEV__ !== "undefined" && __DEV__) {
166
- console.error("[usePhotoUploadState] Error picking image", error);
167
- }
168
- onErrorRef.current?.({
169
- title: translationsRef.current.error,
170
- message: translationsRef.current.uploadFailed,
171
- });
172
- }
173
- }, [pickImage, maxFileSizeMB]);
174
-
175
- const canContinue = image !== null && !isLoading;
176
-
177
- return {
178
- image,
179
- handlePickImage,
180
- canContinue,
181
- clearImage,
182
- };
183
- };
7
+ export { usePhotoUploadState } from "./photo-upload";
8
+ export type {
9
+ PhotoUploadConfig,
10
+ PhotoUploadTranslations,
11
+ PhotoUploadError,
12
+ UsePhotoUploadStateProps,
13
+ UsePhotoUploadStateReturn,
14
+ } from "./photo-upload";
@@ -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,77 @@
1
+ /**
2
+ * Video Queue Generation Hook
3
+ */
4
+
5
+ import { useEffect } from "react";
6
+ import type {
7
+ UseVideoQueueGenerationProps,
8
+ UseVideoQueueGenerationReturn,
9
+ } from "../use-video-queue-generation.types";
10
+ import { useVideoQueueGenerationRefs, useVideoQueueGenerationState } from "./useVideoQueueGenerationRefs";
11
+ import { useCompletionHandler, useErrorHandler } from "./useVideoQueueGenerationCallbacks";
12
+ import { usePollStatus, useCallbackRefs } from "./useVideoQueueGenerationPolling";
13
+ import { useStartGeneration } from "./useVideoQueueGenerationStart";
14
+
15
+ export function useVideoQueueGeneration(props: UseVideoQueueGenerationProps): UseVideoQueueGenerationReturn {
16
+ const refs = useVideoQueueGenerationRefs();
17
+ const state = useVideoQueueGenerationState();
18
+
19
+ const clearPolling = (() => {
20
+ const { pollingRef } = refs;
21
+ return () => {
22
+ if (pollingRef.current) {
23
+ clearInterval(pollingRef.current);
24
+ pollingRef.current = null;
25
+ }
26
+ };
27
+ })();
28
+
29
+ useEffect(() => {
30
+ return () => {
31
+ clearPolling();
32
+ refs.isGeneratingRef.current = false;
33
+ refs.isPollingRef.current = false;
34
+ refs.consecutiveErrorsRef.current = 0;
35
+ refs.pollStartTimeRef.current = null;
36
+ // NOTE: Do NOT null creationIdRef/requestIdRef/modelRef here.
37
+ // In-flight poll callbacks may still resolve after unmount and need
38
+ // these refs to properly save the completed generation to Firestore.
39
+ // They are cleaned up by resetRefs() after handleComplete/handleError.
40
+ state.setIsGenerating(false);
41
+ };
42
+ }, [clearPolling, refs, state]);
43
+
44
+ const resetRefs = (() => {
45
+ const {
46
+ creationIdRef,
47
+ requestIdRef,
48
+ modelRef,
49
+ isGeneratingRef,
50
+ isPollingRef,
51
+ consecutiveErrorsRef,
52
+ pollStartTimeRef,
53
+ } = refs;
54
+ const { setIsGenerating } = state;
55
+
56
+ return () => {
57
+ creationIdRef.current = null;
58
+ requestIdRef.current = null;
59
+ modelRef.current = null;
60
+ isGeneratingRef.current = false;
61
+ isPollingRef.current = false;
62
+ consecutiveErrorsRef.current = 0;
63
+ pollStartTimeRef.current = null;
64
+ setIsGenerating(false);
65
+ };
66
+ })();
67
+
68
+ const handleComplete = useCompletionHandler(props, refs, state, clearPolling, resetRefs);
69
+ const handleError = useErrorHandler(props, refs, clearPolling, resetRefs);
70
+ const pollStatus = usePollStatus(refs);
71
+ const startGeneration = useStartGeneration(props, refs, state, clearPolling);
72
+
73
+ // Sync callback refs
74
+ useCallbackRefs(handleComplete, handleError, pollStatus, refs);
75
+
76
+ return { isGenerating: state.isGenerating, startGeneration };
77
+ }
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Video Queue Utilities
3
+ */
4
+
5
+ export interface VideoQueueUtils {
6
+ isQueueFull: () => boolean;
7
+ getQueueSize: () => number;
8
+ clearQueue: () => void;
9
+ }
10
+
11
+ export function createVideoQueueUtils(): VideoQueueUtils {
12
+ return {
13
+ isQueueFull: () => false,
14
+ getQueueSize: () => 0,
15
+ clearQueue: () => {},
16
+ };
17
+ }
18
+
19
+ import type { VideoQueueRefs } from "./useVideoQueueGenerationRefs";
20
+ import type { VideoQueueState } from "./useVideoQueueGenerationRefs";
21
+
22
+ /**
23
+ * Create a clear polling function
24
+ */
25
+ export function createClearPolling(refs: VideoQueueRefs): () => void {
26
+ const { pollingRef } = refs;
27
+ return () => {
28
+ if (pollingRef.current) {
29
+ clearInterval(pollingRef.current);
30
+ pollingRef.current = null;
31
+ }
32
+ };
33
+ }
34
+
35
+ /**
36
+ * Create a reset refs function
37
+ */
38
+ export function createResetRefs(
39
+ refs: VideoQueueRefs,
40
+ state: VideoQueueState
41
+ ): () => void {
42
+ const {
43
+ creationIdRef,
44
+ requestIdRef,
45
+ modelRef,
46
+ isGeneratingRef,
47
+ isPollingRef,
48
+ consecutiveErrorsRef,
49
+ pollStartTimeRef,
50
+ } = refs;
51
+ const { setIsGenerating } = state;
52
+
53
+ return () => {
54
+ creationIdRef.current = null;
55
+ requestIdRef.current = null;
56
+ modelRef.current = null;
57
+ isGeneratingRef.current = false;
58
+ isPollingRef.current = false;
59
+ consecutiveErrorsRef.current = 0;
60
+ pollStartTimeRef.current = null;
61
+ setIsGenerating(false);
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Log completion event
67
+ */
68
+ export function logCompletion(
69
+ creationId: string | null,
70
+ userId: string | undefined,
71
+ urls: { videoUrl?: string; imageUrl?: string },
72
+ hasCallback: boolean
73
+ ): void {
74
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
75
+ console.log("[VideoQueue] ✅ Generation completed", {
76
+ creationId,
77
+ userId,
78
+ hasVideoUrl: !!urls.videoUrl,
79
+ hasImageUrl: !!urls.imageUrl,
80
+ hasCallback,
81
+ });
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Log error event
87
+ */
88
+ export function logError(message: string, data?: Record<string, unknown>): void {
89
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
90
+ console.error("[VideoQueue] ❌ Error:", message, data);
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Validate completion data
96
+ */
97
+ export function validateCompletionData(
98
+ creationId: string | null,
99
+ userId: string | undefined,
100
+ uri: string
101
+ ): boolean {
102
+ return !!(creationId && userId && uri);
103
+ }
104
+
105
+ /**
106
+ * Extract input metadata for generation
107
+ */
108
+ export function extractInputMetadata(params: {
109
+ readonly model: string;
110
+ readonly prompt: string;
111
+ readonly imageUrls?: string[];
112
+ }): {
113
+ readonly model: string;
114
+ readonly prompt: string;
115
+ readonly imageUrls: string[];
116
+ } {
117
+ return {
118
+ model: params.model,
119
+ prompt: params.prompt,
120
+ imageUrls: params.imageUrls || [],
121
+ };
122
+ }
123
+