@umituz/react-native-ai-generation-content 1.36.2 → 1.37.1

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.
Files changed (22) hide show
  1. package/package.json +2 -2
  2. package/src/domains/creations/infrastructure/repositories/CreationsWriter.ts +124 -199
  3. package/src/domains/creations/presentation/components/GalleryResultPreview.tsx +88 -0
  4. package/src/domains/creations/presentation/hooks/useGalleryCallbacks.ts +127 -0
  5. package/src/domains/creations/presentation/screens/CreationsGalleryScreen.tsx +37 -123
  6. package/src/domains/generation/wizard/presentation/components/GenericWizardFlow.tsx +5 -231
  7. package/src/domains/generation/wizard/presentation/components/WizardContinueButton.tsx +73 -0
  8. package/src/domains/generation/wizard/presentation/components/WizardFlowContent.tsx +181 -0
  9. package/src/domains/generation/wizard/presentation/components/WizardStepRenderer.tsx +18 -134
  10. package/src/domains/generation/wizard/presentation/components/step-renderers/renderPhotoUploadStep.tsx +52 -0
  11. package/src/domains/generation/wizard/presentation/components/step-renderers/renderSelectionStep.tsx +59 -0
  12. package/src/domains/generation/wizard/presentation/components/step-renderers/renderTextInputStep.tsx +62 -0
  13. package/src/domains/generation/wizard/presentation/hooks/useWizardFlowHandlers.ts +133 -0
  14. package/src/domains/generation/wizard/presentation/screens/GenericPhotoUploadScreen.tsx +15 -83
  15. package/src/domains/generation/wizard/presentation/screens/SelectionScreen.tsx +55 -134
  16. package/src/domains/result-preview/presentation/components/GenerationErrorScreen.tsx +19 -122
  17. package/src/domains/result-preview/presentation/hooks/useResultActions.ts +16 -131
  18. package/src/domains/scenarios/presentation/components/ScenarioContinueButton.tsx +76 -0
  19. package/src/domains/scenarios/presentation/hooks/useHierarchicalScenarios.ts +70 -0
  20. package/src/domains/scenarios/presentation/screens/HierarchicalScenarioListScreen.tsx +37 -170
  21. package/src/presentation/hooks/generation/moderation-handler.ts +77 -0
  22. package/src/presentation/hooks/generation/orchestrator.ts +33 -125
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Moderation Handler
3
+ * Handles content moderation logic for generation
4
+ */
5
+
6
+ import { createGenerationError, parseError, getAlertMessage } from "./errors";
7
+ import type { GenerationError, ModerationCallbacks, AlertMessages } from "./types";
8
+
9
+ export interface ModerationHandlerParams<TInput, TResult> {
10
+ readonly input: TInput;
11
+ readonly moderation: ModerationCallbacks | undefined;
12
+ readonly alertMessages: AlertMessages;
13
+ readonly isMountedRef: React.MutableRefObject<boolean>;
14
+ readonly isGeneratingRef: React.MutableRefObject<boolean>;
15
+ readonly setState: (state: { status: string; isGenerating: boolean; result: TResult | null; error: GenerationError | null }) => void;
16
+ readonly resetState: () => void;
17
+ readonly executeGeneration: (input: TInput) => Promise<TResult>;
18
+ readonly showError: (title: string, message: string) => void;
19
+ readonly onError?: (error: GenerationError) => void;
20
+ readonly handleLifecycleComplete: (status: "success" | "error", result?: TResult, error?: GenerationError) => void;
21
+ }
22
+
23
+ export async function handleModeration<TInput, TResult>(
24
+ params: ModerationHandlerParams<TInput, TResult>,
25
+ ): Promise<TResult | undefined> {
26
+ const {
27
+ input,
28
+ moderation,
29
+ alertMessages,
30
+ isMountedRef,
31
+ isGeneratingRef,
32
+ setState,
33
+ resetState,
34
+ executeGeneration,
35
+ showError,
36
+ onError,
37
+ handleLifecycleComplete,
38
+ } = params;
39
+
40
+ if (!moderation) {
41
+ return executeGeneration(input);
42
+ }
43
+
44
+ setState({ status: "moderating", isGenerating: true, result: null, error: null });
45
+ const moderationResult = await moderation.checkContent(input);
46
+
47
+ if (!moderationResult.allowed && moderationResult.warnings.length > 0) {
48
+ if (moderation.onShowWarning) {
49
+ moderation.onShowWarning(
50
+ moderationResult.warnings,
51
+ () => {
52
+ isGeneratingRef.current = false;
53
+ if (isMountedRef.current) resetState();
54
+ },
55
+ async () => {
56
+ try {
57
+ await executeGeneration(input);
58
+ } catch (err) {
59
+ const error = parseError(err);
60
+ if (isMountedRef.current) {
61
+ setState({ status: "error", isGenerating: false, result: null, error });
62
+ }
63
+ showError("Error", getAlertMessage(error, alertMessages));
64
+ onError?.(error);
65
+ handleLifecycleComplete("error", undefined, error);
66
+ } finally {
67
+ isGeneratingRef.current = false;
68
+ }
69
+ },
70
+ );
71
+ return undefined;
72
+ }
73
+ throw createGenerationError("policy", alertMessages.policyViolation);
74
+ }
75
+
76
+ return executeGeneration(input);
77
+ }
@@ -1,22 +1,13 @@
1
1
  /**
2
2
  * Generation Orchestrator
3
- * Handles AI generation execution with:
4
- * - Network check
5
- * - Content moderation (optional)
6
- * - Credit deduction (after success)
7
- * - Error handling
8
- *
9
- * NOTE: Credit CHECK is handled by useFeatureGate before generation starts.
10
- * This orchestrator only DEDUCTS credits after successful generation.
11
- *
12
- * Progress is NOT tracked here - UI uses indeterminate progress bar with
13
- * time-based phase detection (see useGenerationPhase hook).
3
+ * Handles AI generation execution with network check, moderation, credit deduction, and error handling
14
4
  */
