@umituz/react-native-ai-generation-content 1.6.0 → 1.8.0

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.6.0",
3
+ "version": "1.8.0",
4
4
  "description": "Provider-agnostic AI generation orchestration for React Native",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -42,7 +42,10 @@ export interface JobExecutorConfig<TInput = unknown, TResult = unknown> {
42
42
  readonly timeoutMs?: number;
43
43
  }
44
44
 
45
+ export type GenerationMode = "direct" | "queued";
46
+
45
47
  export interface BackgroundQueueConfig {
48
+ readonly mode?: GenerationMode;
46
49
  readonly maxConcurrent?: number;
47
50
  readonly retryCount?: number;
48
51
  readonly retryDelayMs?: number;
@@ -50,6 +53,7 @@ export interface BackgroundQueueConfig {
50
53
  }
51
54
 
52
55
  export const DEFAULT_QUEUE_CONFIG: Required<BackgroundQueueConfig> = {
56
+ mode: "queued",
53
57
  maxConcurrent: 1,
54
58
  retryCount: 2,
55
59
  retryDelayMs: 2000,
package/src/index.ts CHANGED
@@ -55,6 +55,7 @@ export type {
55
55
  UpdateJobInput,
56
56
  JobExecutorConfig,
57
57
  BackgroundQueueConfig,
58
+ GenerationMode,
58
59
  } from "./domain/entities";
59
60
 
60
61
  export { DEFAULT_POLLING_CONFIG, DEFAULT_PROGRESS_STAGES, DEFAULT_QUEUE_CONFIG } from "./domain/entities";
@@ -146,6 +147,7 @@ export type {
146
147
  UsePendingJobsReturn,
147
148
  UseBackgroundGenerationOptions,
148
149
  UseBackgroundGenerationReturn,
150
+ DirectExecutionResult,
149
151
  } from "./presentation/hooks";
150
152
 
151
153
  // =============================================================================
@@ -4,7 +4,13 @@
4
4
  */
5
5
 
6
6
  import React from "react";
7
- import { Modal, View, Text, StyleSheet } from "react-native";
7
+ import {
8
+ Modal,
9
+ View,
10
+ Text,
11
+ TouchableOpacity,
12
+ StyleSheet,
13
+ } from "react-native";
8
14
 
9
15
  export interface GenerationProgressModalProps {
10
16
  visible: boolean;
@@ -12,11 +18,14 @@ export interface GenerationProgressModalProps {
12
18
  title?: string;
13
19
  message?: string;
14
20
  hint?: string;
21
+ dismissLabel?: string;
22
+ onDismiss?: () => void;
15
23
  overlayColor?: string;
16
24
  modalBackgroundColor?: string;
17
25
  textColor?: string;
18
26
  progressColor?: string;
19
27
  progressBackgroundColor?: string;
28
+ dismissButtonColor?: string;
20
29
  renderContent?: (props: GenerationProgressRenderProps) => React.ReactNode;
21
30
  }
22
31
 
@@ -25,6 +34,7 @@ export interface GenerationProgressRenderProps {
25
34
  title?: string;
26
35
  message?: string;
27
36
  hint?: string;
37
+ onDismiss?: () => void;
28
38
  }
29
39
 
30
40
  export const GenerationProgressModal: React.FC<
@@ -35,11 +45,14 @@ export const GenerationProgressModal: React.FC<
35
45
  title,
36
46
  message,
37
47
  hint,
48
+ dismissLabel = "Got it",
49
+ onDismiss,
38
50
  overlayColor = "rgba(0, 0, 0, 0.7)",
39
51
  modalBackgroundColor = "#1C1C1E",
40
52
  textColor = "#FFFFFF",
41
53
  progressColor = "#007AFF",
42
54
  progressBackgroundColor = "#3A3A3C",
55
+ dismissButtonColor = "#007AFF",
43
56
  renderContent,
44
57
  }) => {
45
58
  const clampedProgress = Math.max(0, Math.min(100, progress));
@@ -53,7 +66,13 @@ export const GenerationProgressModal: React.FC<
53
66
  statusBarTranslucent
54
67
  >
55
68
  <View style={[styles.overlay, { backgroundColor: overlayColor }]}>
56
- {renderContent({ progress: clampedProgress, title, message, hint })}
69
+ {renderContent({
70
+ progress: clampedProgress,
71
+ title,
72
+ message,
73
+ hint,
74
+ onDismiss,
75
+ })}
57
76
  </View>
58
77
  </Modal>
59
78
  );
@@ -105,6 +124,18 @@ export const GenerationProgressModal: React.FC<
105
124
  {hint && (
106
125
  <Text style={[styles.hint, { color: textColor }]}>{hint}</Text>
107
126
  )}
127
+
128
+ {onDismiss && (
129
+ <TouchableOpacity
130
+ style={[
131
+ styles.dismissButton,
132
+ { backgroundColor: dismissButtonColor },
133
+ ]}
134
+ onPress={onDismiss}
135
+ >
136
+ <Text style={styles.dismissText}>{dismissLabel}</Text>
137
+ </TouchableOpacity>
138
+ )}
108
139
  </View>
109
140
  </View>
110
141
  </Modal>
@@ -162,5 +193,19 @@ const styles = StyleSheet.create({
162
193
  textAlign: "center",
163
194
  fontStyle: "italic",
164
195
  opacity: 0.6,
196
+ marginBottom: 16,
197
+ },
198
+ dismissButton: {
199
+ marginTop: 8,
200
+ paddingVertical: 14,
201
+ paddingHorizontal: 32,
202
+ borderRadius: 12,
203
+ minWidth: 140,
204
+ alignItems: "center",
205
+ },
206
+ dismissText: {
207
+ color: "#FFFFFF",
208
+ fontSize: 16,
209
+ fontWeight: "600",
165
210
  },
166
211
  });
