@umituz/react-native-ai-generation-content 1.17.307 → 1.17.308

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-ai-generation-content",
3
- "version": "1.17.307",
3
+ "version": "1.17.308",
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",
@@ -1,5 +1,9 @@
1
+ /**
2
+ * useCoupleFutureGeneration Hook
3
+ * Couple future generation with centralized credit management
4
+ */
5
+
1
6
  import { useCallback } from "react";
2
- import { useDeductCredit } from "@umituz/react-native-subscription";
3
7
  import { usePhotoGeneration } from "../../../../presentation/hooks/usePhotoGeneration";
4
8
  import { executeCoupleFuture } from "../../infrastructure/executor";
5
9
  import type { CoupleFutureInput } from "../../domain/types";
@@ -20,6 +24,7 @@ export interface UseCoupleFutureGenerationConfig<
20
24
  input: TInput,
21
25
  ) => Promise<TResult> | TResult;
22
26
  buildCreation?: (result: TResult, input: TInput) => Creation | null;
27
+ onCreditsExhausted?: () => void;
23
28
  onSuccess?: (result: TResult) => void;
24
29
  onError?: (error: string) => void;
25
30
  alertMessages: {
@@ -41,19 +46,25 @@ export const useCoupleFutureGeneration = <
41
46
  userId,
42
47
  processResult,
43
48
  buildCreation,
49
+ onCreditsExhausted,
44
50
  onSuccess,
45
51
  onError,
46
52
  alertMessages,
47
53
  } = config;
48
54
 
49
- const { checkCredits, deductCredit } = useDeductCredit({ userId });
50
55
  const repository = useCallback(
51
56
  () => createCreationsRepository("creations"),
52
57
  [],
53
58
  );
54
59
 
