@umituz/react-native-ai-generation-content 1.72.6 → 1.72.7
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/creations/presentation/hooks/useCreationPersistence.ts +0 -3
- package/src/domains/creations/presentation/hooks/useProcessingJobsPoller.ts +22 -3
- package/src/domains/generation/infrastructure/flow/useFlowStore.ts +19 -9
- package/src/infrastructure/validation/base-validator.ts +4 -0
- package/src/presentation/hooks/flow-state.utils.ts +6 -0
- package/src/presentation/hooks/use-generation.ts +3 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-ai-generation-content",
|
|
3
|
-
"version": "1.72.
|
|
3
|
+
"version": "1.72.7",
|
|
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",
|
|
@@ -71,12 +71,9 @@ export function useCreationPersistence(
|
|
|
71
71
|
|
|
72
72
|
if (creditCost && onCreditDeduct) {
|
|
73
73
|
onCreditDeduct(creditCost).catch((error) => {
|
|
74
|
-
// Log credit deduction errors for debugging
|
|
75
74
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
76
75
|
console.error("[CreationPersistence] Credit deduction failed:", error);
|
|
77
76
|
}
|
|
78
|
-
// Don't throw - credit deduction failure shouldn't block completion
|
|
79
|
-
// But we should notify user or retry in production
|
|
80
77
|
});
|
|
81
78
|
}
|
|
82
79
|
},
|
|
@@ -89,6 +89,19 @@ export function useProcessingJobsPoller(
|
|
|
89
89
|
if (typeof __DEV__ !== "undefined" && __DEV__) console.log("[ProcessingJobsPoller] Completed:", creation.id, urls);
|
|
90
90
|
|
|
91
91
|
const uri = urls.videoUrl || urls.imageUrl || "";
|
|
92
|
+
|
|
93
|
+
// Validate that we have a valid URI before marking as completed
|
|
94
|
+
if (!uri || uri.trim() === "") {
|
|
95
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
96
|
+
console.error("[ProcessingJobsPoller] No valid URI in result:", creation.id);
|
|
97
|
+
}
|
|
98
|
+
await repository.update(userId, creation.id, {
|
|
99
|
+
status: CREATION_STATUS.FAILED,
|
|
100
|
+
metadata: { error: "No valid result URL received" },
|
|
101
|
+
});
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
92
105
|
await repository.update(userId, creation.id, {
|
|
93
106
|
status: CREATION_STATUS.COMPLETED,
|
|
94
107
|
uri,
|
|
@@ -111,6 +124,12 @@ export function useProcessingJobsPoller(
|
|
|
111
124
|
}
|
|
112
125
|
};
|
|
113
126
|
|
|
127
|
+
// Use ref to always get latest creations
|
|
128
|
+
const creationsRef = useRef(creations);
|
|
129
|
+
useEffect(() => {
|
|
130
|
+
creationsRef.current = creations;
|
|
131
|
+
}, [creations]);
|
|
132
|
+
|
|
114
133
|
useEffect(() => {
|
|
115
134
|
if (!enabled || !userId || processingJobIds.length === 0) {
|
|
116
135
|
if (intervalRef.current) {
|
|
@@ -120,9 +139,9 @@ export function useProcessingJobsPoller(
|
|
|
120
139
|
return;
|
|
121
140
|
}
|
|
122
141
|
|
|
123
|
-
// Get current jobs at poll time to avoid stale closures
|
|
142
|
+
// Get current jobs at poll time from ref to avoid stale closures
|
|
124
143
|
const pollCurrentJobs = () => {
|
|
125
|
-
const currentJobs =
|
|
144
|
+
const currentJobs = creationsRef.current.filter(
|
|
126
145
|
(c) => c.status === CREATION_STATUS.PROCESSING && c.requestId && c.model,
|
|
127
146
|
);
|
|
128
147
|
currentJobs.forEach((job) => pollJobRef.current?.(job));
|
|
@@ -144,7 +163,7 @@ export function useProcessingJobsPoller(
|
|
|
144
163
|
intervalRef.current = null;
|
|
145
164
|
}
|
|
146
165
|
};
|
|
147
|
-
}, [enabled, userId, processingJobIds
|
|
166
|
+
}, [enabled, userId, processingJobIds]);
|
|
148
167
|
|
|
149
168
|
return {
|
|
150
169
|
processingCount: processingJobs.length,
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { createStore } from "@umituz/react-native-design-system";
|
|
6
|
+
|
|
7
|
+
declare const __DEV__: boolean;
|
|
6
8
|
import type {
|
|
7
9
|
FlowState,
|
|
8
10
|
FlowActions,
|
|
@@ -34,7 +36,13 @@ export const createFlowStore = (config: FlowStoreConfig) => {
|
|
|
34
36
|
if (config.initialStepIndex !== undefined) {
|
|
35
37
|
initialIndex = Math.max(0, Math.min(config.initialStepIndex, config.steps.length - 1));
|
|
36
38
|
} else if (config.initialStepId) {
|
|
37
|
-
|
|
39
|
+
const foundIndex = config.steps.findIndex((s) => s.id === config.initialStepId);
|
|
40
|
+
if (foundIndex === -1) {
|
|
41
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
42
|
+
console.warn(`[FlowStore] Step "${config.initialStepId}" not found, using first step`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
initialIndex = Math.max(0, foundIndex);
|
|
38
46
|
}
|
|
39
47
|
|
|
40
48
|
const initialStepId = config.steps[initialIndex]?.id ?? "";
|
|
@@ -86,28 +94,30 @@ export const createFlowStore = (config: FlowStoreConfig) => {
|
|
|
86
94
|
setScenario: (scenario: unknown) => set({ selectedScenario: scenario }),
|
|
87
95
|
|
|
88
96
|
setPartnerImage: (partnerId: string, image: FlowUploadedImageData | undefined) =>
|
|
89
|
-
set({ partners: { ...
|
|
97
|
+
set((state) => ({ partners: { ...state.partners, [partnerId]: image } })),
|
|
90
98
|
|
|
91
99
|
setPartnerName: (partnerId: string, name: string) =>
|
|
92
|
-
set({ partnerNames: { ...
|
|
100
|
+
set((state) => ({ partnerNames: { ...state.partnerNames, [partnerId]: name } })),
|
|
93
101
|
|
|
94
102
|
setTextInput: (text: string) => set({ textInput: text }),
|
|
95
103
|
setVisualStyle: (styleId: string) => set({ visualStyle: styleId }),
|
|
96
104
|
|
|
97
105
|
setSelectedFeatures: (featureType: string, ids: readonly string[]) =>
|
|
98
|
-
set({ selectedFeatures: { ...
|
|
106
|
+
set((state) => ({ selectedFeatures: { ...state.selectedFeatures, [featureType]: ids } })),
|
|
99
107
|
|
|
100
108
|
setCustomData: (key: string, value: unknown) =>
|
|
101
|
-
set({ customData: { ...
|
|
109
|
+
set((state) => ({ customData: { ...state.customData, [key]: value } })),
|
|
102
110
|
|
|
103
111
|
startGeneration: () =>
|
|
104
112
|
set({ generationStatus: "preparing", generationProgress: 0, generationError: undefined }),
|
|
105
113
|
|
|
106
|
-
updateProgress: (progress: number) =>
|
|
114
|
+
updateProgress: (progress: number) => {
|
|
115
|
+
const validProgress = Math.max(0, Math.min(100, progress));
|
|
107
116
|
set({
|
|
108
|
-
generationProgress:
|
|
109
|
-
generationStatus:
|
|
110
|
-
})
|
|
117
|
+
generationProgress: validProgress,
|
|
118
|
+
generationStatus: validProgress >= 100 ? "completed" : validProgress > 0 ? "generating" : "preparing",
|
|
119
|
+
});
|
|
120
|
+
},
|
|
111
121
|
|
|
112
122
|
setResult: (result: unknown) =>
|
|
113
123
|
set({ generationResult: result, generationStatus: "completed", generationProgress: 100 }),
|
|
@@ -136,6 +136,10 @@ export function validateBase64(input: unknown): ValidationResult {
|
|
|
136
136
|
return { isValid: false, errors: ["Input must be a string"] };
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
+
if (input.length === 0) {
|
|
140
|
+
return { isValid: false, errors: ["Base64 string cannot be empty"] };
|
|
141
|
+
}
|
|
142
|
+
|
|
139
143
|
const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
140
144
|
if (!base64Regex.test(input)) {
|
|
141
145
|
return { isValid: false, errors: ["Invalid base64 format"] };
|
|
@@ -48,6 +48,12 @@ export function updatePhotoStep(
|
|
|
48
48
|
stepIndex: number,
|
|
49
49
|
updates: Partial<PhotoStepData>,
|
|
50
50
|
): GenerationFlowState {
|
|
51
|
+
// Validate bounds to prevent sparse arrays and undefined access
|
|
52
|
+
if (stepIndex < 0 || stepIndex >= state.photoSteps.length) {
|
|
53
|
+
console.warn(`[FlowState] Invalid stepIndex ${stepIndex}, ignoring update`);
|
|
54
|
+
return state;
|
|
55
|
+
}
|
|
56
|
+
|
|
51
57
|
const newPhotoSteps = [...state.photoSteps];
|
|
52
58
|
newPhotoSteps[stepIndex] = {
|
|
53
59
|
...newPhotoSteps[stepIndex],
|
|
@@ -109,6 +109,9 @@ export function useGeneration<T = unknown>(
|
|
|
109
109
|
if (!abortRef.current && !abortControllerRef.current?.signal.aborted) {
|
|
110
110
|
setIsGenerating(false);
|
|
111
111
|
}
|
|
112
|
+
if (abortControllerRef.current && !abortControllerRef.current.signal.aborted) {
|
|
113
|
+
abortControllerRef.current.abort();
|
|
114
|
+
}
|
|
112
115
|
abortControllerRef.current = null;
|
|
113
116
|
}
|
|
114
117
|
},
|