@umituz/react-native-ai-generation-content 1.17.92 → 1.17.94
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 +3 -1
- package/src/features/text-to-image/index.ts +0 -4
- package/src/features/text-to-image/presentation/hooks/index.ts +1 -9
- package/src/index.ts +9 -0
- package/src/presentation/hooks/generation-callbacks.types.ts +42 -0
- package/src/presentation/hooks/index.ts +15 -0
- package/src/presentation/hooks/useAIFeatureCallbacks.ts +167 -0
- package/src/presentation/hooks/useGenerationCallbacksBuilder.ts +121 -0
- package/src/features/text-to-image/presentation/hooks/useTextToImageCallbacksBuilder.ts +0 -200
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-ai-generation-content",
|
|
3
|
-
"version": "1.17.
|
|
3
|
+
"version": "1.17.94",
|
|
4
4
|
"description": "Provider-agnostic AI generation orchestration for React Native",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"@umituz/react-native-firebase": "*",
|
|
43
43
|
"@umituz/react-native-image": "*",
|
|
44
44
|
"@umituz/react-native-offline": "*",
|
|
45
|
+
"@umituz/react-native-tanstack": "*",
|
|
45
46
|
"@umituz/react-native-timezone": "*",
|
|
46
47
|
"@umituz/react-native-uuid": "*"
|
|
47
48
|
},
|
|
@@ -73,6 +74,7 @@
|
|
|
73
74
|
"@umituz/react-native-localization": "latest",
|
|
74
75
|
"@umituz/react-native-offline": "*",
|
|
75
76
|
"@umituz/react-native-storage": "latest",
|
|
77
|
+
"@umituz/react-native-tanstack": "latest",
|
|
76
78
|
"@umituz/react-native-timezone": "latest",
|
|
77
79
|
"@umituz/react-native-uuid": "*",
|
|
78
80
|
"eslint": "^9.0.0",
|
|
@@ -69,7 +69,6 @@ export {
|
|
|
69
69
|
useFormState,
|
|
70
70
|
useGeneration,
|
|
71
71
|
useTextToImageForm,
|
|
72
|
-
useTextToImageCallbacksBuilder,
|
|
73
72
|
} from "./presentation";
|
|
74
73
|
export type {
|
|
75
74
|
UseFormStateOptions,
|
|
@@ -79,9 +78,6 @@ export type {
|
|
|
79
78
|
UseGenerationReturn,
|
|
80
79
|
UseTextToImageFormOptions,
|
|
81
80
|
UseTextToImageFormReturn,
|
|
82
|
-
TextToImageCallbacksBuilderConfig,
|
|
83
|
-
UseTextToImageCallbacksBuilderOptions,
|
|
84
|
-
UseTextToImageCallbacksBuilderReturn,
|
|
85
81
|
} from "./presentation";
|
|
86
82
|
|
|
87
83
|
// Provider-based Feature Hook
|
|
@@ -22,17 +22,9 @@ export type {
|
|
|
22
22
|
UseTextToImageFormReturn,
|
|
23
23
|
} from "./useTextToImageForm";
|
|
24
24
|
|
|
25
|
-
// Provider-based Feature Hook
|
|
25
|
+
// Provider-based Feature Hook
|
|
26
26
|
export { useTextToImageFeature } from "./useTextToImageFeature";
|
|
27
27
|
export type {
|
|
28
28
|
UseTextToImageFeatureProps,
|
|
29
29
|
UseTextToImageFeatureReturn,
|
|
30
30
|
} from "./useTextToImageFeature";
|
|
31
|
-
|
|
32
|
-
// Callbacks Builder Hook
|
|
33
|
-
export { useTextToImageCallbacksBuilder } from "./useTextToImageCallbacksBuilder";
|
|
34
|
-
export type {
|
|
35
|
-
TextToImageCallbacksBuilderConfig,
|
|
36
|
-
UseTextToImageCallbacksBuilderOptions,
|
|
37
|
-
UseTextToImageCallbacksBuilderReturn,
|
|
38
|
-
} from "./useTextToImageCallbacksBuilder";
|
package/src/index.ts
CHANGED
|
@@ -252,6 +252,8 @@ export {
|
|
|
252
252
|
useBackgroundGeneration,
|
|
253
253
|
usePhotoGeneration,
|
|
254
254
|
useGenerationFlow,
|
|
255
|
+
useGenerationCallbacksBuilder,
|
|
256
|
+
useAIFeatureCallbacks,
|
|
255
257
|
} from "./presentation/hooks";
|
|
256
258
|
|
|
257
259
|
export type {
|
|
@@ -271,6 +273,13 @@ export type {
|
|
|
271
273
|
PhotoGenerationStatus,
|
|
272
274
|
UseGenerationFlowOptions,
|
|
273
275
|
UseGenerationFlowReturn,
|
|
276
|
+
CreditType,
|
|
277
|
+
GenerationExecutionResult,
|
|
278
|
+
GenerationCallbacksConfig,
|
|
279
|
+
GenerationCallbacks,
|
|
280
|
+
UseGenerationCallbacksBuilderOptions,
|
|
281
|
+
AIFeatureCallbacksConfig,
|
|
282
|
+
AIFeatureCallbacks,
|
|
274
283
|
} from "./presentation/hooks";
|
|
275
284
|
|
|
276
285
|
// =============================================================================
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generation Callbacks Types
|
|
3
|
+
* Type definitions for unified generation flow
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type CreditType = "image" | "text" | "video" | "audio";
|
|
7
|
+
|
|
8
|
+
export interface GenerationExecutionResult<T = unknown> {
|
|
9
|
+
success: boolean;
|
|
10
|
+
data?: T;
|
|
11
|
+
error?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface GenerationCallbacksConfig<TRequest, TResult> {
|
|
15
|
+
userId: string | null;
|
|
16
|
+
isAuthenticated: boolean;
|
|
17
|
+
creditBalance: number;
|
|
18
|
+
creditCost: number;
|
|
19
|
+
creditType: CreditType;
|
|
20
|
+
executor: (request: TRequest) => Promise<GenerationExecutionResult<TResult>>;
|
|
21
|
+
deductCredit: (type: CreditType) => Promise<void>;
|
|
22
|
+
openPaywall: () => void;
|
|
23
|
+
showAuthModal?: () => void;
|
|
24
|
+
saveCreation?: (result: TResult) => Promise<void>;
|
|
25
|
+
onNavigateAfterSuccess?: () => void;
|
|
26
|
+
invalidateQueryKeys?: string[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface GenerationCallbacks<TRequest, TResult> {
|
|
30
|
+
canAfford: (cost?: number) => boolean;
|
|
31
|
+
isAuthenticated: () => boolean;
|
|
32
|
+
calculateCost: (multiplier?: number) => number;
|
|
33
|
+
execute: (request: TRequest) => Promise<GenerationExecutionResult<TResult>>;
|
|
34
|
+
onAuthRequired: () => void;
|
|
35
|
+
onCreditsRequired: () => void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface UseGenerationCallbacksBuilderOptions<TRequest, TResult> {
|
|
39
|
+
config: GenerationCallbacksConfig<TRequest, TResult>;
|
|
40
|
+
onSuccess?: (result: TResult) => void;
|
|
41
|
+
onError?: (error: string) => void;
|
|
42
|
+
}
|
|
@@ -43,3 +43,18 @@ export type {
|
|
|
43
43
|
UseGenerationFlowOptions,
|
|
44
44
|
UseGenerationFlowReturn,
|
|
45
45
|
} from "./useGenerationFlow";
|
|
46
|
+
|
|
47
|
+
export { useGenerationCallbacksBuilder } from "./useGenerationCallbacksBuilder";
|
|
48
|
+
export type {
|
|
49
|
+
CreditType,
|
|
50
|
+
GenerationExecutionResult,
|
|
51
|
+
GenerationCallbacksConfig,
|
|
52
|
+
GenerationCallbacks,
|
|
53
|
+
UseGenerationCallbacksBuilderOptions,
|
|
54
|
+
} from "./generation-callbacks.types";
|
|
55
|
+
|
|
56
|
+
export { useAIFeatureCallbacks } from "./useAIFeatureCallbacks";
|
|
57
|
+
export type {
|
|
58
|
+
AIFeatureCallbacksConfig,
|
|
59
|
+
AIFeatureCallbacks,
|
|
60
|
+
} from "./useAIFeatureCallbacks";
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Feature Callbacks Adapter Hook
|
|
3
|
+
* Universal adapter for all AI generation features
|
|
4
|
+
* Works with: TextToImage, TextToVideo, ImageToVideo, etc.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { useCallback, useMemo } from "react";
|
|
8
|
+
|
|
9
|
+
export interface AIFeatureCallbacksConfig<TRequest = unknown, TResult = unknown> {
|
|
10
|
+
// App provides reactive state
|
|
11
|
+
userId: string | null;
|
|
12
|
+
isAuthenticated: boolean;
|
|
13
|
+
creditBalance: number;
|
|
14
|
+
|
|
15
|
+
// Cost config
|
|
16
|
+
creditCostPerUnit: number;
|
|
17
|
+
|
|
18
|
+
// Executor - the actual generation function
|
|
19
|
+
executor: (request: TRequest) => Promise<{
|
|
20
|
+
success: boolean;
|
|
21
|
+
data?: TResult;
|
|
22
|
+
error?: string;
|
|
23
|
+
imageUrl?: string;
|
|
24
|
+
imageUrls?: string[];
|
|
25
|
+
}>;
|
|
26
|
+
|
|
27
|
+
// Actions from app
|
|
28
|
+
showAuthModal: () => void;
|
|
29
|
+
openPaywall: () => void;
|
|
30
|
+
deductCredits?: (amount: number) => Promise<void>;
|
|
31
|
+
|
|
32
|
+
// Optional callbacks
|
|
33
|
+
onSuccess?: (result: TResult) => void;
|
|
34
|
+
onError?: (error: string) => void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Universal callbacks interface that maps to all feature-specific ones
|
|
39
|
+
*/
|
|
40
|
+
export interface AIFeatureCallbacks<TRequest = unknown, TResult = unknown> {
|
|
41
|
+
// TextToImageCallbacks compatible
|
|
42
|
+
executeGeneration: (request: TRequest) => Promise<{
|
|
43
|
+
success: boolean;
|
|
44
|
+
imageUrls?: string[];
|
|
45
|
+
error?: string;
|
|
46
|
+
}>;
|
|
47
|
+
calculateCost: (multiplier?: number, _model?: string | null) => number;
|
|
48
|
+
canAfford: (cost: number) => boolean;
|
|
49
|
+
isAuthenticated: () => boolean;
|
|
50
|
+
onAuthRequired: () => void;
|
|
51
|
+
onCreditsRequired: (cost?: number) => void;
|
|
52
|
+
onSuccess?: (result: TResult) => void;
|
|
53
|
+
onError?: (error: string) => void;
|
|
54
|
+
|
|
55
|
+
// ImageToVideoCallbacks compatible
|
|
56
|
+
onCreditCheck: (cost: number) => boolean;
|
|
57
|
+
onShowPaywall: (cost: number) => void;
|
|
58
|
+
|
|
59
|
+
// TextToVideoCallbacks compatible
|
|
60
|
+
onAuthCheck: () => boolean;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function useAIFeatureCallbacks<TRequest = unknown, TResult = unknown>(
|
|
64
|
+
config: AIFeatureCallbacksConfig<TRequest, TResult>,
|
|
65
|
+
): AIFeatureCallbacks<TRequest, TResult> {
|
|
66
|
+
const {
|
|
67
|
+
userId,
|
|
68
|
+
isAuthenticated: isAuth,
|
|
69
|
+
creditBalance,
|
|
70
|
+
creditCostPerUnit,
|
|
71
|
+
executor,
|
|
72
|
+
showAuthModal,
|
|
73
|
+
openPaywall,
|
|
74
|
+
deductCredits,
|
|
75
|
+
onSuccess,
|
|
76
|
+
onError,
|
|
77
|
+
} = config;
|
|
78
|
+
|
|
79
|
+
const canAfford = useCallback(
|
|
80
|
+
(cost: number): boolean => creditBalance >= cost,
|
|
81
|
+
[creditBalance],
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const isAuthenticated = useCallback(
|
|
85
|
+
(): boolean => isAuth && !!userId,
|
|
86
|
+
[isAuth, userId],
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const calculateCost = useCallback(
|
|
90
|
+
(multiplier = 1, _model?: string | null): number => creditCostPerUnit * multiplier,
|
|
91
|
+
[creditCostPerUnit],
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const onAuthRequired = useCallback(() => {
|
|
95
|
+
showAuthModal();
|
|
96
|
+
}, [showAuthModal]);
|
|
97
|
+
|
|
98
|
+
const onCreditsRequired = useCallback(
|
|
99
|
+
(_cost?: number) => {
|
|
100
|
+
openPaywall();
|
|
101
|
+
},
|
|
102
|
+
[openPaywall],
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const executeGeneration = useCallback(
|
|
106
|
+
async (request: TRequest) => {
|
|
107
|
+
try {
|
|
108
|
+
const result = await executor(request);
|
|
109
|
+
|
|
110
|
+
if (result.success && deductCredits) {
|
|
111
|
+
await deductCredits(creditCostPerUnit);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (result.success && result.data) {
|
|
115
|
+
onSuccess?.(result.data);
|
|
116
|
+
} else if (!result.success && result.error) {
|
|
117
|
+
onError?.(result.error);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
success: result.success,
|
|
122
|
+
imageUrls: result.imageUrls,
|
|
123
|
+
error: result.error,
|
|
124
|
+
};
|
|
125
|
+
} catch (error) {
|
|
126
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
127
|
+
onError?.(message);
|
|
128
|
+
return { success: false, error: message };
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
[executor, deductCredits, creditCostPerUnit, onSuccess, onError],
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
// Aliases for different callback interfaces
|
|
135
|
+
const onCreditCheck = canAfford;
|
|
136
|
+
const onAuthCheck = isAuthenticated;
|
|
137
|
+
const onShowPaywall = onCreditsRequired;
|
|
138
|
+
|
|
139
|
+
return useMemo(
|
|
140
|
+
() => ({
|
|
141
|
+
executeGeneration,
|
|
142
|
+
calculateCost,
|
|
143
|
+
canAfford,
|
|
144
|
+
isAuthenticated,
|
|
145
|
+
onAuthRequired,
|
|
146
|
+
onCreditsRequired,
|
|
147
|
+
onSuccess,
|
|
148
|
+
onError,
|
|
149
|
+
onCreditCheck,
|
|
150
|
+
onAuthCheck,
|
|
151
|
+
onShowPaywall,
|
|
152
|
+
}),
|
|
153
|
+
[
|
|
154
|
+
executeGeneration,
|
|
155
|
+
calculateCost,
|
|
156
|
+
canAfford,
|
|
157
|
+
isAuthenticated,
|
|
158
|
+
onAuthRequired,
|
|
159
|
+
onCreditsRequired,
|
|
160
|
+
onSuccess,
|
|
161
|
+
onError,
|
|
162
|
+
onCreditCheck,
|
|
163
|
+
onAuthCheck,
|
|
164
|
+
onShowPaywall,
|
|
165
|
+
],
|
|
166
|
+
);
|
|
167
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic Generation Callbacks Builder
|
|
3
|
+
* Unified hook for ALL generation types (image, video, audio, text)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useCallback, useMemo, useRef } from "react";
|
|
7
|
+
import { useQueryClient } from "@umituz/react-native-tanstack";
|
|
8
|
+
import type {
|
|
9
|
+
GenerationExecutionResult,
|
|
10
|
+
GenerationCallbacks,
|
|
11
|
+
UseGenerationCallbacksBuilderOptions,
|
|
12
|
+
} from "./generation-callbacks.types";
|
|
13
|
+
|
|
14
|
+
declare const __DEV__: boolean;
|
|
15
|
+
|
|
16
|
+
export function useGenerationCallbacksBuilder<TRequest, TResult>(
|
|
17
|
+
options: UseGenerationCallbacksBuilderOptions<TRequest, TResult>,
|
|
18
|
+
): { callbacks: GenerationCallbacks<TRequest, TResult> } {
|
|
19
|
+
const { config, onSuccess, onError } = options;
|
|
20
|
+
const queryClient = useQueryClient();
|
|
21
|
+
const isExecutingRef = useRef(false);
|
|
22
|
+
|
|
23
|
+
const canAfford = useCallback(
|
|
24
|
+
(cost?: number): boolean => {
|
|
25
|
+
const actualCost = cost ?? config.creditCost;
|
|
26
|
+
return config.creditBalance >= actualCost;
|
|
27
|
+
},
|
|
28
|
+
[config.creditBalance, config.creditCost],
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const isAuthenticated = useCallback(
|
|
32
|
+
(): boolean => config.isAuthenticated,
|
|
33
|
+
[config.isAuthenticated],
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const calculateCost = useCallback(
|
|
37
|
+
(multiplier = 1): number => config.creditCost * multiplier,
|
|
38
|
+
[config.creditCost],
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const onAuthRequired = useCallback(() => {
|
|
42
|
+
config.showAuthModal ? config.showAuthModal() : config.openPaywall();
|
|
43
|
+
}, [config]);
|
|
44
|
+
|
|
45
|
+
const onCreditsRequired = useCallback(() => {
|
|
46
|
+
config.openPaywall();
|
|
47
|
+
}, [config]);
|
|
48
|
+
|
|
49
|
+
const execute = useCallback(
|
|
50
|
+
async (request: TRequest): Promise<GenerationExecutionResult<TResult>> => {
|
|
51
|
+
if (isExecutingRef.current) {
|
|
52
|
+
return { success: false, error: "Generation already in progress" };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!config.isAuthenticated || !config.userId) {
|
|
56
|
+
onAuthRequired();
|
|
57
|
+
return { success: false, error: "Authentication required" };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!canAfford()) {
|
|
61
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
62
|
+
console.log("[Generation] Insufficient credits", {
|
|
63
|
+
balance: config.creditBalance,
|
|
64
|
+
cost: config.creditCost,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
onCreditsRequired();
|
|
68
|
+
return { success: false, error: "Insufficient credits" };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
isExecutingRef.current = true;
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
const result = await config.executor(request);
|
|
75
|
+
|
|
76
|
+
if (!result.success || !result.data) {
|
|
77
|
+
return { success: false, error: result.error || "Generation failed" };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
await config.deductCredit(config.creditType);
|
|
81
|
+
|
|
82
|
+
if (config.saveCreation) {
|
|
83
|
+
try {
|
|
84
|
+
await config.saveCreation(result.data);
|
|
85
|
+
} catch {
|
|
86
|
+
// Silent fail for save
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const keys = config.invalidateQueryKeys ?? ["creations"];
|
|
91
|
+
keys.forEach((key) => queryClient.invalidateQueries({ queryKey: [key] }));
|
|
92
|
+
|
|
93
|
+
onSuccess?.(result.data);
|
|
94
|
+
config.onNavigateAfterSuccess?.();
|
|
95
|
+
|
|
96
|
+
return { success: true, data: result.data };
|
|
97
|
+
} catch (error) {
|
|
98
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
99
|
+
onError?.(message);
|
|
100
|
+
return { success: false, error: message };
|
|
101
|
+
} finally {
|
|
102
|
+
isExecutingRef.current = false;
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
[config, canAfford, onAuthRequired, onCreditsRequired, queryClient, onSuccess, onError],
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
const callbacks = useMemo<GenerationCallbacks<TRequest, TResult>>(
|
|
109
|
+
() => ({
|
|
110
|
+
canAfford,
|
|
111
|
+
isAuthenticated,
|
|
112
|
+
calculateCost,
|
|
113
|
+
execute,
|
|
114
|
+
onAuthRequired,
|
|
115
|
+
onCreditsRequired,
|
|
116
|
+
}),
|
|
117
|
+
[canAfford, isAuthenticated, calculateCost, execute, onAuthRequired, onCreditsRequired],
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
return { callbacks };
|
|
121
|
+
}
|
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Text-to-Image Callbacks Builder Hook
|
|
3
|
-
* Creates callbacks with integrated credit, auth, and creations systems
|
|
4
|
-
* Main app only provides config, package handles everything
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { useCallback, useMemo, useRef } from "react";
|
|
8
|
-
import { useQueryClient } from "@tanstack/react-query";
|
|
9
|
-
import type {
|
|
10
|
-
TextToImageCallbacks,
|
|
11
|
-
TextToImageGenerationRequest,
|
|
12
|
-
TextToImageGenerationResult,
|
|
13
|
-
NumImages,
|
|
14
|
-
TextToImageInputBuilder,
|
|
15
|
-
} from "../../domain/types";
|
|
16
|
-
import { executeTextToImage } from "../../infrastructure";
|
|
17
|
-
|
|
18
|
-
export interface TextToImageCallbacksBuilderConfig {
|
|
19
|
-
userId: string | null;
|
|
20
|
-
isAuthenticated: boolean;
|
|
21
|
-
isPremium: boolean;
|
|
22
|
-
imageCredits: number;
|
|
23
|
-
creditCostPerImage: number;
|
|
24
|
-
model: string;
|
|
25
|
-
buildInput: TextToImageInputBuilder;
|
|
26
|
-
deductCredit: (type: "image" | "text") => Promise<void>;
|
|
27
|
-
openPaywall: () => void;
|
|
28
|
-
onNavigateToCreations?: () => void;
|
|
29
|
-
saveCreation?: (imageUrl: string, prompt: string) => Promise<void>;
|
|
30
|
-
invalidateCreationsQuery?: () => void;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface UseTextToImageCallbacksBuilderOptions {
|
|
34
|
-
config: TextToImageCallbacksBuilderConfig;
|
|
35
|
-
onSuccess?: (imageUrls: string[]) => void;
|
|
36
|
-
onError?: (error: string) => void;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface UseTextToImageCallbacksBuilderReturn {
|
|
40
|
-
callbacks: TextToImageCallbacks;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
declare const __DEV__: boolean;
|
|
44
|
-
|
|
45
|
-
export function useTextToImageCallbacksBuilder(
|
|
46
|
-
options: UseTextToImageCallbacksBuilderOptions,
|
|
47
|
-
): UseTextToImageCallbacksBuilderReturn {
|
|
48
|
-
const { config, onSuccess, onError } = options;
|
|
49
|
-
const queryClient = useQueryClient();
|
|
50
|
-
const isGeneratingRef = useRef(false);
|
|
51
|
-
|
|
52
|
-
const executeGeneration = useCallback(
|
|
53
|
-
async (
|
|
54
|
-
request: TextToImageGenerationRequest,
|
|
55
|
-
): Promise<TextToImageGenerationResult> => {
|
|
56
|
-
if (!config.userId) {
|
|
57
|
-
return { success: false, error: "User ID not found" };
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (isGeneratingRef.current) {
|
|
61
|
-
return { success: false, error: "Generation already in progress" };
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
isGeneratingRef.current = true;
|
|
65
|
-
|
|
66
|
-
try {
|
|
67
|
-
const result = await executeTextToImage(
|
|
68
|
-
{
|
|
69
|
-
prompt: request.prompt,
|
|
70
|
-
userId: config.userId,
|
|
71
|
-
negativePrompt: request.negativePrompt,
|
|
72
|
-
options: {
|
|
73
|
-
aspectRatio: request.aspectRatio,
|
|
74
|
-
size: request.size,
|
|
75
|
-
numImages: request.numImages,
|
|
76
|
-
guidanceScale: request.guidanceScale,
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
model: request.model || config.model,
|
|
81
|
-
buildInput: config.buildInput,
|
|
82
|
-
},
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
if (!result.success) {
|
|
86
|
-
return { success: false, error: result.error || "Generation failed" };
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const imageUrls =
|
|
90
|
-
result.imageUrls || (result.imageUrl ? [result.imageUrl] : []);
|
|
91
|
-
|
|
92
|
-
if (imageUrls.length === 0) {
|
|
93
|
-
return { success: false, error: "No images generated" };
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
await config.deductCredit("image");
|
|
97
|
-
|
|
98
|
-
return { success: true, imageUrls };
|
|
99
|
-
} catch (error) {
|
|
100
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
101
|
-
return { success: false, error: message };
|
|
102
|
-
} finally {
|
|
103
|
-
isGeneratingRef.current = false;
|
|
104
|
-
}
|
|
105
|
-
},
|
|
106
|
-
[config],
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
const calculateCost = useCallback(
|
|
110
|
-
(numImages: NumImages, _model?: string | null): number => {
|
|
111
|
-
return config.creditCostPerImage * numImages;
|
|
112
|
-
},
|
|
113
|
-
[config.creditCostPerImage],
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
const checkCanAfford = useCallback(
|
|
117
|
-
(cost: number): boolean => {
|
|
118
|
-
if (config.isPremium) return true;
|
|
119
|
-
return config.imageCredits >= cost;
|
|
120
|
-
},
|
|
121
|
-
[config.isPremium, config.imageCredits],
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
const checkIsAuthenticated = useCallback(
|
|
125
|
-
(): boolean => config.isAuthenticated,
|
|
126
|
-
[config.isAuthenticated],
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
const handleAuthRequired = useCallback(() => {
|
|
130
|
-
config.openPaywall();
|
|
131
|
-
}, [config]);
|
|
132
|
-
|
|
133
|
-
const handleCreditsRequired = useCallback(
|
|
134
|
-
(_cost: number) => config.openPaywall(),
|
|
135
|
-
[config],
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
const handleSuccess = useCallback(
|
|
139
|
-
async (imageUrls: string[]) => {
|
|
140
|
-
if (!config.userId || imageUrls.length === 0) return;
|
|
141
|
-
|
|
142
|
-
try {
|
|
143
|
-
if (config.saveCreation) {
|
|
144
|
-
for (const imageUrl of imageUrls) {
|
|
145
|
-
await config.saveCreation(imageUrl, "");
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (config.invalidateCreationsQuery) {
|
|
150
|
-
config.invalidateCreationsQuery();
|
|
151
|
-
} else {
|
|
152
|
-
queryClient.invalidateQueries({ queryKey: ["creations"] });
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
onSuccess?.(imageUrls);
|
|
156
|
-
config.onNavigateToCreations?.();
|
|
157
|
-
} catch (error) {
|
|
158
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
159
|
-
console.error("[TextToImage] Failed to save creation:", error);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
},
|
|
163
|
-
[config, queryClient, onSuccess],
|
|
164
|
-
);
|
|
165
|
-
|
|
166
|
-
const handleError = useCallback(
|
|
167
|
-
(error: string) => {
|
|
168
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
169
|
-
console.error("[TextToImage] Generation error:", error);
|
|
170
|
-
}
|
|
171
|
-
onError?.(error);
|
|
172
|
-
},
|
|
173
|
-
[onError],
|
|
174
|
-
);
|
|
175
|
-
|
|
176
|
-
const callbacks = useMemo<TextToImageCallbacks>(
|
|
177
|
-
() => ({
|
|
178
|
-
executeGeneration,
|
|
179
|
-
calculateCost,
|
|
180
|
-
canAfford: checkCanAfford,
|
|
181
|
-
isAuthenticated: checkIsAuthenticated,
|
|
182
|
-
onAuthRequired: handleAuthRequired,
|
|
183
|
-
onCreditsRequired: handleCreditsRequired,
|
|
184
|
-
onSuccess: handleSuccess,
|
|
185
|
-
onError: handleError,
|
|
186
|
-
}),
|
|
187
|
-
[
|
|
188
|
-
executeGeneration,
|
|
189
|
-
calculateCost,
|
|
190
|
-
checkCanAfford,
|
|
191
|
-
checkIsAuthenticated,
|
|
192
|
-
handleAuthRequired,
|
|
193
|
-
handleCreditsRequired,
|
|
194
|
-
handleSuccess,
|
|
195
|
-
handleError,
|
|
196
|
-
],
|
|
197
|
-
);
|
|
198
|
-
|
|
199
|
-
return { callbacks };
|
|
200
|
-
}
|