55
60
  const generationConfig: PhotoGenerationConfig<TInput, TResult, void> = {
56
- generate: async (input: TInput, onProgress?: (progress: number) => void) => {
61
+ userId,
62
+ creditCost: 1,
63
+ onCreditsExhausted,
64
+ generate: async (
65
+ input: TInput,
66
+ onProgress?: (progress: number) => void,
67
+ ) => {
57
68
  const result = await executeCoupleFuture(
58
69
  {
59
70
  partnerABase64: input.partnerABase64,
@@ -69,7 +80,6 @@ export const useCoupleFutureGeneration = <
69
80
 
70
81
  return processResult(result.imageUrl, input);
71
82
  },
72
-
73
83
  save: async (result: TResult, input: TInput) => {
74
84
  if (!userId || !buildCreation) {
75
85
  return;
@@ -79,9 +89,6 @@ export const useCoupleFutureGeneration = <
79
89
  await repository().create(userId, creation);
80
90
  }
81
91
  },
82
-
83
- checkCredits: () => checkCredits(1),
84
- deductCredits: () => deductCredit(1).then(() => {}),
85
92
  onSuccess,
86
93
  onError: (error: PhotoGenerationError) => {
87
94
  onError?.(error.message);
@@ -29,6 +29,13 @@ export type {
29
29
  UsePhotoGenerationReturn,
30
30
  } from "./usePhotoGeneration";
31
31
 
32
+ export { useGenerationCredits, CreditError } from "./useGenerationCredits";
33
+ export type {
34
+ UseGenerationCreditsParams,
35
+ UseGenerationCreditsReturn,
36
+ GenerationCreditsError,
37
+ } from "./useGenerationCredits";
38
+
32
39
  export type {
33
40
  PhotoGenerationInput,
34
41
  PhotoGenerationResult,
@@ -29,14 +29,25 @@ export interface AlertMessages {
29
29
  }
30
30
 
31
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 */
32
37
  generate: (input: TInput, onProgress?: (progress: number) => void) => Promise<TResult>;
38
+ /** Save result function */
33
39
  save?: (result: TResult, input: TInput) => Promise<TSaveInput>;
40
+ /** Build metadata for tracking */
34
41
  buildMetadata?: (input: TInput) => Record<string, unknown>;
35
- checkCredits?: () => Promise<boolean>;
36
- deductCredits?: () => Promise<void>;
42
+ /** Called when credits are exhausted */
43
+ onCreditsExhausted?: () => void;
44
+ /** Success callback */
37
45
  onSuccess?: (result: TResult) => void;
46
+ /** Error callback */
38
47
  onError?: (error: PhotoGenerationError) => void;
48
+ /** Save complete callback */
39
49
  onSaveComplete?: (saveResult: TSaveInput) => void;
50
+ /** Alert messages for errors */
40
51
  alertMessages: AlertMessages;
41
52
  }
42
53
 
@@ -0,0 +1,77 @@
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,10 +1,12 @@
1
1
  /**
2
2
  * usePhotoGeneration Hook
3
3
  * Generic hook for photo-based AI generation workflows
4
+ * Uses centralized credit management
4
5
  */
5
6
 
6
7
  import { useState, useCallback, useRef } from "react";
7
8
  import { useOfflineStore, useAlert } from "@umituz/react-native-design-system";
9
+ import { useGenerationCredits, CreditError } from "./useGenerationCredits";
8
10
  import type {
9
11
  PhotoGenerationConfig,
10
12
  PhotoGenerationState,
@@ -12,7 +14,8 @@ import type {
12
14
  PhotoGenerationStatus,
13
15
  } from "./photo-generation.types";
14
16
 
15
- export interface UsePhotoGenerationReturn<TInput, TResult> extends PhotoGenerationState<TResult> {
17
+ export interface UsePhotoGenerationReturn<TInput, TResult>
18
+ extends PhotoGenerationState<TResult> {
16
19
  generate: (input: TInput) => Promise<void>;
17
20
  reset: () => void;
18
21
  status: PhotoGenerationStatus;
@@ -22,10 +25,11 @@ export const usePhotoGeneration = <TInput, TResult, TSaveInput = unknown>(
22
25
  config: PhotoGenerationConfig<TInput, TResult, TSaveInput>,
23
26
  ): UsePhotoGenerationReturn<TInput, TResult> => {
24
27
  const {
28
+ userId,
29
+ creditCost = 1,
25
30
  generate: generateFn,
26
31
  save: saveFn,
27
- checkCredits,
28
- deductCredits,
32
+ onCreditsExhausted,
29
33
  onSuccess,
30
34
  onError,
31
35
  onSaveComplete,
@@ -44,6 +48,11 @@ export const usePhotoGeneration = <TInput, TResult, TSaveInput = unknown>(
44
48
  const offlineStore = useOfflineStore();
45
49
  const { showError } = useAlert();
46
50
 
51
+ const { checkCredits, deductCredits } = useGenerationCredits({
52
+ userId,
53
+ onCreditsExhausted,
54
+ });
55
+
47
56
  const createError = useCallback(
48
57
  (
49
58
  type: PhotoGenerationError["type"],
@@ -68,31 +77,24 @@ export const usePhotoGeneration = <TInput, TResult, TSaveInput = unknown>(
68
77
  setStatus("validating");
69
78
 
70
79
  try {
71
- // Check network connectivity
72
80
  if (!offlineStore.isOnline) {
73
81
  throw createError("network_error", "No internet connection");
74
82
  }
75
83
 
76
- // Check credits
77
- if (checkCredits) {
78
- const hasCredits = await checkCredits();
79
- if (!hasCredits) {
80
- throw createError("credit_failed", "Insufficient credits");
81
- }
84
+ const hasCredits = await checkCredits(creditCost);
85
+ if (!hasCredits) {
86
+ throw createError("credit_failed", "Insufficient credits");
82
87
  }
83
88
 
84
89
  setStatus("generating");
85
90
  setState((prev) => ({ ...prev, progress: 20 }));
86
91
 
87
- // Generate without timeout - let AI provider handle its own timeout
88
- // Pass progress callback to allow provider to report real progress
89
92
  const result = await generateFn(input, (newProgress) => {
90
93
  setState((prev) => ({ ...prev, progress: newProgress }));
91
94
  });
92
95
 
93
96
  setState((prev) => ({ ...prev, progress: 60 }));
94
97
 
95
- // Save result
96
98
  if (saveFn) {
97
99
  setStatus("saving");
98
100
  try {
@@ -109,14 +111,7 @@ export const usePhotoGeneration = <TInput, TResult, TSaveInput = unknown>(
109
111
 
110
112
  setState((prev) => ({ ...prev, progress: 80 }));
111
113
 
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
- }
114
+ await deductCredits(creditCost);
120
115
 
121
116
  setState({
122
117
  isGenerating: false,
@@ -129,13 +124,28 @@ export const usePhotoGeneration = <TInput, TResult, TSaveInput = unknown>(
129
124
  } catch (err: unknown) {
130
125
  let generationError: PhotoGenerationError;
131
126
 
132
- if (err && typeof err === "object" && "type" in err && "message" in err) {
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
+ ) {
133
135
  generationError = err as PhotoGenerationError;
134
136
  } else if (err instanceof Error) {
135
137
  if (err.name === "ContentPolicyViolationError") {
136
- generationError = createError("policy_violation", "Content policy violation", err);
138
+ generationError = createError(
139
+ "policy_violation",
140
+ "Content policy violation",
141
+ err,
142
+ );
137
143
  } else {
138
- generationError = createError("unknown", err.message || "Generation failed", err);
144
+ generationError = createError(
145
+ "unknown",
146
+ err.message || "Generation failed",
147
+ err,
148
+ );
139
149
  }
140
150
  } else {
141
151
  generationError = createError("unknown", "Generation failed");
@@ -150,11 +160,15 @@ export const usePhotoGeneration = <TInput, TResult, TSaveInput = unknown>(
150
160
  setStatus("error");
151
161
 
152
162
  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;
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;
158
172
 
159
173
  void showError("Error", errorMessage);
160
174
  onError?.(generationError);
@@ -167,13 +181,14 @@ export const usePhotoGeneration = <TInput, TResult, TSaveInput = unknown>(
167
181
  saveFn,
168
182
  checkCredits,
169
183
  deductCredits,
184
+ creditCost,
170
185
  onSuccess,
171
186
  onError,
172
187
  onSaveComplete,
173
188
  createError,
174
189
  offlineStore,
175
190
  alertMessages,
176
- showError
191
+ showError,
177
192
  ],
178
193
  );
179
194