@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.
- package/package.json +1 -4
- package/src/domain/interfaces/app-services-auth.interface.ts +27 -0
- package/src/domain/interfaces/app-services-composite.interface.ts +29 -0
- package/src/domain/interfaces/app-services-optional.interface.ts +42 -0
- package/src/domain/interfaces/app-services.interface.ts +0 -79
- package/src/domains/background/infrastructure/services/job-poller-index.ts +7 -0
- package/src/domains/background/infrastructure/services/job-poller-utils.ts +130 -0
- package/src/domains/background/infrastructure/utils/polling-interval.util.ts +1 -1
- package/src/domains/background/presentation/hooks/use-background-generation.ts +1 -1
- package/src/domains/content-moderation/infrastructure/services/content-moderation.service.ts +1 -1
- package/src/domains/content-moderation/infrastructure/services/moderators/image.moderator.ts +34 -8
- package/src/domains/content-moderation/infrastructure/services/moderators/text.moderator.ts +15 -4
- package/src/domains/content-moderation/infrastructure/services/moderators/video.moderator.ts +34 -8
- package/src/domains/content-moderation/infrastructure/services/moderators/voice.moderator.ts +19 -8
- package/src/domains/creations/domain/types/creation-categories.constants.ts +57 -0
- package/src/domains/creations/domain/types/creation-categories.helpers.ts +67 -0
- package/src/domains/creations/domain/types/creation-categories.ts +5 -111
- package/src/domains/creations/presentation/hooks/creation-validators.ts +31 -29
- package/src/domains/creations/presentation/hooks/job-poller-index.ts +10 -0
- package/src/domains/creations/presentation/hooks/job-poller-utils.filters.ts +34 -0
- package/src/domains/creations/presentation/hooks/job-poller-utils.logger.ts +76 -0
- package/src/domains/creations/presentation/hooks/job-poller-utils.stale-handlers.ts +52 -0
- package/src/domains/creations/presentation/hooks/job-poller-utils.ts +8 -0
- package/src/domains/creations/presentation/screens/CreationsGalleryScreen.tsx +1 -1
- package/src/domains/creations/presentation-exports.ts +2 -2
- package/src/domains/face-detection/domain/entities/FaceDetection.ts +4 -3
- package/src/domains/face-detection/presentation/hooks/useFaceDetection.ts +24 -21
- package/src/domains/generation/infrastructure/couple-generation-builder/builder-couple-preparation.ts +58 -0
- package/src/domains/generation/infrastructure/couple-generation-builder/builder-couple-prompt.ts +69 -0
- package/src/domains/generation/infrastructure/couple-generation-builder/builder-couple-resolution.ts +77 -0
- package/src/domains/generation/infrastructure/couple-generation-builder/builder-couple.ts +54 -0
- package/src/domains/generation/infrastructure/couple-generation-builder/builder-index.ts +8 -0
- package/src/domains/generation/infrastructure/couple-generation-builder/builder-scenario.ts +113 -0
- package/src/domains/generation/infrastructure/couple-generation-builder/builder.ts +7 -0
- package/src/domains/generation/infrastructure/couple-generation-builder/index.ts +20 -0
- package/src/domains/generation/infrastructure/couple-generation-builder/types.ts +44 -0
- package/src/domains/generation/infrastructure/couple-generation-builder/utils/builder-end-logger.ts +18 -0
- package/src/domains/generation/infrastructure/couple-generation-builder/utils/builder-start-logger.ts +57 -0
- package/src/domains/generation/infrastructure/couple-generation-builder/utils/builder-step-logger.ts +106 -0
- package/src/domains/generation/infrastructure/couple-generation-builder/utils/index.ts +8 -0
- package/src/domains/generation/infrastructure/couple-generation-builder/utils/types.ts +49 -0
- package/src/domains/generation/infrastructure/couple-generation-builder/utils.ts +8 -0
- package/src/domains/generation/infrastructure/flow/flow-store-actions.ts +105 -0
- package/src/domains/generation/infrastructure/flow/flow-store-initial-state.ts +26 -0
- package/src/domains/generation/infrastructure/flow/useFlowStore.ts +4 -116
- package/src/domains/generation/presentation/useAIGeneration.hook.ts +1 -1
- package/src/domains/generation/wizard/infrastructure/strategies/image-generation-strategy-index.ts +7 -0
- package/src/domains/generation/wizard/infrastructure/strategies/image-generation.executor.ts +2 -12
- package/src/domains/generation/wizard/infrastructure/strategies/image-generation.executor.types.ts +11 -0
- package/src/domains/generation/wizard/infrastructure/strategies/image-generation.executor.utils.ts +12 -0
- package/src/domains/generation/wizard/infrastructure/strategies/image-generation.strategy.ts +1 -220
- package/src/domains/generation/wizard/infrastructure/strategies/image-input-builder.ts +66 -0
- package/src/domains/generation/wizard/infrastructure/strategies/image-input-extraction.ts +88 -0
- package/src/domains/generation/wizard/infrastructure/strategies/image-input-prompt-builder.ts +75 -0
- package/src/domains/generation/wizard/infrastructure/strategies/image-input-style-enhancements.ts +35 -0
- package/src/domains/generation/wizard/infrastructure/strategies/image-strategy-factory.ts +42 -0
- package/src/domains/generation/wizard/infrastructure/strategies/video-generation-executor-index.ts +11 -0
- package/src/domains/generation/wizard/infrastructure/strategies/video-generation-executor.ts +76 -0
- package/src/domains/generation/wizard/infrastructure/strategies/video-generation-input-builder.ts +46 -0
- package/src/domains/generation/wizard/infrastructure/strategies/video-generation-result-types.ts +17 -0
- package/src/domains/generation/wizard/infrastructure/strategies/video-generation-submission.ts +62 -0
- package/src/domains/generation/wizard/infrastructure/strategies/video-generation.audio-extractor.ts +27 -0
- package/src/domains/generation/wizard/infrastructure/strategies/video-generation.executor.ts +2 -175
- package/src/domains/generation/wizard/infrastructure/strategies/video-generation.input-builder.ts +90 -0
- package/src/domains/generation/wizard/infrastructure/strategies/video-generation.strategy.ts +3 -108
- package/src/domains/generation/wizard/infrastructure/strategies/video-generation.types.ts +0 -129
- package/src/domains/generation/wizard/infrastructure/strategies/video-generation.validation.ts +136 -0
- package/src/domains/generation/wizard/presentation/hooks/photo-upload/index.ts +39 -0
- package/src/domains/generation/wizard/presentation/hooks/photo-upload/types.ts +37 -0
- package/src/domains/generation/wizard/presentation/hooks/photo-upload/usePhotoUploadStateLogic.ts +142 -0
- package/src/domains/generation/wizard/presentation/hooks/use-video-queue-utils.ts +103 -0
- package/src/domains/generation/wizard/presentation/hooks/usePhotoBlockingGeneration.handlers.ts +97 -0
- package/src/domains/generation/wizard/presentation/hooks/usePhotoBlockingGeneration.saver.ts +54 -0
- package/src/domains/generation/wizard/presentation/hooks/usePhotoBlockingGeneration.ts +22 -87
- package/src/domains/generation/wizard/presentation/hooks/usePhotoUploadState.ts +8 -177
- package/src/domains/generation/wizard/presentation/hooks/useVideoQueueGeneration.ts +1 -295
- package/src/domains/generation/wizard/presentation/hooks/useWizardGeneration.ts +1 -1
- package/src/domains/generation/wizard/presentation/hooks/video-queue/index.ts +82 -0
- package/src/domains/generation/wizard/presentation/hooks/video-queue/useVideoQueueGenerationCallbacks.ts +120 -0
- package/src/domains/generation/wizard/presentation/hooks/video-queue/useVideoQueueGenerationPolling.ts +76 -0
- package/src/domains/generation/wizard/presentation/hooks/video-queue/useVideoQueueGenerationRefs.ts +65 -0
- package/src/domains/generation/wizard/presentation/hooks/video-queue/useVideoQueueGenerationStart.ts +123 -0
- package/src/domains/generation/wizard/presentation/hooks/video-queue-index.ts +9 -0
- package/src/domains/image-to-video/domain/types/image-to-video-state.types.ts +11 -4
- package/src/domains/text-to-image/domain/types/text-to-image.types.ts +44 -22
- package/src/domains/text-to-video/domain/types/request.types.ts +33 -9
- package/src/domains/text-to-video/domain/types/state.types.ts +29 -9
- package/src/domains/text-to-video/presentation/hooks/useTextToVideoForm.handlers.ts +44 -0
- package/src/domains/text-to-video/presentation/hooks/useTextToVideoForm.ts +5 -51
- package/src/domains/text-to-video/presentation/hooks/useTextToVideoForm.types.ts +33 -0
- package/src/infrastructure/services/generation-orchestrator.service.ts +2 -2
- package/src/infrastructure/utils/couple-input-context.ts +13 -0
- package/src/infrastructure/utils/couple-input-index.ts +8 -0
- package/src/infrastructure/utils/couple-input-refiner.ts +101 -0
- package/src/infrastructure/utils/couple-input-resolver.ts +71 -0
- package/src/infrastructure/utils/couple-input-types.ts +14 -0
- package/src/infrastructure/utils/couple-input.util.ts +3 -176
- package/src/infrastructure/utils/photo-generation/photo-preparation.util.ts +1 -1
- package/src/infrastructure/validation/base-validator.ts +1 -27
- package/src/infrastructure/validation/base-validator.types.ts +32 -0
- package/src/presentation/hooks/generation/index.ts +1 -1
- package/src/presentation/hooks/generation/orchestrator-abort-logs.ts +48 -0
- package/src/presentation/hooks/generation/orchestrator-execution-logs.ts +67 -0
- package/src/presentation/hooks/generation/orchestrator-index.ts +14 -0
- package/src/presentation/hooks/generation/orchestrator-start-logs.ts +65 -0
- package/src/presentation/hooks/generation/orchestrator-state-utils.ts +17 -0
- package/src/presentation/hooks/generation/orchestrator-types.ts +55 -0
- package/src/presentation/hooks/generation/orchestrator-utils-index.ts +29 -0
- package/src/presentation/hooks/generation/orchestrator-utils.ts +25 -0
- package/src/presentation/hooks/generation/useDualImageGeneration.ts +1 -1
- package/src/presentation/hooks/generation/useImageGeneration.ts +1 -1
- package/src/presentation/hooks/generation/useVideoGeneration.ts +1 -1
- package/src/shared/hooks/factories/generation-hook-index.ts +12 -0
- package/src/shared/hooks/factories/generation-hook-types.ts +47 -0
- package/src/shared/hooks/factories/generation-hook-utils.ts +94 -0
- package/src/shared/hooks/factories/index.ts +1 -1
- package/src/shared/index.ts +1 -1
- package/src/shared/utils/calculations/aspect-ratio-calculations.ts +30 -0
- package/src/shared/utils/calculations/base64-calculations.ts +26 -0
- package/src/shared/utils/calculations/confidence-calculations.ts +21 -0
- package/src/shared/utils/calculations/cost-calculations-index.ts +43 -0
- package/src/shared/utils/calculations/cost-calculations.ts +25 -0
- package/src/shared/utils/calculations/credit-calculations.ts +37 -0
- package/src/shared/utils/calculations/index.ts +46 -0
- package/src/shared/utils/calculations/math-utilities.ts +32 -0
- package/src/shared/utils/calculations/memory-calculations.ts +33 -0
- package/src/shared/utils/calculations/pagination-calculations.ts +38 -0
- package/src/shared/utils/calculations/percentage-calculations.ts +33 -0
- package/src/shared/utils/calculations/time-calculations.ts +99 -0
- package/src/shared/utils/credit.ts +1 -1
- package/src/shared-kernel/application/hooks/index.ts +8 -0
- package/src/shared-kernel/application/hooks/use-feature-state.ts +107 -0
- package/src/shared-kernel/application/hooks/use-generation-handler.ts +110 -0
- package/src/shared-kernel/base-types/base-callbacks.types.ts +73 -0
- package/src/shared-kernel/base-types/base-feature-state.types.ts +77 -0
- package/src/shared-kernel/base-types/base-generation.types.ts +69 -0
- package/src/shared-kernel/base-types/index.ts +30 -0
- package/src/shared-kernel/domain/base-generation-strategy.ts +146 -0
- package/src/shared-kernel/domain/index.ts +7 -0
- package/src/shared-kernel/index.ts +17 -0
- package/src/shared-kernel/infrastructure/validation/common-validators.ts +126 -0
- package/src/shared-kernel/infrastructure/validation/common-validators.types.ts +33 -0
- package/src/shared-kernel/infrastructure/validation/error-handler.ts +52 -0
- package/src/shared-kernel/infrastructure/validation/error-handler.types.ts +38 -0
- package/src/shared-kernel/infrastructure/validation/error-handler.utils.ts +79 -0
- package/src/shared-kernel/infrastructure/validation/index.ts +28 -0
- package/src/domains/background/infrastructure/services/job-poller.service.ts +0 -234
- package/src/domains/creations/presentation/hooks/useProcessingJobsPoller.ts +0 -256
- package/src/domains/generation/infrastructure/couple-generation-builder.ts +0 -374
- package/src/presentation/hooks/generation/orchestrator.ts +0 -276
- package/src/shared/hooks/factories/createGenerationHook.ts +0 -253
- 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
|
+
}
|
package/src/domains/generation/wizard/presentation/hooks/usePhotoBlockingGeneration.handlers.ts
ADDED
|
@@ -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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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";
|