Added query filters getting and setting on homepage

fixes #27
This commit is contained in:
Dmitriy Shishkov 2023-07-20 14:58:05 +03:00
parent 96388a9bea
commit 9b4ef41030
Signed by: dm1sh
GPG Key ID: 027994B0AA357688
5 changed files with 82 additions and 8 deletions

View File

@ -1,4 +1,5 @@
export { default as useStoryDimensions } from './useStoryDimensions' export { default as useStoryDimensions } from './useStoryDimensions'
export { default as useSend } from './useSend' export { default as useSend } from './useSend'
export { default as useFetch } from './useFetch' export { default as useFetch } from './useFetch'
export { default as UseStoryIndex } from './useStoryIndex' export { default as useStoryIndex } from './useStoryIndex'
export { default as useFilters } from './useFilters'

View File

@ -0,0 +1,47 @@
import { useEffect, useState } from 'react'
import { useSearchParams } from 'react-router-dom'
import { FiltersType, URLDecoreFilters, URLEncodeFilters, defaultFilters } from '../utils/filters'
import { SetState } from '../utils/types'
function useFilters(initialFilters: FiltersType = defaultFilters): [FiltersType, SetState<FiltersType>] {
const [searchParams, setSearchParams] = useSearchParams()
const [filters, setFilters] = useState(initialFilters)
const appendFiltersSearchParams = (filters: FiltersType) => (
setSearchParams(params => ({
...Object.fromEntries(params),
...URLEncodeFilters(filters)
}), { replace: true })
)
useEffect(() => {
const urlFilters = URLDecoreFilters(searchParams)
setFilters(prev => ({
...prev,
...urlFilters,
}))
appendFiltersSearchParams({
...URLEncodeFilters(initialFilters),
...URLEncodeFilters(urlFilters),
})
// searchParams have actual query string at first render
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
const withQuery = (f: SetState<FiltersType>) => (
(nextInit: (FiltersType | ((prev: FiltersType) => FiltersType))) => {
const newFilters = (typeof nextInit === 'function') ? nextInit(filters) : nextInit
appendFiltersSearchParams(newFilters)
f(nextInit)
}
)
return [filters, withQuery(setFilters)]
}
export default useFilters

View File

@ -3,15 +3,14 @@ import Stories from 'react-insta-stories'
import { Story } from 'react-insta-stories/dist/interfaces' import { Story } from 'react-insta-stories/dist/interfaces'
import { BottomNavBar, AnnouncementDetails, Filters } from '../components' import { BottomNavBar, AnnouncementDetails, Filters } from '../components'
import { useStoryDimensions } from '../hooks' import { useFilters, useStoryDimensions } from '../hooks'
import { useAnnouncements } from '../hooks/api' import { useAnnouncements } from '../hooks/api'
import { defaultFilters } from '../utils/filters'
import { Announcement } from '../api/announcement/types' import { Announcement } from '../api/announcement/types'
import { categoryGraphics } from '../assets/category' import { categoryGraphics } from '../assets/category'
import { UseFetchReturn, gotError } from '../hooks/useFetch'
import { useStoryIndex } from '../hooks'
import puffSpinner from '../assets/puff.svg' import puffSpinner from '../assets/puff.svg'
import { UseFetchReturn, gotError } from '../hooks/useFetch'
import useStoryIndex from '../hooks/useStoryIndex'
function generateStories(announcements: Announcement[]): Story[] { function generateStories(announcements: Announcement[]): Story[] {
return announcements.map(announcement => { return announcements.map(announcement => {
@ -67,8 +66,8 @@ function HomePage() {
const { height, width } = useStoryDimensions(16 / 9) const { height, width } = useStoryDimensions(16 / 9)
const [filterShown, setFilterShown] = useState(false) const [filterShown, setFilterShown] = useState(false)
const [filter, setFilter] = useState(defaultFilters)
const [filter, setFilter] = useFilters()
const announcements = useAnnouncements(filter) const announcements = useAnnouncements(filter)

View File

@ -1,4 +1,6 @@
import { Announcement } from '../api/announcement/types' import { Announcement } from '../api/announcement/types'
import { isCategory } from '../assets/category'
import { fallbackToUndefined, isInt } from './types'
const filterNames = ['userId', 'category', 'metro', 'bookedBy'] as const const filterNames = ['userId', 'category', 'metro', 'bookedBy'] as const
type FilterNames = typeof filterNames[number] type FilterNames = typeof filterNames[number]
@ -21,5 +23,20 @@ const URLEncodeFilters = (filters: FiltersType) => (
) )
) )
const URLDecoreFilters = (params: URLSearchParams): FiltersType => {
const strFilters = Object.fromEntries(
filterNames.map(
fName => [fName, params.get(fName)]
).filter((p): p is [string, string] => p[1] !== null)
)
return {
bookedBy: fallbackToUndefined(strFilters['bookedBy'], isInt),
category: fallbackToUndefined(strFilters['category'], isCategory),
metro: strFilters['metro'],
userId: fallbackToUndefined(strFilters['userId'], isInt)
}
}
export type { FilterNames, FiltersType } export type { FilterNames, FiltersType }
export { defaultFilters, filterNames, URLEncodeFilters } export { defaultFilters, filterNames, URLEncodeFilters, URLDecoreFilters }

View File

@ -60,8 +60,18 @@ const isString = (obj: unknown): obj is string => (
typeof obj === 'string' typeof obj === 'string'
) )
const isInt = (obj: unknown): obj is number => (
Number.isSafeInteger(obj)
)
function fallbackToUndefined<T>(obj: unknown, isT: ((obj: unknown) => obj is T)) {
if (!isT(obj)) return undefined
return obj
}
type SetState<T> = React.Dispatch<React.SetStateAction<T>> type SetState<T> = React.Dispatch<React.SetStateAction<T>>
export type { SetState } export type { SetState }
export { isRecord, isObject, isConst, isLiteralUnion, isArrayOf, isString } export { isRecord, isObject, isConst, isLiteralUnion, isArrayOf, isString, isInt, fallbackToUndefined }