@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
@@ -1,32 +1,9 @@
1
- /**
2
- * Wizard Flow Handlers Hook
3
- * Extracts callback handlers from WizardFlowContent
4
- */
5
-
6
1
  import { useCallback } from "react";
7
2
  import { AlertType, AlertMode, useAlert } from "@umituz/react-native-design-system";
8
3
  import { StepType, type StepDefinition } from "../../../../../domain/entities/flow-config.types";
9
4
  import type { UploadedImage } from "../../../../../presentation/hooks/generation/useAIGenerateState";
10
5
  import type { Creation } from "../../../../creations/domain/entities/Creation";
11
-
12
- declare const __DEV__: boolean;
13
-
14
- /**
15
- * Type guard to check if result is a valid Creation object
16
- */
17
- function isCreation(result: unknown): result is Creation {
18
- if (!result || typeof result !== "object") {
19
- return false;
20
- }
21
- const creation = result as Partial<Creation>;
22
- return (
23
- typeof creation.id === "string" &&
24
- typeof creation.uri === "string" &&
25
- typeof creation.type === "string" &&
26
- creation.createdAt instanceof Date &&
27
- typeof creation.isShared === "boolean"
28
- );
29
- }
6
+ import { isCreation } from "./typeGuards";
30
7
 
31
8
  export interface UseWizardFlowHandlersProps {
32
9
  readonly currentStepIndex: number;
@@ -77,21 +54,8 @@ export function useWizardFlowHandlers(props: UseWizardFlowHandlersProps) {
77
54
 
78
55
  const handleGenerationComplete = useCallback(
79
56
  (result: unknown) => {
80
- if (typeof __DEV__ !== "undefined" && __DEV__) {
81
- console.log("[WizardFlowHandlers] Generation completed");
82
- }
83
57
  setResult(result);
84
-
85
- // Use type guard to safely check if result is a Creation
86
- if (isCreation(result)) {
87
- setCurrentCreation(result);
88
- } else {
89
- if (typeof __DEV__ !== "undefined" && __DEV__) {
90
- console.warn("[WizardFlowHandlers] Result is not a valid Creation object:", result);
91
- }
92
- setCurrentCreation(null);
93
- }
94
-
58
+ setCurrentCreation(isCreation(result) ? result : null);
95
59
  onGenerationComplete?.(result);
96
60
  if (!skipResultStep) nextStep();
97
61
  },
@@ -100,43 +64,17 @@ export function useWizardFlowHandlers(props: UseWizardFlowHandlersProps) {
100
64
 
101
65
  const handleGenerationError = useCallback(
102
66
  (errorMessage: string) => {
103
- // Ensure we have a meaningful error message
104
67
  const safeErrorMessage = errorMessage?.trim() || "error.generation.unknown";
105
- if (typeof __DEV__ !== "undefined" && __DEV__) {
106
- console.log("[WizardFlowHandlers] Generation error:", {
107
- original: errorMessage,
108
- safe: safeErrorMessage,
109
- });
110
- }
111
- // Translate error key if it looks like a translation key
112
- const displayMessage = safeErrorMessage.startsWith("error.")
113
- ? t(safeErrorMessage)
114
- : safeErrorMessage;
115
- // Show error alert to user
116
- alert.show(
117
- AlertType.ERROR,
118
- AlertMode.MODAL,
119
- t("common.error"),
120
- displayMessage,
121
- );
122
- // Notify parent component
68
+ const displayMessage = safeErrorMessage.startsWith("error.") ? t(safeErrorMessage) : safeErrorMessage;
69
+ alert.show(AlertType.ERROR, AlertMode.MODAL, t("common.error"), displayMessage);
123
70
  onGenerationError?.(safeErrorMessage);
124
- // Close the wizard
125
71
  onBack?.();
126
72
  },
127
73
  [alert, t, onGenerationError, onBack],
128
74
  );
129
75
 
130
76
  const handleDismissGenerating = useCallback(() => {
131
- if (typeof __DEV__ !== "undefined" && __DEV__) {
132
- console.log("[WizardFlowHandlers] Dismissing - generation continues");
133
- }
134
- alert.show(
135
- AlertType.INFO,
136
- AlertMode.TOAST,
137
- t("generator.backgroundTitle"),
138
- t("generator.backgroundMessage"),
139
- );
77
+ alert.show(AlertType.INFO, AlertMode.TOAST, t("generator.backgroundTitle"), t("generator.backgroundMessage"));
140
78
  onBack?.();
141
79
  }, [alert, t, onBack]);
142
80
 
@@ -147,18 +85,7 @@ export function useWizardFlowHandlers(props: UseWizardFlowHandlersProps) {
147
85
 
148
86
  const handleNextStep = useCallback(() => {
149
87
  const nextStepDef = flowSteps[currentStepIndex + 1];
150
- if (typeof __DEV__ !== "undefined" && __DEV__) {
151
- console.log("[WizardFlowHandlers] handleNextStep", {
152
- currentStepIndex,
153
- nextStepType: nextStepDef?.type,
154
- isGenerating: nextStepDef?.type === StepType.GENERATING,
155
- hasOnGenerationStart: !!onGenerationStart,
156
- });
157
- }
158
88
  if (nextStepDef?.type === StepType.GENERATING && onGenerationStart) {
159
- if (typeof __DEV__ !== "undefined" && __DEV__) {
160
- console.log("[WizardFlowHandlers] Calling onGenerationStart callback");
161
- }
162
89
  onGenerationStart(customData, nextStep);
163
90
  return;
164
91
  }
@@ -179,12 +106,7 @@ export function useWizardFlowHandlers(props: UseWizardFlowHandlersProps) {
179
106
  const success = await repository.rate(userId, currentCreation.id, rating, description);
180
107
  if (success) {
181
108
  setHasRated(true);
182
- alert.show(
183
- AlertType.SUCCESS,
184
- AlertMode.TOAST,
185
- t("result.rateSuccessTitle"),
186
- t("result.rateSuccessMessage"),
187
- );
109
+ alert.show(AlertType.SUCCESS, AlertMode.TOAST, t("result.rateSuccessTitle"), t("result.rateSuccessMessage"));
188
110
  }
189
111
  setShowRatingPicker(false);
190
112
  },
@@ -1,75 +1,16 @@
1
1
  /**
2
- * useWizardGeneration Hook
3
- * Orchestrates wizard-based generation by delegating to appropriate mode:
4
- * - Video: Queue-based generation with background support
5
- * - Photo: Blocking execution for quick results
6
- *
7
- * Architecture: State machine pattern with useReducer
8
- * States: IDLE → PREPARING → GENERATING → COMPLETED/ERROR → IDLE
2
+ * Wizard Generation Hook
3
+ * Orchestrates wizard-based generation (Video: queue, Photo: blocking)
9
4
  */
10
5
 
11
6
  import { useEffect, useReducer, useMemo, useRef } from "react";
12
- import { createWizardStrategy, buildWizardInput } from "../../infrastructure/strategies";
7
+ import { createWizardStrategy } from "../../infrastructure/strategies";
13
8
  import { createCreationPersistence } from "../../infrastructure/utils/creation-persistence.util";
14
9
  import { useVideoQueueGeneration } from "./useVideoQueueGeneration";
15
10
  import { usePhotoBlockingGeneration } from "./usePhotoBlockingGeneration";
16
- import type {
17
- UseWizardGenerationProps,
18
- UseWizardGenerationReturn,
19
- } from "./wizard-generation.types";
20
-
21
- declare const __DEV__: boolean;
22
-
23
- /**
24
- * Generation orchestration states
25
- */
26
- type GenerationStatus =
27
- | "IDLE" // Not started
28
- | "PREPARING" // Building input
29
- | "GENERATING" // Generation in progress
30
- | "ERROR" // Failed (prevents retry)
31
- | "COMPLETED"; // Success
32
-
33
- /**
34
- * State machine state
35
- */
36
- interface GenerationState {
37
- status: GenerationStatus;
38
- error?: string;
39
- }
40
-
41
- /**
42
- * State machine actions
43
- */
44
- type GenerationAction =
45
- | { type: "START_PREPARATION" }
46
- | { type: "START_GENERATION" }
47
- | { type: "COMPLETE" }
48
- | { type: "ERROR"; error: string }
49
- | { type: "RESET" };
50
-
51
- /**
52
- * State machine reducer
53
- */
54
- const generationReducer = (
55
- state: GenerationState,
56
- action: GenerationAction,
57
- ): GenerationState => {
58
- switch (action.type) {
59
- case "START_PREPARATION":
60
- return { status: "PREPARING" };
61
- case "START_GENERATION":
62
- return { status: "GENERATING" };
63
- case "COMPLETE":
64
- return { status: "COMPLETED" };
65
- case "ERROR":
66
- return { status: "ERROR", error: action.error };
67
- case "RESET":
68
- return { status: "IDLE" };
69
- default:
70
- return state;
71
- }
72
- };
11
+ import { generationReducer, INITIAL_STATE } from "./generationStateMachine";
12
+ import { executeWizardGeneration } from "./generationExecutor";
13
+ import type { UseWizardGenerationProps, UseWizardGenerationReturn } from "./wizard-generation.types";
73
14
 
74
15
  export type {
75
16
  WizardOutputType,
@@ -78,9 +19,7 @@ export type {
78
19
  UseWizardGenerationReturn,
79
20
  } from "./wizard-generation.types";
80
21
 
81
- export const useWizardGeneration = (
82
- props: UseWizardGenerationProps,
83
- ): UseWizardGenerationReturn => {
22
+ export const useWizardGeneration = (props: UseWizardGenerationProps): UseWizardGenerationReturn => {
84
23
  const {
85
24
  scenario,
86
25
  wizardData,
@@ -93,10 +32,7 @@ export const useWizardGeneration = (
93
32
  onCreditsExhausted,
94
33
  } = props;
95
34
 
96
- // State machine: replaces multiple useRef flags
97
- const [state, dispatch] = useReducer(generationReducer, { status: "IDLE" });
98
-
99
- // Mounted ref to prevent state updates after unmount
35
+ const [state, dispatch] = useReducer(generationReducer, INITIAL_STATE);
100
36
  const isMountedRef = useRef(true);
101
37
 
102
38
  useEffect(() => {
@@ -107,14 +43,9 @@ export const useWizardGeneration = (
107
43
  }, []);
108
44
 
109
45
  const persistence = useMemo(() => createCreationPersistence(), []);
110
- const strategy = useMemo(
111
- () => createWizardStrategy({ scenario, creditCost }),
112
- [scenario, creditCost],
113
- );
114
-
46
+ const strategy = useMemo(() => createWizardStrategy({ scenario, creditCost }), [scenario, creditCost]);
115
47
  const isVideoMode = scenario.outputType === "video" && !!strategy.submitToQueue;
116
48
 
117
- // Video generation hook (queue-based)
118
49
  const videoGeneration = useVideoQueueGeneration({
119
50
  userId,
120
51
  scenario,
@@ -124,7 +55,6 @@ export const useWizardGeneration = (
124
55
  onError,
125
56
  });
126
57
 
127
- // Photo generation hook (blocking)
128
58
  const photoGeneration = usePhotoBlockingGeneration({
129
59
  userId,
130
60
  scenario,
@@ -136,67 +66,25 @@ export const useWizardGeneration = (
136
66
  onCreditsExhausted,
137
67
  });
138
68
 
139
- // Main effect: trigger generation when step becomes active
140
69
  useEffect(() => {
141
70
  const isAlreadyGenerating = videoGeneration.isGenerating || photoGeneration.isGenerating;
142
71
 
143
- // Start generation: Simple single condition using state machine
144
72
  if (isGeneratingStep && state.status === "IDLE" && !isAlreadyGenerating) {
145
73
  dispatch({ type: "START_PREPARATION" });
146
74
 
147
- if (typeof __DEV__ !== "undefined" && __DEV__) {
148
- console.log("[WizardGeneration] State: PREPARING");
149
- }
150
-
151
- buildWizardInput(wizardData, scenario)
152
- .then(async (input) => {
153
- // Check if component is still mounted
154
- if (!isMountedRef.current) return;
155
-
156
- if (!input) {
157
- dispatch({ type: "ERROR", error: "Failed to build generation input" });
158
- onError?.("Failed to build generation input");
159
- return;
160
- }
161
-
162
- dispatch({ type: "START_GENERATION" });
163
-
164
- const typedInput = input as { prompt?: string };
165
-
166
- if (typeof __DEV__ !== "undefined" && __DEV__) {
167
- console.log("[WizardGeneration] State: GENERATING");
168
- console.log("[WizardGeneration] Mode:", isVideoMode ? "VIDEO_QUEUE" : "PHOTO_BLOCKING");
169
- }
170
-
171
- if (isVideoMode) {
172
- await videoGeneration.startGeneration(input, typedInput.prompt || "");
173
- } else {
174
- await photoGeneration.startGeneration(input, typedInput.prompt || "");
175
- }
176
-
177
- // Check again before final state update
178
- if (isMountedRef.current) {
179
- dispatch({ type: "COMPLETE" });
180
- }
181
- })
182
- .catch((error) => {
183
- // Check if component is still mounted before error handling
184
- if (!isMountedRef.current) return;
185
-
186
- const errorMsg = error?.message || "error.generation.unknown";
187
- if (typeof __DEV__ !== "undefined" && __DEV__) {
188
- console.error("[WizardGeneration] Build input error:", errorMsg, error);
189
- }
190
- dispatch({ type: "ERROR", error: errorMsg });
191
- onError?.(errorMsg);
192
- });
75
+ executeWizardGeneration({
76
+ wizardData,
77
+ scenario,
78
+ isVideoMode,
79
+ isMountedRef,
80
+ dispatch,
81
+ onError,
82
+ videoGenerationFn: videoGeneration.startGeneration,
83
+ photoGenerationFn: photoGeneration.startGeneration,
84
+ });
193
85
  }
194
86
 
195
- // Reset state when leaving generating step
196
87
  if (!isGeneratingStep && state.status !== "IDLE") {
197
- if (typeof __DEV__ !== "undefined" && __DEV__) {
198
- console.log("[WizardGeneration] State: RESET");
199
- }
200
88
  dispatch({ type: "RESET" });
201
89
  }
202
90
  }, [
@@ -0,0 +1,59 @@
1
+ import { providerRegistry } from "../../../../../infrastructure/services/provider-registry.service";
2
+ import { extractResultUrl, type FalResult, type GenerationUrls } from "./generation-result.utils";
3
+ import { QUEUE_STATUS } from "../../../../../domain/constants/queue-status.constants";
4
+
5
+ declare const __DEV__: boolean;
6
+
7
+ interface PollParams {
8
+ requestId: string;
9
+ model: string;
10
+ isPollingRef: React.MutableRefObject<boolean>;
11
+ pollingRef: React.MutableRefObject<ReturnType<typeof setInterval> | null>;
12
+ onComplete: (urls: GenerationUrls) => Promise<void>;
13
+ onError: (error: string) => Promise<void>;
14
+ }
15
+
16
+ export const pollQueueStatus = async (params: PollParams): Promise<void> => {
17
+ const { requestId, model, isPollingRef, pollingRef, onComplete, onError } = params;
18
+
19
+ if (isPollingRef.current) return;
20
+
21
+ const provider = providerRegistry.getActiveProvider();
22
+ if (!provider) return;
23
+
24
+ isPollingRef.current = true;
25
+ try {
26
+ const status = await provider.getJobStatus(model, requestId);
27
+ if (__DEV__) console.log("[VideoQueueGeneration] Poll:", status.status);
28
+
29
+ if (status.status === QUEUE_STATUS.COMPLETED || status.status === QUEUE_STATUS.FAILED) {
30
+ if (pollingRef.current) {
31
+ clearInterval(pollingRef.current);
32
+ pollingRef.current = null;
33
+ }
34
+
35
+ if (status.status === QUEUE_STATUS.COMPLETED) {
36
+ try {
37
+ const result = await provider.getJobResult<FalResult>(model, requestId);
38
+ await onComplete(extractResultUrl(result));
39
+ } catch (resultErr) {
40
+ const errorMessage = resultErr instanceof Error ? resultErr.message : "Generation failed";
41
+ if (__DEV__) console.error("[VideoQueueGeneration] Result error:", errorMessage);
42
+ await onError(errorMessage);
43
+ }
44
+ } else {
45
+ await onError("Generation failed");
46
+ }
47
+ }
48
+ } catch (err) {
49
+ if (pollingRef.current) {
50
+ clearInterval(pollingRef.current);
51
+ pollingRef.current = null;
52
+ }
53
+ const errorMessage = err instanceof Error ? err.message : "Generation failed";
54
+ if (__DEV__) console.error("[VideoQueueGeneration] Poll error:", errorMessage);
55
+ await onError(errorMessage);
56
+ } finally {
57
+ isPollingRef.current = false;
58
+ }
59
+ };
@@ -0,0 +1,77 @@
1
+ import { executeImageToVideo } from "../../infrastructure/services";
2
+ import type { GenerationStrategy } from "../../../../presentation/hooks/generation";
3
+ import type {
4
+ ImageToVideoConfig,
5
+ ImageToVideoCallbacks,
6
+ ImageToVideoResult,
7
+ ImageToVideoOptions,
8
+ ImageToVideoInputBuilder,
9
+ ImageToVideoResultExtractor,
10
+ } from "../../domain/types";
11
+
12
+ interface VideoGenerationInput {
13
+ imageUrl: string;
14
+ prompt: string;
15
+ options?: ImageToVideoOptions;
16
+ creationId: string;
17
+ }
18
+
19
+ interface CreateStrategyParams {
20
+ config: ImageToVideoConfig;
21
+ callbacks: ImageToVideoCallbacks;
22
+ buildInput: ImageToVideoInputBuilder;
23
+ extractResult?: ImageToVideoResultExtractor;
24
+ userId: string;
25
+ currentPrompt: string;
26
+ creationIdRef: React.MutableRefObject<string>;
27
+ updateState: (videoUrl: string | null, thumbnailUrl: string | null) => void;
28
+ }
29
+
30
+ export const createImageToVideoStrategy = (
31
+ params: CreateStrategyParams,
32
+ ): GenerationStrategy<VideoGenerationInput, ImageToVideoResult> => {
33
+ const { config, callbacks, buildInput, extractResult, userId, currentPrompt, creationIdRef, updateState } = params;
34
+
35
+ return {
36
+ execute: async (input) => {
37
+ creationIdRef.current = input.creationId;
38
+
39
+ callbacks.onGenerationStart?.({
40
+ creationId: input.creationId,
41
+ type: "image-to-video",
42
+ imageUrl: input.imageUrl,
43
+ prompt: input.prompt,
44
+ metadata: input.options as Record<string, unknown> | undefined,
45
+ }).catch(() => {});
46
+
47
+ const result = await executeImageToVideo(
48
+ { imageUrl: input.imageUrl, prompt: input.prompt, userId, options: input.options },
49
+ { model: config.model, buildInput, extractResult },
50
+ );
51
+
52
+ if (!result.success || !result.videoUrl) {
53
+ throw new Error(result.error || "Generation failed");
54
+ }
55
+
56
+ updateState(result.videoUrl ?? null, result.thumbnailUrl ?? null);
57
+
58
+ return {
59
+ success: true,
60
+ videoUrl: result.videoUrl,
61
+ thumbnailUrl: result.thumbnailUrl,
62
+ };
63
+ },
64
+ getCreditCost: () => config.creditCost,
65
+ save: async (result) => {
66
+ if (result.success && result.videoUrl && creationIdRef.current) {
67
+ await callbacks.onCreationSave?.({
68
+ creationId: creationIdRef.current,
69
+ type: "image-to-video",
70
+ videoUrl: result.videoUrl,
71
+ thumbnailUrl: result.thumbnailUrl,
72
+ prompt: currentPrompt,
73
+ });
74
+ }
75
+ },
76
+ };
77
+ };
@@ -0,0 +1,102 @@
1
+ import { useState, useCallback, useMemo, useRef } from "react";
2
+ import { useGenerationOrchestrator } from "../../../../presentation/hooks/generation";
3
+ import { createImageToVideoStrategy } from "./imageToVideoStrategy";
4
+ import type {
5
+ UseImageToVideoFeatureProps,
6
+ UseImageToVideoFeatureReturn,
7
+ INITIAL_STATE,
8
+ DEFAULT_ALERT_MESSAGES,
9
+ } from "./image-to-video-feature.types";
10
+
11
+ export type {
12
+ UseImageToVideoFeatureProps,
13
+ UseImageToVideoFeatureReturn,
14
+ } from "./image-to-video-feature.types";
15
+
16
+ export function useImageToVideoFeature(props: UseImageToVideoFeatureProps): UseImageToVideoFeatureReturn {
17
+ const { config, callbacks, userId } = props;
18
+ const [state, setState] = useState(INITIAL_STATE);
19
+ const creationIdRef = useRef("");
20
+
21
+ const updateState = useCallback((videoUrl: string | null, thumbnailUrl: string | null) => {
22
+ setState((prev) => ({ ...prev, videoUrl, thumbnailUrl }));
23
+ }, []);
24
+
25
+ const strategy = useMemo(
26
+ () =>
27
+ createImageToVideoStrategy({
28
+ config,
29
+ callbacks,
30
+ buildInput: config.buildInput,
31
+ extractResult: config.extractResult,
32
+ userId,
33
+ currentPrompt: state.motionPrompt || "",
34
+ creationIdRef,
35
+ updateState,
36
+ }),
37
+ [config, callbacks, userId, state.motionPrompt, updateState],
38
+ );
39
+
40
+ const orchestrator = useGenerationOrchestrator(strategy, {
41
+ userId,
42
+ alertMessages: DEFAULT_ALERT_MESSAGES,
43
+ onCreditsExhausted: () => callbacks.onShowPaywall?.(config.creditCost ?? 0),
44
+ onSuccess: (result) => {
45
+ config.onProcessingComplete?.();
46
+ callbacks.onGenerate?.(result);
47
+ },
48
+ onError: (err) => {
49
+ config.onProcessingError?.(err.message);
50
+ callbacks.onError?.(err.message);
51
+ },
52
+ });
53
+
54
+ const setImageUri = useCallback((imageUri: string) => {
55
+ setState((prev) => ({ ...prev, imageUri, error: null }));
56
+ }, []);
57
+
58
+ const setMotionPrompt = useCallback((motionPrompt: string) => {
59
+ setState((prev) => ({ ...prev, motionPrompt, error: null }));
60
+ }, []);
61
+
62
+ const generate = useCallback(
63
+ async (params?: any) => {
64
+ const imageUri = params?.imageUri || state.imageUri;
65
+ if (!imageUri) {
66
+ const error = "Image is required";
67
+ setState((prev) => ({ ...prev, error }));
68
+ return { success: false, error };
69
+ }
70
+
71
+ setState((prev) => ({ ...prev, isProcessing: true, error: null }));
72
+
73
+ try {
74
+ const result = await orchestrator.generate({
75
+ imageUrl: imageUri,
76
+ prompt: state.motionPrompt || "",
77
+ options: params,
78
+ creationId: `image-to-video-${Date.now()}`,
79
+ });
80
+ setState((prev) => ({ ...prev, isProcessing: false }));
81
+ return result;
82
+ } catch (error) {
83
+ const message = error instanceof Error ? error.message : "Generation failed";
84
+ setState((prev) => ({ ...prev, isProcessing: false, error: message }));
85
+ return { success: false, error: message };
86
+ }
87
+ },
88
+ [state.imageUri, state.motionPrompt, orchestrator],
89
+ );
90
+
91
+ const reset = useCallback(() => setState(INITIAL_STATE), []);
92
+
93
+ return {
94
+ state,
95
+ setImageUri,
96
+ setMotionPrompt,
97
+ generate,
98
+ reset,
99
+ isReady: !orchestrator.isGenerating && !state.isProcessing,
100
+ canGenerate: !orchestrator.isGenerating && !state.isProcessing && !!state.imageUri,
101
+ };
102
+ }