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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/package.json +1 -4
  2. package/src/domain/interfaces/app-services-auth.interface.ts +27 -0
  3. package/src/domain/interfaces/app-services-composite.interface.ts +29 -0
  4. package/src/domain/interfaces/app-services-optional.interface.ts +42 -0
  5. package/src/domain/interfaces/app-services.interface.ts +0 -79
  6. package/src/domains/background/infrastructure/services/job-poller-index.ts +7 -0
  7. package/src/domains/background/infrastructure/services/job-poller-utils.ts +130 -0
  8. package/src/domains/background/infrastructure/utils/polling-interval.util.ts +1 -1
  9. package/src/domains/background/presentation/hooks/use-background-generation.ts +1 -1
  10. package/src/domains/content-moderation/infrastructure/services/content-moderation.service.ts +1 -1
  11. package/src/domains/content-moderation/infrastructure/services/moderators/image.moderator.ts +34 -8
  12. package/src/domains/content-moderation/infrastructure/services/moderators/text.moderator.ts +15 -4
  13. package/src/domains/content-moderation/infrastructure/services/moderators/video.moderator.ts +34 -8
  14. package/src/domains/content-moderation/infrastructure/services/moderators/voice.moderator.ts +19 -8
  15. package/src/domains/creations/domain/types/creation-categories.constants.ts +57 -0
  16. package/src/domains/creations/domain/types/creation-categories.helpers.ts +67 -0
  17. package/src/domains/creations/domain/types/creation-categories.ts +5 -111
  18. package/src/domains/creations/presentation/hooks/creation-validators.ts +31 -29
  19. package/src/domains/creations/presentation/hooks/job-poller-index.ts +10 -0
  20. package/src/domains/creations/presentation/hooks/job-poller-utils.filters.ts +34 -0
  21. package/src/domains/creations/presentation/hooks/job-poller-utils.logger.ts +76 -0
  22. package/src/domains/creations/presentation/hooks/job-poller-utils.stale-handlers.ts +52 -0
  23. package/src/domains/creations/presentation/hooks/job-poller-utils.ts +8 -0
  24. package/src/domains/creations/presentation/screens/CreationsGalleryScreen.tsx +1 -1
  25. package/src/domains/creations/presentation-exports.ts +2 -2
  26. package/src/domains/face-detection/domain/entities/FaceDetection.ts +4 -3
  27. package/src/domains/face-detection/presentation/hooks/useFaceDetection.ts +24 -21
  28. package/src/domains/generation/infrastructure/couple-generation-builder/builder-couple-preparation.ts +58 -0
  29. package/src/domains/generation/infrastructure/couple-generation-builder/builder-couple-prompt.ts +69 -0
  30. package/src/domains/generation/infrastructure/couple-generation-builder/builder-couple-resolution.ts +77 -0
  31. package/src/domains/generation/infrastructure/couple-generation-builder/builder-couple.ts +54 -0
  32. package/src/domains/generation/infrastructure/couple-generation-builder/builder-index.ts +8 -0
  33. package/src/domains/generation/infrastructure/couple-generation-builder/builder-scenario.ts +113 -0
  34. package/src/domains/generation/infrastructure/couple-generation-builder/builder.ts +7 -0
  35. package/src/domains/generation/infrastructure/couple-generation-builder/index.ts +20 -0
  36. package/src/domains/generation/infrastructure/couple-generation-builder/types.ts +44 -0
  37. package/src/domains/generation/infrastructure/couple-generation-builder/utils/builder-end-logger.ts +18 -0
  38. package/src/domains/generation/infrastructure/couple-generation-builder/utils/builder-start-logger.ts +57 -0
  39. package/src/domains/generation/infrastructure/couple-generation-builder/utils/builder-step-logger.ts +106 -0
  40. package/src/domains/generation/infrastructure/couple-generation-builder/utils/index.ts +8 -0
  41. package/src/domains/generation/infrastructure/couple-generation-builder/utils/types.ts +49 -0
  42. package/src/domains/generation/infrastructure/couple-generation-builder/utils.ts +8 -0
  43. package/src/domains/generation/infrastructure/flow/flow-store-actions.ts +105 -0
  44. package/src/domains/generation/infrastructure/flow/flow-store-initial-state.ts +26 -0
  45. package/src/domains/generation/infrastructure/flow/useFlowStore.ts +4 -116
  46. package/src/domains/generation/presentation/useAIGeneration.hook.ts +1 -1
  47. package/src/domains/generation/wizard/infrastructure/strategies/image-generation-strategy-index.ts +7 -0
  48. package/src/domains/generation/wizard/infrastructure/strategies/image-generation.executor.ts +2 -12
  49. package/src/domains/generation/wizard/infrastructure/strategies/image-generation.executor.types.ts +11 -0
  50. package/src/domains/generation/wizard/infrastructure/strategies/image-generation.executor.utils.ts +12 -0
  51. package/src/domains/generation/wizard/infrastructure/strategies/image-generation.strategy.ts +1 -220
  52. package/src/domains/generation/wizard/infrastructure/strategies/image-input-builder.ts +66 -0
  53. package/src/domains/generation/wizard/infrastructure/strategies/image-input-extraction.ts +88 -0
  54. package/src/domains/generation/wizard/infrastructure/strategies/image-input-prompt-builder.ts +75 -0
  55. package/src/domains/generation/wizard/infrastructure/strategies/image-input-style-enhancements.ts +35 -0
  56. package/src/domains/generation/wizard/infrastructure/strategies/image-strategy-factory.ts +42 -0
  57. package/src/domains/generation/wizard/infrastructure/strategies/video-generation-executor-index.ts +11 -0
  58. package/src/domains/generation/wizard/infrastructure/strategies/video-generation-executor.ts +76 -0
  59. package/src/domains/generation/wizard/infrastructure/strategies/video-generation-input-builder.ts +46 -0
  60. package/src/domains/generation/wizard/infrastructure/strategies/video-generation-result-types.ts +17 -0
  61. package/src/domains/generation/wizard/infrastructure/strategies/video-generation-submission.ts +62 -0
  62. package/src/domains/generation/wizard/infrastructure/strategies/video-generation.audio-extractor.ts +27 -0
  63. package/src/domains/generation/wizard/infrastructure/strategies/video-generation.executor.ts +2 -175
  64. package/src/domains/generation/wizard/infrastructure/strategies/video-generation.input-builder.ts +90 -0
  65. package/src/domains/generation/wizard/infrastructure/strategies/video-generation.strategy.ts +3 -108
  66. package/src/domains/generation/wizard/infrastructure/strategies/video-generation.types.ts +0 -129
  67. package/src/domains/generation/wizard/infrastructure/strategies/video-generation.validation.ts +136 -0
  68. package/src/domains/generation/wizard/presentation/hooks/photo-upload/index.ts +39 -0
  69. package/src/domains/generation/wizard/presentation/hooks/photo-upload/types.ts +37 -0
  70. package/src/domains/generation/wizard/presentation/hooks/photo-upload/usePhotoUploadStateLogic.ts +142 -0
  71. package/src/domains/generation/wizard/presentation/hooks/use-video-queue-utils.ts +103 -0
  72. package/src/domains/generation/wizard/presentation/hooks/usePhotoBlockingGeneration.handlers.ts +97 -0
  73. package/src/domains/generation/wizard/presentation/hooks/usePhotoBlockingGeneration.saver.ts +54 -0
  74. package/src/domains/generation/wizard/presentation/hooks/usePhotoBlockingGeneration.ts +22 -87
  75. package/src/domains/generation/wizard/presentation/hooks/usePhotoUploadState.ts +8 -177
  76. package/src/domains/generation/wizard/presentation/hooks/useVideoQueueGeneration.ts +1 -295
  77. package/src/domains/generation/wizard/presentation/hooks/useWizardGeneration.ts +1 -1
  78. package/src/domains/generation/wizard/presentation/hooks/video-queue/index.ts +82 -0
  79. package/src/domains/generation/wizard/presentation/hooks/video-queue/useVideoQueueGenerationCallbacks.ts +120 -0
  80. package/src/domains/generation/wizard/presentation/hooks/video-queue/useVideoQueueGenerationPolling.ts +76 -0
  81. package/src/domains/generation/wizard/presentation/hooks/video-queue/useVideoQueueGenerationRefs.ts +65 -0
  82. package/src/domains/generation/wizard/presentation/hooks/video-queue/useVideoQueueGenerationStart.ts +123 -0
  83. package/src/domains/generation/wizard/presentation/hooks/video-queue-index.ts +9 -0
  84. package/src/domains/image-to-video/domain/types/image-to-video-state.types.ts +11 -4
  85. package/src/domains/text-to-image/domain/types/text-to-image.types.ts +44 -22
  86. package/src/domains/text-to-video/domain/types/request.types.ts +33 -9
  87. package/src/domains/text-to-video/domain/types/state.types.ts +29 -9
  88. package/src/domains/text-to-video/presentation/hooks/useTextToVideoForm.handlers.ts +44 -0
  89. package/src/domains/text-to-video/presentation/hooks/useTextToVideoForm.ts +5 -51
  90. package/src/domains/text-to-video/presentation/hooks/useTextToVideoForm.types.ts +33 -0
  91. package/src/infrastructure/services/generation-orchestrator.service.ts +2 -2
  92. package/src/infrastructure/utils/couple-input-context.ts +13 -0
  93. package/src/infrastructure/utils/couple-input-index.ts +8 -0
  94. package/src/infrastructure/utils/couple-input-refiner.ts +101 -0
  95. package/src/infrastructure/utils/couple-input-resolver.ts +71 -0
  96. package/src/infrastructure/utils/couple-input-types.ts +14 -0
  97. package/src/infrastructure/utils/couple-input.util.ts +3 -176
  98. package/src/infrastructure/utils/photo-generation/photo-preparation.util.ts +1 -1
  99. package/src/infrastructure/validation/base-validator.ts +1 -27
  100. package/src/infrastructure/validation/base-validator.types.ts +32 -0
  101. package/src/presentation/hooks/generation/index.ts +1 -1
  102. package/src/presentation/hooks/generation/orchestrator-abort-logs.ts +48 -0
  103. package/src/presentation/hooks/generation/orchestrator-execution-logs.ts +67 -0
  104. package/src/presentation/hooks/generation/orchestrator-index.ts +14 -0
  105. package/src/presentation/hooks/generation/orchestrator-start-logs.ts +65 -0
  106. package/src/presentation/hooks/generation/orchestrator-state-utils.ts +17 -0
  107. package/src/presentation/hooks/generation/orchestrator-types.ts +55 -0
  108. package/src/presentation/hooks/generation/orchestrator-utils-index.ts +29 -0
  109. package/src/presentation/hooks/generation/orchestrator-utils.ts +25 -0
  110. package/src/presentation/hooks/generation/useDualImageGeneration.ts +1 -1
  111. package/src/presentation/hooks/generation/useImageGeneration.ts +1 -1
  112. package/src/presentation/hooks/generation/useVideoGeneration.ts +1 -1
  113. package/src/shared/hooks/factories/generation-hook-index.ts +12 -0
  114. package/src/shared/hooks/factories/generation-hook-types.ts +47 -0
  115. package/src/shared/hooks/factories/generation-hook-utils.ts +94 -0
  116. package/src/shared/hooks/factories/index.ts +1 -1
  117. package/src/shared/index.ts +1 -1
  118. package/src/shared/utils/calculations/aspect-ratio-calculations.ts +30 -0
  119. package/src/shared/utils/calculations/base64-calculations.ts +26 -0
  120. package/src/shared/utils/calculations/confidence-calculations.ts +21 -0
  121. package/src/shared/utils/calculations/cost-calculations-index.ts +43 -0
  122. package/src/shared/utils/calculations/cost-calculations.ts +25 -0
  123. package/src/shared/utils/calculations/credit-calculations.ts +37 -0
  124. package/src/shared/utils/calculations/index.ts +46 -0
  125. package/src/shared/utils/calculations/math-utilities.ts +32 -0
  126. package/src/shared/utils/calculations/memory-calculations.ts +33 -0
  127. package/src/shared/utils/calculations/pagination-calculations.ts +38 -0
  128. package/src/shared/utils/calculations/percentage-calculations.ts +33 -0
  129. package/src/shared/utils/calculations/time-calculations.ts +99 -0
  130. package/src/shared/utils/credit.ts +1 -1
  131. package/src/shared-kernel/application/hooks/index.ts +8 -0
  132. package/src/shared-kernel/application/hooks/use-feature-state.ts +107 -0
  133. package/src/shared-kernel/application/hooks/use-generation-handler.ts +110 -0
  134. package/src/shared-kernel/base-types/base-callbacks.types.ts +73 -0
  135. package/src/shared-kernel/base-types/base-feature-state.types.ts +77 -0
  136. package/src/shared-kernel/base-types/base-generation.types.ts +69 -0
  137. package/src/shared-kernel/base-types/index.ts +30 -0
  138. package/src/shared-kernel/domain/base-generation-strategy.ts +146 -0
  139. package/src/shared-kernel/domain/index.ts +7 -0
  140. package/src/shared-kernel/index.ts +17 -0
  141. package/src/shared-kernel/infrastructure/validation/common-validators.ts +126 -0
  142. package/src/shared-kernel/infrastructure/validation/common-validators.types.ts +33 -0
  143. package/src/shared-kernel/infrastructure/validation/error-handler.ts +52 -0
  144. package/src/shared-kernel/infrastructure/validation/error-handler.types.ts +38 -0
  145. package/src/shared-kernel/infrastructure/validation/error-handler.utils.ts +79 -0
  146. package/src/shared-kernel/infrastructure/validation/index.ts +28 -0
  147. package/src/domains/background/infrastructure/services/job-poller.service.ts +0 -234
  148. package/src/domains/creations/presentation/hooks/useProcessingJobsPoller.ts +0 -256
  149. package/src/domains/generation/infrastructure/couple-generation-builder.ts +0 -374
  150. package/src/presentation/hooks/generation/orchestrator.ts +0 -276
  151. package/src/shared/hooks/factories/createGenerationHook.ts +0 -253
  152. package/src/shared/utils/calculations.util.ts +0 -366
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Video Queue Generation Hook - Utility Functions
3
+ */
4
+
5
+ import type { GenerationUrls } from "./generation-result.utils";
6
+ import type { UseVideoQueueGenerationProps } from "./use-video-queue-generation.types";
7
+
8
+ /**
9
+ * Clear polling interval
10
+ */
11
+ export function createClearPolling(
12
+ pollingRef: React.MutableRefObject<ReturnType<typeof setInterval> | null>
13
+ ) {
14
+ return () => {
15
+ if (pollingRef.current) {
16
+ clearInterval(pollingRef.current);
17
+ pollingRef.current = null;
18
+ }
19
+ };
20
+ }
21
+
22
+ /**
23
+ * Reset all refs to initial state
24
+ */
25
+ export function createResetRefs(
26
+ clearPolling: () => void,
27
+ creationIdRef: React.MutableRefObject<string | null>,
28
+ requestIdRef: React.MutableRefObject<string | null>,
29
+ modelRef: React.MutableRefObject<string | null>,
30
+ isGeneratingRef: React.MutableRefObject<boolean>,
31
+ isPollingRef: React.MutableRefObject<boolean>,
32
+ consecutiveErrorsRef: React.MutableRefObject<number>,
33
+ pollStartTimeRef: React.MutableRefObject<number | null>,
34
+ setIsGenerating: (value: boolean) => void
35
+ ) {
36
+ return () => {
37
+ clearPolling();
38
+ creationIdRef.current = null;
39
+ requestIdRef.current = null;
40
+ modelRef.current = null;
41
+ isGeneratingRef.current = false;
42
+ isPollingRef.current = false;
43
+ consecutiveErrorsRef.current = 0;
44
+ pollStartTimeRef.current = null;
45
+ setIsGenerating(false);
46
+ };
47
+ }
48
+
49
+ /**
50
+ * Extract duration, resolution, and aspect ratio from input
51
+ */
52
+ export function extractInputMetadata(input: unknown): {
53
+ duration?: number;
54
+ resolution?: string;
55
+ aspectRatio?: string;
56
+ } {
57
+ const inputData = input as Record<string, unknown>;
58
+ return {
59
+ duration: typeof inputData?.duration === "number" ? inputData.duration : undefined,
60
+ resolution: typeof inputData?.resolution === "string" ? inputData.resolution : undefined,
61
+ aspectRatio: typeof inputData?.aspectRatio === "string" ? inputData.aspectRatio : undefined,
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Validate completion data
67
+ */
68
+ export function validateCompletionData(
69
+ creationId: string | null,
70
+ userId: string | null,
71
+ uri: string
72
+ ): boolean {
73
+ return !!(creationId && userId && uri && uri.trim() !== "");
74
+ }
75
+
76
+ /**
77
+ * Log completion data for debugging
78
+ */
79
+ export function logCompletion(
80
+ creationId: string | null,
81
+ userId: string | null,
82
+ urls: GenerationUrls,
83
+ hasOnSuccess: boolean
84
+ ): void {
85
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
86
+ console.log("[VideoQueue] ✅ handleComplete called", {
87
+ creationId,
88
+ userId,
89
+ hasVideoUrl: !!urls.videoUrl,
90
+ hasImageUrl: !!urls.imageUrl,
91
+ hasOnSuccess,
92
+ });
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Log error for debugging
98
+ */
99
+ export function logError(message: string, data: Record<string, unknown>): void {
100
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
101
+ console.error("[VideoQueue] ❌", message, data);
102
+ }
103
+ }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * usePhotoBlockingGeneration Handlers
3
+ * Success and error handlers for photo blocking generation
4
+ */
5
+
6
+ import type { CreationPersistence } from "../../infrastructure/utils/creation-persistence.util";
7
+
8
+ interface HandleSuccessProps {
9
+ readonly creationIdRef: React.MutableRefObject<string | null>;
10
+ readonly userId: string | undefined;
11
+ readonly persistence: CreationPersistence;
12
+ readonly deductCredits?: (cost: number) => Promise<boolean>;
13
+ readonly creditCost?: number;
14
+ readonly onSuccess?: (result: unknown) => void;
15
+ readonly onCreditsExhausted?: () => void;
16
+ }
17
+
18
+ interface HandleErrorProps {
19
+ readonly creationIdRef: React.MutableRefObject<string | null>;
20
+ readonly userId: string | undefined;
21
+ readonly persistence: CreationPersistence;
22
+ readonly onError?: (error: string) => void;
23
+ }
24
+
25
+ export function createSuccessHandler(props: HandleSuccessProps) {
26
+ const {
27
+ creationIdRef,
28
+ userId,
29
+ persistence,
30
+ deductCredits,
31
+ creditCost,
32
+ onSuccess,
33
+ onCreditsExhausted,
34
+ } = props;
35
+
36
+ return async (result: unknown) => {
37
+ const typedResult = result as { imageUrl?: string; videoUrl?: string; audioUrl?: string; logSessionId?: string };
38
+ const creationId = creationIdRef.current;
39
+ const resultUri = typedResult.imageUrl || typedResult.videoUrl || typedResult.audioUrl;
40
+
41
+ if (creationId && userId && resultUri) {
42
+ try {
43
+ await persistence.updateToCompleted(userId, creationId, {
44
+ uri: resultUri,
45
+ imageUrl: typedResult.imageUrl,
46
+ videoUrl: typedResult.videoUrl,
47
+ audioUrl: typedResult.audioUrl,
48
+ logSessionId: typedResult.logSessionId,
49
+ });
50
+ } catch (err) {
51
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
52
+ console.error("[PhotoBlockingGeneration] updateToCompleted error:", err);
53
+ }
54
+ }
55
+ }
56
+
57
+ creationIdRef.current = null;
58
+
59
+ // Deduct credits after successful generation
60
+ if (deductCredits && creditCost) {
61
+ try {
62
+ const deducted = await deductCredits(creditCost);
63
+ if (!deducted) {
64
+ onCreditsExhausted?.();
65
+ }
66
+ } catch (err) {
67
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
68
+ console.error("[PhotoBlockingGeneration] deductCredits error:", err);
69
+ }
70
+ }
71
+ }
72
+
73
+ onSuccess?.(result);
74
+ };
75
+ }
76
+
77
+ export function createErrorHandler(props: HandleErrorProps) {
78
+ const { creationIdRef, userId, persistence, onError } = props;
79
+
80
+ return async (err: { message: string; originalError?: Error & { logSessionId?: string } }) => {
81
+ const creationId = creationIdRef.current;
82
+ const logSessionId = err.originalError?.logSessionId;
83
+
84
+ if (creationId && userId) {
85
+ try {
86
+ await persistence.updateToFailed(userId, creationId, err.message, logSessionId);
87
+ } catch (updateErr) {
88
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
89
+ console.error("[PhotoBlockingGeneration] updateToFailed error:", updateErr);
90
+ }
91
+ }
92
+ }
93
+
94
+ creationIdRef.current = null;
95
+ onError?.(err.message);
96
+ };
97
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * usePhotoBlockingGeneration Saver
3
+ * Handles saving creation to processing state
4
+ */
5
+
6
+ import type { CreationPersistence } from "../../infrastructure/utils/creation-persistence.util";
7
+ import type { WizardScenarioData } from "./wizard-generation.types";
8
+
9
+ interface SaveCreationProps {
10
+ readonly userId: string | undefined;
11
+ readonly scenario: WizardScenarioData;
12
+ readonly persistence: CreationPersistence;
13
+ readonly creditCost?: number;
14
+ readonly creationIdRef: React.MutableRefObject<string | null>;
15
+ }
16
+
17
+ export async function saveCreationToProcessing(
18
+ props: SaveCreationProps,
19
+ input: unknown,
20
+ prompt: string,
21
+ ): Promise<void> {
22
+ const { userId, scenario, persistence, creditCost, creationIdRef } = props;
23
+
24
+ if (!userId || !prompt) return;
25
+
26
+ try {
27
+ // Extract generation parameters from input (for image generation, no duration/resolution)
28
+ const inputData = input as Record<string, unknown>;
29
+ const duration = typeof inputData?.duration === "number" ? inputData.duration : undefined;
30
+ const resolution = typeof inputData?.resolution === "string" ? inputData.resolution : undefined;
31
+ const aspectRatio = typeof inputData?.aspectRatio === "string" ? inputData.aspectRatio : undefined;
32
+
33
+ const result = await persistence.saveAsProcessing(userId, {
34
+ scenarioId: scenario.id,
35
+ scenarioTitle: scenario.title || scenario.id,
36
+ prompt,
37
+ duration,
38
+ resolution,
39
+ creditCost,
40
+ aspectRatio,
41
+ provider: scenario.providerId ?? "fal",
42
+ outputType: scenario.outputType,
43
+ });
44
+ creationIdRef.current = result.creationId;
45
+
46
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
47
+ console.log("[PhotoBlockingGeneration] Saved as processing:", result.creationId);
48
+ }
49
+ } catch (err) {
50
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
51
+ console.error("[PhotoBlockingGeneration] saveAsProcessing error:", err);
52
+ }
53
+ }
54
+ }
@@ -12,6 +12,8 @@ import type { CreationPersistence } from "../../infrastructure/utils/creation-pe
12
12
  import type { WizardStrategy } from "../../infrastructure/strategies/wizard-strategy.types";
