@umituz/react-native-ai-generation-content 1.17.307 → 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 +59 -62
- 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 -14
- package/src/presentation/hooks/photo-generation.types.ts +0 -50
- package/src/presentation/hooks/usePhotoGeneration.ts +0 -192
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,93 +1,90 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* useCoupleFutureGeneration Hook
|
|
3
|
+
* Couple future generation using centralized orchestrator
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useMemo, useCallback } from "react";
|
|
7
|
+
import {
|
|
8
|
+
useGenerationOrchestrator,
|
|
9
|
+
type GenerationStrategy,
|
|
10
|
+
type AlertMessages,
|
|
11
|
+
} from "../../../../presentation/hooks/generation";
|
|
4
12
|
import { executeCoupleFuture } from "../../infrastructure/executor";
|
|
5
13
|
import type { CoupleFutureInput } from "../../domain/types";
|
|
6
|
-
import type {
|
|
7
|
-
PhotoGenerationConfig,
|
|
8
|
-
PhotoGenerationError,
|
|
9
|
-
} from "../../../../presentation/hooks/photo-generation.types";
|
|
10
14
|
import { createCreationsRepository } from "../../../../domains/creations/infrastructure/adapters";
|
|
11
15
|
import type { Creation } from "../../../../domains/creations/domain/entities/Creation";
|
|
12
16
|
|
|
13
|
-
export interface
|
|
14
|
-
TInput extends CoupleFutureInput,
|
|
15
|
-
TResult,
|
|
16
|
-
> {
|
|
17
|
+
export interface CoupleFutureConfig<TResult> {
|
|
17
18
|
userId: string | undefined;
|
|
18
|
-
processResult: (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
) => Promise<TResult> | TResult;
|
|
22
|
-
buildCreation?: (result: TResult, input: TInput) => Creation | null;
|
|
19
|
+
processResult: (imageUrl: string, input: CoupleFutureInput) => TResult;
|
|
20
|
+
buildCreation?: (result: TResult, input: CoupleFutureInput) => Creation | null;
|
|
21
|
+
onCreditsExhausted?: () => void;
|
|
23
22
|
onSuccess?: (result: TResult) => void;
|
|
24
23
|
onError?: (error: string) => void;
|
|
25
|
-
alertMessages:
|
|
26
|
-
networkError: string;
|
|
27
|
-
policyViolation: string;
|
|
28
|
-
saveFailed: string;
|
|
29
|
-
creditFailed: string;
|
|
30
|
-
unknown: string;
|
|
31
|
-
};
|
|
24
|
+
alertMessages: AlertMessages;
|
|
32
25
|
}
|
|
33
26
|
|
|
34
|
-
export const useCoupleFutureGeneration = <
|
|
35
|
-
|
|
36
|
-
TResult,
|
|
37
|
-
>(
|
|
38
|
-
config: UseCoupleFutureGenerationConfig<TInput, TResult>,
|
|
27
|
+
export const useCoupleFutureGeneration = <TResult>(
|
|
28
|
+
config: CoupleFutureConfig<TResult>,
|
|
39
29
|
) => {
|
|
40
30
|
const {
|
|
41
31
|
userId,
|
|
42
32
|
processResult,
|
|
43
33
|
buildCreation,
|
|
34
|
+
onCreditsExhausted,
|
|
44
35
|
onSuccess,
|
|
45
36
|
onError,
|
|
46
37
|
alertMessages,
|
|
47
38
|
} = config;
|
|
48
39
|
|
|
49
|
-
const
|
|
50
|
-
const repository = useCallback(
|
|
40
|
+
const repository = useMemo(
|
|
51
41
|
() => createCreationsRepository("creations"),
|
|
52
42
|
[],
|
|
53
43
|
);
|
|
54
44
|
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (!result.success || !result.imageUrl) {
|
|
67
|
-
throw new Error(result.error || "Generation failed");
|
|
68
|
-
}
|
|
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
|
+
);
|
|
69
56
|
|
|
70
|
-
|
|
71
|
-
|
|
57
|
+
if (!result.success || !result.imageUrl) {
|
|
58
|
+
throw new Error(result.error || "Generation failed");
|
|
59
|
+
}
|
|
72
60
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
+
);
|
|
82
75
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
onSuccess,
|
|
86
|
-
onError: (error: PhotoGenerationError) => {
|
|
76
|
+
const handleError = useCallback(
|
|
77
|
+
(error: { message: string }) => {
|
|
87
78
|
onError?.(error.message);
|
|
88
79
|
},
|
|
89
|
-
|
|
90
|
-
|
|
80
|
+
[onError],
|
|
81
|
+
);
|
|
91
82
|
|
|
92
|
-
return
|
|
83
|
+
return useGenerationOrchestrator(strategy, {
|
|
84
|
+
userId,
|
|
85
|
+
alertMessages,
|
|
86
|
+
onCreditsExhausted,
|
|
87
|
+
onSuccess: onSuccess as (result: unknown) => void,
|
|
88
|
+
onError: handleError,
|
|
89
|
+
});
|
|
93
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,20 +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 type {
|
|
33
|
-
PhotoGenerationInput,
|
|
34
|
-
PhotoGenerationResult,
|
|
35
|
-
PhotoGenerationError,
|
|
36
|
-
PhotoGenerationConfig,
|
|
37
|
-
PhotoGenerationState,
|
|
38
|
-
PhotoGenerationStatus,
|
|
39
|
-
} from "./photo-generation.types";
|
|
40
|
-
|
|
41
45
|
export { useGenerationFlow } from "./useGenerationFlow";
|
|
42
46
|
export type {
|
|
43
47
|
UseGenerationFlowOptions,
|
|
@@ -1,50 +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
|
-
generate: (input: TInput, onProgress?: (progress: number) => void) => Promise<TResult>;
|
|
33
|
-
save?: (result: TResult, input: TInput) => Promise<TSaveInput>;
|
|
34
|
-
buildMetadata?: (input: TInput) => Record<string, unknown>;
|
|
35
|
-
checkCredits?: () => Promise<boolean>;
|
|
36
|
-
deductCredits?: () => Promise<void>;
|
|
37
|
-
onSuccess?: (result: TResult) => void;
|
|
38
|
-
onError?: (error: PhotoGenerationError) => void;
|
|
39
|
-
onSaveComplete?: (saveResult: TSaveInput) => void;
|
|
40
|
-
alertMessages: AlertMessages;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export interface PhotoGenerationState<TResult = unknown> {
|
|
44
|
-
isGenerating: boolean;
|
|
45
|
-
result: TResult | null;
|
|
46
|
-
error: PhotoGenerationError | null;
|
|
47
|
-
progress: number;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export type PhotoGenerationStatus = "idle" | "validating" | "generating" | "saving" | "success" | "error";
|
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* usePhotoGeneration Hook
|
|
3
|
-
* Generic hook for photo-based AI generation workflows
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { useState, useCallback, useRef } from "react";
|
|
7
|
-
import { useOfflineStore, useAlert } from "@umituz/react-native-design-system";
|
|
8
|
-
import type {
|
|
9
|
-
PhotoGenerationConfig,
|
|
10
|
-
PhotoGenerationState,
|
|
11
|
-
PhotoGenerationError,
|
|
12
|
-
PhotoGenerationStatus,
|
|
13
|
-
} from "./photo-generation.types";
|
|
14
|
-
|
|
15
|
-
export interface UsePhotoGenerationReturn<TInput, TResult> extends PhotoGenerationState<TResult> {
|
|
16
|
-
generate: (input: TInput) => Promise<void>;
|
|
17
|
-
reset: () => void;
|
|
18
|
-
status: PhotoGenerationStatus;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const usePhotoGeneration = <TInput, TResult, TSaveInput = unknown>(
|
|
22
|
-
config: PhotoGenerationConfig<TInput, TResult, TSaveInput>,
|
|
23
|
-
): UsePhotoGenerationReturn<TInput, TResult> => {
|
|
24
|
-
const {
|
|
25
|
-
generate: generateFn,
|
|
26
|
-
save: saveFn,
|
|
27
|
-
checkCredits,
|
|
28
|
-
deductCredits,
|
|
29
|
-
onSuccess,
|
|
30
|
-
onError,
|
|
31
|
-
onSaveComplete,
|
|
32
|
-
alertMessages,
|
|
33
|
-
} = config;
|
|
34
|
-
|
|
35
|
-
const [state, setState] = useState<PhotoGenerationState<TResult>>({
|
|
36
|
-
isGenerating: false,
|
|
37
|
-
result: null,
|
|
38
|
-
error: null,
|
|
39
|
-
progress: 0,
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
const [status, setStatus] = useState<PhotoGenerationStatus>("idle");
|
|
43
|
-
const isGeneratingRef = useRef(false);
|
|
44
|
-
const offlineStore = useOfflineStore();
|
|
45
|
-
const { showError } = useAlert();
|
|
46
|
-
|
|
47
|
-
const createError = useCallback(
|
|
48
|
-
(
|
|
49
|
-
type: PhotoGenerationError["type"],
|
|
50
|
-
message: string,
|
|
51
|
-
originalError?: Error,
|
|
52
|
-
): PhotoGenerationError => ({
|
|
53
|
-
type,
|
|
54
|
-
message,
|
|
55
|
-
originalError,
|
|
56
|
-
}),
|
|
57
|
-
[],
|
|
58
|
-
);
|
|
59
|
-
|
|
60
|
-
const generate = useCallback(
|
|
61
|
-
async (input: TInput) => {
|
|
62
|
-
if (isGeneratingRef.current) {
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
isGeneratingRef.current = true;
|
|
67
|
-
setState({ isGenerating: true, result: null, error: null, progress: 0 });
|
|
68
|
-
setStatus("validating");
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
// Check network connectivity
|
|
72
|
-
if (!offlineStore.isOnline) {
|
|
73
|
-
throw createError("network_error", "No internet connection");
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Check credits
|
|
77
|
-
if (checkCredits) {
|
|
78
|
-
const hasCredits = await checkCredits();
|
|
79
|
-
if (!hasCredits) {
|
|
80
|
-
throw createError("credit_failed", "Insufficient credits");
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
setStatus("generating");
|
|
85
|
-
setState((prev) => ({ ...prev, progress: 20 }));
|
|
86
|
-
|
|
87
|
-
// Generate without timeout - let AI provider handle its own timeout
|
|
88
|
-
// Pass progress callback to allow provider to report real progress
|
|
89
|
-
const result = await generateFn(input, (newProgress) => {
|
|
90
|
-
setState((prev) => ({ ...prev, progress: newProgress }));
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
setState((prev) => ({ ...prev, progress: 60 }));
|
|
94
|
-
|
|
95
|
-
// Save result
|
|
96
|
-
if (saveFn) {
|
|
97
|
-
setStatus("saving");
|
|
98
|
-
try {
|
|
99
|
-
const saveResult = await saveFn(result, input);
|
|
100
|
-
onSaveComplete?.(saveResult);
|
|
101
|
-
} catch (saveError) {
|
|
102
|
-
throw createError(
|
|
103
|
-
"save_failed",
|
|
104
|
-
"Failed to save result",
|
|
105
|
-
saveError instanceof Error ? saveError : new Error(String(saveError)),
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
setState((prev) => ({ ...prev, progress: 80 }));
|
|
111
|
-
|
|
112
|
-
// Deduct credits after successful generation
|
|
113
|
-
if (deductCredits) {
|
|
114
|
-
try {
|
|
115
|
-
await deductCredits();
|
|
116
|
-
} catch {
|
|
117
|
-
// Silently fail credit deduction as generation succeeded
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
setState({
|
|
122
|
-
isGenerating: false,
|
|
123
|
-
result,
|
|
124
|
-
error: null,
|
|
125
|
-
progress: 100,
|
|
126
|
-
});
|
|
127
|
-
setStatus("success");
|
|
128
|
-
onSuccess?.(result);
|
|
129
|
-
} catch (err: unknown) {
|
|
130
|
-
let generationError: PhotoGenerationError;
|
|
131
|
-
|
|
132
|
-
if (err && typeof err === "object" && "type" in err && "message" in err) {
|
|
133
|
-
generationError = err as PhotoGenerationError;
|
|
134
|
-
} else if (err instanceof Error) {
|
|
135
|
-
if (err.name === "ContentPolicyViolationError") {
|
|
136
|
-
generationError = createError("policy_violation", "Content policy violation", err);
|
|
137
|
-
} else {
|
|
138
|
-
generationError = createError("unknown", err.message || "Generation failed", err);
|
|
139
|
-
}
|
|
140
|
-
} else {
|
|
141
|
-
generationError = createError("unknown", "Generation failed");
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
setState({
|
|
145
|
-
isGenerating: false,
|
|
146
|
-
result: null,
|
|
147
|
-
error: generationError,
|
|
148
|
-
progress: 0,
|
|
149
|
-
});
|
|
150
|
-
setStatus("error");
|
|
151
|
-
|
|
152
|
-
const errorMessage =
|
|
153
|
-
generationError.type === "network_error" ? alertMessages.networkError :
|
|
154
|
-
generationError.type === "policy_violation" ? alertMessages.policyViolation :
|
|
155
|
-
generationError.type === "save_failed" ? alertMessages.saveFailed :
|
|
156
|
-
generationError.type === "credit_failed" ? alertMessages.creditFailed :
|
|
157
|
-
alertMessages.unknown;
|
|
158
|
-
|
|
159
|
-
void showError("Error", errorMessage);
|
|
160
|
-
onError?.(generationError);
|
|
161
|
-
} finally {
|
|
162
|
-
isGeneratingRef.current = false;
|
|
163
|
-
}
|
|
164
|
-
},
|
|
165
|
-
[
|
|
166
|
-
generateFn,
|
|
167
|
-
saveFn,
|
|
168
|
-
checkCredits,
|
|
169
|
-
deductCredits,
|
|
170
|
-
onSuccess,
|
|
171
|
-
onError,
|
|
172
|
-
onSaveComplete,
|
|
173
|
-
createError,
|
|
174
|
-
offlineStore,
|
|
175
|
-
alertMessages,
|
|
176
|
-
showError
|
|
177
|
-
],
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
const reset = useCallback(() => {
|
|
181
|
-
setState({ isGenerating: false, result: null, error: null, progress: 0 });
|
|
182
|
-
setStatus("idle");
|
|
183
|
-
isGeneratingRef.current = false;
|
|
184
|
-
}, []);
|
|
185
|
-
|
|
186
|
-
return {
|
|
187
|
-
...state,
|
|
188
|
-
generate,
|
|
189
|
-
reset,
|
|
190
|
-
status,
|
|
191
|
-
};
|
|
192
|
-
};
|