@umituz/react-native-ai-generation-content 1.8.0 → 1.8.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-ai-generation-content",
3
- "version": "1.8.0",
3
+ "version": "1.8.1",
4
4
  "description": "Provider-agnostic AI generation orchestration for React Native",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -31,17 +31,20 @@
31
31
  },
32
32
  "peerDependencies": {
33
33
  "@tanstack/react-query": ">=5.0.0",
34
+ "@umituz/react-native-design-system-theme": "latest",
34
35
  "react": ">=18.0.0",
35
36
  "react-native": ">=0.74.0"
36
37
  },
37
38
  "devDependencies": {
38
39
  "@tanstack/react-query": "^5.0.0",
39
- "@types/react": "^19.0.0",
40
+ "@umituz/react-native-design-system-theme": "latest",
41
+ "@types/react": "~19.1.10",
40
42
  "@types/react-native": "^0.73.0",
41
43
  "@typescript-eslint/eslint-plugin": "^7.0.0",
42
44
  "@typescript-eslint/parser": "^7.0.0",
43
45
  "eslint": "^8.57.0",
44
- "react-native": "^0.76.0",
46
+ "react": "19.1.0",
47
+ "react-native": "0.81.5",
45
48
  "typescript": "^5.3.0"
46
49
  },
47
50
  "publishConfig": {
package/src/index.ts CHANGED
@@ -156,14 +156,20 @@ export type {
156
156
 
157
157
  export {
158
158
  GenerationProgressModal,
159
+ GenerationProgressContent,
160
+ GenerationProgressBar,
159
161
  PendingJobCard,
162
+ PendingJobProgressBar,
163
+ PendingJobCardActions,
160
164
  } from "./presentation/components";
161
165
 
162
166
  export type {
163
167
  GenerationProgressModalProps,
164
168
  GenerationProgressRenderProps,
169
+ GenerationProgressContentProps,
170
+ GenerationProgressBarProps,
165
171
  PendingJobCardProps,
166
- PendingJobCardStyles,
167
- PendingJobCardColors,
168
172
  StatusLabels,
173
+ PendingJobProgressBarProps,
174
+ PendingJobCardActionsProps,
169
175
  } from "./presentation/components";
@@ -0,0 +1,77 @@
1
+ /**
2
+ * GenerationProgressBar
3
+ * Individual progress bar component for AI generation
4
+ */
5
+
6
+ import React from "react";
7
+ import { View, StyleSheet, Text } from "react-native";
8
+ import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
9
+
10
+ export interface GenerationProgressBarProps {
11
+ progress: number;
12
+ textColor?: string;
13
+ progressColor?: string;
14
+ backgroundColor?: string;
15
+ }
16
+
17
+ export const GenerationProgressBar: React.FC<GenerationProgressBarProps> = ({
18
+ progress,
19
+ textColor,
20
+ progressColor,
21
+ backgroundColor,
22
+ }) => {
23
+ const tokens = useAppDesignTokens();
24
+ const clampedProgress = Math.max(0, Math.min(100, progress));
25
+
26
+ return (
27
+ <View style={styles.container}>
28
+ <View
29
+ style={[
30
+ styles.background,
31
+ { backgroundColor: backgroundColor || tokens.colors.borderLight },
32
+ ]}
33
+ >
34
+ <View
35
+ style={[
36
+ styles.fill,
37
+ {
38
+ backgroundColor: progressColor || tokens.colors.primary,
39
+ width: `${clampedProgress}%`,
40
+ },
41
+ ]}
42
+ />
43
+ </View>
44
+ <Text
45
+ style={[
46
+ styles.text,
47
+ { color: textColor || tokens.colors.textPrimary },
48
+ ]}
49
+ >
50
+ {Math.round(clampedProgress)}%
51
+ </Text>
52
+ </View>
53
+ );
54
+ };
55
+
56
+ const styles = StyleSheet.create({
57
+ container: {
58
+ width: "100%",
59
+ marginBottom: 16,
60
+ alignItems: "center",
61
+ },
62
+ background: {
63
+ width: "100%",
64
+ height: 8,
65
+ borderRadius: 4,
66
+ overflow: "hidden",
67
+ },
68
+ fill: {
69
+ height: "100%",
70
+ borderRadius: 4,
71
+ },
72
+ text: {
73
+ fontSize: 14,
74
+ fontWeight: "600",
75
+ marginTop: 8,
76
+ },
77
+ });
@@ -0,0 +1,123 @@
1
+ /**
2
+ * GenerationProgressContent
3
+ * Content for the AI generation progress modal
4
+ */
5
+
6
+ import React from "react";
7
+ import { View, Text, TouchableOpacity, StyleSheet } from "react-native";
8
+ import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
9
+ import { GenerationProgressBar } from "./GenerationProgressBar";
10
+
11
+ export interface GenerationProgressContentProps {
12
+ progress: number;
13
+ title?: string;
14
+ message?: string;
15
+ hint?: string;
16
+ dismissLabel?: string;
17
+ onDismiss?: () => void;
18
+ backgroundColor?: string;
19
+ textColor?: string;
20
+ progressColor?: string;
21
+ progressBackgroundColor?: string;
22
+ dismissButtonColor?: string;
23
+ }
24
+
25
+ export const GenerationProgressContent: React.FC<
26
+ GenerationProgressContentProps
27
+ > = ({
28
+ progress,
29
+ title,
30
+ message,
31
+ hint,
32
+ dismissLabel = "Got it",
33
+ onDismiss,
34
+ backgroundColor,
35
+ textColor,
36
+ progressColor,
37
+ progressBackgroundColor,
38
+ dismissButtonColor,
39
+ }) => {
40
+ const tokens = useAppDesignTokens();
41
+
42
+ const activeTextColor = textColor || tokens.colors.textPrimary;
43
+ const activeBgColor = backgroundColor || tokens.colors.surface;
44
+
45
+ return (
46
+ <View style={[styles.modal, { backgroundColor: activeBgColor }]}>
47
+ {title && (
48
+ <Text style={[styles.title, { color: activeTextColor }]}>{title}</Text>
49
+ )}
50
+
51
+ {message && (
52
+ <Text style={[styles.message, { color: activeTextColor }]}>
53
+ {message}
54
+ </Text>
55
+ )}
56
+
57
+ <GenerationProgressBar
58
+ progress={progress}
59
+ textColor={activeTextColor}
60
+ progressColor={progressColor}
61
+ backgroundColor={progressBackgroundColor}
62
+ />
63
+
64
+ {hint && (
65
+ <Text style={[styles.hint, { color: activeTextColor }]}>{hint}</Text>
66
+ )}
67
+
68
+ {onDismiss && (
69
+ <TouchableOpacity
70
+ style={[
71
+ styles.dismissButton,
72
+ { backgroundColor: dismissButtonColor || tokens.colors.primary },
73
+ ]}
74
+ onPress={onDismiss}
75
+ >
76
+ <Text style={styles.dismissText}>{dismissLabel}</Text>
77
+ </TouchableOpacity>
78
+ )}
79
+ </View>
80
+ );
81
+ };
82
+
83
+ const styles = StyleSheet.create({
84
+ modal: {
85
+ width: "100%",
86
+ maxWidth: 400,
87
+ borderRadius: 24,
88
+ padding: 32,
89
+ alignItems: "center",
90
+ },
91
+ title: {
92
+ fontSize: 20,
93
+ fontWeight: "700",
94
+ marginBottom: 12,
95
+ textAlign: "center",
96
+ },
97
+ message: {
98
+ fontSize: 16,
99
+ marginBottom: 24,
100
+ textAlign: "center",
101
+ opacity: 0.8,
102
+ },
103
+ hint: {
104
+ fontSize: 14,
105
+ textAlign: "center",
106
+ fontStyle: "italic",
107
+ opacity: 0.6,
108
+ marginBottom: 16,
109
+ },
110
+ dismissButton: {
111
+ marginTop: 8,
112
+ paddingVertical: 14,
113
+ paddingHorizontal: 32,
114
+ borderRadius: 12,
115
+ minWidth: 140,
116
+ alignItems: "center",
117
+ },
118
+ dismissText: {
119
+ color: "#FFFFFF",
120
+ fontSize: 16,
121
+ fontWeight: "600",
122
+ },
123
+ });
@@ -4,39 +4,29 @@
4
4
  */
5
5
 
6
6
  import React from "react";
7
+ import { Modal, View, StyleSheet } from "react-native";
8
+ import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
7
9
  import {
8
- Modal,
9
- View,
10
- Text,
11
- TouchableOpacity,
12
- StyleSheet,
13
- } from "react-native";
10
+ GenerationProgressContent,
11
+ GenerationProgressContentProps,
12
+ } from "./GenerationProgressContent";
14
13
 
15
- export interface GenerationProgressModalProps {
16
- visible: boolean;
14
+ export interface GenerationProgressRenderProps {
17
15
  progress: number;
18
16
  title?: string;
19
17
  message?: string;
20
18
  hint?: string;
21
- dismissLabel?: string;
22
19
  onDismiss?: () => void;
20
+ }
21
+
22
+ export interface GenerationProgressModalProps
23
+ extends Omit<GenerationProgressContentProps, "backgroundColor"> {
24
+ visible: boolean;
23
25
  overlayColor?: string;
24
26
  modalBackgroundColor?: string;
25
- textColor?: string;
26
- progressColor?: string;
27
- progressBackgroundColor?: string;
28
- dismissButtonColor?: string;
29
27
  renderContent?: (props: GenerationProgressRenderProps) => React.ReactNode;
30
28
  }
31
29
 
32
- export interface GenerationProgressRenderProps {
33
- progress: number;
34
- title?: string;
35
- message?: string;
36
- hint?: string;
37
- onDismiss?: () => void;
38
- }
39
-
40
30
  export const GenerationProgressModal: React.FC<
41
31
  GenerationProgressModalProps
42
32
  > = ({
@@ -45,19 +35,45 @@ export const GenerationProgressModal: React.FC<
45
35
  title,
46
36
  message,
47
37
  hint,
48
- dismissLabel = "Got it",
38
+ dismissLabel,
49
39
  onDismiss,
50
40
  overlayColor = "rgba(0, 0, 0, 0.7)",
51
- modalBackgroundColor = "#1C1C1E",
52
- textColor = "#FFFFFF",
53
- progressColor = "#007AFF",
54
- progressBackgroundColor = "#3A3A3C",
55
- dismissButtonColor = "#007AFF",
41
+ modalBackgroundColor,
42
+ textColor,
43
+ progressColor,
44
+ progressBackgroundColor,
45
+ dismissButtonColor,
56
46
  renderContent,
57
47
  }) => {
58
- const clampedProgress = Math.max(0, Math.min(100, progress));
48
+ const tokens = useAppDesignTokens();
49
+ const clampedProgress = Math.max(0, Math.min(100, progress));
50
+
51
+ const content = renderContent ? (
52
+ renderContent({
53
+ progress: clampedProgress,
54
+ title,
55
+ message,
56
+ hint,
57
+ onDismiss,
58
+ })
59
+ ) : (
60
+ <GenerationProgressContent
61
+ progress={clampedProgress}
62
+ title={title}
63
+ message={message}
64
+ hint={hint}
65
+ dismissLabel={dismissLabel}
66
+ onDismiss={onDismiss}
67
+ backgroundColor={modalBackgroundColor || tokens.colors.surface}
68
+ textColor={textColor || tokens.colors.textPrimary}
69
+ progressColor={progressColor || tokens.colors.primary}
70
+ progressBackgroundColor={
71
+ progressBackgroundColor || tokens.colors.borderLight
72
+ }
73
+ dismissButtonColor={dismissButtonColor || tokens.colors.primary}
74
+ />
75
+ );
59
76
 
60
- if (renderContent) {
61
77
  return (
62
78
  <Modal
63
79
  visible={visible}
@@ -66,81 +82,11 @@ export const GenerationProgressModal: React.FC<
66
82
  statusBarTranslucent
67
83
  >
68
84
  <View style={[styles.overlay, { backgroundColor: overlayColor }]}>
69
- {renderContent({
70
- progress: clampedProgress,
71
- title,
72
- message,
73
- hint,
74
- onDismiss,
75
- })}
85
+ {content}
76
86
  </View>
77
87
  </Modal>
78
88
  );
79
- }
80
-
81
- return (
82
- <Modal
83
- visible={visible}
84
- transparent
85
- animationType="fade"
86
- statusBarTranslucent
87
- >
88
- <View style={[styles.overlay, { backgroundColor: overlayColor }]}>
89
- <View
90
- style={[styles.modal, { backgroundColor: modalBackgroundColor }]}
91
- >
92
- {title && (
93
- <Text style={[styles.title, { color: textColor }]}>{title}</Text>
94
- )}
95
-
96
- {message && (
97
- <Text style={[styles.message, { color: textColor }]}>
98
- {message}
99
- </Text>
100
- )}
101
-
102
- <View style={styles.progressContainer}>
103
- <View
104
- style={[
105
- styles.progressBackground,
106
- { backgroundColor: progressBackgroundColor },
107
- ]}
108
- >
109
- <View
110
- style={[
111
- styles.progressFill,
112
- {
113
- backgroundColor: progressColor,
114
- width: `${clampedProgress}%`,
115
- },
116
- ]}
117
- />
118
- </View>
119
- <Text style={[styles.progressText, { color: textColor }]}>
120
- {Math.round(clampedProgress)}%
121
- </Text>
122
- </View>
123
-
124
- {hint && (
125
- <Text style={[styles.hint, { color: textColor }]}>{hint}</Text>
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
- )}
139
- </View>
140
- </View>
141
- </Modal>
142
- );
143
- };
89
+ };
144
90
 
145
91
  const styles = StyleSheet.create({
146
92
  overlay: {
@@ -149,63 +95,4 @@ const styles = StyleSheet.create({
149
95
  alignItems: "center",
150
96
  padding: 20,
151
97
  },
152
- modal: {
153
- width: "100%",
154
- maxWidth: 400,
155
- borderRadius: 24,
156
- padding: 32,
157
- alignItems: "center",
158
- },
159
- title: {
160
- fontSize: 20,
161
- fontWeight: "700",
162
- marginBottom: 12,
163
- textAlign: "center",
164
- },
165
- message: {
166
- fontSize: 16,
167
- marginBottom: 24,
168
- textAlign: "center",
169
- opacity: 0.8,
170
- },
171
- progressContainer: {
172
- width: "100%",
173
- marginBottom: 16,
174
- alignItems: "center",
175
- },
176
- progressBackground: {
177
- width: "100%",
178
- height: 8,
179
- borderRadius: 4,
180
- overflow: "hidden",
181
- },
182
- progressFill: {
183
- height: "100%",
184
- borderRadius: 4,
185
- },
186
- progressText: {
187
- fontSize: 14,
188
- fontWeight: "600",
189
- marginTop: 8,
190
- },
191
- hint: {
192
- fontSize: 14,
193
- textAlign: "center",
194
- fontStyle: "italic",
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",
210
- },
211
98
  });
@@ -4,36 +4,11 @@
4
4
  */
5
5
 
6
6
  import React from "react";
7
- import {
8
- View,
9
- Text,
10
- TouchableOpacity,
11
- ActivityIndicator,
12
- StyleSheet,
13
- } from "react-native";
7
+ import { View, Text, ActivityIndicator, StyleSheet } from "react-native";
8
+ import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
14
9
  import type { BackgroundJob } from "../../domain/entities/job.types";
15
-
16
- export interface PendingJobCardStyles {
17
- readonly card?: object;
18
- readonly content?: object;
19
- readonly header?: object;
20
- readonly typeText?: object;
21
- readonly statusText?: object;
22
- readonly progressBar?: object;
23
- readonly progressFill?: object;
24
- readonly actions?: object;
25
- readonly actionButton?: object;
26
- }
27
-
28
- export interface PendingJobCardColors {
29
- readonly background?: string;
30
- readonly text?: string;
31
- readonly textSecondary?: string;
32
- readonly error?: string;
33
- readonly progressBackground?: string;
34
- readonly progressFill?: string;
35
- readonly actionBackground?: string;
36
- }
10
+ import { PendingJobProgressBar } from "./PendingJobProgressBar";
11
+ import { PendingJobCardActions } from "./PendingJobCardActions";
37
12
 
38
13
  export interface StatusLabels {
39
14
  readonly queued?: string;
@@ -49,10 +24,12 @@ export interface PendingJobCardProps<TInput = unknown, TResult = unknown> {
49
24
  readonly onRetry?: (id: string) => void;
50
25
  readonly typeLabel?: string;
51
26
  readonly statusLabels?: StatusLabels;
52
- readonly colors?: PendingJobCardColors;
53
- readonly styles?: PendingJobCardStyles;
54
- readonly renderThumbnail?: (job: BackgroundJob<TInput, TResult>) => React.ReactNode;
55
- readonly renderActions?: (job: BackgroundJob<TInput, TResult>) => React.ReactNode;
27
+ readonly renderThumbnail?: (
28
+ job: BackgroundJob<TInput, TResult>,
29
+ ) => React.ReactNode;
30
+ readonly renderActions?: (
31
+ job: BackgroundJob<TInput, TResult>,
32
+ ) => React.ReactNode;
56
33
  }
57
34
 
58
35
  const DEFAULT_STATUS_LABELS: StatusLabels = {
@@ -63,28 +40,16 @@ const DEFAULT_STATUS_LABELS: StatusLabels = {
63
40
  failed: "Failed",
64
41
  };
65
42
 
66
- const DEFAULT_COLORS: Required<PendingJobCardColors> = {
67
- background: "#1C1C1E",
68
- text: "#FFFFFF",
69
- textSecondary: "#8E8E93",
70
- error: "#FF453A",
71
- progressBackground: "#3A3A3C",
72
- progressFill: "#007AFF",
73
- actionBackground: "#2C2C2E",
74
- };
75
-
76
43
  export function PendingJobCard<TInput = unknown, TResult = unknown>({
77
44
  job,
78
45
  onCancel,
79
46
  onRetry,
80
47
  typeLabel,
81
48
  statusLabels = DEFAULT_STATUS_LABELS,
82
- colors = {},
83
- styles: customStyles = {},
84
49
  renderThumbnail,
85
50
  renderActions,
86
51
  }: PendingJobCardProps<TInput, TResult>): React.ReactElement {
87
- const mergedColors = { ...DEFAULT_COLORS, ...colors };
52
+ const tokens = useAppDesignTokens();
88
53
  const isFailed = job.status === "failed";
89
54
 
90
55
  const statusText =
@@ -95,86 +60,44 @@ export function PendingJobCard<TInput = unknown, TResult = unknown>({
95
60
  const styles = StyleSheet.create({
96
61
  card: {
97
62
  flexDirection: "row",
98
- backgroundColor: mergedColors.background,
63
+ backgroundColor: tokens.colors.surface,
99
64
  borderRadius: 16,
100
65
  overflow: "hidden",
101
66
  opacity: isFailed ? 0.7 : 1,
102
- ...((customStyles.card as object) || {}),
67
+ },
68
+ thumbnailWrapper: {
69
+ width: 80,
70
+ height: 80,
71
+ justifyContent: "center",
72
+ alignItems: "center",
73
+ backgroundColor: tokens.colors.backgroundSecondary,
103
74
  },
104
75
  content: {
105
76
  flex: 1,
106
77
  padding: 12,
107
78
  justifyContent: "space-between",
108
- ...((customStyles.content as object) || {}),
109
- },
110
- header: {
111
- flexDirection: "row",
112
- alignItems: "center",
113
- gap: 8,
114
- ...((customStyles.header as object) || {}),
115
79
  },
116
80
  typeText: {
117
81
  fontSize: 14,
118
82
  fontWeight: "600",
119
- color: mergedColors.text,
120
- ...((customStyles.typeText as object) || {}),
83
+ color: tokens.colors.textPrimary,
121
84
  },
122
85
  statusText: {
123
86
  fontSize: 12,
124
- color: isFailed ? mergedColors.error : mergedColors.textSecondary,
87
+ color: isFailed ? tokens.colors.error : tokens.colors.textSecondary,
125
88
  marginTop: 4,
126
- ...((customStyles.statusText as object) || {}),
127
- },
128
- progressBar: {
129
- height: 4,
130
- backgroundColor: mergedColors.progressBackground,
131
- borderRadius: 2,
132
- marginTop: 8,
133
- overflow: "hidden",
134
- ...((customStyles.progressBar as object) || {}),
135
- },
136
- progressFill: {
137
- height: "100%",
138
- backgroundColor: mergedColors.progressFill,
139
- borderRadius: 2,
140
- width: `${job.progress}%`,
141
- ...((customStyles.progressFill as object) || {}),
142
- },
143
- actions: {
144
- flexDirection: "row",
145
- gap: 8,
146
- marginTop: 8,
147
- ...((customStyles.actions as object) || {}),
148
- },
149
- actionButton: {
150
- width: 32,
151
- height: 32,
152
- borderRadius: 16,
153
- backgroundColor: mergedColors.actionBackground,
154
- justifyContent: "center",
155
- alignItems: "center",
156
- ...((customStyles.actionButton as object) || {}),
157
- },
158
- thumbnail: {
159
- width: 80,
160
- height: 80,
161
- justifyContent: "center",
162
- alignItems: "center",
163
- backgroundColor: mergedColors.progressBackground,
164
- },
165
- loader: {
166
- position: "absolute",
167
89
  },
90
+ loader: { position: "absolute" },
168
91
  });
169
92
 
170
93
  return (
171
94
  <View style={styles.card}>
172
95
  {renderThumbnail && (
173
- <View style={styles.thumbnail}>
96
+ <View style={styles.thumbnailWrapper}>
174
97
  {renderThumbnail(job)}
175
98
  {!isFailed && (
176
99
  <ActivityIndicator
177
- color={mergedColors.text}
100
+ color={tokens.colors.primary}
178
101
  size="small"
179
102
  style={styles.loader}
180
103
  />
@@ -183,39 +106,21 @@ export function PendingJobCard<TInput = unknown, TResult = unknown>({
183
106
  )}
184
107
  <View style={styles.content}>
185
108
  <View>
186
- <View style={styles.header}>
187
- {typeLabel && <Text style={styles.typeText}>{typeLabel}</Text>}
188
- </View>
109
+ {typeLabel && <Text style={styles.typeText}>{typeLabel}</Text>}
189
110
  <Text style={styles.statusText} numberOfLines={1}>
190
111
  {statusText}
191
112
  </Text>
192
- {!isFailed && (
193
- <View style={styles.progressBar}>
194
- <View style={styles.progressFill} />
195
- </View>
196
- )}
113
+ {!isFailed && <PendingJobProgressBar progress={job.progress} />}
197
114
  </View>
198
115
  {renderActions ? (
199
116
  renderActions(job)
200
117
  ) : (
201
- <View style={styles.actions}>
202
- {isFailed && onRetry && (
203
- <TouchableOpacity
204
- style={styles.actionButton}
205
- onPress={() => onRetry(job.id)}
206
- >
207
- <Text style={{ color: mergedColors.text }}>↻</Text>
208
- </TouchableOpacity>
209
- )}
210
- {onCancel && (
211
- <TouchableOpacity
212
- style={styles.actionButton}
213
- onPress={() => onCancel(job.id)}
214
- >
215
- <Text style={{ color: mergedColors.error }}>✕</Text>
216
- </TouchableOpacity>
217
- )}
218
- </View>
118
+ <PendingJobCardActions
119
+ id={job.id}
120
+ isFailed={isFailed}
121
+ onCancel={onCancel}
122
+ onRetry={onRetry}
123
+ />
219
124
  )}
220
125
  </View>
221
126
  </View>
@@ -0,0 +1,73 @@
1
+ /**
2
+ * PendingJobCardActions
3
+ * Action buttons for the PendingJobCard
4
+ */
5
+
6
+ import React from "react";
7
+ import { View, TouchableOpacity, Text, StyleSheet } from "react-native";
8
+ import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
9
+
10
+ export interface PendingJobCardActionsProps {
11
+ id: string;
12
+ isFailed: boolean;
13
+ onCancel?: (id: string) => void;
14
+ onRetry?: (id: string) => void;
15
+ textColor?: string;
16
+ errorColor?: string;
17
+ backgroundColor?: string;
18
+ }
19
+
20
+ export const PendingJobCardActions: React.FC<PendingJobCardActionsProps> = ({
21
+ id,
22
+ isFailed,
23
+ onCancel,
24
+ onRetry,
25
+ textColor,
26
+ errorColor,
27
+ backgroundColor,
28
+ }) => {
29
+ const tokens = useAppDesignTokens();
30
+
31
+ const styles = StyleSheet.create({
32
+ actions: {
33
+ flexDirection: "row",
34
+ gap: 8,
35
+ marginTop: 8,
36
+ },
37
+ actionButton: {
38
+ width: 32,
39
+ height: 32,
40
+ borderRadius: 16,
41
+ backgroundColor: backgroundColor || tokens.colors.backgroundSecondary,
42
+ justifyContent: "center",
43
+ alignItems: "center",
44
+ },
45
+ text: {
46
+ color: textColor || tokens.colors.textPrimary,
47
+ },
48
+ errorText: {
49
+ color: errorColor || tokens.colors.error,
50
+ },
51
+ });
52
+
53
+ return (
54
+ <View style={styles.actions}>
55
+ {isFailed && onRetry && (
56
+ <TouchableOpacity
57
+ style={styles.actionButton}
58
+ onPress={() => onRetry(id)}
59
+ >
60
+ <Text style={styles.text}>↻</Text>
61
+ </TouchableOpacity>
62
+ )}
63
+ {onCancel && (
64
+ <TouchableOpacity
65
+ style={styles.actionButton}
66
+ onPress={() => onCancel(id)}
67
+ >
68
+ <Text style={styles.errorText}>✕</Text>
69
+ </TouchableOpacity>
70
+ )}
71
+ </View>
72
+ );
73
+ };
@@ -0,0 +1,54 @@
1
+ /**
2
+ * PendingJobProgressBar
3
+ * Individual progress bar for the PendingJobCard
4
+ */
5
+
6
+ import React from "react";
7
+ import { View, StyleSheet } from "react-native";
8
+ import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
9
+
10
+ export interface PendingJobProgressBarProps {
11
+ progress: number;
12
+ backgroundColor?: string;
13
+ fillColor?: string;
14
+ }
15
+
16
+ export const PendingJobProgressBar: React.FC<PendingJobProgressBarProps> = ({
17
+ progress,
18
+ backgroundColor,
19
+ fillColor,
20
+ }) => {
21
+ const tokens = useAppDesignTokens();
22
+
23
+ return (
24
+ <View
25
+ style={[
26
+ styles.progressContainer,
27
+ { backgroundColor: backgroundColor || tokens.colors.borderLight },
28
+ ]}
29
+ >
30
+ <View
31
+ style={[
32
+ styles.progressFill,
33
+ {
34
+ backgroundColor: fillColor || tokens.colors.primary,
35
+ width: `${Math.max(0, Math.min(100, progress))}%`,
36
+ },
37
+ ]}
38
+ />
39
+ </View>
40
+ );
41
+ };
42
+
43
+ const styles = StyleSheet.create({
44
+ progressContainer: {
45
+ height: 4,
46
+ borderRadius: 2,
47
+ marginTop: 8,
48
+ overflow: "hidden",
49
+ },
50
+ progressFill: {
51
+ height: "100%",
52
+ borderRadius: 2,
53
+ },
54
+ });
@@ -1,21 +1,22 @@
1
- /**
2
- * Presentation Components
3
- */
4
-
5
- export {
6
- GenerationProgressModal,
7
- } from "./GenerationProgressModal";
1
+ export { GenerationProgressModal } from "./GenerationProgressModal";
2
+ export { GenerationProgressContent } from "./GenerationProgressContent";
3
+ export { GenerationProgressBar } from "./GenerationProgressBar";
4
+ export { PendingJobCard } from "./PendingJobCard";
5
+ export { PendingJobProgressBar } from "./PendingJobProgressBar";
6
+ export { PendingJobCardActions } from "./PendingJobCardActions";
8
7
 
9
8
  export type {
10
9
  GenerationProgressModalProps,
11
10
  GenerationProgressRenderProps,
12
11
  } from "./GenerationProgressModal";
13
12
 
14
- export { PendingJobCard } from "./PendingJobCard";
13
+ export type { GenerationProgressContentProps } from "./GenerationProgressContent";
14
+ export type { GenerationProgressBarProps } from "./GenerationProgressBar";
15
15
 
16
16
  export type {
17
17
  PendingJobCardProps,
18
- PendingJobCardStyles,
19
- PendingJobCardColors,
20
18
  StatusLabels,
21
19
  } from "./PendingJobCard";
20
+
21
+ export type { PendingJobProgressBarProps } from "./PendingJobProgressBar";
22
+ export type { PendingJobCardActionsProps } from "./PendingJobCardActions";
@@ -159,7 +159,7 @@ export function useBackgroundGeneration<TInput = unknown, TResult = unknown>(
159
159
  });
160
160
 
161
161
  activeJobsRef.current.add(jobId);
162
- executeJob(jobId, input);
162
+ void executeJob(jobId, input);
163
163
 
164
164
  return jobId;
165
165
  },
@@ -180,7 +180,7 @@ export function useBackgroundGeneration<TInput = unknown, TResult = unknown>(
180
180
  const jobData = jobInputsRef.current.get(id);
181
181
  if (!jobData) return;
182
182
  removeJob(id);
183
- startJob(jobData.input, jobData.type);
183
+ void startJob(jobData.input, jobData.type);
184
184
  },
185
185
  [removeJob, startJob],
186
186
  );
@@ -43,7 +43,7 @@ export function usePendingJobs<TInput = unknown, TResult = unknown>(
43
43
  });
44
44
 
45
45
  const addJobMutation = useMutation({
46
- mutationFn: async (input: AddJobInput<TInput>) => {
46
+ mutationFn: (input: AddJobInput<TInput>) => {
47
47
  const newJob: BackgroundJob<TInput, TResult> = {
48
48
  id: input.id,
49
49
  input: input.input,
@@ -52,7 +52,7 @@ export function usePendingJobs<TInput = unknown, TResult = unknown>(
52
52
  progress: input.progress ?? 0,
53
53
  createdAt: new Date(),
54
54
  };
55
- return newJob;
55
+ return Promise.resolve(newJob);
56
56
  },
57
57
  onSuccess: (newJob) => {
58
58
  queryClient.setQueryData<BackgroundJob<TInput, TResult>[]>(
@@ -63,7 +63,8 @@ export function usePendingJobs<TInput = unknown, TResult = unknown>(
63
63
  });
64
64
 
65
65
  const updateJobMutation = useMutation({
66
- mutationFn: async ({ id, updates }: UpdateJobInput) => ({ id, updates }),
66
+ mutationFn: ({ id, updates }: UpdateJobInput) =>
67
+ Promise.resolve({ id, updates }),
67
68
  onSuccess: ({ id, updates }) => {
68
69
  queryClient.setQueryData<BackgroundJob<TInput, TResult>[]>(
69
70
  queryKey,
@@ -78,7 +79,7 @@ export function usePendingJobs<TInput = unknown, TResult = unknown>(
78
79
  });
79
80
 
80
81
  const removeJobMutation = useMutation({
81
- mutationFn: async (id: string) => id,
82
+ mutationFn: (id: string) => Promise.resolve(id),
82
83
  onSuccess: (id) => {
83
84
  queryClient.setQueryData<BackgroundJob<TInput, TResult>[]>(
84
85
  queryKey,
@@ -88,7 +89,7 @@ export function usePendingJobs<TInput = unknown, TResult = unknown>(
88
89
  });
89
90
 
90
91
  const clearCompletedMutation = useMutation({
91
- mutationFn: async () => null,
92
+ mutationFn: () => Promise.resolve(null),
92
93
  onSuccess: () => {
93
94
  queryClient.setQueryData<BackgroundJob<TInput, TResult>[]>(
94
95
  queryKey,
@@ -98,7 +99,7 @@ export function usePendingJobs<TInput = unknown, TResult = unknown>(
98
99
  });
99
100
 
100
101
  const clearFailedMutation = useMutation({
101
- mutationFn: async () => null,
102
+ mutationFn: () => Promise.resolve(null),
102
103
  onSuccess: () => {
103
104
  queryClient.setQueryData<BackgroundJob<TInput, TResult>[]>(
104
105
  queryKey,