save search and filters at main page to path params

This commit is contained in:
NlightN22 2024-11-30 18:50:37 +07:00
parent ab3bf6f162
commit 88083fba6a
6 changed files with 44 additions and 16 deletions

16
src/hooks/useDebounce.ts Normal file
View File

@ -0,0 +1,16 @@
import { useRef } from "react";
export const useDebounce = <T extends any>(
callback: (args: T) => void,
delay: number
) => {
const debounceRef = useRef<NodeJS.Timeout | null>(null);
return (args: T) => {
if (debounceRef.current) clearTimeout(debounceRef.current);
debounceRef.current = setTimeout(() => {
callback(args);
}, delay);
};
};

View File

@ -2,9 +2,11 @@ import { Flex, Grid } from '@mantine/core';
import { IconSearch } from '@tabler/icons-react';
import { useQuery } from '@tanstack/react-query';
import { observer } from 'mobx-react-lite';
import { useContext, useEffect, useMemo, useState } from 'react';
import { ChangeEvent, useContext, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { Context } from '..';
import { useDebounce } from '../hooks/useDebounce';
import { useRealmUser } from '../hooks/useRealmUser';
import { frigateApi, frigateQueryKeys } from '../services/frigate.proxy/frigate.api';
import { GetCameraWHostWConfig } from '../services/frigate.proxy/frigate.schema';
@ -15,7 +17,6 @@ import CameraCard from '../widgets/CameraCard';
import MainFiltersRightSide from '../widgets/sidebars/MainFiltersRightSide';
import { SideBarContext } from '../widgets/sidebars/SideBarContext';
import RetryErrorPage from './RetryErrorPage';
import { useSearchParams } from 'react-router-dom';
export const mainPageParams = {
hostId: 'hostId',
@ -25,12 +26,12 @@ export const mainPageParams = {
const MainPage = () => {
const { t } = useTranslation()
const navigate = useNavigate()
const { mainStore } = useContext(Context)
const [searchParams] = useSearchParams()
const { setRightChildren } = useContext(SideBarContext)
const { hostId: selectedHostId, selectedTags } = mainStore.filters
const [searchQuery, setSearchQuery] = useState<string>()
const { hostId: selectedHostId, selectedTags, searchQuery } = mainStore.filters
const [filteredCameras, setFilteredCameras] = useState<GetCameraWHostWConfig[]>()
const realmUser = useRealmUser()
@ -42,14 +43,14 @@ const MainPage = () => {
})
useEffect(() => {
setRightChildren(<MainFiltersRightSide />);
const serializedTags = searchParams.get(mainPageParams.selectedTags)
const deSerializedTags = serializedTags ? mainStore.getArrayParam(serializedTags) : []
const deSerializedTags = mainStore.getArrayParam(mainPageParams.selectedTags)
mainStore.loadFiltersFromPage({
hostId: searchParams.get(mainPageParams.hostId) || undefined,
searchQuery: searchParams.get(mainPageParams.searchQuery) || undefined,
selectedTags: deSerializedTags,
})
setRightChildren(<MainFiltersRightSide />);
return () => setRightChildren(null);
}, []);
@ -62,7 +63,7 @@ const MainPage = () => {
const filterCameras = (camera: GetCameraWHostWConfig) => {
const matchesHostId = selectedHostId ? camera.frigateHost?.id === selectedHostId : true
const matchesSearchQuery = searchQuery ? camera.name.toLowerCase().includes(searchQuery.toLowerCase()) : true
const matchesTags = selectedTags.length === 0 || camera.tags.some( tag => selectedTags.includes(tag.id))
const matchesTags = selectedTags ? selectedTags.length === 0 || camera.tags.some(tag => selectedTags.includes(tag.id)) : true
return matchesHostId && matchesSearchQuery && matchesTags
}
@ -94,6 +95,14 @@ const MainPage = () => {
else return []
}, [cameras, filteredCameras])
const debouncedHandleSearchQuery = useDebounce((value: string) => {
mainStore.setSearchQuery(value, navigate);
}, 600);
const onInputChange = (event: ChangeEvent<HTMLInputElement>) => {
debouncedHandleSearchQuery(event.currentTarget.value)
}
if (isPending) return <CenterLoader />
if (isError) return <RetryErrorPage onRetry={refetch} />
@ -111,8 +120,8 @@ const MainPage = () => {
style={{ flexGrow: 1 }}
placeholder={t('search')}
icon={<IconSearch size="0.9rem" stroke={1.5} />}
value={searchQuery}
onChange={(event) => setSearchQuery(event.currentTarget.value)}
value={searchQuery || undefined}
onChange={onInputChange}
/>
</Flex>
<Flex justify='center' h='100%' direction='column' w='100%' >

View File

@ -14,17 +14,19 @@ import { IconAlertCircle } from '@tabler/icons-react';
interface UserTagsFilterProps {
selectedTags?: string[]
onChange?(tagIds: string[]): void
}
const UserTagsFilter: React.FC<UserTagsFilterProps> = ({
selectedTags,
onChange
}) => {
const { t } = useTranslation()
const queryClient = useQueryClient()
const [selectedList, setSelectedList] = useState<string[]>([])
const [selectedList, setSelectedList] = useState<string[]>(selectedTags || [])
useEffect(() => {
if (onChange) onChange(selectedList)

View File

@ -15,7 +15,7 @@ const ClearableTextInput: React.FC<ClearableTextInputProps> = ({
const [text, setText] = useState(value)
const handleClear = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
setText(''); // обновляем локальное состояние
setText('')
if (onChange) {
const fakeEvent = {
target: { value: '' },

View File

@ -7,18 +7,18 @@ import { isProduction } from "../env.const";
interface Filters {
hostId?: string | null
searchQuery?: string | null
selectedTags: string[]
selectedTags?: string[]
}
export class MainStore {
filters: Filters = { selectedTags: []}
filters: Filters = {}
constructor() {
makeAutoObservable(this)
}
loadFiltersFromPage(filters: Filters) {
if (!isProduction) console.log('MainPage load filters')
if (!isProduction) console.log('MainPage load filters', filters)
this.filters = filters
}

View File

@ -13,7 +13,7 @@ const MainFiltersRightSide = () => {
const navigate = useNavigate()
const { mainStore } = useContext(Context)
const { hostId } = mainStore.filters
const { hostId, selectedTags } = mainStore.filters
const handleSelectHost = (value: string) => {
if (!isProduction) console.log('handleSelectHost value', value)
@ -35,6 +35,7 @@ const MainFiltersRightSide = () => {
/>
<UserTagsFilter
selectedTags={selectedTags}
onChange={handleSelectTags}
/>