15
5
 
16
6
  import { useState, useCallback, useRef, useEffect } from "react";
17
7
  import { useOfflineStore, useAlert } from "@umituz/react-native-design-system";
18
8
  import { useDeductCredit } from "@umituz/react-native-subscription";
19
9
  import { createGenerationError, getAlertMessage, parseError } from "./errors";
10
+ import { handleModeration } from "./moderation-handler";
20
11
  import type {
21
12
  GenerationStrategy,
22
13
  GenerationConfig,
@@ -27,26 +18,13 @@ import type {
27
18
 
28
19
  declare const __DEV__: boolean;
29
20
 
30
- const INITIAL_STATE = {
31
- status: "idle" as const,
32
- isGenerating: false,
33
- result: null,
34
- error: null,
35
- };
21
+ const INITIAL_STATE = { status: "idle" as const, isGenerating: false, result: null, error: null };
36
22
 
37
23
  export const useGenerationOrchestrator = <TInput, TResult>(
38
24
  strategy: GenerationStrategy<TInput, TResult>,
39
25
  config: GenerationConfig,
40
26
  ): UseGenerationOrchestratorReturn<TInput, TResult> => {
41
- const {
42
- userId,
43
- alertMessages,
44
- onCreditsExhausted,
45
- onSuccess,
46
- onError,
47
- moderation,
48
- lifecycle,
49
- } = config;
27
+ const { userId, alertMessages, onCreditsExhausted, onSuccess, onError, moderation, lifecycle } = config;
50
28
 
51
29
  if (typeof __DEV__ !== "undefined" && __DEV__) {
52
30
  console.log("[Orchestrator] Hook initialized:", { userId });
@@ -62,20 +40,14 @@ export const useGenerationOrchestrator = <TInput, TResult>(
62
40
 
63
41
  useEffect(() => {
64
42
  isMountedRef.current = true;
65
- return () => {
66
- isMountedRef.current = false;
67
- };
43
+ return () => { isMountedRef.current = false; };
68
44
  }, []);
69
45
 
70
46
  const handleLifecycleComplete = useCallback(
71
47
  (status: "success" | "error", result?: TResult, error?: GenerationError) => {
72
48
  if (!lifecycle?.onComplete) return;
73
49
  const delay = lifecycle.completeDelay ?? 500;
74
- setTimeout(() => {
75
- if (isMountedRef.current) {
76
- lifecycle.onComplete?.(status, result, error);
77
- }
78
- }, delay);
50
+ setTimeout(() => { if (isMountedRef.current) lifecycle.onComplete?.(status, result, error); }, delay);
79
51
  },
80
52
  [lifecycle],
81
53
  );
@@ -83,10 +55,7 @@ export const useGenerationOrchestrator = <TInput, TResult>(
83
55
  const executeGeneration = useCallback(
84
56
  async (input: TInput) => {
85
57
  setState((prev) => ({ ...prev, status: "generating" }));
86
-
87
- if (typeof __DEV__ !== "undefined" && __DEV__) {
88
- console.log("[Orchestrator] Starting generation");
89
- }
58
+ if (typeof __DEV__ !== "undefined" && __DEV__) console.log("[Orchestrator] Starting generation");
90
59
 
91
60
  const result = await strategy.execute(input);
92
61
 
@@ -95,34 +64,19 @@ export const useGenerationOrchestrator = <TInput, TResult>(
95
64
  try {
96
65
  await strategy.save(result, userId);
97
66
  } catch (saveErr) {
98
- throw createGenerationError(
99
- "save",
100
- alertMessages.saveFailed,
101
- saveErr instanceof Error ? saveErr : undefined,
102
- );
67
+ throw createGenerationError("save", alertMessages.saveFailed, saveErr instanceof Error ? saveErr : undefined);
103
68
  }
104
69
  }
105
70
 
106
- const creditCost = strategy.getCreditCost();
107
- const creditDeducted = await deductCredit(creditCost);
108
- if (!creditDeducted) {
109
- throw createGenerationError("credits", alertMessages.creditFailed);
110
- }
71
+ const creditDeducted = await deductCredit(strategy.getCreditCost());
72
+ if (!creditDeducted) throw createGenerationError("credits", alertMessages.creditFailed);
111
73
 
112
- if (isMountedRef.current) {
113
- setState({ status: "success", isGenerating: false, result, error: null });
114
- }
115
-
116
- if (typeof __DEV__ !== "undefined" && __DEV__) {
117
- console.log("[Orchestrator] Generation SUCCESS");
118
- }
74
+ if (isMountedRef.current) setState({ status: "success", isGenerating: false, result, error: null });
75
+ if (typeof __DEV__ !== "undefined" && __DEV__) console.log("[Orchestrator] Generation SUCCESS");
119
76
 
120
- if (alertMessages.success) {
121
- showSuccess("Success", alertMessages.success);
122
- }
77
+ if (alertMessages.success) showSuccess("Success", alertMessages.success);
123
78
  onSuccess?.(result);
124
79
  handleLifecycleComplete("success", result);
125
-
126
80
  return result;
127
81
  },
128
82
  [strategy, userId, alertMessages, deductCredit, showSuccess, onSuccess, handleLifecycleComplete],
@@ -130,63 +84,32 @@ export const useGenerationOrchestrator = <TInput, TResult>(
130
84
 
131
85
  const generate = useCallback(
132
86
  async (input: TInput) => {
133
- if (typeof __DEV__ !== "undefined" && __DEV__) {
134
- console.log("[Orchestrator] generate() called");
135
- }
136
-
87
+ if (typeof __DEV__ !== "undefined" && __DEV__) console.log("[Orchestrator] generate() called");
137
88
  if (isGeneratingRef.current) return;
138
89
 
139
90
  isGeneratingRef.current = true;
140
91
  setState({ ...INITIAL_STATE, status: "checking", isGenerating: true });
141
92
 
142
93
  try {
143
- if (!offlineStore.isOnline) {
144
- throw createGenerationError("network", alertMessages.networkError);
145
- }
146
-
147
- if (moderation) {
148
- setState((prev) => ({ ...prev, status: "moderating" }));
149
- const moderationResult = await moderation.checkContent(input);
150
-
151
- if (!moderationResult.allowed && moderationResult.warnings.length > 0) {
152
- if (moderation.onShowWarning) {
153
- moderation.onShowWarning(
154
- moderationResult.warnings,
155
- () => {
156
- isGeneratingRef.current = false;
157
- if (isMountedRef.current) setState(INITIAL_STATE);
158
- },
159
- async () => {
160
- try {
161
- await executeGeneration(input);
162
- } catch (err) {
163
- const error = parseError(err);
164
- if (isMountedRef.current) {
165
- setState({ status: "error", isGenerating: false, result: null, error });
166
- }
167
- showError("Error", getAlertMessage(error, alertMessages));
168
- onError?.(error);
169
- handleLifecycleComplete("error", undefined, error);
170
- } finally {
171
- isGeneratingRef.current = false;
172
- }
173
- },
174
- );
175
- return;
176
- }
177
- throw createGenerationError("policy", alertMessages.policyViolation);
178
- }
179
- }
180
-
181
- return await executeGeneration(input);
94
+ if (!offlineStore.isOnline) throw createGenerationError("network", alertMessages.networkError);
95
+
96
+ return await handleModeration({
97
+ input,
98
+ moderation,
99
+ alertMessages,
100
+ isMountedRef,
101
+ isGeneratingRef,
102
+ setState: (s) => setState(s as GenerationState<TResult>),
103
+ resetState: () => setState(INITIAL_STATE),
104
+ executeGeneration,
105
+ showError,
106
+ onError,
107
+ handleLifecycleComplete,
108
+ });
182
109
  } catch (err) {
183
110
  const error = parseError(err);
184
- if (typeof __DEV__ !== "undefined" && __DEV__) {
185
- console.log("[Orchestrator] Error:", error);
186
- }
187
- if (isMountedRef.current) {
188
- setState({ status: "error", isGenerating: false, result: null, error });
189
- }
111
+ if (typeof __DEV__ !== "undefined" && __DEV__) console.log("[Orchestrator] Error:", error);
112
+ if (isMountedRef.current) setState({ status: "error", isGenerating: false, result: null, error });
190
113
  showError("Error", getAlertMessage(error, alertMessages));
191
114
  onError?.(error);
192
115
  handleLifecycleComplete("error", undefined, error);
@@ -195,15 +118,7 @@ export const useGenerationOrchestrator = <TInput, TResult>(
195
118
  isGeneratingRef.current = false;
196
119
  }
197
120
  },
198
- [
199
- moderation,
200
- alertMessages,
201
- offlineStore.isOnline,
202
- executeGeneration,
203
- showError,
204
- onError,
205
- handleLifecycleComplete,
206
- ],
121
+ [moderation, alertMessages, offlineStore.isOnline, executeGeneration, showError, onError, handleLifecycleComplete],
207
122
  );
208
123
 
209
124
  const reset = useCallback(() => {
@@ -211,12 +126,5 @@ export const useGenerationOrchestrator = <TInput, TResult>(
211
126
  isGeneratingRef.current = false;
212
127
  }, []);
213
128
 
214
- return {
215
- generate,
216
- reset,
217
- status: state.status,
218
- isGenerating: state.isGenerating,
219
- result: state.result,
220
- error: state.error,
221
- };
129
+ return { generate, reset, status: state.status, isGenerating: state.isGenerating, result: state.result, error: state.error };
222
130
  };