@umituz/react-native-ai-generation-content 1.61.62 → 1.61.64

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 (121) hide show
  1. package/package.json +1 -1
  2. package/src/core/index.ts +1 -1
  3. package/src/domain/entities/index.ts +1 -1
  4. package/src/domain/interfaces/ai-provider.interface.ts +1 -1
  5. package/src/domain/interfaces/index.ts +1 -1
  6. package/src/domains/background/domain/entities/index.ts +1 -0
  7. package/src/domains/background/domain/interfaces/index.ts +1 -0
  8. package/src/{domain → domains/background/domain}/interfaces/provider-job-manager.interface.ts +1 -1
  9. package/src/domains/background/domain/types/background-generation.types.ts +28 -0
  10. package/src/domains/background/infrastructure/executors/backgroundJobExecutor.ts +105 -0
  11. package/src/{infrastructure → domains/background/infrastructure}/services/job-poller-factory.ts +1 -1
  12. package/src/{infrastructure → domains/background/infrastructure}/services/job-poller.service.ts +1 -1
  13. package/src/{infrastructure → domains/background/infrastructure}/services/job-poller.types.ts +2 -2
  14. package/src/{infrastructure → domains/background/infrastructure}/utils/polling-interval.util.ts +1 -1
  15. package/src/{infrastructure → domains/background/infrastructure}/utils/status-checker.util.ts +1 -1
  16. package/src/domains/background/presentation/hooks/use-background-generation.ts +97 -0
  17. package/src/domains/creations/presentation/components/PendingJobsSection.tsx +1 -1
  18. package/src/domains/generation/wizard/presentation/hooks/generationExecutor.ts +65 -0
  19. package/src/domains/generation/wizard/presentation/hooks/generationStateMachine.ts +35 -0
  20. package/src/domains/generation/wizard/presentation/hooks/typeGuards.ts +13 -0
  21. package/src/domains/generation/wizard/presentation/hooks/useVideoQueueGeneration.ts +34 -71
  22. package/src/domains/generation/wizard/presentation/hooks/useWizardFlowHandlers.ts +6 -84
  23. package/src/domains/generation/wizard/presentation/hooks/useWizardGeneration.ts +19 -131
  24. package/src/domains/generation/wizard/presentation/hooks/videoQueuePoller.ts +59 -0
  25. package/src/domains/image-to-video/presentation/hooks/imageToVideoStrategy.ts +77 -0
  26. package/src/domains/image-to-video/presentation/hooks/useImageToVideoFeature.ts +102 -0
  27. package/src/domains/scenarios/presentation/containers/CategoryNavigationContainer.tsx +4 -80
  28. package/src/{features → domains}/text-to-image/infrastructure/services/text-to-image-executor.ts +2 -82
  29. package/src/domains/text-to-image/infrastructure/utils/imageResultExtractor.ts +58 -0
  30. package/src/domains/text-to-video/presentation/hooks/textToVideoStrategy.ts +75 -0
  31. package/src/domains/text-to-video/presentation/hooks/useTextToVideoFeature.ts +120 -0
  32. package/src/exports/features.ts +12 -12
  33. package/src/presentation/components/PendingJobCard.tsx +1 -1
  34. package/src/features/image-to-video/presentation/hooks/useImageToVideoFeature.ts +0 -186
  35. package/src/features/text-to-video/presentation/hooks/useTextToVideoFeature.ts +0 -186
  36. package/src/presentation/hooks/use-background-generation.ts +0 -185
  37. /package/src/{domain → domains/background/domain}/entities/job.types.ts +0 -0
  38. /package/src/{infrastructure → domains/background/infrastructure}/utils/result-validator.util.ts +0 -0
  39. /package/src/{presentation → domains/background/presentation}/hooks/use-pending-jobs.ts +0 -0
  40. /package/src/{features → domains}/image-to-video/README.md +0 -0
  41. /package/src/{features → domains}/image-to-video/domain/constants/animation.constants.ts +0 -0
  42. /package/src/{features → domains}/image-to-video/domain/constants/duration.constants.ts +0 -0
  43. /package/src/{features → domains}/image-to-video/domain/constants/form.constants.ts +0 -0
  44. /package/src/{features → domains}/image-to-video/domain/constants/index.ts +0 -0
  45. /package/src/{features → domains}/image-to-video/domain/constants/music.constants.ts +0 -0
  46. /package/src/{features → domains}/image-to-video/domain/index.ts +0 -0
  47. /package/src/{features → domains}/image-to-video/domain/types/animation.types.ts +0 -0
  48. /package/src/{features → domains}/image-to-video/domain/types/config.types.ts +0 -0
  49. /package/src/{features → domains}/image-to-video/domain/types/duration.types.ts +0 -0
  50. /package/src/{features → domains}/image-to-video/domain/types/form.types.ts +0 -0
  51. /package/src/{features → domains}/image-to-video/domain/types/image-to-video.types.ts +0 -0
  52. /package/src/{features → domains}/image-to-video/domain/types/index.ts +0 -0
  53. /package/src/{features → domains}/image-to-video/domain/types/music.types.ts +0 -0
  54. /package/src/{features → domains}/image-to-video/index.ts +0 -0
  55. /package/src/{features → domains}/image-to-video/infrastructure/index.ts +0 -0
  56. /package/src/{features → domains}/image-to-video/infrastructure/services/image-to-video-executor.ts +0 -0
  57. /package/src/{features → domains}/image-to-video/infrastructure/services/index.ts +0 -0
  58. /package/src/{features → domains}/image-to-video/presentation/components/AddMoreCard.tsx +0 -0
  59. /package/src/{features → domains}/image-to-video/presentation/components/AnimationStyleSelector.tsx +0 -0
  60. /package/src/{features → domains}/image-to-video/presentation/components/DurationSelector.tsx +0 -0
  61. /package/src/{features → domains}/image-to-video/presentation/components/EmptyGridState.tsx +0 -0
  62. /package/src/{features → domains}/image-to-video/presentation/components/GridImageItem.tsx +0 -0
  63. /package/src/{features → domains}/image-to-video/presentation/components/ImageSelectionGrid.styles.ts +0 -0
  64. /package/src/{features → domains}/image-to-video/presentation/components/ImageSelectionGrid.tsx +0 -0
  65. /package/src/{features → domains}/image-to-video/presentation/components/ImageSelectionGrid.types.ts +0 -0
  66. /package/src/{features → domains}/image-to-video/presentation/components/MusicMoodSelector.tsx +0 -0
  67. /package/src/{features → domains}/image-to-video/presentation/components/index.ts +0 -0
  68. /package/src/{features → domains}/image-to-video/presentation/hooks/image-to-video-feature.types.ts +0 -0
  69. /package/src/{features → domains}/image-to-video/presentation/hooks/index.ts +0 -0
  70. /package/src/{features → domains}/image-to-video/presentation/hooks/useFormState.ts +0 -0
  71. /package/src/{features → domains}/image-to-video/presentation/hooks/useGeneration.ts +0 -0
  72. /package/src/{features → domains}/image-to-video/presentation/hooks/useImageToVideoForm.ts +0 -0
  73. /package/src/{features → domains}/image-to-video/presentation/index.ts +0 -0
  74. /package/src/{features → domains}/image-to-video/presentation/screens/ImageToVideoWizardFlow.tsx +0 -0
  75. /package/src/{features → domains}/shared/index.ts +0 -0
  76. /package/src/{features → domains}/shared/presentation/components/AutoSkipPreview.tsx +0 -0
  77. /package/src/{features → domains}/shared/presentation/components/index.ts +0 -0
  78. /package/src/{features → domains}/shared/presentation/utils/index.ts +0 -0
  79. /package/src/{features → domains}/shared/presentation/utils/wizard-flow.utils.ts +0 -0
  80. /package/src/{features → domains}/text-to-image/README.md +0 -0
  81. /package/src/{features → domains}/text-to-image/domain/constants/index.ts +0 -0
  82. /package/src/{features → domains}/text-to-image/domain/constants/options.constants.ts +0 -0
  83. /package/src/{features → domains}/text-to-image/domain/constants/styles.constants.ts +0 -0
  84. /package/src/{features → domains}/text-to-image/domain/index.ts +0 -0
  85. /package/src/{features → domains}/text-to-image/domain/types/config.types.ts +0 -0
  86. /package/src/{features → domains}/text-to-image/domain/types/form.types.ts +0 -0
  87. /package/src/{features → domains}/text-to-image/domain/types/index.ts +0 -0
  88. /package/src/{features → domains}/text-to-image/domain/types/text-to-image.types.ts +0 -0
  89. /package/src/{features → domains}/text-to-image/index.ts +0 -0
  90. /package/src/{features → domains}/text-to-image/infrastructure/index.ts +0 -0
  91. /package/src/{features → domains}/text-to-image/infrastructure/services/index.ts +0 -0
  92. /package/src/{features → domains}/text-to-image/presentation/components/index.ts +0 -0
  93. /package/src/{features → domains}/text-to-image/presentation/hooks/index.ts +0 -0
  94. /package/src/{features → domains}/text-to-image/presentation/hooks/useFormState.ts +0 -0
  95. /package/src/{features → domains}/text-to-image/presentation/hooks/useGeneration.ts +0 -0
  96. /package/src/{features → domains}/text-to-image/presentation/hooks/useTextToImageForm.ts +0 -0
  97. /package/src/{features → domains}/text-to-image/presentation/index.ts +0 -0
  98. /package/src/{features → domains}/text-to-image/presentation/screens/TextToImageWizardFlow.tsx +0 -0
  99. /package/src/{features → domains}/text-to-image/presentation/screens/TextToImageWizardFlow.types.ts +0 -0
  100. /package/src/{features → domains}/text-to-video/README.md +0 -0
  101. /package/src/{features → domains}/text-to-video/domain/index.ts +0 -0
  102. /package/src/{features → domains}/text-to-video/domain/types/callback.types.ts +0 -0
  103. /package/src/{features → domains}/text-to-video/domain/types/component.types.ts +0 -0
  104. /package/src/{features → domains}/text-to-video/domain/types/config.types.ts +0 -0
  105. /package/src/{features → domains}/text-to-video/domain/types/index.ts +0 -0
  106. /package/src/{features → domains}/text-to-video/domain/types/request.types.ts +0 -0
  107. /package/src/{features → domains}/text-to-video/domain/types/state.types.ts +0 -0
  108. /package/src/{features → domains}/text-to-video/index.ts +0 -0
  109. /package/src/{features → domains}/text-to-video/infrastructure/index.ts +0 -0
  110. /package/src/{features → domains}/text-to-video/infrastructure/services/index.ts +0 -0
  111. /package/src/{features → domains}/text-to-video/infrastructure/services/text-to-video-executor.ts +0 -0
  112. /package/src/{features → domains}/text-to-video/presentation/components/FrameSelector.tsx +0 -0
  113. /package/src/{features → domains}/text-to-video/presentation/components/GenerationTabs.tsx +0 -0
  114. /package/src/{features → domains}/text-to-video/presentation/components/HeroSection.tsx +0 -0
  115. /package/src/{features → domains}/text-to-video/presentation/components/HintCarousel.tsx +0 -0
  116. /package/src/{features → domains}/text-to-video/presentation/components/OptionsPanel.tsx +0 -0
  117. /package/src/{features → domains}/text-to-video/presentation/components/index.ts +0 -0
  118. /package/src/{features → domains}/text-to-video/presentation/hooks/index.ts +0 -0
  119. /package/src/{features → domains}/text-to-video/presentation/hooks/useTextToVideoForm.ts +0 -0
  120. /package/src/{features → domains}/text-to-video/presentation/index.ts +0 -0
  121. /package/src/{features → domains}/text-to-video/presentation/screens/TextToVideoWizardFlow.tsx +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-ai-generation-content",
