@umituz/react-native-ai-generation-content 1.37.13 → 1.37.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/domains/generation/wizard/infrastructure/strategies/image-generation.strategy.ts +2 -32
- package/src/domains/generation/wizard/infrastructure/strategies/video-generation.strategy.ts +2 -26
- package/src/domains/generation/wizard/infrastructure/strategies/wizard-strategy.types.ts +2 -1
- package/src/domains/generation/wizard/infrastructure/utils/creation-persistence.util.ts +109 -0
- package/src/domains/generation/wizard/presentation/hooks/useWizardGeneration.ts +66 -79
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-ai-generation-content",
|
|
3
|
-
"version": "1.37.
|
|
3
|
+
"version": "1.37.15",
|
|
4
4
|
"description": "Provider-agnostic AI generation orchestration for React Native with result preview components",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
package/src/domains/generation/wizard/infrastructure/strategies/image-generation.strategy.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Image Generation Strategy
|
|
3
|
-
* Handles image-specific generation logic
|
|
3
|
+
* Handles image-specific generation logic (execution only)
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { createCreationsRepository } from "../../../../creations/infrastructure/adapters";
|
|
7
6
|
import type { WizardScenarioData } from "../../presentation/hooks/useWizardGeneration";
|
|
8
7
|
import type { WizardStrategy } from "./wizard-strategy.types";
|
|
9
8
|
import { DEFAULT_STYLE_VALUE, IMAGE_PROCESSING_PROMPTS } from "./wizard-strategy.constants";
|
|
@@ -81,10 +80,7 @@ function applyStyleEnhancements(prompt: string, wizardData: Record<string, unkno
|
|
|
81
80
|
// ============================================================================
|
|
82
81
|
|
|
83
82
|
export function createImageStrategy(options: CreateImageStrategyOptions): WizardStrategy {
|
|
84
|
-
const { scenario
|
|
85
|
-
const repository = createCreationsRepository(collectionName);
|
|
86
|
-
|
|
87
|
-
let lastInputRef: WizardImageInput | null = null;
|
|
83
|
+
const { scenario } = options;
|
|
88
84
|
|
|
89
85
|
return {
|
|
90
86
|
execute: async (input: unknown) => {
|
|
@@ -93,8 +89,6 @@ export function createImageStrategy(options: CreateImageStrategyOptions): Wizard
|
|
|
93
89
|
throw new Error("Model is required for image generation");
|
|
94
90
|
}
|
|
95
91
|
|
|
96
|
-
lastInputRef = imageInput;
|
|
97
|
-
|
|
98
92
|
const result = await executeImageGeneration(imageInput, scenario.model);
|
|
99
93
|
|
|
100
94
|
if (!result.success || !result.imageUrl) {
|
|
@@ -105,29 +99,5 @@ export function createImageStrategy(options: CreateImageStrategyOptions): Wizard
|
|
|
105
99
|
},
|
|
106
100
|
|
|
107
101
|
getCreditCost: () => 1,
|
|
108
|
-
|
|
109
|
-
save: async (result: unknown, uid) => {
|
|
110
|
-
const input = lastInputRef;
|
|
111
|
-
const imageResult = result as { imageUrl?: string };
|
|
112
|
-
if (!input || !scenario?.id || !imageResult.imageUrl) return;
|
|
113
|
-
|
|
114
|
-
const creation = {
|
|
115
|
-
id: `${scenario.id}_${Date.now()}`,
|
|
116
|
-
uri: imageResult.imageUrl,
|
|
117
|
-
type: scenario.id,
|
|
118
|
-
prompt: input.prompt,
|
|
119
|
-
status: "completed" as const,
|
|
120
|
-
createdAt: new Date(),
|
|
121
|
-
isShared: false,
|
|
122
|
-
isFavorite: false,
|
|
123
|
-
metadata: {
|
|
124
|
-
scenarioId: scenario.id,
|
|
125
|
-
scenarioTitle: scenario.title,
|
|
126
|
-
},
|
|
127
|
-
output: { imageUrl: imageResult.imageUrl },
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
await repository.create(uid, creation);
|
|
131
|
-
},
|
|
132
102
|
};
|
|
133
103
|
}
|
package/src/domains/generation/wizard/infrastructure/strategies/video-generation.strategy.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Video Generation Strategy
|
|
3
|
-
* Handles video-specific generation logic
|
|
3
|
+
* Handles video-specific generation logic (execution only)
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { executeVideoFeature } from "../../../../../infrastructure/services/video-feature-executor.service";
|
|
7
|
-
import { createCreationsRepository } from "../../../../creations/infrastructure/adapters";
|
|
8
7
|
import { buildUnifiedPrompt } from "./shared/unified-prompt-builder";
|
|
9
8
|
import type { WizardScenarioData } from "../../presentation/hooks/useWizardGeneration";
|
|
10
9
|
import type { WizardStrategy } from "./wizard-strategy.types";
|
|
@@ -71,16 +70,12 @@ export async function buildVideoInput(
|
|
|
71
70
|
}
|
|
72
71
|
|
|
73
72
|
export function createVideoStrategy(options: CreateVideoStrategyOptions): WizardStrategy {
|
|
74
|
-
const { scenario
|
|
75
|
-
const repository = createCreationsRepository(collectionName);
|
|
73
|
+
const { scenario } = options;
|
|
76
74
|
const videoFeatureType = getVideoFeatureType(scenario.id);
|
|
77
75
|
|
|
78
|
-
let lastInputRef: WizardVideoInput | null = null;
|
|
79
|
-
|
|
80
76
|
return {
|
|
81
77
|
execute: async (input: unknown) => {
|
|
82
78
|
const videoInput = input as WizardVideoInput;
|
|
83
|
-
lastInputRef = videoInput;
|
|
84
79
|
|
|
85
80
|
const result = await executeVideoFeature(videoFeatureType, {
|
|
86
81
|
sourceImageBase64: videoInput.sourceImageBase64,
|
|
@@ -101,24 +96,5 @@ export function createVideoStrategy(options: CreateVideoStrategyOptions): Wizard
|
|
|
101
96
|
},
|
|
102
97
|
|
|
103
98
|
getCreditCost: () => 1,
|
|
104
|
-
|
|
105
|
-
save: async (result: unknown, uid) => {
|
|
106
|
-
const input = lastInputRef;
|
|
107
|
-
const videoResult = result as { videoUrl?: string };
|
|
108
|
-
if (!input || !scenario?.id || !videoResult.videoUrl) return;
|
|
109
|
-
|
|
110
|
-
await repository.create(uid, {
|
|
111
|
-
id: `${scenario.id}_${Date.now()}`,
|
|
112
|
-
uri: videoResult.videoUrl,
|
|
113
|
-
type: scenario.id,
|
|
114
|
-
prompt: input.prompt,
|
|
115
|
-
status: "completed" as const,
|
|
116
|
-
createdAt: new Date(),
|
|
117
|
-
isShared: false,
|
|
118
|
-
isFavorite: false,
|
|
119
|
-
metadata: { scenarioId: scenario.id, scenarioTitle: scenario.title },
|
|
120
|
-
output: { videoUrl: videoResult.videoUrl },
|
|
121
|
-
});
|
|
122
|
-
},
|
|
123
99
|
};
|
|
124
100
|
}
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
export interface WizardStrategy {
|
|
7
|
+
/** Execute the generation - returns result with URLs */
|
|
7
8
|
execute: (input: unknown) => Promise<{ imageUrl?: string; videoUrl?: string }>;
|
|
9
|
+
/** Get credit cost for this generation */
|
|
8
10
|
getCreditCost: () => number;
|
|
9
|
-
save?: (result: unknown, userId: string) => Promise<void>;
|
|
10
11
|
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creation Persistence Utility
|
|
3
|
+
* Handles all Firestore creation operations for wizard generation
|
|
4
|
+
* Single source of truth for processing → completed flow
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { createCreationsRepository } from "../../../../creations/infrastructure/adapters";
|
|
8
|
+
|
|
9
|
+
declare const __DEV__: boolean;
|
|
10
|
+
|
|
11
|
+
export interface CreationPersistenceConfig {
|
|
12
|
+
readonly collectionName?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ProcessingCreationData {
|
|
16
|
+
readonly scenarioId: string;
|
|
17
|
+
readonly scenarioTitle: string;
|
|
18
|
+
readonly prompt: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface CompletedCreationData {
|
|
22
|
+
readonly uri: string;
|
|
23
|
+
readonly imageUrl?: string;
|
|
24
|
+
readonly videoUrl?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Creates a creation persistence handler for a wizard flow
|
|
29
|
+
*/
|
|
30
|
+
export function createCreationPersistence(config: CreationPersistenceConfig = {}) {
|
|
31
|
+
const { collectionName = "creations" } = config;
|
|
32
|
+
const repository = createCreationsRepository(collectionName);
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
/**
|
|
36
|
+
* Save creation with status="processing" when generation starts
|
|
37
|
+
* Returns the creation ID for later update
|
|
38
|
+
*/
|
|
39
|
+
saveAsProcessing: async (
|
|
40
|
+
userId: string,
|
|
41
|
+
data: ProcessingCreationData,
|
|
42
|
+
): Promise<string> => {
|
|
43
|
+
const creationId = `${data.scenarioId}_${Date.now()}`;
|
|
44
|
+
|
|
45
|
+
await repository.create(userId, {
|
|
46
|
+
id: creationId,
|
|
47
|
+
uri: "",
|
|
48
|
+
type: data.scenarioId,
|
|
49
|
+
prompt: data.prompt,
|
|
50
|
+
status: "processing" as const,
|
|
51
|
+
createdAt: new Date(),
|
|
52
|
+
isShared: false,
|
|
53
|
+
isFavorite: false,
|
|
54
|
+
metadata: {
|
|
55
|
+
scenarioId: data.scenarioId,
|
|
56
|
+
scenarioTitle: data.scenarioTitle,
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
61
|
+
console.log("[CreationPersistence] Saved as processing", { creationId });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return creationId;
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Update creation to status="completed" when generation finishes
|
|
69
|
+
*/
|
|
70
|
+
updateToCompleted: async (
|
|
71
|
+
userId: string,
|
|
72
|
+
creationId: string,
|
|
73
|
+
data: CompletedCreationData,
|
|
74
|
+
): Promise<void> => {
|
|
75
|
+
await repository.update(userId, creationId, {
|
|
76
|
+
uri: data.uri,
|
|
77
|
+
status: "completed" as const,
|
|
78
|
+
output: {
|
|
79
|
+
imageUrl: data.imageUrl,
|
|
80
|
+
videoUrl: data.videoUrl,
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
85
|
+
console.log("[CreationPersistence] Updated to completed", { creationId });
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Update creation to status="failed" when generation fails
|
|
91
|
+
*/
|
|
92
|
+
updateToFailed: async (
|
|
93
|
+
userId: string,
|
|
94
|
+
creationId: string,
|
|
95
|
+
error: string,
|
|
96
|
+
): Promise<void> => {
|
|
97
|
+
await repository.update(userId, creationId, {
|
|
98
|
+
status: "failed" as const,
|
|
99
|
+
metadata: { error },
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
103
|
+
console.log("[CreationPersistence] Updated to failed", { creationId, error });
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export type CreationPersistence = ReturnType<typeof createCreationPersistence>;
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* useWizardGeneration Hook
|
|
3
|
-
* Wizard generation
|
|
4
|
-
*
|
|
3
|
+
* Wizard generation with Firestore persistence
|
|
4
|
+
* - Saves status="processing" at start
|
|
5
|
+
* - Updates to status="completed" on success
|
|
6
|
+
* - Updates to status="failed" on error
|
|
5
7
|
*/
|
|
6
8
|
|
|
7
9
|
import { useEffect, useRef, useMemo, useCallback } from "react";
|
|
8
10
|
import { useGenerationOrchestrator } from "../../../../../presentation/hooks/generation";
|
|
9
|
-
import { usePendingJobs } from "../../../../../presentation/hooks/use-pending-jobs";
|
|
10
11
|
import { createWizardStrategy, buildWizardInput } from "../../infrastructure/strategies";
|
|
12
|
+
import { createCreationPersistence } from "../../infrastructure/utils/creation-persistence.util";
|
|
11
13
|
import type {
|
|
12
14
|
UseWizardGenerationProps,
|
|
13
15
|
UseWizardGenerationReturn,
|
|
@@ -34,63 +36,65 @@ export const useWizardGeneration = (
|
|
|
34
36
|
onSuccess,
|
|
35
37
|
onError,
|
|
36
38
|
onCreditsExhausted,
|
|
37
|
-
trackAsBackgroundJob = true,
|
|
38
39
|
} = props;
|
|
39
40
|
|
|
40
41
|
const hasStarted = useRef(false);
|
|
41
|
-
const
|
|
42
|
+
const creationIdRef = useRef<string | null>(null);
|
|
43
|
+
const inputRef = useRef<{ prompt: string } | null>(null);
|
|
42
44
|
|
|
43
|
-
|
|
45
|
+
// Persistence utility - separate from strategy
|
|
46
|
+
const persistence = useMemo(() => createCreationPersistence(), []);
|
|
44
47
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
console.log("[useWizardGeneration] Initialized", {
|
|
48
|
-
scenarioId: scenario.id,
|
|
49
|
-
outputType: scenario.outputType,
|
|
50
|
-
trackAsBackgroundJob,
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
}, [scenario.id, scenario.outputType, trackAsBackgroundJob]);
|
|
54
|
-
|
|
55
|
-
const strategy = useMemo(() => {
|
|
56
|
-
return createWizardStrategy({
|
|
57
|
-
scenario,
|
|
58
|
-
collectionName: "creations",
|
|
59
|
-
});
|
|
60
|
-
}, [scenario]);
|
|
48
|
+
// Strategy - only handles execution
|
|
49
|
+
const strategy = useMemo(() => createWizardStrategy({ scenario }), [scenario]);
|
|
61
50
|
|
|
62
51
|
const handleSuccess = useCallback(
|
|
63
|
-
(result: unknown) => {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (
|
|
69
|
-
|
|
70
|
-
|
|
52
|
+
async (result: unknown) => {
|
|
53
|
+
const typedResult = result as { imageUrl?: string; videoUrl?: string };
|
|
54
|
+
const creationId = creationIdRef.current;
|
|
55
|
+
|
|
56
|
+
// Update to completed in Firestore
|
|
57
|
+
if (creationId && userId) {
|
|
58
|
+
try {
|
|
59
|
+
await persistence.updateToCompleted(userId, creationId, {
|
|
60
|
+
uri: typedResult.imageUrl || typedResult.videoUrl || "",
|
|
61
|
+
imageUrl: typedResult.imageUrl,
|
|
62
|
+
videoUrl: typedResult.videoUrl,
|
|
63
|
+
});
|
|
64
|
+
} catch (err) {
|
|
65
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
66
|
+
console.error("[useWizardGeneration] updateToCompleted error:", err);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
71
69
|
}
|
|
72
70
|
|
|
71
|
+
creationIdRef.current = null;
|
|
72
|
+
inputRef.current = null;
|
|
73
73
|
onSuccess?.(result);
|
|
74
74
|
},
|
|
75
|
-
[
|
|
75
|
+
[userId, persistence, onSuccess],
|
|
76
76
|
);
|
|
77
77
|
|
|
78
78
|
const handleError = useCallback(
|
|
79
|
-
(err: { message: string }) => {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
79
|
+
async (err: { message: string }) => {
|
|
80
|
+
const creationId = creationIdRef.current;
|
|
81
|
+
|
|
82
|
+
// Update to failed in Firestore
|
|
83
|
+
if (creationId && userId) {
|
|
84
|
+
try {
|
|
85
|
+
await persistence.updateToFailed(userId, creationId, err.message);
|
|
86
|
+
} catch (updateErr) {
|
|
87
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
88
|
+
console.error("[useWizardGeneration] updateToFailed error:", updateErr);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
89
91
|
}
|
|
90
92
|
|
|
93
|
+
creationIdRef.current = null;
|
|
94
|
+
inputRef.current = null;
|
|
91
95
|
onError?.(err.message);
|
|
92
96
|
},
|
|
93
|
-
[
|
|
97
|
+
[userId, persistence, onError],
|
|
94
98
|
);
|
|
95
99
|
|
|
96
100
|
const { generate, isGenerating } = useGenerationOrchestrator(strategy, {
|
|
@@ -103,50 +107,42 @@ export const useWizardGeneration = (
|
|
|
103
107
|
|
|
104
108
|
useEffect(() => {
|
|
105
109
|
if (isGeneratingStep && !hasStarted.current && !isGenerating) {
|
|
106
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
107
|
-
console.log("[useWizardGeneration] Starting generation", {
|
|
108
|
-
scenarioId: scenario.id,
|
|
109
|
-
wizardDataKeys: Object.keys(wizardData),
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
|
|
113
110
|
hasStarted.current = true;
|
|
114
111
|
|
|
115
112
|
buildWizardInput(wizardData, scenario)
|
|
116
|
-
.then((input) => {
|
|
113
|
+
.then(async (input) => {
|
|
117
114
|
if (!input) {
|
|
118
115
|
hasStarted.current = false;
|
|
119
116
|
onError?.("Failed to build generation input");
|
|
120
117
|
return;
|
|
121
118
|
}
|
|
122
119
|
|
|
123
|
-
|
|
124
|
-
const jobId = `wizard-${scenario.id}-${Date.now()}`;
|
|
125
|
-
currentJobIdRef.current = jobId;
|
|
120
|
+
inputRef.current = input as { prompt: string };
|
|
126
121
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
122
|
+
// Save to Firestore with status="processing"
|
|
123
|
+
const typedInput = input as { prompt?: string };
|
|
124
|
+
if (userId && typedInput.prompt) {
|
|
125
|
+
try {
|
|
126
|
+
const creationId = await persistence.saveAsProcessing(userId, {
|
|
130
127
|
scenarioId: scenario.id,
|
|
131
|
-
scenarioTitle: scenario.title,
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
128
|
+
scenarioTitle: scenario.title || scenario.id,
|
|
129
|
+
prompt: typedInput.prompt,
|
|
130
|
+
});
|
|
131
|
+
creationIdRef.current = creationId;
|
|
132
|
+
|
|
133
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
134
|
+
console.log("[useWizardGeneration] Saved as processing:", creationId);
|
|
135
|
+
}
|
|
136
|
+
} catch (err) {
|
|
137
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
138
|
+
console.error("[useWizardGeneration] saveAsProcessing error:", err);
|
|
139
|
+
}
|
|
141
140
|
}
|
|
142
141
|
}
|
|
143
142
|
|
|
144
143
|
generate(input);
|
|
145
144
|
})
|
|
146
145
|
.catch((error) => {
|
|
147
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
148
|
-
console.error("[useWizardGeneration] Input build error:", error);
|
|
149
|
-
}
|
|
150
146
|
hasStarted.current = false;
|
|
151
147
|
onError?.(error.message);
|
|
152
148
|
});
|
|
@@ -155,16 +151,7 @@ export const useWizardGeneration = (
|
|
|
155
151
|
if (!isGeneratingStep && hasStarted.current) {
|
|
156
152
|
hasStarted.current = false;
|
|
157
153
|
}
|
|
158
|
-
}, [
|
|
159
|
-
isGeneratingStep,
|
|
160
|
-
scenario,
|
|
161
|
-
wizardData,
|
|
162
|
-
isGenerating,
|
|
163
|
-
generate,
|
|
164
|
-
onError,
|
|
165
|
-
trackAsBackgroundJob,
|
|
166
|
-
addJob,
|
|
167
|
-
]);
|
|
154
|
+
}, [isGeneratingStep, scenario, wizardData, isGenerating, generate, onError, userId, persistence]);
|
|
168
155
|
|
|
169
156
|
return { isGenerating };
|
|
170
157
|
};
|