@@ -18,4 +18,5 @@ export { useBackgroundGeneration } from "./use-background-generation";
18
18
  export type {
19
19
  UseBackgroundGenerationOptions,
20
20
  UseBackgroundGenerationReturn,
21
+ DirectExecutionResult,
21
22
  } from "./use-background-generation";
@@ -1,9 +1,11 @@
1
1
  /**
2
2
  * useBackgroundGeneration Hook
3
- * Executes AI generation tasks in the background with queue management
3
+ * Executes AI generation tasks with optional queue management
4
+ * - mode: 'direct' - Execute immediately, no queue UI (for images)
5
+ * - mode: 'queued' - Use pending jobs queue with UI (for videos)
4
6
  */
5
7
 
6
- import { useCallback, useRef } from "react";
8
+ import { useCallback, useRef, useState } from "react";
7
9
  import { usePendingJobs } from "./use-pending-jobs";
8
10
  import type {
9
11
  BackgroundJob,
@@ -18,15 +20,27 @@ export interface UseBackgroundGenerationOptions<TInput, TResult>
18
20
  readonly onJobComplete?: (job: BackgroundJob<TInput, TResult>) => void;
19
21
  readonly onJobError?: (job: BackgroundJob<TInput, TResult>) => void;
20
22
  readonly onAllComplete?: () => void;
23
+ readonly onProgress?: (progress: number) => void;
24
+ }
25
+
26
+ export interface DirectExecutionResult<TResult> {
27
+ readonly success: boolean;
28
+ readonly result?: TResult;
29
+ readonly error?: string;
21
30
  }
22
31
 
23
32
  export interface UseBackgroundGenerationReturn<TInput, TResult> {
24
33
  readonly startJob: (input: TInput, type: string) => Promise<string>;
34
+ readonly executeDirectly: (
35
+ input: TInput,
36
+ ) => Promise<DirectExecutionResult<TResult>>;
25
37
  readonly cancelJob: (id: string) => void;
26
38
  readonly retryJob: (id: string) => void;
27
39
  readonly pendingJobs: BackgroundJob<TInput, TResult>[];
28
40
  readonly activeJobCount: number;
29
41
  readonly hasActiveJobs: boolean;
42
+ readonly isProcessing: boolean;
43
+ readonly progress: number;
30
44
  }
31
45
 
