@umituz/react-native-ai-generation-content 1.35.6 → 1.35.7

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.35.6",
3
+ "version": "1.35.7",
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",
@@ -102,9 +102,9 @@ export function GalleryEmptyStates({
102
102
  return (
103
103
  <View style={styles.centerContainer}>
104
104
  <EmptyState
105
- title={t("common.no_results") || "No results"}
106
- description={t("common.no_results_description") || "Try changing your filters"}
107
- actionLabel={t("common.clear_all") || "Clear All"}
105
+ title={t("common.no_results")}
106
+ description={t("common.no_results_description")}
107
+ actionLabel={t("common.clear_all")}
108
108
  onAction={onClearFilters}
109
109
  />
110
110
  </View>
@@ -19,6 +19,10 @@ interface GalleryHeaderProps {
19
19
  readonly filterButtons?: FilterButtonConfig[];
20
20
  readonly showFilter?: boolean;
21
21
  readonly style?: ViewStyle;
22
+ /** Number of pending/processing jobs to show as badge */
23
+ readonly pendingCount?: number;
24
+ /** Label for pending badge tooltip */
25
+ readonly pendingLabel?: string;
22
26
  }
23
27
 
24
28
  export const GalleryHeader: React.FC<GalleryHeaderProps> = ({
@@ -28,6 +32,8 @@ export const GalleryHeader: React.FC<GalleryHeaderProps> = ({
28
32
  filterButtons = [],
29
33
  showFilter = true,
30
34
  style,
35
+ pendingCount = 0,
36
+ pendingLabel,
31
37
  }) => {
32
38
  const tokens = useAppDesignTokens();
33
39
  const styles = useStyles(tokens);
@@ -35,9 +41,20 @@ export const GalleryHeader: React.FC<GalleryHeaderProps> = ({
35
41
  return (
36
42
  <View style={[styles.headerArea, style]}>
37
43
  <View>
38
- <AtomicText style={styles.title}>{title}</AtomicText>
44
+ <View style={styles.titleRow}>
45
+ <AtomicText style={styles.title}>{title}</AtomicText>
46
+ {pendingCount > 0 && (
47
+ <View style={[styles.pendingBadge, { backgroundColor: tokens.colors.primary }]}>
48
+ <AtomicIcon name="Loader" size="xs" color="onPrimary" />
49
+ <AtomicText style={[styles.pendingBadgeText, { color: tokens.colors.onPrimary }]}>
50
+ {pendingCount}
51
+ </AtomicText>
52
+ </View>
53
+ )}
54
+ </View>
39
55
  <AtomicText style={styles.subtitle}>
40
56
  {count} {countLabel}
57
+ {pendingCount > 0 && pendingLabel ? ` · ${pendingCount} ${pendingLabel}` : ""}
41
58
  </AtomicText>
42
59
  </View>
43
60
  {showFilter && filterButtons.length > 0 && (
@@ -83,11 +100,28 @@ const useStyles = (tokens: DesignTokens) =>
83
100
  paddingVertical: tokens.spacing.sm,
84
101
  marginBottom: tokens.spacing.sm,
85
102
  },
103
+ titleRow: {
104
+ flexDirection: "row",
105
+ alignItems: "center",
106
+ gap: tokens.spacing.sm,
107
+ marginBottom: 4,
108
+ },
86
109
  title: {
87
110
  fontSize: 20,
88
111
  fontWeight: "700",
89
112
  color: tokens.colors.textPrimary,
90
- marginBottom: 4,
113
+ },
114
+ pendingBadge: {
115
+ flexDirection: "row",
116
+ alignItems: "center",
117
+ gap: 4,
118
+ paddingHorizontal: 8,
119
+ paddingVertical: 4,
120
+ borderRadius: 12,
121
+ },
122
+ pendingBadgeText: {
123
+ fontSize: 12,
124
+ fontWeight: "600",
91
125
  },
92
126
  subtitle: {
93
127
  fontSize: 14,
@@ -4,9 +4,10 @@
4
4
  * SOLID: Coordinates filter hooks without business logic
5
5
  */
6
6
 
7
- import { useState, useCallback } from "react";
7
+ import { useState, useCallback, useMemo } from "react";
8
8
  import type { Creation } from "../../domain/entities/Creation";
9
9
  import type { FilterOption } from "../../domain/types/creation-filter";
10
+ import type { BackgroundJob } from "../../../../domain/entities/job.types";
10
11
  import { useFilter } from "./useFilter";
11
12
  import { useCreationsFilter } from "./useCreationsFilter";
12
13
 
@@ -15,6 +16,8 @@ interface UseGalleryFiltersProps {
15
16
  readonly statusOptions: FilterOption[];
16
17
  readonly mediaOptions: FilterOption[];
17
18
  readonly t: (key: string) => string;
19
+ /** Pending background jobs to include in status counts */
20
+ readonly pendingJobs?: BackgroundJob[];
18
21
  }
19
22
 
20
23
  interface UseGalleryFiltersReturn {
@@ -36,12 +39,30 @@ export function useGalleryFilters({
36
39
  creations,
37
40
  statusOptions,
38
41
  mediaOptions,
39
- t
42
+ t,
43
+ pendingJobs = [],
40
44
  }: UseGalleryFiltersProps): UseGalleryFiltersReturn {
41
45
  const [statusFilterVisible, setStatusFilterVisible] = useState(false);
42
46
  const [mediaFilterVisible, setMediaFilterVisible] = useState(false);
43
47
 
44
- const statusFilter = useFilter({ options: statusOptions, t });
48
+ // Calculate pending jobs count for status filter
49
+ const processingJobsCount = useMemo(() => {
50
+ return pendingJobs.filter(
51
+ (job) => job.status === "processing" || job.status === "queued",
52
+ ).length;
53
+ }, [pendingJobs]);
54
+
55
+ // Enrich status options with dynamic counts
56
+ const enrichedStatusOptions = useMemo(() => {
57
+ return statusOptions.map((option) => {
58
+ if (option.id === "processing" && processingJobsCount > 0) {
59
+ return { ...option, count: processingJobsCount };
60
+ }
61
+ return option;
62
+ });
63
+ }, [statusOptions, processingJobsCount]);
64
+
65
+ const statusFilter = useFilter({ options: enrichedStatusOptions, t });
45
66
  const mediaFilter = useFilter({ options: mediaOptions, t });
46
67
 
47
68
  const { filtered, isFiltered, activeFiltersCount } = useCreationsFilter({
@@ -91,7 +91,7 @@ export function CreationsGalleryScreen({
91
91
  const showStatusFilter = config.filterConfig?.showStatusFilter ?? true;
92
92
  const showMediaFilter = config.filterConfig?.showMediaFilter ?? true;
93
93
 
94
- const filters = useGalleryFilters({ creations, statusOptions, mediaOptions, t });
94
+ const filters = useGalleryFilters({ creations, statusOptions, mediaOptions, t, pendingJobs });
95
95
 
96
96
  useAppFocusEffect(useCallback(() => { void refetch(); }, [refetch]));
97
97
 
@@ -192,11 +192,11 @@ export function CreationsGalleryScreen({
192
192
 
193
193
  // Status labels for pending jobs
194
194
  const pendingJobStatusLabels = useMemo(() => ({
195
- queued: t("generator.status.queued") || "Waiting in queue...",
196
- processing: t("generator.status.processing") || "Processing...",
197
- uploading: t("generator.status.uploading") || "Uploading...",
198
- completed: t("generator.status.completed") || "Completed",
199
- failed: t("generator.status.failed") || "Failed",
195
+ queued: t("generator.status.queued"),
196
+ processing: t("generator.status.processing"),
197
+ uploading: t("generator.status.uploading"),
198
+ completed: t("generator.status.completed"),
199
+ failed: t("generator.status.failed"),
200
200
  }), [t]);
201
201
 
202
202
  const renderHeader = useMemo(() => {
@@ -212,7 +212,7 @@ export function CreationsGalleryScreen({
212
212
  <PendingJobsSection
213
213
  jobs={pendingJobs}
214
214
  onCancel={removeJob}
215
- title={pendingJobsTitle || t("creations.pendingJobs") || "Processing..."}
215
+ title={pendingJobsTitle || t("creations.pendingJobs")}
216
216
  statusLabels={pendingJobStatusLabels}
217
217
  getTypeLabel={getScenarioTitle}
218
218
  />
@@ -227,6 +227,8 @@ export function CreationsGalleryScreen({
227
227
  countLabel={t(config.translations.photoCount)}
228
228
  showFilter={showFilter}
229
229
  filterButtons={filterButtons}
230
+ pendingCount={pendingJobs.filter((j) => j.status === "processing" || j.status === "queued").length}
231
+ pendingLabel={t("creations.processing")}
230
232
  />
231
233
  </View>
232
234
  )}