parent
96388a9bea
commit
9b4ef41030
@ -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'
|
||||||
|
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 { 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)
|
||||||
|
|
||||||
|
@ -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 }
|
||||||
|
@ -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 }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user