diff --git a/apps/mobile/src/components/overview/CategoryItem.tsx b/apps/mobile/src/components/overview/CategoryItem.tsx index a7412eab2..1dacbc048 100644 --- a/apps/mobile/src/components/overview/CategoryItem.tsx +++ b/apps/mobile/src/components/overview/CategoryItem.tsx @@ -1,8 +1,10 @@ +import { formatNumber } from '@sd/client'; import { Pressable, Text, View } from 'react-native'; import { ClassInput } from 'twrnc'; -import { formatNumber } from '@sd/client'; import { tw, twStyle } from '~/lib/tailwind'; +import { useNavigation } from '@react-navigation/native'; +import { useSearchStore } from '~/stores/searchStore'; import { Icon, IconName } from '../icons/Icon'; interface CategoryItemProps { @@ -16,7 +18,9 @@ interface CategoryItemProps { style?: ClassInput; } -const CategoryItem = ({ name, icon, items, style }: CategoryItemProps) => { +const CategoryItem = ({ name, icon, items, style, kind }: CategoryItemProps) => { + const navigation = useNavigation(); + const searchStore = useSearchStore(); return ( { style )} onPress={() => { - //TODO: implement + searchStore.updateFilters('kind', { + name, + icon: icon + '20' as IconName, + id: kind + }, true); + navigation.navigate('SearchStack', { + screen: 'Search', + }) }} > diff --git a/apps/mobile/src/hooks/useFiltersSearch.ts b/apps/mobile/src/hooks/useFiltersSearch.ts index 98337fd0c..37c17608f 100644 --- a/apps/mobile/src/hooks/useFiltersSearch.ts +++ b/apps/mobile/src/hooks/useFiltersSearch.ts @@ -1,4 +1,4 @@ -import { SearchFilterArgs } from '@sd/client'; +import { SearchFilterArgs, useLibraryQuery } from '@sd/client'; import { useEffect, useMemo } from 'react'; import { Filters, SearchFilters, getSearchStore, useSearchStore } from '~/stores/searchStore'; @@ -6,11 +6,19 @@ import { Filters, SearchFilters, getSearchStore, useSearchStore } from '~/stores * This hook merges the selected filters from Filters page in order * to make query calls for saved searches and setups filters for the search * the data structure has been designed to match the desktop app + * @param search - search input string value */ -export function useFiltersSearch() { - const searchStore = useSearchStore();; +export function useFiltersSearch(search: string) { + + const [name, ext] = useMemo(() => search.split('.'), [search]); + const searchStore = useSearchStore(); + + const locations = useLibraryQuery(['locations.list'], { + keepPreviousData: true, + enabled: (name || ext) ? true : false, + }); const filterFactory = (key: SearchFilters, value: Filters[keyof Filters]) => { @@ -49,8 +57,19 @@ export function useFiltersSearch() { const mergedFilters = useMemo(() => { - const filters = [] as SearchFilterArgs[]; + const filters = [] as SearchFilterArgs[]; + //It's a global search if no locations have been selected + if (searchStore.filters.locations.length === 0 || !name || !ext) { + const locationIds = locations.data?.map((l) => l.id); + if (locationIds) filters.push({ filePath: { locations: { in: locationIds } } }); + } + + //handle search input + if (name) filters.push({ filePath: { name: { contains: name } } }); + if (ext) filters.push({ filePath: { extension: { in: [ext] } } }); + + // handle selected filters for (const key in searchStore.filters) { const filterKey = key as SearchFilters; @@ -77,10 +96,10 @@ export function useFiltersSearch() { // makes sure the array is not 2D return filters.flat(); - }, [searchStore.filters]); + }, [searchStore.filters, search]); useEffect(() => { getSearchStore().mergedFilters = mergedFilters; - }, [searchStore.filters]); + }, [searchStore.filters, search]); }; diff --git a/apps/mobile/src/screens/overview/Categories.tsx b/apps/mobile/src/screens/overview/Categories.tsx index 97a9cf479..65a4317a0 100644 --- a/apps/mobile/src/screens/overview/Categories.tsx +++ b/apps/mobile/src/screens/overview/Categories.tsx @@ -1,7 +1,7 @@ +import { useLibraryQuery } from '@sd/client'; import { useMemo } from 'react'; import { FlatList, View } from 'react-native'; import { useDebounce } from 'use-debounce'; -import { useLibraryQuery } from '@sd/client'; import { IconName } from '~/components/icons/Icon'; import ScreenContainer from '~/components/layout/ScreenContainer'; import CategoryItem from '~/components/overview/CategoryItem'; diff --git a/apps/mobile/src/screens/search/Search.tsx b/apps/mobile/src/screens/search/Search.tsx index d98c7c97f..3db6f926d 100644 --- a/apps/mobile/src/screens/search/Search.tsx +++ b/apps/mobile/src/screens/search/Search.tsx @@ -1,7 +1,7 @@ import { useIsFocused } from '@react-navigation/native'; -import { SearchFilterArgs, useLibraryQuery, usePathsExplorerQuery } from '@sd/client'; -import { ArrowLeft, DotsThreeOutline, FunnelSimple, MagnifyingGlass } from 'phosphor-react-native'; -import { Suspense, useDeferredValue, useMemo, useState } from 'react'; +import { usePathsExplorerQuery } from '@sd/client'; +import { ArrowLeft, DotsThreeOutline, FunnelSimple } from 'phosphor-react-native'; +import { Suspense, useDeferredValue, useState } from 'react'; import { ActivityIndicator, Platform, Pressable, TextInput, View } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import Explorer from '~/components/explorer/Explorer'; @@ -15,42 +15,21 @@ import { useSearchStore } from '~/stores/searchStore'; const SearchScreen = ({ navigation }: SearchStackScreenProps<'Search'>) => { const headerHeight = useSafeAreaInsets().top; - const [loading, setLoading] = useState(false); const searchStore = useSearchStore(); const explorerStore = useExplorerStore(); const isFocused = useIsFocused(); - const appliedFiltersLength = Object.keys(searchStore.appliedFilters).length; - const isAndroid = Platform.OS === 'android'; - const locations = useLibraryQuery(['locations.list'], { - keepPreviousData: true, - }); - const [search, setSearch] = useState(''); const deferredSearch = useDeferredValue(search); - const filters = useMemo(() => { - const [name, ext] = deferredSearch.split('.'); - - const filters: SearchFilterArgs[] = []; - - if (name) filters.push({ filePath: { name: { contains: name } } }); - if (ext) filters.push({ filePath: { extension: { in: [ext] } } }); - - if (name || ext) { - // Add locations filter to search all locations - if (locations.data && locations.data.length > 0) filters.push({ filePath: { locations: { in: - locations.data?.map((location) => location.id) } } }); - } - - return searchStore.mergedFilters.concat(filters); - }, [deferredSearch, searchStore.mergedFilters, locations.data]); + const appliedFiltersLength = Object.keys(searchStore.appliedFilters).length; + const isAndroid = Platform.OS === 'android'; const objects = usePathsExplorerQuery({ arg: { take: 30, - filters + filters: searchStore.mergedFilters, }, - enabled: isFocused && filters.length > 1, // only fetch when screen is focused & filters are applied + enabled: isFocused && searchStore.mergedFilters.length > 1, // only fetch when screen is focused & filters are applied suspense: true, order: null, onSuccess: () => getExplorerStore().resetNewThumbnails() @@ -60,7 +39,7 @@ const SearchScreen = ({ navigation }: SearchStackScreenProps<'Search'>) => { const noObjects = objects.items?.length === 0 || !objects.items; const noSearch = deferredSearch.length === 0 && appliedFiltersLength === 0; - useFiltersSearch(); + useFiltersSearch(deferredSearch); return ( ) => { style={tw`h-10 w-4/5 flex-wrap rounded-md border border-app-inputborder bg-app-input`} > - - {loading ? ( - - ) : ( - - )} - setSearch(t)} diff --git a/apps/mobile/src/stores/searchStore.ts b/apps/mobile/src/stores/searchStore.ts index 1328c1e06..8500cc7c5 100644 --- a/apps/mobile/src/stores/searchStore.ts +++ b/apps/mobile/src/stores/searchStore.ts @@ -76,7 +76,8 @@ const searchStore = proxy< State & { updateFilters: ( filter: K, - value: State['filters'][K] extends Array ? U : State['filters'][K] + value: State['filters'][K] extends Array ? U : State['filters'][K], + apply?: boolean ) => void; applyFilters: () => void; setSearch: (search: string) => void; @@ -89,7 +90,7 @@ const searchStore = proxy< >({ ...initialState, //for updating the filters upon value selection - updateFilters: (filter, value) => { + updateFilters: (filter, value, apply = false) => { if (filter === 'hidden') { // Directly assign boolean values without an array operation searchStore.filters['hidden'] = value as boolean; @@ -107,6 +108,9 @@ const searchStore = proxy< searchStore.filters[filter] = updatedFilter; } } + //instead of a useEffect or subscription - we can call applyFilters directly + // useful when you want to apply the filters from another screen + if (apply) searchStore.applyFilters(); }, //for clicking add filters and applying the selection applyFilters: () => {