@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 +1 -1
- package/src/domains/creations/presentation/components/GalleryEmptyStates.tsx +3 -3
- package/src/domains/creations/presentation/components/GalleryHeader.tsx +36 -2
- package/src/domains/creations/presentation/hooks/useGalleryFilters.ts +24 -3
- package/src/domains/creations/presentation/screens/CreationsGalleryScreen.tsx +9 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-ai-generation-content",
|
|
3
|
-
"version": "1.35.
|
|
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")
|
|
106
|
-
description={t("common.no_results_description")
|
|
107
|
-
actionLabel={t("common.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
|
-
<
|
|
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
|
-
|
|
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
|
-
|
|
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")
|
|
196
|
-
processing: t("generator.status.processing")
|
|
197
|
-
uploading: t("generator.status.uploading")
|
|
198
|
-
completed: t("generator.status.completed")
|
|
199
|
-
failed: t("generator.status.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")
|
|
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
|
)}
|