13
13
  import type { WizardScenarioData } from "./wizard-generation.types";
14
14
  import type { AlertMessages } from "../../../../../presentation/hooks/generation/types";
15
+ import { createSuccessHandler, createErrorHandler } from "./usePhotoBlockingGeneration.handlers";
16
+ import { saveCreationToProcessing } from "./usePhotoBlockingGeneration.saver";
15
17
 
16
18
 
17
19
  interface UsePhotoBlockingGenerationProps {
@@ -51,66 +53,25 @@ export function usePhotoBlockingGeneration(
51
53
  const creationIdRef = useRef<string | null>(null);
52
54
 
53
55
  const handleSuccess = useCallback(
54
- async (result: unknown) => {
55
- const typedResult = result as { imageUrl?: string; videoUrl?: string; audioUrl?: string; logSessionId?: string };
56
- const creationId = creationIdRef.current;
57
- const resultUri = typedResult.imageUrl || typedResult.videoUrl || typedResult.audioUrl;
58
-
59
- if (creationId && userId && resultUri) {
60
- try {
61
- await persistence.updateToCompleted(userId, creationId, {
62
- uri: resultUri,
63
- imageUrl: typedResult.imageUrl,
64
- videoUrl: typedResult.videoUrl,
65
- audioUrl: typedResult.audioUrl,
66
- logSessionId: typedResult.logSessionId,
67
- });
68
- } catch (err) {
69
- if (typeof __DEV__ !== "undefined" && __DEV__) {
70
- console.error("[PhotoBlockingGeneration] updateToCompleted error:", err);
71
- }
72
- }
73
- }
74
-
75
- creationIdRef.current = null;
76
-
77
- // Deduct credits after successful generation
78
- if (deductCredits && creditCost) {
79
- try {
80
- const deducted = await deductCredits(creditCost);
81
- if (!deducted) {
82
- onCreditsExhausted?.();
83
- }
84
- } catch (err) {
85
- if (typeof __DEV__ !== "undefined" && __DEV__) {
86
- console.error("[PhotoBlockingGeneration] deductCredits error:", err);
87
- }
88
- }
89
- }
90
-
91
- onSuccess?.(result);
92
- },
56
+ createSuccessHandler({
57
+ creationIdRef,
58
+ userId,
59
+ persistence,
60
+ deductCredits,
61
+ creditCost,
62
+ onSuccess,
63
+ onCreditsExhausted,
64
+ }),
93
65
  [userId, persistence, deductCredits, creditCost, onSuccess, onCreditsExhausted],
94
66
  );
95
67
 
96
68
  const handleError = useCallback(
97
- async (err: { message: string; originalError?: Error & { logSessionId?: string } }) => {
98
- const creationId = creationIdRef.current;
99
- const logSessionId = err.originalError?.logSessionId;
100
-
101
- if (creationId && userId) {
102
- try {
103
- await persistence.updateToFailed(userId, creationId, err.message, logSessionId);
104
- } catch (updateErr) {
105
- if (typeof __DEV__ !== "undefined" && __DEV__) {
106
- console.error("[PhotoBlockingGeneration] updateToFailed error:", updateErr);
107
- }
108
- }
109
- }
110
-
111
- creationIdRef.current = null;
112
- onError?.(err.message);
113
- },
69
+ createErrorHandler({
70
+ creationIdRef,
71
+ userId,
72
+ persistence,
73
+ onError,
74
+ }),
114
75
  [userId, persistence, onError],
115
76
  );
116
77
 
@@ -124,37 +85,11 @@ export function usePhotoBlockingGeneration(
124
85
  const startGeneration = useCallback(
125
86
  async (input: unknown, prompt: string) => {
126
87
  // Save to Firestore first
127
- if (userId && prompt) {
128
- try {
129
- // Extract generation parameters from input (for image generation, no duration/resolution)
130
- const inputData = input as Record<string, unknown>;
131
- const duration = typeof inputData?.duration === "number" ? inputData.duration : undefined;
132
- const resolution = typeof inputData?.resolution === "string" ? inputData.resolution : undefined;
133
-
134
- const aspectRatio = typeof inputData?.aspectRatio === "string" ? inputData.aspectRatio : undefined;
135
-
136
- const result = await persistence.saveAsProcessing(userId, {
137
- scenarioId: scenario.id,
138
- scenarioTitle: scenario.title || scenario.id,
139
- prompt,
140
- duration,
141
- resolution,
142
- creditCost,
143
- aspectRatio,
144
- provider: scenario.providerId ?? "fal",
145
- outputType: scenario.outputType,
146
- });
147
- creationIdRef.current = result.creationId;
148
-
149
- if (typeof __DEV__ !== "undefined" && __DEV__) {
150
- console.log("[PhotoBlockingGeneration] Saved as processing:", result.creationId);
151
- }
152
- } catch (err) {
153
- if (typeof __DEV__ !== "undefined" && __DEV__) {
154
- console.error("[PhotoBlockingGeneration] saveAsProcessing error:", err);
155
- }
156
- }
157
- }
88
+ await saveCreationToProcessing(
89
+ { userId, scenario, persistence, creditCost, creationIdRef },
90
+ input,
91
+ prompt,
92
+ );
158
93
 
159
94
  // Start blocking generation
160
95
  await generate(input);
@@ -4,180 +4,11 @@
4
4
  * Uses design system's useMedia hook for media picking with built-in validation
5
5
  */
6
6
 
7
- import { useState, useCallback, useEffect, useRef } from "react";
8
-
9
- import { useMedia, MediaQuality, MediaValidationError, MEDIA_CONSTANTS } from "@umituz/react-native-design-system/media";
10
- import type { UploadedImage } from "../../../../../presentation/hooks/generation/useAIGenerateState";
11
-
12
- export interface PhotoUploadConfig {
13
- readonly maxFileSizeMB?: number;
14
- }
15
-
16
- export interface PhotoUploadTranslations {
17
- readonly fileTooLarge: string;
18
- readonly maxFileSize: string;
19
- readonly error: string;
20
- readonly uploadFailed: string;
21
- readonly permissionDenied?: string;
22
- }
23
-
24
- export interface PhotoUploadError {
25
- readonly title: string;
26
- readonly message: string;
27
- }
28
-
29
- export interface UsePhotoUploadStateProps {
30
- readonly config?: PhotoUploadConfig;
31
- readonly translations: PhotoUploadTranslations;
32
- readonly initialImage?: UploadedImage;
33
- readonly stepId?: string;
34
- readonly onError?: (error: PhotoUploadError) => void;
35
- }
36
-
37
- export interface UsePhotoUploadStateReturn {
38
- readonly image: UploadedImage | null;
39
- readonly handlePickImage: () => Promise<void>;
40
- readonly canContinue: boolean;
41
- readonly clearImage: () => void;
42
- }
43
-
44
- export const usePhotoUploadState = ({
45
- config,
46
- translations,
47
- initialImage,
48
- stepId,
49
- onError,
50
- }: UsePhotoUploadStateProps): UsePhotoUploadStateReturn => {
51
- const [image, setImage] = useState<UploadedImage | null>(initialImage || null);
52
- const { pickImage, isLoading } = useMedia();
53
- const timeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
54
-
55
- // Use refs to avoid effect re-runs on callback changes
56
- const onErrorRef = useRef(onError);
57
- const translationsRef = useRef(translations);
58
-
59
- useEffect(() => {
60
- onErrorRef.current = onError;
61
- translationsRef.current = translations;
62
- }, [onError, translations]);
63
-
64
- const maxFileSizeMB = config?.maxFileSizeMB ?? MEDIA_CONSTANTS.MAX_IMAGE_SIZE_MB;
65
-
66
- useEffect(() => {
67
- if (typeof __DEV__ !== "undefined" && __DEV__) {
68
- console.log("[usePhotoUploadState] Step changed, resetting image", { stepId, hasInitialImage: !!initialImage });
69
- }
70
- setImage(initialImage || null);
71
- }, [stepId, initialImage]);
72
-
73
- useEffect(() => {
74
- // Clear any existing timeout first
75
- if (timeoutRef.current) {
76
- clearTimeout(timeoutRef.current);
77
- timeoutRef.current = undefined;
78
- }
79
-
80
- if (isLoading) {
81
- timeoutRef.current = setTimeout(() => {
82
- if (typeof __DEV__ !== "undefined" && __DEV__) {
83
- console.warn("[usePhotoUploadState] Image picker timeout - possible stuck state (DEV warning only)");
84
- }
85
- // NOTE: Do NOT call onError here — the picker may still complete successfully.
86
- // Showing a modal alert while the picker is open blocks the UI.
87
- }, 30000);
88
- }
89
-
90
- return () => {
91
- if (timeoutRef.current) {
92
- clearTimeout(timeoutRef.current);
93
- timeoutRef.current = undefined;
94
- }
95
- };
96
- }, [isLoading]);
97
-
98
- const clearImage = useCallback(() => {
99
- setImage(null);
100
- }, []);
101
-
102
- const handlePickImage = useCallback(async () => {
103
- try {
104
- if (typeof __DEV__ !== "undefined" && __DEV__) {
105
- console.log("[usePhotoUploadState] Starting image pick");
106
- }
107
-
108
- const result = await pickImage({
109
- allowsEditing: true,
110
- aspect: [1, 1],
111
- quality: MediaQuality.MEDIUM,
112
- maxFileSizeMB,
113
- });
114
-
115
- if (result.error) {
116
- if (typeof __DEV__ !== "undefined" && __DEV__) {
117
- console.log("[usePhotoUploadState] Validation error", result.error);
118
- }
119
-
120
- if (result.error === MediaValidationError.FILE_TOO_LARGE) {
121
- onErrorRef.current?.({
122
- title: translationsRef.current.fileTooLarge,
123
- message: translationsRef.current.maxFileSize.replace("{size}", maxFileSizeMB.toString()),
124
- });
125
- } else if (result.error === MediaValidationError.PERMISSION_DENIED) {
126
- onErrorRef.current?.({
127
- title: translationsRef.current.error,
128
- message: translationsRef.current.permissionDenied ?? "Permission to access media library is required",
129
- });
130
- }
131
- return;
132
- }
133
-
134
- if (result.canceled || !result.assets || result.assets.length === 0) {
135
- if (typeof __DEV__ !== "undefined" && __DEV__) {
136
- console.log("[usePhotoUploadState] Image pick canceled");
137
- }
138
- return;
139
- }
140
-
141
- const selectedAsset = result.assets[0];
142
- if (!selectedAsset) {
143
- return;
144
- }
145
-
146
- const uploadedImage: UploadedImage = {
147
- uri: selectedAsset.uri,
148
- previewUrl: selectedAsset.uri,
149
- width: selectedAsset.width,
150
- height: selectedAsset.height,
151
- fileSize: selectedAsset.fileSize,
152
- };
153
-
154
- setImage(uploadedImage);
155
-
156
- if (typeof __DEV__ !== "undefined" && __DEV__) {
157
- const fileSizeMB = (selectedAsset.fileSize ?? 0) / (1024 * 1024);
158
- console.log("[usePhotoUploadState] Image selected", {
159
- width: uploadedImage.width,
160
- height: uploadedImage.height,
161
- fileSizeMB: fileSizeMB.toFixed(2),
162
- });
163
- }
164
- } catch (error) {
165
- if (typeof __DEV__ !== "undefined" && __DEV__) {
166
- console.error("[usePhotoUploadState] Error picking image", error);
167
- }
168
- onErrorRef.current?.({
169
- title: translationsRef.current.error,
170
- message: translationsRef.current.uploadFailed,
171
- });
172
- }
173
- }, [pickImage, maxFileSizeMB]);
174
-
175
- const canContinue = image !== null && !isLoading;
176
-
177
- return {
178
- image,
179
- handlePickImage,
180
- canContinue,
181
- clearImage,
182
- };
183
- };
7
+ export { usePhotoUploadState } from "./photo-upload";
8
+ export type {
9
+ PhotoUploadConfig,
10
+ PhotoUploadTranslations,
11
+ PhotoUploadError,
12
+ UsePhotoUploadStateProps,
13
+ UsePhotoUploadStateReturn,
14
+ } from "./photo-upload";