3
- "version": "1.61.62",
3
+ "version": "1.61.64",
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",
package/src/core/index.ts CHANGED
@@ -57,7 +57,7 @@ export type {
57
57
  // Segregated provider sub-interfaces
58
58
  export type { IAIProviderLifecycle } from "../domain/interfaces/provider-lifecycle.interface";
59
59
  export type { IAIProviderCapabilities } from "../domain/interfaces/provider-capabilities.interface";
60
- export type { IAIProviderJobManager } from "../domain/interfaces/provider-job-manager.interface";
60
+ export type { IAIProviderJobManager } from "../domains/background/domain/interfaces/provider-job-manager.interface";
61
61
  export type { IAIProviderExecutor } from "../domain/interfaces/provider-executor.interface";
62
62
  export type { IAIProviderImageFeatures } from "../domain/interfaces/provider-image-features.interface";
63
63
  export type { IAIProviderVideoFeatures } from "../domain/interfaces/provider-video-features.interface";
@@ -6,6 +6,6 @@
6
6
  export * from "./error.types";
7
7
  export * from "./generation.types";
8
8
  export * from "./polling.types";
9
- export * from "./job.types";
9
+ export * from "../../domains/background/domain/entities/job.types";
10
10
  export * from "./processing-modes.types";
11
11
  export * from "./flow-config.types";
@@ -155,7 +155,7 @@ export interface VideoFeatureInputData {
155
155
 
156
156
  import type { IAIProviderLifecycle } from "./provider-lifecycle.interface";
157
157
  import type { IAIProviderCapabilities } from "./provider-capabilities.interface";
158
- import type { IAIProviderJobManager } from "./provider-job-manager.interface";
158
+ import type { IAIProviderJobManager } from "../../domains/background/domain/interfaces/provider-job-manager.interface";
159
159
  import type { IAIProviderExecutor } from "./provider-executor.interface";
160
160
  import type { IAIProviderImageFeatures } from "./provider-image-features.interface";
161
161
  import type { IAIProviderVideoFeatures } from "./provider-video-features.interface";
@@ -9,7 +9,7 @@ export * from "./app-services.interface";
9
9
  // Interface Segregation - Split provider interfaces
10
10
  export type { IAIProviderLifecycle } from "./provider-lifecycle.interface";
11
11
  export type { IAIProviderCapabilities } from "./provider-capabilities.interface";
12
- export type { IAIProviderJobManager } from "./provider-job-manager.interface";
12
+ export type { IAIProviderJobManager } from "../../domains/background/domain/interfaces/provider-job-manager.interface";
13
13
  export type { IAIProviderExecutor } from "./provider-executor.interface";
14
14
  export type { IAIProviderImageFeatures } from "./provider-image-features.interface";
15
15
  export type { IAIProviderVideoFeatures } from "./provider-video-features.interface";
@@ -0,0 +1 @@
1
+ export * from "./job.types";
@@ -0,0 +1 @@
1
+ export * from "./provider-job-manager.interface";
@@ -3,7 +3,7 @@
3
3
  * Single Responsibility: Async job submission, status checking, and result retrieval
4
4
  */
5
5
 
6
- import type { JobSubmission, JobStatus } from "./ai-provider.interface";
6
+ import type { JobSubmission, JobStatus } from "../../../../domain/interfaces/ai-provider.interface";
7
7
 
8
8
  export interface IAIProviderJobManager {
9
9
  /**
@@ -0,0 +1,28 @@
1
+ import type {
2
+ BackgroundJob,
3
+ BackgroundQueueConfig,
4
+ JobExecutorConfig,
5
+ } from "../entities/job.types";
6
+ import type { executeDirectGeneration } from "../../infrastructure/executors/backgroundJobExecutor";
7
+
8
+ export type { DirectExecutionResult } from "../../infrastructure/executors/backgroundJobExecutor";
9
+
10
+ export interface UseBackgroundGenerationOptions<TInput, TResult>
11
+ extends Partial<BackgroundQueueConfig> {
12
+ readonly executor: JobExecutorConfig<TInput, TResult>;
13
+ readonly onJobComplete?: (job: BackgroundJob<TInput, TResult>) => void;
14
+ readonly onJobError?: (job: BackgroundJob<TInput, TResult>) => void;
15
+ readonly onAllComplete?: () => void;
16
+ readonly onProgress?: (progress: number) => void;
17
+ }
18
+
19
+ export interface UseBackgroundGenerationReturn<TInput, TResult> {
20
+ readonly startJob: (input: TInput, type: string) => Promise<string>;
21
+ readonly executeDirectly: (input: TInput) => ReturnType<typeof executeDirectGeneration<TInput, TResult>>;
22
+ readonly cancelJob: (id: string) => void;
23
+ readonly pendingJobs: BackgroundJob<TInput, TResult>[];
24
+ readonly activeJobCount: number;
25
+ readonly hasActiveJobs: boolean;
26
+ readonly isProcessing: boolean;
27
+ readonly progress: number;
28
+ }
@@ -0,0 +1,105 @@
1
+ import type { BackgroundJob, JobExecutorConfig } from "../../domain/entities/job.types";
2
+
3
+ export interface DirectExecutionResult<TResult> {
4
+ readonly success: boolean;
5
+ readonly result?: TResult;
6
+ readonly error?: string;
7
+ }
8
+
9
+ interface DirectExecutionParams<TInput, TResult> {
10
+ input: TInput;
11
+ executor: JobExecutorConfig<TInput, TResult>;
12
+ onProgress?: (progress: number) => void;
13
+ setProgress: (progress: number) => void;
14
+ setIsProcessing: (isProcessing: boolean) => void;
15
+ }
16
+
17
+ export const executeDirectGeneration = async <TInput, TResult>(
18
+ params: DirectExecutionParams<TInput, TResult>,
19
+ ): Promise<DirectExecutionResult<TResult>> => {
20
+ const { input, executor, onProgress, setProgress, setIsProcessing } = params;
21
+
22
+ setIsProcessing(true);
23
+ setProgress(0);
24
+
25
+ try {
26
+ const result = await executor.execute(input, (p) => {
27
+ setProgress(p);
28
+ onProgress?.(p);
29
+ });
30
+
31
+ setProgress(100);
32
+ return { success: true, result };
33
+ } catch (error) {
34
+ const errorMsg = error instanceof Error ? error.message : String(error);
35
+ return { success: false, error: errorMsg };
36
+ } finally {
37
+ setIsProcessing(false);
38
+ }
39
+ };
40
+
41
+ interface QueuedExecutionParams<TInput, TResult> {
42
+ jobId: string;
43
+ input: TInput;
44
+ executor: JobExecutorConfig<TInput, TResult>;
45
+ updateJob: (params: { id: string; updates: Partial<BackgroundJob<TInput, TResult>> }) => void;
46
+ removeJob: (id: string) => void;
47
+ getJob: (id: string) => BackgroundJob<TInput, TResult> | undefined;
48
+ activeJobsRef: React.MutableRefObject<Set<string>>;
49
+ onJobComplete?: (job: BackgroundJob<TInput, TResult>) => void;
50
+ onJobError?: (job: BackgroundJob<TInput, TResult>) => void;
51
+ onAllComplete?: () => void;
52
+ }
53
+
54
+ export const executeQueuedJob = async <TInput, TResult>(
55
+ params: QueuedExecutionParams<TInput, TResult>,
56
+ ): Promise<void> => {
57
+ const {
58
+ jobId,
59
+ input,
60
+ executor,
61
+ updateJob,
62
+ removeJob,
63
+ getJob,
64
+ activeJobsRef,
65
+ onJobComplete,
66
+ onJobError,
67
+ onAllComplete,
68
+ } = params;
69
+
70
+ try {
71
+ updateJob({ id: jobId, updates: { status: "processing", progress: 10 } });
72
+
73
+ const result = await executor.execute(input, (p) => {
74
+ updateJob({ id: jobId, updates: { progress: p } });
75
+ });
76
+
77
+ updateJob({
78
+ id: jobId,
79
+ updates: { status: "completed", progress: 100, result, completedAt: new Date() },
80
+ });
81
+
82
+ const completedJob = getJob(jobId);
83
+ if (completedJob) {
84
+ await executor.onComplete?.(completedJob);
85
+ onJobComplete?.(completedJob);
86
+ }
87
+
88
+ removeJob(jobId);
89
+ } catch (error) {
90
+ const errorMsg = error instanceof Error ? error.message : String(error);
91
+
92
+ updateJob({ id: jobId, updates: { status: "failed", error: errorMsg, progress: 0 } });
93
+
94
+ const failedJob = getJob(jobId);
95
+ if (failedJob) {
96
+ await executor.onError?.(failedJob, error instanceof Error ? error : new Error(errorMsg));
97
+ onJobError?.(failedJob);
98
+ }
99
+ } finally {
100
+ activeJobsRef.current.delete(jobId);
101
+ if (activeJobsRef.current.size === 0) {
102
+ onAllComplete?.();
103
+ }
104
+ }
105
+ };
@@ -3,7 +3,7 @@
3
3
  * Creates pre-configured job poller instances
4
4
  */
5
5
 
6
- import type { PollingConfig } from "../../domain/entities";
6
+ import type { PollingConfig } from "../../../../domain/entities/polling.types";
7
7
  import type { PollJobOptions } from "./job-poller.types";
8
8
  import { pollJob } from "./job-poller.service";
9
9
 
@@ -4,7 +4,7 @@
4
4
  * Reports only real status - no fake progress
5
5
  */
6
6
 
7
- import { DEFAULT_POLLING_CONFIG } from "../../domain/entities";
7
+ import { DEFAULT_POLLING_CONFIG } from "../../../../domain/entities/polling.types";
8
8
  import { calculatePollingInterval } from "../utils/polling-interval.util";
9
9
  import { checkStatusForErrors, isJobComplete } from "../utils/status-checker.util";
10
10
  import { validateResult } from "../utils/result-validator.util";
@@ -2,8 +2,8 @@
2
2
  * Job Poller Type Definitions
3
3
  */
4
4
 
5
- import type { IAIProvider, JobStatus } from "../../domain/interfaces";
6
- import type { PollingConfig } from "../../domain/entities";
5
+ import type { IAIProvider, JobStatus } from "../../../../domain/interfaces/ai-provider.interface";
6
+ import type { PollingConfig } from "../../../../domain/entities/polling.types";
7
7
 
8
8
  export interface PollJobOptions {
9
9
  provider: IAIProvider;
@@ -6,7 +6,7 @@
6
6
  import {
7
7
  DEFAULT_POLLING_CONFIG,
8
8
  type PollingConfig,
9
- } from "../../domain/entities";
9
+ } from "../../../../domain/entities/polling.types";
10
10
 
11
11
  export interface IntervalOptions {
12
12
  attempt: number;
@@ -3,7 +3,7 @@
3
3
  * Checks job status responses for errors
4
4
  */
5
5
 
6
- import type { JobStatus, AILogEntry } from "../../domain/interfaces";
6
+ import type { JobStatus, AILogEntry } from "../../../../domain/interfaces/ai-provider.interface";
7
7
 
8
8
  export interface StatusCheckResult {
9
9
  status: string;
@@ -0,0 +1,97 @@
1
+ import { useCallback, useRef, useState } from "react";
2
+ import { usePendingJobs } from "./use-pending-jobs";
3
+ import { executeDirectGeneration, executeQueuedJob } from "../../infrastructure/executors/backgroundJobExecutor";
4
+ import { DEFAULT_QUEUE_CONFIG } from "../../domain/entities/job.types";
5
+ import type {
6
+ UseBackgroundGenerationOptions,
7
+ UseBackgroundGenerationReturn,
8
+ } from "../../domain/types/background-generation.types";
9
+
10
+ export type { DirectExecutionResult, UseBackgroundGenerationOptions, UseBackgroundGenerationReturn } from "../../domain/types/background-generation.types";
11
+
12
+ export function useBackgroundGeneration<TInput = unknown, TResult = unknown>(
13
+ options: UseBackgroundGenerationOptions<TInput, TResult>,
14
+ ): UseBackgroundGenerationReturn<TInput, TResult> {
15
+ const config = { ...DEFAULT_QUEUE_CONFIG, ...options };
16
+ const activeJobsRef = useRef<Set<string>>(new Set());
17
+ const jobInputsRef = useRef<Map<string, { input: TInput; type: string }>>(
18
+ new Map(),
19
+ );
20
+
21
+ const [isProcessing, setIsProcessing] = useState(false);
22
+ const [progress, setProgress] = useState(0);
23
+
24
+ const { jobs, addJobAsync, updateJob, removeJob, getJob } = usePendingJobs<
25
+ TInput,
26
+ TResult
27
+ >({
28
+ queryKey: config.queryKey,
29
+ });
30
+
31
+ const { executor, onProgress, onJobComplete, onJobError, onAllComplete } = options;
32
+
33
+ const executeDirectly = useCallback(
34
+ (input: TInput) =>
35
+ executeDirectGeneration({ input, executor, onProgress, setProgress, setIsProcessing }),
36
+ [executor, onProgress],
37
+ );
38
+
39
+ const executeJob = useCallback(
40
+ (jobId: string, input: TInput) =>
41
+ executeQueuedJob({
42
+ jobId,
43
+ input,
44
+ executor,
45
+ updateJob,
46
+ removeJob,
47
+ getJob,
48
+ activeJobsRef,
49
+ onJobComplete,
50
+ onJobError,
51
+ onAllComplete,
52
+ }),
53
+ [executor, onJobComplete, onJobError, onAllComplete, updateJob, removeJob, getJob],
54
+ );
55
+
56
+ const startJob = useCallback(
57
+ async (input: TInput, type: string): Promise<string> => {
58
+ const jobId = `job-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
59
+
60
+ jobInputsRef.current.set(jobId, { input, type });
61
+
62
+ await addJobAsync({
63
+ id: jobId,
64
+ input,
65
+ type,
66
+ status: "queued",
67
+ progress: 0,
68
+ });
69
+
70
+ activeJobsRef.current.add(jobId);
71
+ void executeJob(jobId, input);
72
+
73
+ return jobId;
74
+ },
75
+ [addJobAsync, executeJob],
76
+ );
77
+
78
+ const cancelJob = useCallback(
79
+ (id: string) => {
80
+ activeJobsRef.current.delete(id);
81
+ jobInputsRef.current.delete(id);
82
+ removeJob(id);
83
+ },
84
+ [removeJob],
85
+ );
86
+
87
+ return {
88
+ startJob,
89
+ executeDirectly,
90
+ cancelJob,
91
+ pendingJobs: jobs,
92
+ activeJobCount: activeJobsRef.current.size,
93
+ hasActiveJobs: activeJobsRef.current.size > 0,
94
+ isProcessing,
95
+ progress,
96
+ };
97
+ }
@@ -6,7 +6,7 @@
6
6
  import React from "react";
7
7
  import { View, StyleSheet } from "react-native";
8
8
  import { AtomicText, useAppDesignTokens } from "@umituz/react-native-design-system";
9
- import type { BackgroundJob } from "../../../../domain/entities/job.types";
9
+ import type { BackgroundJob } from "../../../background/domain/entities/job.types";
10
10
  import { PendingJobCard } from "../../../../presentation/components/PendingJobCard";
11
11
 
12
12
  export interface PendingJobsSectionProps {
@@ -0,0 +1,65 @@
1
+ import { buildWizardInput } from "../../infrastructure/strategies";
2
+ import type { WizardScenarioData } from "./wizard-generation.types";
3
+ import type { Scenario } from "../../../../scenarios/domain/scenario.types";
4
+ import type { GenerationAction } from "./wizard-state-machine";
5
+
6
+ declare const __DEV__: boolean;
7
+
8
+ interface ExecuteGenerationParams {
9
+ wizardData: WizardScenarioData;
10
+ scenario: Scenario;
11
+ isVideoMode: boolean;
12
+ isMountedRef: React.MutableRefObject<boolean>;
13
+ dispatch: React.Dispatch<GenerationAction>;
14
+ onError?: (error: string) => void;
15
+ videoGenerationFn: (input: any, prompt: string) => Promise<void>;
16
+ photoGenerationFn: (input: any, prompt: string) => Promise<void>;
17
+ }
18
+
19
+ export const executeWizardGeneration = async (params: ExecuteGenerationParams): Promise<void> => {
20
+ const {
21
+ wizardData,
22
+ scenario,
23
+ isVideoMode,
24
+ isMountedRef,
25
+ dispatch,
26
+ onError,
27
+ videoGenerationFn,
28
+ photoGenerationFn,
29
+ } = params;
30
+
31
+ try {
32
+ const input = await buildWizardInput(wizardData, scenario);
33
+
34
+ if (!isMountedRef.current) return;
35
+
36
+ if (!input) {
37
+ dispatch({ type: "ERROR", error: "Failed to build generation input" });
38
+ onError?.("Failed to build generation input");
39
+ return;
40
+ }
41
+
42
+ dispatch({ type: "START_GENERATION" });
43
+
44
+ if (__DEV__) {
45
+ console.log("[WizardGeneration] GENERATING -", isVideoMode ? "VIDEO" : "PHOTO");
46
+ }
47
+
48
+ const typedInput = input as { prompt?: string };
49
+ const generationFn = isVideoMode ? videoGenerationFn : photoGenerationFn;
50
+ await generationFn(input, typedInput.prompt || "");
51
+
52
+ if (isMountedRef.current) {
53
+ dispatch({ type: "COMPLETE" });
54
+ }
55
+ } catch (error: any) {
56
+ if (!isMountedRef.current) return;
57
+
58
+ const errorMsg = error?.message || "error.generation.unknown";
59
+ if (__DEV__) {
60
+ console.error("[WizardGeneration] Error:", errorMsg, error);
61
+ }
62
+ dispatch({ type: "ERROR", error: errorMsg });
63
+ onError?.(errorMsg);
64
+ }
65
+ };
@@ -0,0 +1,35 @@
1
+ export type GenerationStatus = "IDLE" | "PREPARING" | "GENERATING" | "ERROR" | "COMPLETED";
2
+
3
+ export interface GenerationState {
4
+ status: GenerationStatus;
5
+ error?: string;
6
+ }
7
+
8
+ export type GenerationAction =
9
+ | { type: "START_PREPARATION" }
10
+ | { type: "START_GENERATION" }
11
+ | { type: "COMPLETE" }
12
+ | { type: "ERROR"; error: string }
13
+ | { type: "RESET" };
14
+
15
+ export const generationReducer = (
16
+ state: GenerationState,
17
+ action: GenerationAction,
18
+ ): GenerationState => {
19
+ switch (action.type) {
20
+ case "START_PREPARATION":
21
+ return { status: "PREPARING" };
22
+ case "START_GENERATION":
23
+ return { status: "GENERATING" };
24
+ case "COMPLETE":
25
+ return { status: "COMPLETED" };
26
+ case "ERROR":
27
+ return { status: "ERROR", error: action.error };
28
+ case "RESET":
29
+ return { status: "IDLE" };
30
+ default:
31
+ return state;
32
+ }
33
+ };
34
+
35
+ export const INITIAL_STATE: GenerationState = { status: "IDLE" };
@@ -0,0 +1,13 @@
1
+ import type { Creation } from "../../../../creations/domain/entities/Creation";
2
+
3
+ export const isCreation = (result: unknown): result is Creation => {
4
+ if (!result || typeof result !== "object") return false;
5
+ const creation = result as Partial<Creation>;
6
+ return (
7
+ typeof creation.id === "string" &&
8
+ typeof creation.uri === "string" &&
9
+ typeof creation.type === "string" &&
10
+ creation.createdAt instanceof Date &&
11
+ typeof creation.isShared === "boolean"
12
+ );
13
+ };
@@ -1,19 +1,10 @@
1
- /**
2
- * useVideoQueueGeneration Hook
3
- * Handles video generation via FAL queue with background support
4
- * - Submits to queue for non-blocking generation
5
- * - Polls for completion status
6
- * - Supports background generation (user can dismiss wizard)
7
- */
8
-
9
1
  import { useEffect, useRef, useCallback, useState } from "react";
10
- import { providerRegistry } from "../../../../../infrastructure/services/provider-registry.service";
11
- import { extractResultUrl, type FalResult, type GenerationUrls } from "./generation-result.utils";
12
- import { QUEUE_STATUS } from "../../../../../domain/constants/queue-status.constants";
2
+ import { pollQueueStatus } from "./videoQueuePoller";
13
3
  import { DEFAULT_POLL_INTERVAL_MS } from "../../../../../infrastructure/constants/polling.constants";
14
4
  import type { CreationPersistence } from "../../infrastructure/utils/creation-persistence.util";
15
5
  import type { WizardStrategy } from "../../infrastructure/strategies/wizard-strategy.types";
16
6
  import type { WizardScenarioData } from "./wizard-generation.types";
7
+ import type { GenerationUrls } from "./generation-result.utils";
17
8
 
18
9
  declare const __DEV__: boolean;
19
10
 
@@ -31,9 +22,7 @@ export interface UseVideoQueueGenerationReturn {
31
22
  readonly startGeneration: (input: unknown, prompt: string) => Promise<void>;
32
23
  }
33
24
 
34
- export function useVideoQueueGeneration(
35
- props: UseVideoQueueGenerationProps,
36
- ): UseVideoQueueGenerationReturn {
25
+ export function useVideoQueueGeneration(props: UseVideoQueueGenerationProps): UseVideoQueueGenerationReturn {
37
26
  const { userId, scenario, persistence, strategy, onSuccess, onError } = props;
38
27
 
39
28
  const creationIdRef = useRef<string | null>(null);
@@ -44,7 +33,6 @@ export function useVideoQueueGeneration(
44
33
  const isPollingRef = useRef(false);
45
34
  const [isGenerating, setIsGenerating] = useState(false);
46
35
 
47
- // Cleanup polling on unmount
48
36
  useEffect(() => {
49
37
  return () => {
50
38
  if (pollingRef.current) {
@@ -74,7 +62,7 @@ export function useVideoQueueGeneration(
74
62
  videoUrl: urls.videoUrl,
75
63
  });
76
64
  } catch (err) {
77
- if (typeof __DEV__ !== "undefined" && __DEV__) console.error("[VideoQueueGeneration] updateToCompleted error:", err);
65
+ if (__DEV__) console.error("[VideoQueueGeneration] updateToCompleted error:", err);
78
66
  }
79
67
  }
80
68
  resetRefs();
@@ -90,7 +78,7 @@ export function useVideoQueueGeneration(
90
78
  try {
91
79
  await persistence.updateToFailed(userId, creationId, errorMsg);
92
80
  } catch (err) {
93
- if (typeof __DEV__ !== "undefined" && __DEV__) console.error("[VideoQueueGeneration] updateToFailed error:", err);
81
+ if (__DEV__) console.error("[VideoQueueGeneration] updateToFailed error:", err);
94
82
  }
95
83
  }
96
84
  resetRefs();
@@ -99,75 +87,52 @@ export function useVideoQueueGeneration(
99
87
  [userId, persistence, onError, resetRefs],
100
88
  );
101
89
 
102
- const pollQueueStatus = useCallback(async () => {
103
- // Guard against concurrent polls
104
- if (isPollingRef.current) return;
105
-
90
+ const pollStatus = useCallback(async () => {
106
91
  const requestId = requestIdRef.current;
107
92
  const model = modelRef.current;
108
- const provider = providerRegistry.getActiveProvider();
109
- if (!requestId || !model || !provider) return;
110
-
111
- isPollingRef.current = true;
112
- try {
113
- const status = await provider.getJobStatus(model, requestId);
114
- if (typeof __DEV__ !== "undefined" && __DEV__) console.log("[VideoQueueGeneration] Poll:", status.status);
115
-
116
- if (status.status === QUEUE_STATUS.COMPLETED || status.status === QUEUE_STATUS.FAILED) {
117
- if (pollingRef.current) { clearInterval(pollingRef.current); pollingRef.current = null; }
118
- if (status.status === QUEUE_STATUS.COMPLETED) {
119
- try {
120
- const result = await provider.getJobResult<FalResult>(model, requestId);
121
- await handleComplete(extractResultUrl(result));
122
- } catch (resultErr) {
123
- // Handle errors when getting/extracting result (e.g., ValidationError, content policy)
124
- const errorMessage = resultErr instanceof Error ? resultErr.message : "Generation failed";
125
- if (typeof __DEV__ !== "undefined" && __DEV__) {
126
- console.error("[VideoQueueGeneration] Result error:", errorMessage);
127
- }
128
- await handleError(errorMessage);
129
- }
130
- } else {
131
- await handleError("Generation failed");
132
- }
133
- }
134
- } catch (err) {
135
- // Handle polling errors - stop polling and show error to user
136
- if (pollingRef.current) { clearInterval(pollingRef.current); pollingRef.current = null; }
137
- const errorMessage = err instanceof Error ? err.message : "Generation failed";
138
- if (typeof __DEV__ !== "undefined" && __DEV__) {
139
- console.error("[VideoQueueGeneration] Poll error:", errorMessage);
140
- }
141
- await handleError(errorMessage);
142
- } finally {
143
- isPollingRef.current = false;
144
- }
93
+ if (!requestId || !model) return;
94
+
95
+ await pollQueueStatus({
96
+ requestId,
97
+ model,
98
+ isPollingRef,
99
+ pollingRef,
100
+ onComplete: handleComplete,
101
+ onError: handleError,
102
+ });
145
103
  }, [handleComplete, handleError]);
146
104
 
147
105
  const startGeneration = useCallback(
148
106
  async (input: unknown, prompt: string) => {
149
- if (!strategy.submitToQueue) { onError?.("Queue submission not available"); return; }
107
+ if (!strategy.submitToQueue) {
108
+ onError?.("Queue submission not available");
109
+ return;
110
+ }
150
111
  if (isGeneratingRef.current) return;
112
+
151
113
  isGeneratingRef.current = true;
152
114
  setIsGenerating(true);
153
115
 
154
- // Save to Firestore FIRST (enables background visibility)
155
116
  let creationId: string | null = null;
156
117
  if (userId && prompt) {
157
118
  try {
158
119
  creationId = await persistence.saveAsProcessing(userId, {
159
- scenarioId: scenario.id, scenarioTitle: scenario.title || scenario.id, prompt,
120
+ scenarioId: scenario.id,
121
+ scenarioTitle: scenario.title || scenario.id,
122
+ prompt,
160
123
  });
161
124
  creationIdRef.current = creationId;
162
- if (typeof __DEV__ !== "undefined" && __DEV__) console.log("[VideoQueueGeneration] Saved:", creationId);
125
+ if (__DEV__) console.log("[VideoQueueGeneration] Saved:", creationId);
163
126
  } catch (err) {
164
- if (typeof __DEV__ !== "undefined" && __DEV__) console.error("[VideoQueueGeneration] save error:", err);
127
+ if (__DEV__) console.error("[VideoQueueGeneration] save error:", err);
165
128
  }
166
129
  }
167
130
 
168
131
  const queueResult = await strategy.submitToQueue(input);
169
132
  if (!queueResult.success || !queueResult.requestId || !queueResult.model) {
170
- if (creationId && userId) await persistence.updateToFailed(userId, creationId, queueResult.error || "Queue submission failed");
133
+ if (creationId && userId) {
134
+ await persistence.updateToFailed(userId, creationId, queueResult.error || "Queue submission failed");
135
+ }
171
136
  setIsGenerating(false);
172
137
  onError?.(queueResult.error || "Queue submission failed");
173
138
  return;
@@ -176,21 +141,19 @@ export function useVideoQueueGeneration(
176
141
  requestIdRef.current = queueResult.requestId;
177
142
  modelRef.current = queueResult.model;
178
143
 
179
- // Update with requestId for background polling
180
144
  if (creationId && userId) {
181
145
  try {
182
146
  await persistence.updateRequestId(userId, creationId, queueResult.requestId, queueResult.model);
183
- if (typeof __DEV__ !== "undefined" && __DEV__) console.log("[VideoQueueGeneration] Updated requestId:", queueResult.requestId);
147
+ if (__DEV__) console.log("[VideoQueueGeneration] Updated requestId:", queueResult.requestId);
184
148
  } catch (err) {
185
- if (typeof __DEV__ !== "undefined" && __DEV__) console.error("[VideoQueueGeneration] updateRequestId error:", err);
149
+ if (__DEV__) console.error("[VideoQueueGeneration] updateRequestId error:", err);
186
150
  }
187
151
  }
188
152
 
189
- pollingRef.current = setInterval(() => void pollQueueStatus(), DEFAULT_POLL_INTERVAL_MS);
190
- // Immediate poll to avoid waiting for first interval tick
191
- void pollQueueStatus();
153
+ pollingRef.current = setInterval(() => void pollStatus(), DEFAULT_POLL_INTERVAL_MS);
154
+ void pollStatus();
192
155
  },
193
- [userId, scenario, persistence, strategy, pollQueueStatus, onError],
156
+ [userId, scenario, persistence, strategy, pollStatus, onError],
194
157
  );
195
158
 
196
159
  return { isGenerating, startGeneration };