@umituz/react-native-ai-creations 1.2.2 → 1.2.3

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-creations",
3
- "version": "1.2.2",
3
+ "version": "1.2.3",
4
4
  "description": "AI-generated creations gallery with filtering, sharing, and management for React Native apps",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -34,18 +34,24 @@
34
34
  "@umituz/react-native-image": "latest",
35
35
  "@umituz/react-native-sharing": "latest",
36
36
  "firebase": ">=11.0.0",
37
- "react": ">=18.2.0",
38
- "react-native": ">=0.74.0",
39
- "react-native-safe-area-context": ">=4.0.0"
37
+ "react": ">=19.1.0",
38
+ "react-native": ">=0.81.5",
39
+ "react-native-safe-area-context": ">=5.0.0",
40
+ "@umituz/react-native-filter": "^1.4.2",
41
+ "@umituz/react-native-bottom-sheet": "latest",
42
+ "@umituz/react-native-design-system-atoms": "latest"
40
43
  },
41
44
  "devDependencies": {
42
45
  "@tanstack/react-query": "^5.62.16",
43
46
  "@types/react": "^19.0.0",
44
47
  "@umituz/react-native-image": "^1.1.1",
45
48
  "firebase": "^11.0.0",
46
- "react": "^19.0.0",
47
- "react-native": "^0.76.0",
48
- "react-native-safe-area-context": "^4.14.0",
49
+ "react": "19.1.0",
50
+ "react-native": "0.81.5",
51
+ "react-native-safe-area-context": "^5.6.0",
52
+ "@umituz/react-native-filter": "^1.4.2",
53
+ "@umituz/react-native-bottom-sheet": "latest",
54
+ "@umituz/react-native-design-system-atoms": "latest",
49
55
  "typescript": "^5.3.3"
50
56
  },
