parent
96388a9bea
commit
9b4ef41030
@ -1,4 +1,5 @@
|
||||
export { default as useStoryDimensions } from './useStoryDimensions'
|
||||
export { default as useSend } from './useSend'
|
||||
export { default as useFetch } from './useFetch'
|
||||
export { default as UseStoryIndex } from './useStoryIndex'
|
||||
export { default as useStoryIndex } from './useStoryIndex'
|
||||
export { default as useFilters } from './useFilters'
|
||||
|
47
front/src/hooks/useFilters.ts
Normal file
47
front/src/hooks/useFilters.ts
Normal 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
|
@ -3,15 +3,14 @@ import Stories from 'react-insta-stories'
|
||||
import { Story } from 'react-insta-stories/dist/interfaces'
|
||||
|
||||
import { BottomNavBar, AnnouncementDetails, Filters } from '../components'
|
||||
import { useStoryDimensions } from '../hooks'
|
||||
import { useFilters, useStoryDimensions } from '../hooks'
|
||||
import { useAnnouncements } from '../hooks/api'
|
||||
import { defaultFilters } from '../utils/filters'
|
||||
import { Announcement } from '../api/announcement/types'
|
||||
import { categoryGraphics } from '../assets/category'
|
||||
import { UseFetchReturn, gotError } from '../hooks/useFetch'
|
||||
import { useStoryIndex } from '../hooks'
|
||||
|
||||
import puffSpinner from '../assets/puff.svg'
|
||||
import { UseFetchReturn, gotError } from '../hooks/useFetch'
|
||||
import useStoryIndex from '../hooks/useStoryIndex'
|
||||
|
||||
function generateStories(announcements: Announcement[]): Story[] {
|
||||
return announcements.map(announcement => {
|
||||
@ -67,8 +66,8 @@ function HomePage() {
|
||||
const { height, width } = useStoryDimensions(16 / 9)
|
||||
|
||||
const [filterShown, setFilterShown] = useState(false)
|
||||
const [filter, setFilter] = useState(defaultFilters)
|
||||
|
||||
const [filter, setFilter] = useFilters()
|
||||
|
||||
const announcements = useAnnouncements(filter)
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
import { Announcement } from '../api/announcement/types'
|
||||
import { isCategory } from '../assets/category'
|
||||
import { fallbackToUndefined, isInt } from './types'
|
||||
|
||||
const filterNames = ['userId', 'category', 'metro', 'bookedBy'] as const
|
||||
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 { defaultFilters, filterNames, URLEncodeFilters }
|
||||
export { defaultFilters, filterNames, URLEncodeFilters, URLDecoreFilters }
|
||||
|
@ -60,8 +60,18 @@ const isString = (obj: unknown): obj is 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>>
|
||||
|
||||
export type { SetState }
|
||||
|
||||
export { isRecord, isObject, isConst, isLiteralUnion, isArrayOf, isString }
|
||||
export { isRecord, isObject, isConst, isLiteralUnion, isArrayOf, isString, isInt, fallbackToUndefined }
|
||||
|
Loading…
x
Reference in New Issue
Block a user