32
46
  export function useBackgroundGeneration<TInput = unknown, TResult = unknown>(
@@ -38,16 +52,41 @@ export function useBackgroundGeneration<TInput = unknown, TResult = unknown>(
38
52
  new Map(),
39
53
  );
40
54
 
41
- const {
42
- jobs,
43
- addJobAsync,
44
- updateJob,
45
- removeJob,
46
- getJob,
47
- } = usePendingJobs<TInput, TResult>({
55
+ const [isProcessing, setIsProcessing] = useState(false);
56
+ const [progress, setProgress] = useState(0);
57
+
58
+ const { jobs, addJobAsync, updateJob, removeJob, getJob } = usePendingJobs<
59
+ TInput,
60
+ TResult
61
+ >({
48
62
  queryKey: config.queryKey,
49
63
  });
50
64
 
65
+ const executeDirectly = useCallback(
66
+ async (input: TInput): Promise<DirectExecutionResult<TResult>> => {
67
+ const { executor } = options;
68
+
69
+ setIsProcessing(true);
70
+ setProgress(0);
71
+
72
+ try {
73
+ const result = await executor.execute(input, (p) => {
74
+ setProgress(p);
75
+ options.onProgress?.(p);
76
+ });
77
+
78
+ setProgress(100);
79
+ return { success: true, result };
80
+ } catch (error) {
81
+ const errorMsg = error instanceof Error ? error.message : String(error);
82
+ return { success: false, error: errorMsg };
83
+ } finally {
84
+ setIsProcessing(false);
85
+ }
86
+ },
87
+ [options],
88
+ );
89
+
51
90
  const executeJob = useCallback(
52
91
  async (jobId: string, input: TInput) => {
53
92
  const { executor } = options;
@@ -58,8 +97,8 @@ export function useBackgroundGeneration<TInput = unknown, TResult = unknown>(
58
97
  updates: { status: "processing", progress: 10 },
59
98
  });
60
99
 
61
- const result = await executor.execute(input, (progress) => {
62
- updateJob({ id: jobId, updates: { progress } });
100
+ const result = await executor.execute(input, (p) => {
101
+ updateJob({ id: jobId, updates: { progress: p } });
63
102
  });
64
103
 
65
104
  updateJob({
@@ -84,11 +123,7 @@ export function useBackgroundGeneration<TInput = unknown, TResult = unknown>(
84
123
 
85
124
  updateJob({
86
125
  id: jobId,
87
- updates: {
88
- status: "failed",
89
- error: errorMsg,
90
- progress: 0,
91
- },
126
+ updates: { status: "failed", error: errorMsg, progress: 0 },
92
127
  });
93
128
 
94
129
  const failedJob = getJob(jobId);
@@ -101,7 +136,6 @@ export function useBackgroundGeneration<TInput = unknown, TResult = unknown>(
101
136
  }
102
137
  } finally {
103
138
  activeJobsRef.current.delete(jobId);
104
-
105
139
  if (activeJobsRef.current.size === 0) {
106
140
  options.onAllComplete?.();
107
141
  }
@@ -125,7 +159,6 @@ export function useBackgroundGeneration<TInput = unknown, TResult = unknown>(
125
159
  });
126
160
 
127
161
  activeJobsRef.current.add(jobId);
128
-
129
162
  executeJob(jobId, input);
130
163
 
131
164
  return jobId;
@@ -146,7 +179,6 @@ export function useBackgroundGeneration<TInput = unknown, TResult = unknown>(
146
179
  (id: string) => {
147
180
  const jobData = jobInputsRef.current.get(id);
148
181
  if (!jobData) return;
149
-
150
182
  removeJob(id);
151
183
  startJob(jobData.input, jobData.type);
152
184
  },
@@ -155,10 +187,13 @@ export function useBackgroundGeneration<TInput = unknown, TResult = unknown>(
155
187
 
156
188
  return {
157
189
  startJob,
190
+ executeDirectly,
158
191
  cancelJob,
159
192
  retryJob,
160
193
  pendingJobs: jobs,
161
194
  activeJobCount: activeJobsRef.current.size,
162
195
  hasActiveJobs: activeJobsRef.current.size > 0,
196
+ isProcessing,
197
+ progress,
163
198
  };
164
199
  }