@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.
- package/package.json +1 -1
- package/src/core/index.ts +1 -1
- package/src/domain/entities/index.ts +1 -1
- package/src/domain/interfaces/ai-provider.interface.ts +1 -1
- package/src/domain/interfaces/index.ts +1 -1
- package/src/domains/background/domain/entities/index.ts +1 -0
- package/src/domains/background/domain/interfaces/index.ts +1 -0
- package/src/{domain → domains/background/domain}/interfaces/provider-job-manager.interface.ts +1 -1
- package/src/domains/background/domain/types/background-generation.types.ts +28 -0
- package/src/domains/background/infrastructure/executors/backgroundJobExecutor.ts +105 -0
- package/src/{infrastructure → domains/background/infrastructure}/services/job-poller-factory.ts +1 -1
- package/src/{infrastructure → domains/background/infrastructure}/services/job-poller.service.ts +1 -1
- package/src/{infrastructure → domains/background/infrastructure}/services/job-poller.types.ts +2 -2
- package/src/{infrastructure → domains/background/infrastructure}/utils/polling-interval.util.ts +1 -1
- package/src/{infrastructure → domains/background/infrastructure}/utils/status-checker.util.ts +1 -1
- package/src/domains/background/presentation/hooks/use-background-generation.ts +97 -0
- package/src/domains/creations/presentation/components/PendingJobsSection.tsx +1 -1
- package/src/domains/generation/wizard/presentation/hooks/generationExecutor.ts +65 -0
- package/src/domains/generation/wizard/presentation/hooks/generationStateMachine.ts +35 -0
- package/src/domains/generation/wizard/presentation/hooks/typeGuards.ts +13 -0
- package/src/domains/generation/wizard/presentation/hooks/useVideoQueueGeneration.ts +34 -71
- package/src/domains/generation/wizard/presentation/hooks/useWizardFlowHandlers.ts +6 -84
- package/src/domains/generation/wizard/presentation/hooks/useWizardGeneration.ts +19 -131
- package/src/domains/generation/wizard/presentation/hooks/videoQueuePoller.ts +59 -0
- package/src/domains/image-to-video/presentation/hooks/imageToVideoStrategy.ts +77 -0
- package/src/domains/image-to-video/presentation/hooks/useImageToVideoFeature.ts +102 -0
- package/src/domains/scenarios/presentation/containers/CategoryNavigationContainer.tsx +4 -80
- package/src/{features → domains}/text-to-image/infrastructure/services/text-to-image-executor.ts +2 -82
- package/src/domains/text-to-image/infrastructure/utils/imageResultExtractor.ts +58 -0
- package/src/domains/text-to-video/presentation/hooks/textToVideoStrategy.ts +75 -0
- package/src/domains/text-to-video/presentation/hooks/useTextToVideoFeature.ts +120 -0
- package/src/exports/features.ts +12 -12
- package/src/presentation/components/PendingJobCard.tsx +1 -1
- package/src/features/image-to-video/presentation/hooks/useImageToVideoFeature.ts +0 -186
- package/src/features/text-to-video/presentation/hooks/useTextToVideoFeature.ts +0 -186
- package/src/presentation/hooks/use-background-generation.ts +0 -185
- /package/src/{domain → domains/background/domain}/entities/job.types.ts +0 -0
- /package/src/{infrastructure → domains/background/infrastructure}/utils/result-validator.util.ts +0 -0
- /package/src/{presentation → domains/background/presentation}/hooks/use-pending-jobs.ts +0 -0
- /package/src/{features → domains}/image-to-video/README.md +0 -0
- /package/src/{features → domains}/image-to-video/domain/constants/animation.constants.ts +0 -0
- /package/src/{features → domains}/image-to-video/domain/constants/duration.constants.ts +0 -0
- /package/src/{features → domains}/image-to-video/domain/constants/form.constants.ts +0 -0
- /package/src/{features → domains}/image-to-video/domain/constants/index.ts +0 -0
- /package/src/{features → domains}/image-to-video/domain/constants/music.constants.ts +0 -0
- /package/src/{features → domains}/image-to-video/domain/index.ts +0 -0
- /package/src/{features → domains}/image-to-video/domain/types/animation.types.ts +0 -0
- /package/src/{features → domains}/image-to-video/domain/types/config.types.ts +0 -0
- /package/src/{features → domains}/image-to-video/domain/types/duration.types.ts +0 -0
- /package/src/{features → domains}/image-to-video/domain/types/form.types.ts +0 -0
- /package/src/{features → domains}/image-to-video/domain/types/image-to-video.types.ts +0 -0
- /package/src/{features → domains}/image-to-video/domain/types/index.ts +0 -0
- /package/src/{features → domains}/image-to-video/domain/types/music.types.ts +0 -0
- /package/src/{features → domains}/image-to-video/index.ts +0 -0
- /package/src/{features → domains}/image-to-video/infrastructure/index.ts +0 -0
- /package/src/{features → domains}/image-to-video/infrastructure/services/image-to-video-executor.ts +0 -0
- /package/src/{features → domains}/image-to-video/infrastructure/services/index.ts +0 -0
- /package/src/{features → domains}/image-to-video/presentation/components/AddMoreCard.tsx +0 -0
- /package/src/{features → domains}/image-to-video/presentation/components/AnimationStyleSelector.tsx +0 -0
- /package/src/{features → domains}/image-to-video/presentation/components/DurationSelector.tsx +0 -0
- /package/src/{features → domains}/image-to-video/presentation/components/EmptyGridState.tsx +0 -0
- /package/src/{features → domains}/image-to-video/presentation/components/GridImageItem.tsx +0 -0
- /package/src/{features → domains}/image-to-video/presentation/components/ImageSelectionGrid.styles.ts +0 -0
- /package/src/{features → domains}/image-to-video/presentation/components/ImageSelectionGrid.tsx +0 -0
- /package/src/{features → domains}/image-to-video/presentation/components/ImageSelectionGrid.types.ts +0 -0
- /package/src/{features → domains}/image-to-video/presentation/components/MusicMoodSelector.tsx +0 -0
- /package/src/{features → domains}/image-to-video/presentation/components/index.ts +0 -0
- /package/src/{features → domains}/image-to-video/presentation/hooks/image-to-video-feature.types.ts +0 -0
- /package/src/{features → domains}/image-to-video/presentation/hooks/index.ts +0 -0
- /package/src/{features → domains}/image-to-video/presentation/hooks/useFormState.ts +0 -0
- /package/src/{features → domains}/image-to-video/presentation/hooks/useGeneration.ts +0 -0
- /package/src/{features → domains}/image-to-video/presentation/hooks/useImageToVideoForm.ts +0 -0
- /package/src/{features → domains}/image-to-video/presentation/index.ts +0 -0
- /package/src/{features → domains}/image-to-video/presentation/screens/ImageToVideoWizardFlow.tsx +0 -0
- /package/src/{features → domains}/shared/index.ts +0 -0
- /package/src/{features → domains}/shared/presentation/components/AutoSkipPreview.tsx +0 -0
- /package/src/{features → domains}/shared/presentation/components/index.ts +0 -0
- /package/src/{features → domains}/shared/presentation/utils/index.ts +0 -0
- /package/src/{features → domains}/shared/presentation/utils/wizard-flow.utils.ts +0 -0
- /package/src/{features → domains}/text-to-image/README.md +0 -0
- /package/src/{features → domains}/text-to-image/domain/constants/index.ts +0 -0
- /package/src/{features → domains}/text-to-image/domain/constants/options.constants.ts +0 -0
- /package/src/{features → domains}/text-to-image/domain/constants/styles.constants.ts +0 -0
- /package/src/{features → domains}/text-to-image/domain/index.ts +0 -0
- /package/src/{features → domains}/text-to-image/domain/types/config.types.ts +0 -0
- /package/src/{features → domains}/text-to-image/domain/types/form.types.ts +0 -0
- /package/src/{features → domains}/text-to-image/domain/types/index.ts +0 -0
- /package/src/{features → domains}/text-to-image/domain/types/text-to-image.types.ts +0 -0
- /package/src/{features → domains}/text-to-image/index.ts +0 -0
- /package/src/{features → domains}/text-to-image/infrastructure/index.ts +0 -0
- /package/src/{features → domains}/text-to-image/infrastructure/services/index.ts +0 -0
- /package/src/{features → domains}/text-to-image/presentation/components/index.ts +0 -0
- /package/src/{features → domains}/text-to-image/presentation/hooks/index.ts +0 -0
- /package/src/{features → domains}/text-to-image/presentation/hooks/useFormState.ts +0 -0
- /package/src/{features → domains}/text-to-image/presentation/hooks/useGeneration.ts +0 -0
- /package/src/{features → domains}/text-to-image/presentation/hooks/useTextToImageForm.ts +0 -0
- /package/src/{features → domains}/text-to-image/presentation/index.ts +0 -0
- /package/src/{features → domains}/text-to-image/presentation/screens/TextToImageWizardFlow.tsx +0 -0
- /package/src/{features → domains}/text-to-image/presentation/screens/TextToImageWizardFlow.types.ts +0 -0
- /package/src/{features → domains}/text-to-video/README.md +0 -0
- /package/src/{features → domains}/text-to-video/domain/index.ts +0 -0
- /package/src/{features → domains}/text-to-video/domain/types/callback.types.ts +0 -0
- /package/src/{features → domains}/text-to-video/domain/types/component.types.ts +0 -0
- /package/src/{features → domains}/text-to-video/domain/types/config.types.ts +0 -0
- /package/src/{features → domains}/text-to-video/domain/types/index.ts +0 -0
- /package/src/{features → domains}/text-to-video/domain/types/request.types.ts +0 -0
- /package/src/{features → domains}/text-to-video/domain/types/state.types.ts +0 -0
- /package/src/{features → domains}/text-to-video/index.ts +0 -0
- /package/src/{features → domains}/text-to-video/infrastructure/index.ts +0 -0
- /package/src/{features → domains}/text-to-video/infrastructure/services/index.ts +0 -0
- /package/src/{features → domains}/text-to-video/infrastructure/services/text-to-video-executor.ts +0 -0
- /package/src/{features → domains}/text-to-video/presentation/components/FrameSelector.tsx +0 -0
- /package/src/{features → domains}/text-to-video/presentation/components/GenerationTabs.tsx +0 -0
- /package/src/{features → domains}/text-to-video/presentation/components/HeroSection.tsx +0 -0
- /package/src/{features → domains}/text-to-video/presentation/components/HintCarousel.tsx +0 -0
- /package/src/{features → domains}/text-to-video/presentation/components/OptionsPanel.tsx +0 -0
- /package/src/{features → domains}/text-to-video/presentation/components/index.ts +0 -0
- /package/src/{features → domains}/text-to-video/presentation/hooks/index.ts +0 -0
- /package/src/{features → domains}/text-to-video/presentation/hooks/useTextToVideoForm.ts +0 -0
- /package/src/{features → domains}/text-to-video/presentation/index.ts +0 -0
- /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.
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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";
|
package/src/{domain → domains/background/domain}/interfaces/provider-job-manager.interface.ts
RENAMED
|
@@ -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 "
|
|
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
|
+
};
|
package/src/{infrastructure → domains/background/infrastructure}/services/job-poller-factory.ts
RENAMED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Creates pre-configured job poller instances
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type { PollingConfig } from "
|
|
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
|
|
package/src/{infrastructure → domains/background/infrastructure}/services/job-poller.service.ts
RENAMED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Reports only real status - no fake progress
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { DEFAULT_POLLING_CONFIG } from "
|
|
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";
|
package/src/{infrastructure → domains/background/infrastructure}/services/job-poller.types.ts
RENAMED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Job Poller Type Definitions
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import type { IAIProvider, JobStatus } from "
|
|
6
|
-
import type { PollingConfig } from "
|
|
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;
|
package/src/{infrastructure → domains/background/infrastructure}/utils/status-checker.util.ts
RENAMED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Checks job status responses for errors
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type { JobStatus, AILogEntry } from "
|
|
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 "
|
|
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 {
|
|
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 (
|
|
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 (
|
|
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
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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) {
|
|
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,
|
|
120
|
+
scenarioId: scenario.id,
|
|
121
|
+
scenarioTitle: scenario.title || scenario.id,
|
|
122
|
+
prompt,
|
|
160
123
|
});
|
|
161
124
|
creationIdRef.current = creationId;
|
|
162
|
-
if (
|
|
125
|
+
if (__DEV__) console.log("[VideoQueueGeneration] Saved:", creationId);
|
|
163
126
|
} catch (err) {
|
|
164
|
-
if (
|
|
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)
|
|
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 (
|
|
147
|
+
if (__DEV__) console.log("[VideoQueueGeneration] Updated requestId:", queueResult.requestId);
|
|
184
148
|
} catch (err) {
|
|
185
|
-
if (
|
|
149
|
+
if (__DEV__) console.error("[VideoQueueGeneration] updateRequestId error:", err);
|
|
186
150
|
}
|
|
187
151
|
}
|
|
188
152
|
|
|
189
|
-
pollingRef.current = setInterval(() => void
|
|
190
|
-
|
|
191
|
-
void pollQueueStatus();
|
|
153
|
+
pollingRef.current = setInterval(() => void pollStatus(), DEFAULT_POLL_INTERVAL_MS);
|
|
154
|
+
void pollStatus();
|
|
192
155
|
},
|
|
193
|
-
[userId, scenario, persistence, strategy,
|
|
156
|
+
[userId, scenario, persistence, strategy, pollStatus, onError],
|
|
194
157
|
);
|
|
195
158
|
|
|
196
159
|
return { isGenerating, startGeneration };
|