remove fixed filters (#2257)

* remove fixed filters

* fix: remove blank `console.log`

---------

Co-authored-by: jake <77554505+brxken128@users.noreply.github.com>
This commit is contained in:
Brendan Allan 2024-03-29 06:46:55 +08:00 committed by GitHub
parent 99ed9006cf
commit 1959226fb9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 110 additions and 199 deletions

View file

@ -25,19 +25,7 @@ export function Component() {
const explorerSettingsSnapshot = explorerSettings.useSettingsSnapshot(); const explorerSettingsSnapshot = explorerSettings.useSettingsSnapshot();
const fixedFilters = useMemo<SearchFilterArgs[]>( const search = useSearch();
() => [
// { object: { favorite: true } },
...(explorerSettingsSnapshot.layoutMode === 'media'
? [{ object: { kind: { in: [ObjectKindEnum.Image, ObjectKindEnum.Video] } } }]
: [])
],
[explorerSettingsSnapshot.layoutMode]
);
const search = useSearch({
fixedFilters
});
const objects = useObjectsExplorerQuery({ const objects = useObjectsExplorerQuery({
arg: { arg: {
@ -45,7 +33,10 @@ export function Component() {
filters: [ filters: [
...search.allFilters, ...search.allFilters,
// TODO: Add filter to search options // TODO: Add filter to search options
{ object: { favorite: true } } { object: { favorite: true } },
...(explorerSettingsSnapshot.layoutMode === 'media'
? [{ object: { kind: { in: [ObjectKindEnum.Image, ObjectKindEnum.Video] } } }]
: [])
] ]
}, },
order: explorerSettings.useSettingsSnapshot().order order: explorerSettings.useSettingsSnapshot().order

View file

@ -27,7 +27,7 @@ export function Component() {
// const explorerSettingsSnapshot = explorerSettings.useSettingsSnapshot(); // const explorerSettingsSnapshot = explorerSettings.useSettingsSnapshot();
// const fixedFilters = useMemo<SearchFilterArgs[]>( // const filters = useMemo<SearchFilterArgs[]>(
// () => [ // () => [
// ...(explorerSettingsSnapshot.layoutMode === 'media' // ...(explorerSettingsSnapshot.layoutMode === 'media'
// ? [{ object: { kind: { in: [ObjectKindEnum.Image, ObjectKindEnum.Video] } } }] // ? [{ object: { kind: { in: [ObjectKindEnum.Image, ObjectKindEnum.Video] } } }]
@ -36,7 +36,7 @@ export function Component() {
// [explorerSettingsSnapshot.layoutMode] // [explorerSettingsSnapshot.layoutMode]
// ); // );
const search = useSearch({}); const search = useSearch();
// const objects = useObjectsExplorerQuery({ // const objects = useObjectsExplorerQuery({
// arg: { // arg: {

View file

@ -71,7 +71,9 @@ const LocationExplorer = ({ location }: { location: Location; path?: string }) =
const { layoutMode, mediaViewWithDescendants, showHiddenFiles } = const { layoutMode, mediaViewWithDescendants, showHiddenFiles } =
explorerSettings.useSettingsSnapshot(); explorerSettings.useSettingsSnapshot();
const search = useLocationSearch(explorerSettings, location); const search = useLocationSearch(location);
const explorerSettingsSnapshot = explorerSettings.useSettingsSnapshot();
const paths = usePathsExplorerQuery({ const paths = usePathsExplorerQuery({
arg: { arg: {
@ -84,12 +86,15 @@ const LocationExplorer = ({ location }: { location: Location; path?: string }) =
path: path ?? '', path: path ?? '',
include_descendants: include_descendants:
search.search !== '' || search.search !== '' ||
search.dynamicFilters.length > 0 || search.filters.length > 0 ||
(layoutMode === 'media' && mediaViewWithDescendants) (layoutMode === 'media' && mediaViewWithDescendants)
} }
} }
}, },
!showHiddenFiles && { filePath: { hidden: false } } !showHiddenFiles && { filePath: { hidden: false } },
explorerSettingsSnapshot.layoutMode === 'media' && [
{ object: { kind: { in: [ObjectKindEnum.Image, ObjectKindEnum.Video] } } }
]
].filter(Boolean) as any, ].filter(Boolean) as any,
take take
}, },
@ -268,47 +273,32 @@ function useLocationExplorerSettings(location: Location) {
}; };
} }
function useLocationSearch( function useLocationSearch(location: Location) {
explorerSettings: UseExplorerSettings<FilePathOrder>,
location: Location
) {
const [searchParams, setSearchParams] = useRawSearchParams(); const [searchParams, setSearchParams] = useRawSearchParams();
const explorerSettingsSnapshot = explorerSettings.useSettingsSnapshot();
const fixedFilters = useMemo(
() => [
{ filePath: { locations: { in: [location.id] } } },
...(explorerSettingsSnapshot.layoutMode === 'media'
? [{ object: { kind: { in: [ObjectKindEnum.Image, ObjectKindEnum.Video] } } }]
: [])
],
[location.id, explorerSettingsSnapshot.layoutMode]
);
const filtersParam = searchParams.get('filters'); const filtersParam = searchParams.get('filters');
const dynamicFilters = useMemo(() => JSON.parse(filtersParam ?? '[]'), [filtersParam]); const filters = useMemo(() => JSON.parse(filtersParam ?? '[]'), [filtersParam]);
const searchQueryParam = searchParams.get('search'); const searchQueryParam = searchParams.get('search');
const search = useSearch({ const search = useSearch({
open: !!searchQueryParam || dynamicFilters.length > 0 || undefined, open: !!searchQueryParam || filters.length > 0 || undefined,
search: searchParams.get('search') ?? undefined, search: searchParams.get('search') ?? undefined,
fixedFilters, defaultFilters: [{ filePath: { locations: { in: [location.id] } } }],
dynamicFilters filters: filters
}); });
useEffect(() => { useEffect(() => {
setSearchParams( setSearchParams(
(p) => { (p) => {
if (search.dynamicFilters.length > 0) if (search.filters.length > 0) p.set('filters', JSON.stringify(search.filters));
p.set('filters', JSON.stringify(search.dynamicFilters));
else p.delete('filters'); else p.delete('filters');
return p; return p;
}, },
{ replace: true } { replace: true }
); );
}, [search.dynamicFilters, setSearchParams]); }, [search.filters, setSearchParams]);
const searchQuery = search.search; const searchQuery = search.search;

View file

@ -25,19 +25,7 @@ export function Component() {
const explorerSettingsSnapshot = explorerSettings.useSettingsSnapshot(); const explorerSettingsSnapshot = explorerSettings.useSettingsSnapshot();
const fixedFilters = useMemo<SearchFilterArgs[]>( const search = useSearch();
() => [
// { object: { dateAccessed: { from: new Date(0).toISOString() } } },
...(explorerSettingsSnapshot.layoutMode === 'media'
? [{ object: { kind: { in: [ObjectKindEnum.Image, ObjectKindEnum.Video] } } }]
: [])
],
[explorerSettingsSnapshot.layoutMode]
);
const search = useSearch({
fixedFilters
});
const objects = useObjectsExplorerQuery({ const objects = useObjectsExplorerQuery({
arg: { arg: {
@ -45,7 +33,10 @@ export function Component() {
filters: [ filters: [
...search.allFilters, ...search.allFilters,
// TODO: Add fil ter to search options // TODO: Add fil ter to search options
{ object: { dateAccessed: { from: new Date(0).toISOString() } } } { object: { dateAccessed: { from: new Date(0).toISOString() } } },
...(explorerSettingsSnapshot.layoutMode === 'media'
? [{ object: { kind: { in: [ObjectKindEnum.Image, ObjectKindEnum.Video] } } }]
: [])
] ]
}, },
order: explorerSettings.useSettingsSnapshot().order order: explorerSettings.useSettingsSnapshot().order

View file

@ -46,14 +46,14 @@ export const Component = () => {
const rawFilters = savedSearch.data?.filters; const rawFilters = savedSearch.data?.filters;
const dynamicFilters = useMemo(() => { const filters = useMemo(() => {
if (rawFilters) return JSON.parse(rawFilters) as SearchFilterArgs[]; if (rawFilters) return JSON.parse(rawFilters) as SearchFilterArgs[];
}, [rawFilters]); }, [rawFilters]);
const search = useSearch({ const search = useSearch({
open: true, open: true,
search: savedSearch.data?.search ?? undefined, search: savedSearch.data?.search ?? undefined,
dynamicFilters filters: filters
}); });
const paths = usePathsExplorerQuery({ const paths = usePathsExplorerQuery({
@ -85,7 +85,7 @@ export const Component = () => {
> >
<hr className="w-full border-t border-sidebar-divider bg-sidebar-divider" /> <hr className="w-full border-t border-sidebar-divider bg-sidebar-divider" />
<SearchOptions> <SearchOptions>
{(search.dynamicFilters !== dynamicFilters || {(search.filters !== filters ||
search.search !== savedSearch.data?.search) && ( search.search !== savedSearch.data?.search) && (
<SaveButton searchId={id} /> <SaveButton searchId={id} />
)} )}
@ -123,7 +123,7 @@ function SaveButton({ searchId }: { searchId: number }) {
updateSavedSearch.mutate([ updateSavedSearch.mutate([
searchId, searchId,
{ {
filters: JSON.stringify(search.dynamicFilters), filters: JSON.stringify(search.filters),
search: search.search search: search.search
} }
]); ]);

View file

@ -55,13 +55,11 @@ export const AppliedFilters = ({ allowRemove = true }: { allowRemove?: boolean }
onDelete={ onDelete={
removalIndex !== null && allowRemove removalIndex !== null && allowRemove
? () => { ? () => {
search.updateDynamicFilters( search.updateFilters((dyanmicFilters) => {
(dyanmicFilters) => { dyanmicFilters.splice(removalIndex, 1);
dyanmicFilters.splice(removalIndex, 1);
return dyanmicFilters; return dyanmicFilters;
} });
);
} }
: undefined : undefined
} }

View file

@ -71,28 +71,25 @@ export function useToggleOptionSelected({ search }: { search: UseSearch }) {
option: FilterOption; option: FilterOption;
select: boolean; select: boolean;
}) => { }) => {
search.updateDynamicFilters((dynamicFilters) => { search.updateFilters((filters) => {
const key = getKey({ ...option, type: filter.name }); const rawArg = filters.find((arg) => filter.extract(arg));
if (search.fixedFiltersKeys?.has(key)) return dynamicFilters;
const rawArg = dynamicFilters.find((arg) => filter.extract(arg));
if (!rawArg) { if (!rawArg) {
const arg = filter.create(option.value); const arg = filter.create(option.value);
dynamicFilters.push(arg); filters.push(arg);
} else { } else {
const rawArgIndex = dynamicFilters.findIndex((arg) => filter.extract(arg))!; const rawArgIndex = filters.findIndex((arg) => filter.extract(arg))!;
const arg = filter.extract(rawArg)!; const arg = filter.extract(rawArg)!;
if (select) { if (select) {
if (rawArg) filter.applyAdd(arg, option); if (rawArg) filter.applyAdd(arg, option);
} else { } else {
if (!filter.applyRemove(arg, option)) dynamicFilters.splice(rawArgIndex, 1); if (!filter.applyRemove(arg, option)) filters.splice(rawArgIndex, 1);
} }
} }
return dynamicFilters; return filters;
}); });
}; };
} }
@ -159,14 +156,14 @@ const FilterOptionText = ({ filter, search }: { filter: SearchFilterCRUD; search
className="flex gap-1.5" className="flex gap-1.5"
onSubmit={(e) => { onSubmit={(e) => {
e.preventDefault(); e.preventDefault();
search.updateDynamicFilters((dynamicFilters) => { search.updateFilters((filters) => {
if (allFiltersKeys.has(key)) return dynamicFilters; if (allFiltersKeys.has(key)) return filters;
const arg = filter.create(value); const arg = filter.create(value);
dynamicFilters.push(arg); filters.push(arg);
setValue(''); setValue('');
return dynamicFilters; return filters;
}); });
}} }}
> >
@ -191,7 +188,7 @@ const FilterOptionBoolean = ({
filter: SearchFilterCRUD; filter: SearchFilterCRUD;
search: UseSearch; search: UseSearch;
}) => { }) => {
const { fixedFiltersKeys, allFiltersKeys } = search; const { allFiltersKeys } = search;
const key = getKey({ const key = getKey({
type: filter.name, type: filter.name,
@ -204,19 +201,17 @@ const FilterOptionBoolean = ({
icon={filter.icon} icon={filter.icon}
selected={allFiltersKeys?.has(key)} selected={allFiltersKeys?.has(key)}
setSelected={() => { setSelected={() => {
search.updateDynamicFilters((dynamicFilters) => { search.updateFilters((filters) => {
if (fixedFiltersKeys?.has(key)) return dynamicFilters; const index = filters.findIndex((f) => filter.extract(f) !== undefined);
const index = dynamicFilters.findIndex((f) => filter.extract(f) !== undefined);
if (index !== -1) { if (index !== -1) {
dynamicFilters.splice(index, 1); filters.splice(index, 1);
} else { } else {
const arg = filter.create(true); const arg = filter.create(true);
dynamicFilters.push(arg); filters.push(arg);
} }
return dynamicFilters; return filters;
}); });
}} }}
> >

View file

@ -81,6 +81,7 @@ export default ({ redirectToSearch }: Props) => {
function clearValue() { function clearValue() {
search.setSearch(''); search.setSearch('');
search.setFilters([]);
} }
return ( return (
@ -99,7 +100,10 @@ export default ({ redirectToSearch }: Props) => {
search.setSearchBarFocused(false); search.setSearchBarFocused(false);
} }
}} }}
onFocus={() => search.setSearchBarFocused(true)} onFocus={() => {
search.setSearchBarFocused(true);
if (search.defaultFilters) search.setFilters(search.defaultFilters);
}}
right={ right={
<div className="pointer-events-none flex h-7 items-center space-x-1 opacity-70 group-focus-within:hidden"> <div className="pointer-events-none flex h-7 items-center space-x-1 opacity-70 group-focus-within:hidden">
{ {

View file

@ -124,9 +124,7 @@ export const SearchOptions = ({
{children ?? ( {children ?? (
<> <>
{(search.dynamicFilters.length > 0 || search.search !== '') && ( {(search.filters.length > 0 || search.search !== '') && <SaveSearchButton />}
<SaveSearchButton />
)}
<EscapeButton /> <EscapeButton />
</> </>

View file

@ -27,13 +27,19 @@ export function Component() {
}, []), }, []),
orderingKeys: objectOrderingKeysSchema orderingKeys: objectOrderingKeysSchema
}); });
const explorerSettingsSnapshot = explorerSettings.useSettingsSnapshot();
const search = useSearchWithFilters(explorerSettings); const search = useSearchWithFilters();
const objects = useObjectsExplorerQuery({ const objects = useObjectsExplorerQuery({
arg: { arg: {
take: 100, take: 100,
filters: search.allFilters filters: [
...search.allFilters,
...(explorerSettingsSnapshot.layoutMode === 'media'
? [{ object: { kind: { in: [ObjectKindEnum.Image, ObjectKindEnum.Video] } } }]
: [])
]
}, },
order: explorerSettings.useSettingsSnapshot().order order: explorerSettings.useSettingsSnapshot().order
}); });
@ -77,43 +83,31 @@ export function Component() {
); );
} }
function useSearchWithFilters(explorerSettings: UseExplorerSettings<ObjectOrder>) { function useSearchWithFilters() {
const [searchParams, setSearchParams] = useRawSearchParams(); const [searchParams, setSearchParams] = useRawSearchParams();
const explorerSettingsSnapshot = explorerSettings.useSettingsSnapshot();
const fixedFilters = useMemo(
() => [
...(explorerSettingsSnapshot.layoutMode === 'media'
? [{ object: { kind: { in: [ObjectKindEnum.Image, ObjectKindEnum.Video] } } }]
: [])
],
[explorerSettingsSnapshot.layoutMode]
);
const filtersParam = searchParams.get('filters'); const filtersParam = searchParams.get('filters');
const dynamicFilters = useMemo(() => JSON.parse(filtersParam ?? '[]'), [filtersParam]); const filters = useMemo(() => JSON.parse(filtersParam ?? '[]'), [filtersParam]);
const searchQueryParam = searchParams.get('search'); const searchQueryParam = searchParams.get('search');
const search = useSearch({ const search = useSearch({
open: !!searchQueryParam || dynamicFilters.length > 0 || undefined, open: !!searchQueryParam || filters.length > 0 || undefined,
search: searchParams.get('search') ?? undefined, search: searchParams.get('search') ?? undefined,
fixedFilters, filters
dynamicFilters
}); });
useEffect(() => { useEffect(() => {
setSearchParams( setSearchParams(
(p) => { (p) => {
if (search.dynamicFilters.length > 0) if (search.filters.length > 0) p.set('filters', JSON.stringify(search.filters));
p.set('filters', JSON.stringify(search.dynamicFilters));
else p.delete('filters'); else p.delete('filters');
return p; return p;
}, },
{ replace: true } { replace: true }
); );
}, [search.dynamicFilters, setSearchParams]); }, [search.filters, setSearchParams]);
const searchQuery = search.search; const searchQuery = search.search;

View file

@ -9,15 +9,12 @@ import { argsToOptions, getKey, useSearchStore } from './store';
export interface UseSearchProps { export interface UseSearchProps {
open?: boolean; open?: boolean;
search?: string; search?: string;
/**
* Filters that cannot be removed
*/
fixedFilters?: SearchFilterArgs[];
/** /**
* Filters that can be removed. * Filters that can be removed.
* When this value changes dynamic filters stored internally will reset. * When this value changes dynamic filters stored internally will reset.
*/ */
dynamicFilters?: SearchFilterArgs[]; filters?: SearchFilterArgs[];
defaultFilters?: SearchFilterArgs[];
} }
export function useSearch(props?: UseSearchProps) { export function useSearch(props?: UseSearchProps) {
@ -25,45 +22,22 @@ export function useSearch(props?: UseSearchProps) {
const searchState = useSearchStore(); const searchState = useSearchStore();
// Filters that can't be removed const [filters, setFilters] = useState(props?.filters ?? []);
const [filtersFromProps, setFiltersFromProps] = useState(props?.filters);
const fixedFilters = useMemo(() => props?.fixedFilters ?? [], [props?.fixedFilters]); if (filtersFromProps !== props?.filters) {
setFiltersFromProps(props?.filters);
const fixedFiltersAsOptions = useMemo( setFilters(props?.filters ?? []);
() => argsToOptions(fixedFilters, searchState.filterOptions),
[fixedFilters, searchState.filterOptions]
);
const fixedFiltersKeys: Set<string> = useMemo(() => {
return new Set(
fixedFiltersAsOptions.map(({ arg, filter }) =>
getKey({
type: filter.name,
name: arg.name,
value: arg.value
})
)
);
}, [fixedFiltersAsOptions]);
// Filters that can be removed
const [dynamicFilters, setDynamicFilters] = useState(props?.dynamicFilters ?? []);
const [dynamicFiltersFromProps, setDynamicFiltersFromProps] = useState(props?.dynamicFilters);
if (dynamicFiltersFromProps !== props?.dynamicFilters) {
setDynamicFiltersFromProps(props?.dynamicFilters);
setDynamicFilters(props?.dynamicFilters ?? []);
} }
const dynamicFiltersAsOptions = useMemo( const filtersAsOptions = useMemo(
() => argsToOptions(dynamicFilters, searchState.filterOptions), () => argsToOptions(filters, searchState.filterOptions),
[dynamicFilters, searchState.filterOptions] [filters, searchState.filterOptions]
); );
const dynamicFiltersKeys: Set<string> = useMemo(() => { const filtersKeys: Set<string> = useMemo(() => {
return new Set( return new Set(
dynamicFiltersAsOptions.map(({ arg, filter }) => filtersAsOptions.map(({ arg, filter }) =>
getKey({ getKey({
type: filter.name, type: filter.name,
name: arg.name, name: arg.name,
@ -71,52 +45,31 @@ export function useSearch(props?: UseSearchProps) {
}) })
) )
); );
}, [dynamicFiltersAsOptions]); }, [filtersAsOptions]);
const updateDynamicFilters = useCallback( const updateFilters = useCallback(
(cb: (args: SearchFilterArgs[]) => SearchFilterArgs[]) => (cb: (args: SearchFilterArgs[]) => SearchFilterArgs[]) =>
setDynamicFilters((filters) => produce(filters, cb)), setFilters((filters) => produce(filters, cb)),
[] []
); );
// Merging of filters that should be ORed // Merging of filters that should be ORed
const mergedFilters = useMemo(() => { const mergedFilters = useMemo(() => {
const value: { arg: SearchFilterArgs; removalIndex: number | null }[] = fixedFilters.map( const value: { arg: SearchFilterArgs; removalIndex: number | null }[] = [];
(arg) => ({
arg,
removalIndex: null
})
);
for (const [index, arg] of dynamicFilters.entries()) { for (const [index, arg] of filters.entries()) {
const filter = filterRegistry.find((f) => f.extract(arg)); const filter = filterRegistry.find((f) => f.extract(arg));
if (!filter) continue; if (!filter) continue;
const fixedEquivalentIndex = fixedFilters.findIndex( value.push({
(a) => filter.extract(a) !== undefined arg,
); removalIndex: index
});
if (fixedEquivalentIndex !== -1) {
const merged = filter.merge(
filter.extract(fixedFilters[fixedEquivalentIndex]!)! as any,
filter.extract(arg)! as any
);
value[fixedEquivalentIndex] = {
arg: filter.create(merged),
removalIndex: fixedEquivalentIndex
};
} else {
value.push({
arg,
removalIndex: index
});
}
} }
return value; return value;
}, [fixedFilters, dynamicFilters]); }, [filters]);
// Filters generated from the search query // Filters generated from the search query
@ -167,17 +120,16 @@ export function useSearch(props?: UseSearchProps) {
return { return {
open: props?.open || searchBarFocused, open: props?.open || searchBarFocused,
fixedFilters,
fixedFiltersKeys,
search, search,
rawSearch, rawSearch,
setSearch: setRawSearch, setSearch: setRawSearch,
searchBarFocused, searchBarFocused,
setSearchBarFocused, setSearchBarFocused,
dynamicFilters, defaultFilters: props?.defaultFilters,
setDynamicFilters, filters,
updateDynamicFilters, setFilters,
dynamicFiltersKeys, updateFilters,
filtersKeys,
mergedFilters, mergedFilters,
allFilters, allFilters,
allFiltersKeys allFiltersKeys

View file

@ -82,13 +82,13 @@ function EditForm({ savedSearch, onDelete }: { savedSearch: SavedSearch; onDelet
updateSavedSearch.mutate([savedSearch.id, { name: data.name ?? '' }]); updateSavedSearch.mutate([savedSearch.id, { name: data.name ?? '' }]);
}); });
const fixedFilters = useMemo(() => { const filters = useMemo(() => {
if (savedSearch.filters === null) return []; if (savedSearch.filters === null) return [];
return JSON.parse(savedSearch.filters) as SearchFilterArgs[]; return JSON.parse(savedSearch.filters) as SearchFilterArgs[];
}, [savedSearch.filters]); }, [savedSearch.filters]);
const search = useSearch({ search: savedSearch.search ?? undefined, fixedFilters }); const search = useSearch({ search: savedSearch.search ?? undefined, filters });
return ( return (
<Form form={form}> <Form form={form}>

View file

@ -38,22 +38,20 @@ export function Component() {
const explorerSettingsSnapshot = explorerSettings.useSettingsSnapshot(); const explorerSettingsSnapshot = explorerSettings.useSettingsSnapshot();
const fixedFilters = useMemo(
() => [
{ object: { tags: { in: [tag!.id] } } },
...(explorerSettingsSnapshot.layoutMode === 'media'
? [{ object: { kind: { in: [ObjectKindEnum.Image, ObjectKindEnum.Video] } } }]
: [])
],
[tag, explorerSettingsSnapshot.layoutMode]
);
const search = useSearch({ const search = useSearch({
fixedFilters defaultFilters: [{ object: { tags: { in: [tag!.id] } } }]
}); });
const objects = useObjectsExplorerQuery({ const objects = useObjectsExplorerQuery({
arg: { take: 100, filters: search.allFilters }, arg: {
take: 100,
filters: [
...search.allFilters,
...(explorerSettingsSnapshot.layoutMode === 'media'
? [{ object: { kind: { in: [ObjectKindEnum.Image, ObjectKindEnum.Video] } } }]
: [])
]
},
order: explorerSettings.useSettingsSnapshot().order order: explorerSettings.useSettingsSnapshot().order
}); });