@umituz/react-native-ai-generation-content 1.17.308 → 1.17.309
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/features/couple-future/index.ts +1 -1
- package/src/features/couple-future/presentation/hooks/useCoupleFutureGeneration.ts +54 -64
- package/src/index.ts +7 -6
- package/src/presentation/hooks/generation/errors.ts +58 -0
- package/src/presentation/hooks/generation/index.ts +23 -0
- package/src/presentation/hooks/generation/orchestrator.ts +146 -0
- package/src/presentation/hooks/generation/types.ts +72 -0
- package/src/presentation/hooks/index.ts +18 -21
- package/src/presentation/hooks/photo-generation.types.ts +0 -61
- package/src/presentation/hooks/useGenerationCredits.ts +0 -77
- package/src/presentation/hooks/usePhotoGeneration.ts +0 -207
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.309",
|
|
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",
|
|
@@ -15,7 +15,7 @@ export type {
|
|
|
15
15
|
} from "./domain/types";
|
|
16
16
|
export { COUPLE_FUTURE_DEFAULTS } from "./domain/types";
|
|
17
17
|
export { useCoupleFutureGeneration } from "./presentation/hooks/useCoupleFutureGeneration";
|
|
18
|
-
export type { UseCoupleFutureGenerationConfig } from "./presentation/hooks/useCoupleFutureGeneration";
|
|
18
|
+
export type { CoupleFutureConfig as UseCoupleFutureGenerationConfig } from "./presentation/hooks/useCoupleFutureGeneration";
|
|
19
19
|
export {
|
|
20
20
|
RomanticMoodSelector,
|
|
21
21
|
ArtStyleSelector,
|
|
@@ -1,46 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* useCoupleFutureGeneration Hook
|
|
3
|
-
* Couple future generation
|
|
3
|
+
* Couple future generation using centralized orchestrator
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { useCallback } from "react";
|
|
7
|
-
import {
|
|
6
|
+
import { useMemo, useCallback } from "react";
|
|
7
|
+
import {
|
|
8
|
+
useGenerationOrchestrator,
|
|
9
|
+
type GenerationStrategy,
|
|
10
|
+
type AlertMessages,
|
|
11
|
+
} from "../../../../presentation/hooks/generation";
|
|
8
12
|
import { executeCoupleFuture } from "../../infrastructure/executor";
|
|
9
13
|
import type { CoupleFutureInput } from "../../domain/types";
|
|
10
|
-
import type {
|
|
11
|
-
PhotoGenerationConfig,
|
|
12
|
-
PhotoGenerationError,
|
|
13
|
-
} from "../../../../presentation/hooks/photo-generation.types";
|
|
14
14
|
import { createCreationsRepository } from "../../../../domains/creations/infrastructure/adapters";
|
|
15
15
|
import type { Creation } from "../../../../domains/creations/domain/entities/Creation";
|
|
16
16
|
|
|
17
|
-
export interface
|
|
18
|
-
TInput extends CoupleFutureInput,
|
|
19
|
-
TResult,
|
|
20
|
-
> {
|
|
17
|
+
export interface CoupleFutureConfig<TResult> {
|
|
21
18
|
userId: string | undefined;
|
|
22
|
-
processResult: (
|
|
23
|
-
|
|
24
|
-
input: TInput,
|
|
25
|
-
) => Promise<TResult> | TResult;
|
|
26
|
-
buildCreation?: (result: TResult, input: TInput) => Creation | null;
|
|
19
|
+
processResult: (imageUrl: string, input: CoupleFutureInput) => TResult;
|
|
20
|
+
buildCreation?: (result: TResult, input: CoupleFutureInput) => Creation | null;
|
|
27
21
|
onCreditsExhausted?: () => void;
|
|
28
22
|
onSuccess?: (result: TResult) => void;
|
|
29
23
|
onError?: (error: string) => void;
|
|
30
|
-
alertMessages:
|
|
31
|
-
networkError: string;
|
|
32
|
-
policyViolation: string;
|
|
33
|
-
saveFailed: string;
|
|
34
|
-
creditFailed: string;
|
|
35
|
-
unknown: string;
|
|
36
|
-
};
|
|
24
|
+
alertMessages: AlertMessages;
|
|
37
25
|
}
|
|
38
26
|
|
|
39
|
-
export const useCoupleFutureGeneration = <
|
|
40
|
-
|
|
41
|
-
TResult,
|
|
42
|
-
>(
|
|
43
|
-
config: UseCoupleFutureGenerationConfig<TInput, TResult>,
|
|
27
|
+
export const useCoupleFutureGeneration = <TResult>(
|
|
28
|
+
config: CoupleFutureConfig<TResult>,
|
|
44
29
|
) => {
|
|
45
30
|
const {
|
|
46
31
|
userId,
|
|
@@ -52,49 +37,54 @@ export const useCoupleFutureGeneration = <
|
|
|
52
37
|
alertMessages,
|
|
53
38
|
} = config;
|
|
54
39
|
|
|
55
|
-
const repository =
|
|
40
|
+
const repository = useMemo(
|
|
56
41
|
() => createCreationsRepository("creations"),
|
|
57
42
|
[],
|
|
58
43
|
);
|
|
59
44
|
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
partnerBBase64: input.partnerBBase64,
|
|
72
|
-
prompt: input.prompt,
|
|
73
|
-
},
|
|
74
|
-
{ onProgress },
|
|
75
|
-
);
|
|
45
|
+
const strategy: GenerationStrategy<CoupleFutureInput, TResult> = useMemo(
|
|
46
|
+
() => ({
|
|
47
|
+
execute: async (input, onProgress) => {
|
|
48
|
+
const result = await executeCoupleFuture(
|
|
49
|
+
{
|
|
50
|
+
partnerABase64: input.partnerABase64,
|
|
51
|
+
partnerBBase64: input.partnerBBase64,
|
|
52
|
+
prompt: input.prompt,
|
|
53
|
+
},
|
|
54
|
+
{ onProgress },
|
|
55
|
+
);
|
|
76
56
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
57
|
+
if (!result.success || !result.imageUrl) {
|
|
58
|
+
throw new Error(result.error || "Generation failed");
|
|
59
|
+
}
|
|
80
60
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
61
|
+
return processResult(result.imageUrl, input);
|
|
62
|
+
},
|
|
63
|
+
getCreditCost: () => 1,
|
|
64
|
+
save: buildCreation
|
|
65
|
+
? async (result, uid) => {
|
|
66
|
+
const creation = buildCreation(result, {} as CoupleFutureInput);
|
|
67
|
+
if (creation) {
|
|
68
|
+
await repository.create(uid, creation);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
: undefined,
|
|
72
|
+
}),
|
|
73
|
+
[processResult, buildCreation, repository],
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const handleError = useCallback(
|
|
77
|
+
(error: { message: string }) => {
|
|
94
78
|
onError?.(error.message);
|
|
95
79
|
},
|
|
96
|
-
|
|
97
|
-
|
|
80
|
+
[onError],
|
|
81
|
+
);
|
|
98
82
|
|
|
99
|
-
return
|
|
83
|
+
return useGenerationOrchestrator(strategy, {
|
|
84
|
+
userId,
|
|
85
|
+
alertMessages,
|
|
86
|
+
onCreditsExhausted,
|
|
87
|
+
onSuccess: onSuccess as (result: unknown) => void,
|
|
88
|
+
onError: handleError,
|
|
89
|
+
});
|
|
100
90
|
};
|
package/src/index.ts
CHANGED
|
@@ -67,18 +67,19 @@ export { enhancePromptWithLanguage, getSupportedLanguages, getLanguageName, Mode
|
|
|
67
67
|
export type { ModerationResult, ModerationConfig, SynchronousGenerationInput, SynchronousGenerationConfig } from "./infrastructure/wrappers";
|
|
68
68
|
|
|
69
69
|
export {
|
|
70
|
-
useGeneration, usePendingJobs, useBackgroundGeneration,
|
|
70
|
+
useGeneration, usePendingJobs, useBackgroundGeneration,
|
|
71
71
|
useGenerationFlow, useGenerationCallbacksBuilder, useAIFeatureCallbacks,
|
|
72
|
+
useGenerationOrchestrator, createGenerationError, getAlertMessage, parseError,
|
|
72
73
|
} from "./presentation/hooks";
|
|
73
74
|
|
|
74
75
|
export type {
|
|
75
76
|
UseGenerationOptions, UseGenerationReturn, UsePendingJobsOptions, UsePendingJobsReturn,
|
|
76
77
|
UseBackgroundGenerationOptions, UseBackgroundGenerationReturn, DirectExecutionResult,
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
UseGenerationFlowOptions, UseGenerationFlowReturn, CreditType, GenerationExecutionResult,
|
|
79
|
+
GenerationCallbacksConfig, GenerationCallbacks, UseGenerationCallbacksBuilderOptions,
|
|
80
|
+
AIFeatureCallbacksConfig, AIFeatureCallbacks, AIFeatureGenerationResult,
|
|
81
|
+
GenerationStrategy, GenerationConfig, GenerationState, OrchestratorStatus,
|
|
82
|
+
GenerationError, GenerationErrorType, AlertMessages, UseGenerationOrchestratorReturn,
|
|
82
83
|
} from "./presentation/hooks";
|
|
83
84
|
|
|
84
85
|
export {
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generation Errors
|
|
3
|
+
* Centralized error handling for generation orchestration
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { GenerationError, GenerationErrorType, AlertMessages } from "./types";
|
|
7
|
+
|
|
8
|
+
export const createGenerationError = (
|
|
9
|
+
type: GenerationErrorType,
|
|
10
|
+
message: string,
|
|
11
|
+
originalError?: Error,
|
|
12
|
+
): GenerationError => ({
|
|
13
|
+
type,
|
|
14
|
+
message,
|
|
15
|
+
originalError,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export const getAlertMessage = (
|
|
19
|
+
error: GenerationError,
|
|
20
|
+
messages: AlertMessages,
|
|
21
|
+
): string => {
|
|
22
|
+
switch (error.type) {
|
|
23
|
+
case "network":
|
|
24
|
+
return messages.networkError;
|
|
25
|
+
case "credits":
|
|
26
|
+
return messages.creditFailed;
|
|
27
|
+
case "policy":
|
|
28
|
+
return messages.policyViolation;
|
|
29
|
+
case "save":
|
|
30
|
+
return messages.saveFailed;
|
|
31
|
+
default:
|
|
32
|
+
return messages.unknown;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const parseError = (err: unknown): GenerationError => {
|
|
37
|
+
if (isGenerationError(err)) {
|
|
38
|
+
return err;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (err instanceof Error) {
|
|
42
|
+
if (err.name === "ContentPolicyViolationError") {
|
|
43
|
+
return createGenerationError("policy", err.message, err);
|
|
44
|
+
}
|
|
45
|
+
return createGenerationError("unknown", err.message, err);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return createGenerationError("unknown", "Generation failed");
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const isGenerationError = (err: unknown): err is GenerationError => {
|
|
52
|
+
return (
|
|
53
|
+
typeof err === "object" &&
|
|
54
|
+
err !== null &&
|
|
55
|
+
"type" in err &&
|
|
56
|
+
"message" in err
|
|
57
|
+
);
|
|
58
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generation Module
|
|
3
|
+
* Feature-agnostic AI generation orchestration
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { useGenerationOrchestrator } from "./orchestrator";
|
|
7
|
+
|
|
8
|
+
export type {
|
|
9
|
+
GenerationStrategy,
|
|
10
|
+
GenerationConfig,
|
|
11
|
+
GenerationState,
|
|
12
|
+
OrchestratorStatus,
|
|
13
|
+
GenerationError,
|
|
14
|
+
GenerationErrorType,
|
|
15
|
+
AlertMessages,
|
|
16
|
+
UseGenerationOrchestratorReturn,
|
|
17
|
+
} from "./types";
|
|
18
|
+
|
|
19
|
+
export {
|
|
20
|
+
createGenerationError,
|
|
21
|
+
getAlertMessage,
|
|
22
|
+
parseError,
|
|
23
|
+
} from "./errors";
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generation Orchestrator
|
|
3
|
+
* Feature-agnostic hook for AI generation with centralized:
|
|
4
|
+
* - Credit management
|
|
5
|
+
* - Error handling
|
|
6
|
+
* - Alert display
|
|
7
|
+
* - Progress tracking
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { useState, useCallback, useRef } from "react";
|
|
11
|
+
import { useOfflineStore, useAlert } from "@umituz/react-native-design-system";
|
|
12
|
+
import { useDeductCredit } from "@umituz/react-native-subscription";
|
|
13
|
+
import { createGenerationError, getAlertMessage, parseError } from "./errors";
|
|
14
|
+
import type {
|
|
15
|
+
GenerationStrategy,
|
|
16
|
+
GenerationConfig,
|
|
17
|
+
GenerationState,
|
|
18
|
+
UseGenerationOrchestratorReturn,
|
|
19
|
+
} from "./types";
|
|
20
|
+
|
|
21
|
+
const INITIAL_STATE = {
|
|
22
|
+
status: "idle" as const,
|
|
23
|
+
isGenerating: false,
|
|
24
|
+
progress: 0,
|
|
25
|
+
result: null,
|
|
26
|
+
error: null,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const useGenerationOrchestrator = <TInput, TResult>(
|
|
30
|
+
strategy: GenerationStrategy<TInput, TResult>,
|
|
31
|
+
config: GenerationConfig,
|
|
32
|
+
): UseGenerationOrchestratorReturn<TInput, TResult> => {
|
|
33
|
+
const { userId, alertMessages, onCreditsExhausted, onSuccess, onError } =
|
|
34
|
+
config;
|
|
35
|
+
|
|
36
|
+
const [state, setState] = useState<GenerationState<TResult>>(INITIAL_STATE);
|
|
37
|
+
const isGeneratingRef = useRef(false);
|
|
38
|
+
const offlineStore = useOfflineStore();
|
|
39
|
+
const { showError, showSuccess } = useAlert();
|
|
40
|
+
const { checkCredits, deductCredit } = useDeductCredit({
|
|
41
|
+
userId,
|
|
42
|
+
onCreditsExhausted,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const generate = useCallback(
|
|
46
|
+
async (input: TInput) => {
|
|
47
|
+
if (isGeneratingRef.current) return;
|
|
48
|
+
|
|
49
|
+
isGeneratingRef.current = true;
|
|
50
|
+
setState({ ...INITIAL_STATE, status: "checking", isGenerating: true });
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
if (!offlineStore.isOnline) {
|
|
54
|
+
throw createGenerationError("network", "No internet connection");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const creditCost = strategy.getCreditCost();
|
|
58
|
+
const hasCredits = await checkCredits(creditCost);
|
|
59
|
+
if (!hasCredits) {
|
|
60
|
+
throw createGenerationError("credits", "Insufficient credits");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
setState((prev) => ({ ...prev, status: "generating", progress: 10 }));
|
|
64
|
+
|
|
65
|
+
const result = await strategy.execute(input, (progress) => {
|
|
66
|
+
setState((prev) => ({ ...prev, progress }));
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
setState((prev) => ({ ...prev, progress: 70 }));
|
|
70
|
+
|
|
71
|
+
if (strategy.save && userId) {
|
|
72
|
+
setState((prev) => ({ ...prev, status: "saving" }));
|
|
73
|
+
try {
|
|
74
|
+
await strategy.save(result, userId);
|
|
75
|
+
} catch (saveErr) {
|
|
76
|
+
throw createGenerationError(
|
|
77
|
+
"save",
|
|
78
|
+
"Failed to save",
|
|
79
|
+
saveErr instanceof Error ? saveErr : undefined,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
setState((prev) => ({ ...prev, progress: 90 }));
|
|
85
|
+
|
|
86
|
+
await deductCredit(creditCost);
|
|
87
|
+
|
|
88
|
+
setState({
|
|
89
|
+
status: "success",
|
|
90
|
+
isGenerating: false,
|
|
91
|
+
progress: 100,
|
|
92
|
+
result,
|
|
93
|
+
error: null,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
if (alertMessages.success) {
|
|
97
|
+
void showSuccess("Success", alertMessages.success);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
onSuccess?.(result);
|
|
101
|
+
} catch (err) {
|
|
102
|
+
const error = parseError(err);
|
|
103
|
+
|
|
104
|
+
setState({
|
|
105
|
+
status: "error",
|
|
106
|
+
isGenerating: false,
|
|
107
|
+
progress: 0,
|
|
108
|
+
result: null,
|
|
109
|
+
error,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
void showError("Error", getAlertMessage(error, alertMessages));
|
|
113
|
+
onError?.(error);
|
|
114
|
+
} finally {
|
|
115
|
+
isGeneratingRef.current = false;
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
[
|
|
119
|
+
strategy,
|
|
120
|
+
userId,
|
|
121
|
+
alertMessages,
|
|
122
|
+
offlineStore.isOnline,
|
|
123
|
+
checkCredits,
|
|
124
|
+
deductCredit,
|
|
125
|
+
showError,
|
|
126
|
+
showSuccess,
|
|
127
|
+
onSuccess,
|
|
128
|
+
onError,
|
|
129
|
+
],
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
const reset = useCallback(() => {
|
|
133
|
+
setState(INITIAL_STATE);
|
|
134
|
+
isGeneratingRef.current = false;
|
|
135
|
+
}, []);
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
generate,
|
|
139
|
+
reset,
|
|
140
|
+
status: state.status,
|
|
141
|
+
isGenerating: state.isGenerating,
|
|
142
|
+
progress: state.progress,
|
|
143
|
+
result: state.result,
|
|
144
|
+
error: state.error,
|
|
145
|
+
};
|
|
146
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generation Types
|
|
3
|
+
* Type definitions for feature-agnostic generation orchestration
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type OrchestratorStatus =
|
|
7
|
+
| "idle"
|
|
8
|
+
| "checking"
|
|
9
|
+
| "generating"
|
|
10
|
+
| "saving"
|
|
11
|
+
| "success"
|
|
12
|
+
| "error";
|
|
13
|
+
|
|
14
|
+
export interface GenerationStrategy<TInput, TResult> {
|
|
15
|
+
/** Execute the generation */
|
|
16
|
+
execute: (
|
|
17
|
+
input: TInput,
|
|
18
|
+
onProgress?: (progress: number) => void,
|
|
19
|
+
) => Promise<TResult>;
|
|
20
|
+
/** Credit cost for this generation */
|
|
21
|
+
getCreditCost: () => number;
|
|
22
|
+
/** Optional: Save result to storage */
|
|
23
|
+
save?: (result: TResult, userId: string) => Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface AlertMessages {
|
|
27
|
+
networkError: string;
|
|
28
|
+
policyViolation: string;
|
|
29
|
+
saveFailed: string;
|
|
30
|
+
creditFailed: string;
|
|
31
|
+
unknown: string;
|
|
32
|
+
success?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface GenerationConfig {
|
|
36
|
+
userId: string | undefined;
|
|
37
|
+
alertMessages: AlertMessages;
|
|
38
|
+
onCreditsExhausted?: () => void;
|
|
39
|
+
onSuccess?: (result: unknown) => void;
|
|
40
|
+
onError?: (error: GenerationError) => void;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface GenerationState<TResult> {
|
|
44
|
+
status: OrchestratorStatus;
|
|
45
|
+
isGenerating: boolean;
|
|
46
|
+
progress: number;
|
|
47
|
+
result: TResult | null;
|
|
48
|
+
error: GenerationError | null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface GenerationError {
|
|
52
|
+
type: GenerationErrorType;
|
|
53
|
+
message: string;
|
|
54
|
+
originalError?: Error;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export type GenerationErrorType =
|
|
58
|
+
| "network"
|
|
59
|
+
| "credits"
|
|
60
|
+
| "policy"
|
|
61
|
+
| "save"
|
|
62
|
+
| "unknown";
|
|
63
|
+
|
|
64
|
+
export interface UseGenerationOrchestratorReturn<TInput, TResult> {
|
|
65
|
+
generate: (input: TInput) => Promise<void>;
|
|
66
|
+
reset: () => void;
|
|
67
|
+
status: OrchestratorStatus;
|
|
68
|
+
isGenerating: boolean;
|
|
69
|
+
progress: number;
|
|
70
|
+
result: TResult | null;
|
|
71
|
+
error: GenerationError | null;
|
|
72
|
+
}
|
|
@@ -5,6 +5,24 @@
|
|
|
5
5
|
// Base Feature Hooks (Provider-Agnostic)
|
|
6
6
|
export * from "./base";
|
|
7
7
|
|
|
8
|
+
// Generation Orchestrator (Centralized)
|
|
9
|
+
export {
|
|
10
|
+
useGenerationOrchestrator,
|
|
11
|
+
createGenerationError,
|
|
12
|
+
getAlertMessage,
|
|
13
|
+
parseError,
|
|
14
|
+
} from "./generation";
|
|
15
|
+
export type {
|
|
16
|
+
GenerationStrategy,
|
|
17
|
+
GenerationConfig,
|
|
18
|
+
GenerationState,
|
|
19
|
+
OrchestratorStatus,
|
|
20
|
+
GenerationError,
|
|
21
|
+
GenerationErrorType,
|
|
22
|
+
AlertMessages,
|
|
23
|
+
UseGenerationOrchestratorReturn,
|
|
24
|
+
} from "./generation";
|
|
25
|
+
|
|
8
26
|
export { useGeneration } from "./use-generation";
|
|
9
27
|
export type {
|
|
10
28
|
UseGenerationOptions,
|
|
@@ -24,27 +42,6 @@ export type {
|
|
|
24
42
|
DirectExecutionResult,
|
|
25
43
|
} from "./use-background-generation";
|
|
26
44
|
|
|
27
|
-
export { usePhotoGeneration } from "./usePhotoGeneration";
|
|
28
|
-
export type {
|
|
29
|
-
UsePhotoGenerationReturn,
|
|
30
|
-
} from "./usePhotoGeneration";
|
|
31
|
-
|
|
32
|
-
export { useGenerationCredits, CreditError } from "./useGenerationCredits";
|
|
33
|
-
export type {
|
|
34
|
-
UseGenerationCreditsParams,
|
|
35
|
-
UseGenerationCreditsReturn,
|
|
36
|
-
GenerationCreditsError,
|
|
37
|
-
} from "./useGenerationCredits";
|
|
38
|
-
|
|
39
|
-
export type {
|
|
40
|
-
PhotoGenerationInput,
|
|
41
|
-
PhotoGenerationResult,
|
|
42
|
-
PhotoGenerationError,
|
|
43
|
-
PhotoGenerationConfig,
|
|
44
|
-
PhotoGenerationState,
|
|
45
|
-
PhotoGenerationStatus,
|
|
46
|
-
} from "./photo-generation.types";
|
|
47
|
-
|
|
48
45
|
export { useGenerationFlow } from "./useGenerationFlow";
|
|
49
46
|
export type {
|
|
50
47
|
UseGenerationFlowOptions,
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Photo Generation Types
|
|
3
|
-
* Generic types for photo-based AI generation workflows
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export interface PhotoGenerationInput<TMetadata = unknown> {
|
|
7
|
-
photos: Array<{ uri: string; base64: string }>;
|
|
8
|
-
metadata?: TMetadata;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface PhotoGenerationResult<TResult = unknown> {
|
|
12
|
-
success: boolean;
|
|
13
|
-
data?: TResult;
|
|
14
|
-
error?: PhotoGenerationError;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface PhotoGenerationError {
|
|
18
|
-
type: "network_error" | "policy_violation" | "save_failed" | "credit_failed" | "unknown";
|
|
19
|
-
message: string;
|
|
20
|
-
originalError?: Error;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface AlertMessages {
|
|
24
|
-
networkError: string;
|
|
25
|
-
policyViolation: string;
|
|
26
|
-
saveFailed: string;
|
|
27
|
-
creditFailed: string;
|
|
28
|
-
unknown: string;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface PhotoGenerationConfig<TInput, TResult, TSaveInput> {
|
|
32
|
-
/** User ID for credit operations - required for credit check/deduct */
|
|
33
|
-
userId: string | undefined;
|
|
34
|
-
/** Credit cost per generation (default: 1) */
|
|
35
|
-
creditCost?: number;
|
|
36
|
-
/** Generation function */
|
|
37
|
-
generate: (input: TInput, onProgress?: (progress: number) => void) => Promise<TResult>;
|
|
38
|
-
/** Save result function */
|
|
39
|
-
save?: (result: TResult, input: TInput) => Promise<TSaveInput>;
|
|
40
|
-
/** Build metadata for tracking */
|
|
41
|
-
buildMetadata?: (input: TInput) => Record<string, unknown>;
|
|
42
|
-
/** Called when credits are exhausted */
|
|
43
|
-
onCreditsExhausted?: () => void;
|
|
44
|
-
/** Success callback */
|
|
45
|
-
onSuccess?: (result: TResult) => void;
|
|
46
|
-
/** Error callback */
|
|
47
|
-
onError?: (error: PhotoGenerationError) => void;
|
|
48
|
-
/** Save complete callback */
|
|
49
|
-
onSaveComplete?: (saveResult: TSaveInput) => void;
|
|
50
|
-
/** Alert messages for errors */
|
|
51
|
-
alertMessages: AlertMessages;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export interface PhotoGenerationState<TResult = unknown> {
|
|
55
|
-
isGenerating: boolean;
|
|
56
|
-
result: TResult | null;
|
|
57
|
-
error: PhotoGenerationError | null;
|
|
58
|
-
progress: number;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export type PhotoGenerationStatus = "idle" | "validating" | "generating" | "saving" | "success" | "error";
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useGenerationCredits Hook
|
|
3
|
-
* Centralized credit management for all AI generation features
|
|
4
|
-
* Provides server-side credit validation before generation
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { useCallback } from "react";
|
|
8
|
-
import { useDeductCredit } from "@umituz/react-native-subscription";
|
|
9
|
-
|
|
10
|
-
export interface UseGenerationCreditsParams {
|
|
11
|
-
userId: string | undefined;
|
|
12
|
-
onCreditsExhausted?: () => void;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface GenerationCreditsError {
|
|
16
|
-
type: "no_credits" | "deduct_failed";
|
|
17
|
-
message: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface UseGenerationCreditsReturn {
|
|
21
|
-
/** Server-side credit check */
|
|
22
|
-
checkCredits: (cost?: number) => Promise<boolean>;
|
|
23
|
-
/** Deduct credits after successful generation */
|
|
24
|
-
deductCredits: (cost?: number) => Promise<boolean>;
|
|
25
|
-
/** Whether credit operation is in progress */
|
|
26
|
-
isProcessing: boolean;
|
|
27
|
-
/** Execute generation with automatic credit check and deduct */
|
|
28
|
-
withCredits: <T>(
|
|
29
|
-
generate: () => Promise<T>,
|
|
30
|
-
cost?: number,
|
|
31
|
-
) => Promise<T>;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export const useGenerationCredits = ({
|
|
35
|
-
userId,
|
|
36
|
-
onCreditsExhausted,
|
|
37
|
-
}: UseGenerationCreditsParams): UseGenerationCreditsReturn => {
|
|
38
|
-
const { checkCredits, deductCredit, isDeducting } = useDeductCredit({
|
|
39
|
-
userId,
|
|
40
|
-
onCreditsExhausted,
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
const withCredits = useCallback(
|
|
44
|
-
async <T>(generate: () => Promise<T>, cost: number = 1): Promise<T> => {
|
|
45
|
-
const hasCredits = await checkCredits(cost);
|
|
46
|
-
if (!hasCredits) {
|
|
47
|
-
throw new CreditError("no_credits", "Insufficient credits");
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const result = await generate();
|
|
51
|
-
|
|
52
|
-
await deductCredit(cost);
|
|
53
|
-
|
|
54
|
-
return result;
|
|
55
|
-
},
|
|
56
|
-
[checkCredits, deductCredit],
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
return {
|
|
60
|
-
checkCredits,
|
|
61
|
-
deductCredits: deductCredit,
|
|
62
|
-
isProcessing: isDeducting,
|
|
63
|
-
withCredits,
|
|
64
|
-
};
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
class CreditError extends Error {
|
|
68
|
-
constructor(
|
|
69
|
-
public type: GenerationCreditsError["type"],
|
|
70
|
-
message: string,
|
|
71
|
-
) {
|
|
72
|
-
super(message);
|
|
73
|
-
this.name = "CreditError";
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export { CreditError };
|
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* usePhotoGeneration Hook
|
|
3
|
-
* Generic hook for photo-based AI generation workflows
|
|
4
|
-
* Uses centralized credit management
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { useState, useCallback, useRef } from "react";
|
|
8
|
-
import { useOfflineStore, useAlert } from "@umituz/react-native-design-system";
|
|
9
|
-
import { useGenerationCredits, CreditError } from "./useGenerationCredits";
|
|
10
|
-
import type {
|
|
11
|
-
PhotoGenerationConfig,
|
|
12
|
-
PhotoGenerationState,
|
|
13
|
-
PhotoGenerationError,
|
|
14
|
-
PhotoGenerationStatus,
|
|
15
|
-
} from "./photo-generation.types";
|
|
16
|
-
|
|
17
|
-
export interface UsePhotoGenerationReturn<TInput, TResult>
|
|
18
|
-
extends PhotoGenerationState<TResult> {
|
|
19
|
-
generate: (input: TInput) => Promise<void>;
|
|
20
|
-
reset: () => void;
|
|
21
|
-
status: PhotoGenerationStatus;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export const usePhotoGeneration = <TInput, TResult, TSaveInput = unknown>(
|
|
25
|
-
config: PhotoGenerationConfig<TInput, TResult, TSaveInput>,
|
|
26
|
-
): UsePhotoGenerationReturn<TInput, TResult> => {
|
|
27
|
-
const {
|
|
28
|
-
userId,
|
|
29
|
-
creditCost = 1,
|
|
30
|
-
generate: generateFn,
|
|
31
|
-
save: saveFn,
|
|
32
|
-
onCreditsExhausted,
|
|
33
|
-
onSuccess,
|
|
34
|
-
onError,
|
|
35
|
-
onSaveComplete,
|
|
36
|
-
alertMessages,
|
|
37
|
-
} = config;
|
|
38
|
-
|
|
39
|
-
const [state, setState] = useState<PhotoGenerationState<TResult>>({
|
|
40
|
-
isGenerating: false,
|
|
41
|
-
result: null,
|
|
42
|
-
error: null,
|
|
43
|
-
progress: 0,
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
const [status, setStatus] = useState<PhotoGenerationStatus>("idle");
|
|
47
|
-
const isGeneratingRef = useRef(false);
|
|
48
|
-
const offlineStore = useOfflineStore();
|
|
49
|
-
const { showError } = useAlert();
|
|
50
|
-
|
|
51
|
-
const { checkCredits, deductCredits } = useGenerationCredits({
|
|
52
|
-
userId,
|
|
53
|
-
onCreditsExhausted,
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
const createError = useCallback(
|
|
57
|
-
(
|
|
58
|
-
type: PhotoGenerationError["type"],
|
|
59
|
-
message: string,
|
|
60
|
-
originalError?: Error,
|
|
61
|
-
): PhotoGenerationError => ({
|
|
62
|
-
type,
|
|
63
|
-
message,
|
|
64
|
-
originalError,
|
|
65
|
-
}),
|
|
66
|
-
[],
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
const generate = useCallback(
|
|
70
|
-
async (input: TInput) => {
|
|
71
|
-
if (isGeneratingRef.current) {
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
isGeneratingRef.current = true;
|
|
76
|
-
setState({ isGenerating: true, result: null, error: null, progress: 0 });
|
|
77
|
-
setStatus("validating");
|
|
78
|
-
|
|
79
|
-
try {
|
|
80
|
-
if (!offlineStore.isOnline) {
|
|
81
|
-
throw createError("network_error", "No internet connection");
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const hasCredits = await checkCredits(creditCost);
|
|
85
|
-
if (!hasCredits) {
|
|
86
|
-
throw createError("credit_failed", "Insufficient credits");
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
setStatus("generating");
|
|
90
|
-
setState((prev) => ({ ...prev, progress: 20 }));
|
|
91
|
-
|
|
92
|
-
const result = await generateFn(input, (newProgress) => {
|
|
93
|
-
setState((prev) => ({ ...prev, progress: newProgress }));
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
setState((prev) => ({ ...prev, progress: 60 }));
|
|
97
|
-
|
|
98
|
-
if (saveFn) {
|
|
99
|
-
setStatus("saving");
|
|
100
|
-
try {
|
|
101
|
-
const saveResult = await saveFn(result, input);
|
|
102
|
-
onSaveComplete?.(saveResult);
|
|
103
|
-
} catch (saveError) {
|
|
104
|
-
throw createError(
|
|
105
|
-
"save_failed",
|
|
106
|
-
"Failed to save result",
|
|
107
|
-
saveError instanceof Error ? saveError : new Error(String(saveError)),
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
setState((prev) => ({ ...prev, progress: 80 }));
|
|
113
|
-
|
|
114
|
-
await deductCredits(creditCost);
|
|
115
|
-
|
|
116
|
-
setState({
|
|
117
|
-
isGenerating: false,
|
|
118
|
-
result,
|
|
119
|
-
error: null,
|
|
120
|
-
progress: 100,
|
|
121
|
-
});
|
|
122
|
-
setStatus("success");
|
|
123
|
-
onSuccess?.(result);
|
|
124
|
-
} catch (err: unknown) {
|
|
125
|
-
let generationError: PhotoGenerationError;
|
|
126
|
-
|
|
127
|
-
if (err instanceof CreditError) {
|
|
128
|
-
generationError = createError("credit_failed", err.message);
|
|
129
|
-
} else if (
|
|
130
|
-
err &&
|
|
131
|
-
typeof err === "object" &&
|
|
132
|
-
"type" in err &&
|
|
133
|
-
"message" in err
|
|
134
|
-
) {
|
|
135
|
-
generationError = err as PhotoGenerationError;
|
|
136
|
-
} else if (err instanceof Error) {
|
|
137
|
-
if (err.name === "ContentPolicyViolationError") {
|
|
138
|
-
generationError = createError(
|
|
139
|
-
"policy_violation",
|
|
140
|
-
"Content policy violation",
|
|
141
|
-
err,
|
|
142
|
-
);
|
|
143
|
-
} else {
|
|
144
|
-
generationError = createError(
|
|
145
|
-
"unknown",
|
|
146
|
-
err.message || "Generation failed",
|
|
147
|
-
err,
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
} else {
|
|
151
|
-
generationError = createError("unknown", "Generation failed");
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
setState({
|
|
155
|
-
isGenerating: false,
|
|
156
|
-
result: null,
|
|
157
|
-
error: generationError,
|
|
158
|
-
progress: 0,
|
|
159
|
-
});
|
|
160
|
-
setStatus("error");
|
|
161
|
-
|
|
162
|
-
const errorMessage =
|
|
163
|
-
generationError.type === "network_error"
|
|
164
|
-
? alertMessages.networkError
|
|
165
|
-
: generationError.type === "policy_violation"
|
|
166
|
-
? alertMessages.policyViolation
|
|
167
|
-
: generationError.type === "save_failed"
|
|
168
|
-
? alertMessages.saveFailed
|
|
169
|
-
: generationError.type === "credit_failed"
|
|
170
|
-
? alertMessages.creditFailed
|
|
171
|
-
: alertMessages.unknown;
|
|
172
|
-
|
|
173
|
-
void showError("Error", errorMessage);
|
|
174
|
-
onError?.(generationError);
|
|
175
|
-
} finally {
|
|
176
|
-
isGeneratingRef.current = false;
|
|
177
|
-
}
|
|
178
|
-
},
|
|
179
|
-
[
|
|
180
|
-
generateFn,
|
|
181
|
-
saveFn,
|
|
182
|
-
checkCredits,
|
|
183
|
-
deductCredits,
|
|
184
|
-
creditCost,
|
|
185
|
-
onSuccess,
|
|
186
|
-
onError,
|
|
187
|
-
onSaveComplete,
|
|
188
|
-
createError,
|
|
189
|
-
offlineStore,
|
|
190
|
-
alertMessages,
|
|
191
|
-
showError,
|
|
192
|
-
],
|
|
193
|
-
);
|
|
194
|
-
|
|
195
|
-
const reset = useCallback(() => {
|
|
196
|
-
setState({ isGenerating: false, result: null, error: null, progress: 0 });
|
|
197
|
-
setStatus("idle");
|
|
198
|
-
isGeneratingRef.current = false;
|
|
199
|
-
}, []);
|
|
200
|
-
|
|
201
|
-
return {
|
|
202
|
-
...state,
|
|
203
|
-
generate,
|
|
204
|
-
reset,
|
|
205
|
-
status,
|
|
206
|
-
};
|
|
207
|
-
};
|