51
57
  "publishConfig": {
@@ -39,9 +39,12 @@ export type PathBuilder = (userId: string) => string[];
39
39
  */
40
40
  export type DocumentMapper = (id: string, data: CreationDocument) => Creation;
41
41
 
42
+ import type { FilterCategory } from "@umituz/react-native-filter";
43
+
42
44
  export interface CreationsConfig {
43
45
  readonly collectionName: string;
44
46
  readonly types: readonly CreationType[];
47
+ readonly filterCategories?: readonly FilterCategory[];
45
48
  readonly translations: CreationsTranslations;
46
49
  readonly maxThumbnails?: number;
47
50
  readonly gridColumns?: number;
@@ -14,6 +14,7 @@ interface FilterChipsProps {
14
14
  readonly selectedType: string;
15
15
  readonly allLabel: string;
16
16
  readonly onSelect: (type: string) => void;
17
+ readonly style?: any;
17
18
  }
18
19
 
19
20
  export function FilterChips({
@@ -22,6 +23,7 @@ export function FilterChips({
22
23
  selectedType,
23
24
  allLabel,
24
25
  onSelect,
26
+ style,
25
27
  }: FilterChipsProps) {
26
28
  const tokens = useAppDesignTokens();
27
29
 
@@ -59,7 +61,7 @@ export function FilterChips({
59
61
  const visibleTypes = types.filter((t) => availableTypes.includes(t.id));
60
62
 
61
63
  return (
62
- <View style={styles.container}>
64
+ <View style={[styles.container, style]}>
63
65
  <ScrollView
64
66
  horizontal
65
67
  showsHorizontalScrollIndicator={false}
@@ -12,35 +12,42 @@ interface UseCreationsFilterProps {
12
12
  readonly creations: Creation[] | undefined;
13
13
  }
14
14
 
15
- export function useCreationsFilter({ creations }: UseCreationsFilterProps) {
16
- const [selectedType, setSelectedType] = useState<string>(ALL_FILTER);
15
+ import { FilterUtils } from "@umituz/react-native-filter";
17
16
 
18
- const availableTypes = useMemo(() => {
19
- if (!creations) return [];
20
- const types = new Set(creations.map((c) => c.type));
21
- return Array.from(types);
22
- }, [creations]);
17
+ interface UseCreationsFilterProps {
18
+ readonly creations: Creation[] | undefined;
19
+ readonly defaultFilterId?: string;
20
+ }
21
+
22
+ export function useCreationsFilter({
23
+ creations,
24
+ defaultFilterId = "all"
25
+ }: UseCreationsFilterProps) {
26
+ const [selectedIds, setSelectedIds] = useState<string[]>([defaultFilterId]);
23
27
 
24
28
  const filtered = useMemo(() => {
25
29
  if (!creations) return [];
26
- if (selectedType === ALL_FILTER) return creations;
27
- return creations.filter((c) => c.type === selectedType);
28
- }, [creations, selectedType]);
30
+ if (selectedIds.includes(defaultFilterId)) return creations;
31
+
32
+ return creations.filter((c) =>
33
+ selectedIds.includes(c.type) ||
34
+ selectedIds.some(id => (c as any).metadata?.tags?.includes(id))
35
+ );
36
+ }, [creations, selectedIds, defaultFilterId]);
29
37
 
30
- const selectType = useCallback((type: string) => {
31
- setSelectedType(type);
32
- }, []);
38
+ const toggleFilter = useCallback((filterId: string, multiSelect: boolean = false) => {
39
+ setSelectedIds(prev => FilterUtils.toggleFilter(prev, filterId, multiSelect, defaultFilterId));
40
+ }, [defaultFilterId]);
33
41
 
34
- const resetFilter = useCallback(() => {
35
- setSelectedType(ALL_FILTER);
36
- }, []);
42
+ const clearFilters = useCallback(() => {
43
+ setSelectedIds([defaultFilterId]);
44
+ }, [defaultFilterId]);
37
45
 
38
46
  return {
39
47
  filtered,
40
- selectedType,
41
- availableTypes,
42
- selectType,
43
- resetFilter,
44
- isFiltered: selectedType !== ALL_FILTER,
48
+ selectedIds,
49
+ toggleFilter,
50
+ clearFilters,
51
+ isFiltered: !selectedIds.includes(defaultFilterId),
45
52
  };
46
53
  }
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import React, { useMemo, useCallback, useState } from "react";
7
- import { View, FlatList, StyleSheet, RefreshControl, Alert } from "react-native";
7
+ import { View, FlatList, StyleSheet, RefreshControl, Alert, TouchableOpacity } from "react-native";
8
8
  import { useAppDesignTokens } from "@umituz/react-native-design-system";
9
9
  import { useSharing } from "@umituz/react-native-sharing";
10
10
  import { ImageGallery } from "@umituz/react-native-image";
@@ -18,6 +18,9 @@ import { useCreationsFilter } from "../hooks/useCreationsFilter";
18
18
  import { CreationCard } from "../components/CreationCard";
19
19
  import { FilterChips } from "../components/FilterChips";
20
20
  import { EmptyState } from "../components/EmptyState";
21
+ import { FilterBottomSheet, type FilterCategory } from "@umituz/react-native-filter";
22
+ import { BottomSheetModalRef } from "@umituz/react-native-bottom-sheet";
23
+ import { AtomicIcon, AtomicText } from "@umituz/react-native-design-system-atoms";
21
24
 
22
25
  interface CreationsGalleryScreenProps {
23
26
  readonly userId: string | null;
@@ -41,6 +44,7 @@ export function CreationsGalleryScreen({
41
44
  const { share } = useSharing();
42
45
  const [viewerVisible, setViewerVisible] = useState(false);
43
46
  const [viewerIndex, setViewerIndex] = useState(0);
47
+ const filterSheetRef = React.useRef<BottomSheetModalRef>(null);
44
48
 
45
49
  const { data: creations, isLoading, refetch } = useCreations({
46
50
  userId,
@@ -48,9 +52,15 @@ export function CreationsGalleryScreen({
48
52
  });
49
53
 
50
54
  const deleteMutation = useDeleteCreation({ userId, repository });
51
- const { filtered, selectedType, availableTypes, selectType } =
55
+ const { filtered, selectedIds, toggleFilter, clearFilters, isFiltered } =
52
56
  useCreationsFilter({ creations });
53
57
 
58
+ const availableTypes = useMemo(() => {
59
+ if (!creations) return [];
60
+ const types = new Set(creations.map((c) => c.type));
61
+ return Array.from(types);
62
+ }, [creations]);
63
+
54
64
  const handleView = useCallback(
55
65
  (creation: Creation) => {
56
66
  const index = filtered.findIndex((c) => c.id === creation.id);
@@ -111,6 +121,20 @@ export function CreationsGalleryScreen({
111
121
  flex: 1,
112
122
  backgroundColor: tokens.colors.backgroundPrimary,
113
123
  },
124
+ headerArea: {
125
+ flexDirection: "row",
126
+ alignItems: "center",
127
+ paddingRight: tokens.spacing.md,
128
+ },
129
+ filterButton: {
130
+ padding: tokens.spacing.sm,
131
+ borderRadius: (tokens as any).borders?.radius?.full || 999,
132
+ backgroundColor: tokens.colors.surfaceVariant,
133
+ marginLeft: tokens.spacing.sm,
134
+ },
135
+ filterButtonActive: {
136
+ backgroundColor: tokens.colors.primary + "15",
137
+ },
114
138
  list: {
115
139
  padding: tokens.spacing.md,
116
140
  paddingBottom: insets.bottom + tokens.spacing.xl,
@@ -151,15 +175,26 @@ export function CreationsGalleryScreen({
151
175
 
152
176
  return (
153
177
  <View style={styles.container}>
154
- {config.types.length > 0 && availableTypes.length > 1 && (
155
- <FilterChips
156
- types={config.types}
157
- availableTypes={availableTypes}
158
- selectedType={selectedType}
159
- allLabel={t(config.translations.filterAll)}
160
- onSelect={selectType}
161
- />
162
- )}
178
+ <View style={styles.headerArea}>
179
+ {config.types.length > 0 && availableTypes.length > 1 && (
180
+ <FilterChips
181
+ style={{ flex: 1 }}
182
+ types={config.types}
183
+ availableTypes={availableTypes}
184
+ selectedType={selectedIds[0] || "all"}
185
+ allLabel={t(config.translations.filterAll)}
186
+ onSelect={(type) => toggleFilter(type, false)}
187
+ />
188
+ )}
189
+ {(config.filterCategories?.length || 0) > 0 && (
190
+ <TouchableOpacity
191
+ onPress={() => filterSheetRef.current?.present()}
192
+ style={[styles.filterButton, isFiltered && styles.filterButtonActive]}
193
+ >
194
+ <AtomicIcon name="ListFilter" size="md" color={isFiltered ? "primary" : "secondary"} />
195
+ </TouchableOpacity>
196
+ )}
197
+ </View>
163
198
  <FlatList
164
199
  data={filtered}
165
200
  renderItem={renderItem}
@@ -182,6 +217,20 @@ export function CreationsGalleryScreen({
182
217
  enableEditing={enableEditing}
183
218
  onImageChange={handleImageChange}
184
219
  />
220
+
221
+ {config.filterCategories && (
222
+ <FilterBottomSheet
223
+ ref={filterSheetRef}
224
+ categories={config.filterCategories as FilterCategory[]}
225
+ selectedIds={selectedIds}
226
+ onFilterPress={(id, catId) => {
227
+ const category = config.filterCategories?.find(c => c.id === catId);
228
+ toggleFilter(id, category?.multiSelect);
229
+ }}
230
+ onClearFilters={clearFilters}
231
+ title={t("common.filter")}
232
+ />
233
+ )}
185
234
  </View>
186
235
  );
187
236
  }