parent
7ef4194bbd
commit
48a48f9364
0
front/src/api/announcement/index.ts
Normal file
0
front/src/api/announcement/index.ts
Normal file
59
front/src/api/announcement/types.ts
Normal file
59
front/src/api/announcement/types.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { isObject } from '../../utils/types'
|
||||||
|
import { Category, isCategory } from '../../assets/category'
|
||||||
|
|
||||||
|
type AnnouncementResponse = {
|
||||||
|
id: number,
|
||||||
|
user_id: number,
|
||||||
|
name: string,
|
||||||
|
category: Category,
|
||||||
|
best_by: number,
|
||||||
|
address: string,
|
||||||
|
longtitude: number,
|
||||||
|
latitude: number,
|
||||||
|
description: string,
|
||||||
|
src: string | null,
|
||||||
|
metro: string,
|
||||||
|
trashId: number | null,
|
||||||
|
booked_by: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const isAnnouncementResponse = (obj: unknown): obj is AnnouncementResponse => isObject(obj, {
|
||||||
|
'id': 'number',
|
||||||
|
'user_id': 'number',
|
||||||
|
'name': 'string',
|
||||||
|
'category': isCategory,
|
||||||
|
'best_by': 'number',
|
||||||
|
'address': 'string',
|
||||||
|
'longtitude': 'number',
|
||||||
|
'latitude': 'number',
|
||||||
|
'description': 'string',
|
||||||
|
'src': 'string?',
|
||||||
|
'metro': 'string',
|
||||||
|
'trashId': 'number?',
|
||||||
|
'booked_by': 'number'
|
||||||
|
})
|
||||||
|
|
||||||
|
type Announcement = {
|
||||||
|
id: number,
|
||||||
|
userId: number,
|
||||||
|
name: string,
|
||||||
|
category: Category,
|
||||||
|
bestBy: number,
|
||||||
|
address: string,
|
||||||
|
lng: number,
|
||||||
|
lat: number,
|
||||||
|
description: string | null,
|
||||||
|
src: string | null,
|
||||||
|
metro: string,
|
||||||
|
trashId: number | null,
|
||||||
|
bookedBy: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type {
|
||||||
|
Announcement,
|
||||||
|
AnnouncementResponse,
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
isAnnouncementResponse,
|
||||||
|
}
|
24
front/src/api/announcements/index.ts
Normal file
24
front/src/api/announcements/index.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { API_URL } from '../../config'
|
||||||
|
import { FiltersType, URLEncodeFilters } from '../../utils/filters'
|
||||||
|
import { Announcement } from '../announcement/types'
|
||||||
|
import { AnnouncementsResponse } from './types'
|
||||||
|
|
||||||
|
const initialAnnouncements: Announcement[] = []
|
||||||
|
|
||||||
|
const composeAnnouncementsURL = (filters: FiltersType) =>
|
||||||
|
API_URL + '/announcements?' + new URLSearchParams(URLEncodeFilters(filters)).toString()
|
||||||
|
|
||||||
|
const processAnnouncements = (data: AnnouncementsResponse): Announcement[] => {
|
||||||
|
const annList = data.list_of_announcements
|
||||||
|
|
||||||
|
return annList.map(ann => ({
|
||||||
|
...ann,
|
||||||
|
lat: ann.latitude,
|
||||||
|
lng: ann.longtitude,
|
||||||
|
bestBy: ann.best_by,
|
||||||
|
bookedBy: ann.booked_by,
|
||||||
|
userId: ann.user_id
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
export { initialAnnouncements, composeAnnouncementsURL, processAnnouncements }
|
20
front/src/api/announcements/types.ts
Normal file
20
front/src/api/announcements/types.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { isArrayOf, isObject } from '../../utils/types'
|
||||||
|
import { AnnouncementResponse, isAnnouncementResponse } from '../announcement/types'
|
||||||
|
|
||||||
|
type AnnouncementsResponse = {
|
||||||
|
list_of_announcements: AnnouncementResponse[],
|
||||||
|
Success: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const isAnnouncementsResponse = (obj: unknown): obj is AnnouncementsResponse => isObject(obj, {
|
||||||
|
'list_of_announcements': obj => isArrayOf<AnnouncementResponse>(obj, isAnnouncementResponse),
|
||||||
|
'Success': 'boolean'
|
||||||
|
})
|
||||||
|
|
||||||
|
export type {
|
||||||
|
AnnouncementsResponse,
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
isAnnouncementsResponse,
|
||||||
|
}
|
11
front/src/api/trashbox/index.ts
Normal file
11
front/src/api/trashbox/index.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { LatLng } from 'leaflet'
|
||||||
|
|
||||||
|
import { API_URL } from '../../config'
|
||||||
|
|
||||||
|
const composeTrashboxURL = (position: LatLng) =>
|
||||||
|
API_URL + '/trashbox?' + new URLSearchParams({
|
||||||
|
lat: position.lat.toString(),
|
||||||
|
lng: position.lng.toString()
|
||||||
|
}).toString()
|
||||||
|
|
||||||
|
export { composeTrashboxURL }
|
22
front/src/api/trashbox/types.ts
Normal file
22
front/src/api/trashbox/types.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { isArrayOf, isObject, isString } from '../../utils/types'
|
||||||
|
|
||||||
|
type Trashbox = {
|
||||||
|
Lat: number,
|
||||||
|
Lng: number,
|
||||||
|
Address: string,
|
||||||
|
Categories: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const isTrashbox = (obj: unknown): obj is Trashbox => isObject(obj, {
|
||||||
|
'Lat': 'number',
|
||||||
|
'Lng': 'number',
|
||||||
|
'Address': 'string',
|
||||||
|
'Categories': obj => isArrayOf<string>(obj, isString)
|
||||||
|
})
|
||||||
|
|
||||||
|
type TrashboxResponse = Trashbox[]
|
||||||
|
|
||||||
|
const isTrashboxResponse = (obj: unknown): obj is Trashbox[] => isArrayOf(obj, isTrashbox)
|
||||||
|
|
||||||
|
export type { Trashbox, TrashboxResponse }
|
||||||
|
export { isTrashbox, isTrashboxResponse }
|
@ -4,7 +4,7 @@ import { MapContainer, Marker, Popup, TileLayer } from 'react-leaflet'
|
|||||||
import LineDot from './LineDot'
|
import LineDot from './LineDot'
|
||||||
import { categoryNames } from '../assets/category'
|
import { categoryNames } from '../assets/category'
|
||||||
import { useBook } from '../hooks/api'
|
import { useBook } from '../hooks/api'
|
||||||
import { Announcement } from '../hooks/api/useHomeAnnouncementList'
|
import { Announcement } from '../hooks/api/useAnnouncements'
|
||||||
import { iconItem } from '../utils/markerIcons'
|
import { iconItem } from '../utils/markerIcons'
|
||||||
|
|
||||||
type AnnouncementDetailsProps = {
|
type AnnouncementDetailsProps = {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
export { default as useHomeAnnouncementList } from './useHomeAnnouncementList'
|
export { default as useAnnouncements } from './useAnnouncements'
|
||||||
export { default as useBook } from './useBook'
|
export { default as useBook } from './useBook'
|
||||||
export { default as useAuth } from './useAuth'
|
export { default as useAuth } from './useAuth'
|
||||||
export { default as useTrashboxes } from './useTrashboxes'
|
export { default as useTrashboxes } from './useTrashboxes'
|
||||||
|
18
front/src/hooks/api/useAnnouncements.ts
Normal file
18
front/src/hooks/api/useAnnouncements.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import useFetch from './useFetch'
|
||||||
|
import { FiltersType } from '../../utils/filters'
|
||||||
|
import { composeAnnouncementsURL, initialAnnouncements, processAnnouncements } from '../../api/announcements'
|
||||||
|
|
||||||
|
import { isAnnouncementsResponse } from '../../api/announcements/types'
|
||||||
|
|
||||||
|
const useAnnouncements = (filters: FiltersType) => {
|
||||||
|
return useFetch(
|
||||||
|
composeAnnouncementsURL(filters),
|
||||||
|
'GET',
|
||||||
|
false,
|
||||||
|
processAnnouncements,
|
||||||
|
isAnnouncementsResponse,
|
||||||
|
initialAnnouncements
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useAnnouncements
|
@ -1,11 +1,53 @@
|
|||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
|
|
||||||
import { handleHTTPErrors, isAborted } from '../../utils'
|
import { handleHTTPErrors, isAborted } from '../../utils'
|
||||||
|
import { getToken } from '../../utils/auth'
|
||||||
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
|
||||||
const useFetch = <T>(url: string, params: RequestInit | undefined, initialData: T, dataGuard: (obj: unknown) => obj is T) => {
|
type UseFetchShared = {
|
||||||
|
loading: boolean,
|
||||||
|
abort?: () => void,
|
||||||
|
}
|
||||||
|
|
||||||
|
type UseFetchSucced<T> = {
|
||||||
|
error: null,
|
||||||
|
data: T,
|
||||||
|
} & UseFetchShared
|
||||||
|
|
||||||
|
type UseFetchErrored = {
|
||||||
|
error: string,
|
||||||
|
data: undefined
|
||||||
|
} & UseFetchShared
|
||||||
|
|
||||||
|
const gotError = <T>(res: UseFetchErrored | UseFetchSucced<T>): res is UseFetchErrored => {
|
||||||
|
return typeof res.error === 'string'
|
||||||
|
}
|
||||||
|
|
||||||
|
type UseFetchReturn<T> = ({
|
||||||
|
error: null,
|
||||||
|
data: T
|
||||||
|
} | {
|
||||||
|
error: string,
|
||||||
|
data: undefined
|
||||||
|
}) & {
|
||||||
|
loading: boolean,
|
||||||
|
abort?: (() => void)
|
||||||
|
}
|
||||||
|
|
||||||
|
const useFetch = <R, T>(
|
||||||
|
url: string,
|
||||||
|
method: RequestInit['method'],
|
||||||
|
needAuth: boolean,
|
||||||
|
processData: (data: R) => T,
|
||||||
|
guardResponse: (data: unknown) => data is R,
|
||||||
|
initialData?: T,
|
||||||
|
params?: RequestInit
|
||||||
|
): UseFetchReturn<T> => {
|
||||||
const [data, setData] = useState(initialData)
|
const [data, setData] = useState(initialData)
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
const [error, setError] = useState('')
|
const [error, setError] = useState<string | null>(null)
|
||||||
|
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
const abortControllerRef = useRef<AbortController>()
|
const abortControllerRef = useRef<AbortController>()
|
||||||
|
|
||||||
@ -17,39 +59,65 @@ const useFetch = <T>(url: string, params: RequestInit | undefined, initialData:
|
|||||||
const abortController = new AbortController()
|
const abortController = new AbortController()
|
||||||
abortControllerRef.current = abortController
|
abortControllerRef.current = abortController
|
||||||
|
|
||||||
fetch(url, { ...params, signal: abortControllerRef.current.signal })
|
const headers = new Headers({
|
||||||
|
...params?.headers
|
||||||
|
})
|
||||||
|
|
||||||
|
if (needAuth) {
|
||||||
|
const token = getToken()
|
||||||
|
|
||||||
|
if (token === null) {
|
||||||
|
return navigate('/login')
|
||||||
|
}
|
||||||
|
|
||||||
|
headers.append('Auth', `Bearer ${token}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(url, {
|
||||||
|
method,
|
||||||
|
...params,
|
||||||
|
headers,
|
||||||
|
signal: abortControllerRef.current.signal,
|
||||||
|
})
|
||||||
.then(res => {
|
.then(res => {
|
||||||
handleHTTPErrors(res)
|
handleHTTPErrors(res)
|
||||||
|
|
||||||
return res.json()
|
return res.json()
|
||||||
})
|
})
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (!dataGuard(data)) {
|
if (!guardResponse(data)) {
|
||||||
throw new Error('Неверный ответ от сервера')
|
throw new Error('Malformed server response')
|
||||||
}
|
}
|
||||||
|
|
||||||
setData(data)
|
setData(processData(data))
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
if (err instanceof Error && !isAborted(err)) {
|
if (err instanceof Error && !isAborted(err)) {
|
||||||
setError('Ошибка сети')
|
setError('Ошибка сети')
|
||||||
}
|
|
||||||
|
|
||||||
setLoading(false)
|
|
||||||
|
|
||||||
if (import.meta.env.DEV) {
|
if (import.meta.env.DEV) {
|
||||||
console.log(url, params, err)
|
console.log(url, params, err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
return () => abortControllerRef.current?.abort()
|
return () => abortControllerRef.current?.abort()
|
||||||
}, [url, params, dataGuard])
|
}, [url, method, needAuth, params, guardResponse, processData, navigate])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data, loading, error,
|
...(
|
||||||
|
error === null ? ({
|
||||||
|
data: data!, error: null
|
||||||
|
}) : ({ data: undefined, error })
|
||||||
|
),
|
||||||
|
loading,
|
||||||
abort: abortControllerRef.current?.abort.bind(abortControllerRef.current)
|
abort: abortControllerRef.current?.abort.bind(abortControllerRef.current)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default useFetch
|
export default useFetch
|
||||||
|
|
||||||
|
export { gotError }
|
||||||
|
@ -1,96 +0,0 @@
|
|||||||
import useFetch from './useFetch'
|
|
||||||
import { FiltersType, filterNames } from '../../utils/filters'
|
|
||||||
import { isArrayOf, isObject } from '../../utils/types'
|
|
||||||
import { API_URL } from '../../config'
|
|
||||||
import { Category, isCategory } from '../../assets/category'
|
|
||||||
|
|
||||||
const initialAnnouncements = { list_of_announcements: [], Success: true }
|
|
||||||
|
|
||||||
type AnnouncementsListResponse = {
|
|
||||||
list_of_announcements: AnnouncementResponse[],
|
|
||||||
Success: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
const isAnnouncementsListResponse = (obj: unknown): obj is AnnouncementsListResponse => isObject(obj, {
|
|
||||||
'list_of_announcements': obj => isArrayOf<AnnouncementResponse>(obj, isAnnouncementResponse),
|
|
||||||
'Success': 'boolean'
|
|
||||||
})
|
|
||||||
|
|
||||||
type AnnouncementResponse = {
|
|
||||||
id: number,
|
|
||||||
user_id: number,
|
|
||||||
name: string,
|
|
||||||
category: Category,
|
|
||||||
best_by: number,
|
|
||||||
address: string,
|
|
||||||
longtitude: number,
|
|
||||||
latitude: number,
|
|
||||||
description: string,
|
|
||||||
src: string | null,
|
|
||||||
metro: string,
|
|
||||||
trashId: number | null,
|
|
||||||
booked_by: number
|
|
||||||
}
|
|
||||||
|
|
||||||
const isAnnouncementResponse = (obj: unknown): obj is AnnouncementResponse => isObject(obj, {
|
|
||||||
'id': 'number',
|
|
||||||
'user_id': 'number',
|
|
||||||
'name': 'string',
|
|
||||||
'category': isCategory,
|
|
||||||
'best_by': 'number',
|
|
||||||
'address': 'string',
|
|
||||||
'longtitude': 'number',
|
|
||||||
'latitude': 'number',
|
|
||||||
'description': 'string',
|
|
||||||
'src': 'string?',
|
|
||||||
'metro': 'string',
|
|
||||||
'trashId': 'number?',
|
|
||||||
'booked_by': 'number'
|
|
||||||
})
|
|
||||||
|
|
||||||
type Announcement = {
|
|
||||||
id: number,
|
|
||||||
userId: number,
|
|
||||||
name: string,
|
|
||||||
category: Category,
|
|
||||||
bestBy: number,
|
|
||||||
address: string,
|
|
||||||
lng: number,
|
|
||||||
lat: number,
|
|
||||||
description: string | null,
|
|
||||||
src: string | null,
|
|
||||||
metro: string,
|
|
||||||
trashId: number | null,
|
|
||||||
bookedBy: number
|
|
||||||
}
|
|
||||||
|
|
||||||
const composeFilters = (filters: FiltersType) => Object.fromEntries(
|
|
||||||
filterNames.map(
|
|
||||||
fName => [fName, filters[fName]?.toString()]
|
|
||||||
).filter((p): p is [string, string] => typeof p[1] !== 'undefined')
|
|
||||||
)
|
|
||||||
|
|
||||||
const useHomeAnnouncementList = (filters: FiltersType) => {
|
|
||||||
const { data, loading, error } = useFetch(
|
|
||||||
API_URL + '/announcements?' + new URLSearchParams(composeFilters(filters)).toString(),
|
|
||||||
undefined,
|
|
||||||
initialAnnouncements,
|
|
||||||
isAnnouncementsListResponse
|
|
||||||
)
|
|
||||||
|
|
||||||
const annList = data.list_of_announcements
|
|
||||||
|
|
||||||
const res: Announcement[] = annList.map(ann => ({
|
|
||||||
...ann,
|
|
||||||
lat: ann.latitude,
|
|
||||||
lng: ann.longtitude,
|
|
||||||
bestBy: ann.best_by,
|
|
||||||
bookedBy: ann.booked_by,
|
|
||||||
userId: ann.user_id
|
|
||||||
}))
|
|
||||||
|
|
||||||
return { data: error ? [] : res, loading, error }
|
|
||||||
}
|
|
||||||
|
|
||||||
export type { Announcement, AnnouncementsListResponse }
|
|
||||||
export default useHomeAnnouncementList
|
|
@ -1,35 +1,18 @@
|
|||||||
import { LatLng } from 'leaflet'
|
import { LatLng } from 'leaflet'
|
||||||
|
|
||||||
import { API_URL } from '../../config'
|
|
||||||
import { isArrayOf, isObject } from '../../utils/types'
|
|
||||||
import useFetch from './useFetch'
|
import useFetch from './useFetch'
|
||||||
import { isString } from '../../utils/types'
|
import { composeTrashboxURL } from '../../api/trashbox'
|
||||||
|
import { isTrashboxResponse } from '../../api/trashbox/types'
|
||||||
type Trashbox = {
|
|
||||||
Lat: number,
|
|
||||||
Lng: number,
|
|
||||||
Address: string,
|
|
||||||
Categories: string[]
|
|
||||||
}
|
|
||||||
|
|
||||||
const isTrashbox = (obj: unknown): obj is Trashbox => isObject(obj, {
|
|
||||||
'Lat': 'number',
|
|
||||||
'Lng': 'number',
|
|
||||||
'Address': 'string',
|
|
||||||
'Categories': obj => isArrayOf<string>(obj, isString)
|
|
||||||
})
|
|
||||||
|
|
||||||
const useTrashboxes = (position: LatLng) => {
|
const useTrashboxes = (position: LatLng) => {
|
||||||
return useFetch(
|
return useFetch(
|
||||||
API_URL + '/trashbox?' + new URLSearchParams({
|
composeTrashboxURL(position),
|
||||||
lat: position.lat.toString(),
|
'GET',
|
||||||
lng: position.lng.toString()
|
true,
|
||||||
}).toString(),
|
(data) => data,
|
||||||
undefined,
|
isTrashboxResponse,
|
||||||
[],
|
[]
|
||||||
(obj): obj is Trashbox[] => isArrayOf(obj, isTrashbox)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export type { Trashbox }
|
|
||||||
export default useTrashboxes
|
export default useTrashboxes
|
||||||
|
@ -9,6 +9,7 @@ import { isObject } from '../utils/types'
|
|||||||
import { handleHTTPErrors } from '../utils'
|
import { handleHTTPErrors } from '../utils'
|
||||||
import { categories, categoryNames } from '../assets/category'
|
import { categories, categoryNames } from '../assets/category'
|
||||||
import { stations, lines, lineNames } from '../assets/metro'
|
import { stations, lines, lineNames } from '../assets/metro'
|
||||||
|
import { gotError } from '../hooks/api/useFetch'
|
||||||
|
|
||||||
function AddPage() {
|
function AddPage() {
|
||||||
const [addressPosition, setAddressPosition] = useState(latLng(59.972, 30.3227))
|
const [addressPosition, setAddressPosition] = useState(latLng(59.972, 30.3227))
|
||||||
@ -165,7 +166,7 @@ function AddPage() {
|
|||||||
<p>Загрузка...</p>
|
<p>Загрузка...</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
trashboxes.error ? (
|
gotError(trashboxes) ? (
|
||||||
<p
|
<p
|
||||||
style={{ height: 400 }}
|
style={{ height: 400 }}
|
||||||
className='text-danger'
|
className='text-danger'
|
||||||
@ -191,7 +192,7 @@ function AddPage() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
{selectedTrashbox.index > -1 ? (
|
{!gotError(trashboxes) && selectedTrashbox.index > -1 ? (
|
||||||
<p>Выбран пункт сбора мусора на {
|
<p>Выбран пункт сбора мусора на {
|
||||||
trashboxes.data[selectedTrashbox.index].Address
|
trashboxes.data[selectedTrashbox.index].Address
|
||||||
} с категорией {selectedTrashbox.category}</p>
|
} с категорией {selectedTrashbox.category}</p>
|
||||||
|
@ -4,12 +4,14 @@ 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 { useStoryDimensions } from '../hooks'
|
||||||
import { useHomeAnnouncementList } from '../hooks/api'
|
import { useAnnouncements } from '../hooks/api'
|
||||||
import { defaultFilters } from '../utils/filters'
|
import { defaultFilters } from '../utils/filters'
|
||||||
import { Announcement } from '../hooks/api/useHomeAnnouncementList'
|
import { Announcement } from '../api/announcement/types'
|
||||||
import puffSpinner from '../assets/puff.svg'
|
|
||||||
import { categoryGraphics } from '../assets/category'
|
import { categoryGraphics } from '../assets/category'
|
||||||
|
|
||||||
|
import puffSpinner from '../assets/puff.svg'
|
||||||
|
import { gotError } from '../hooks/api/useFetch'
|
||||||
|
|
||||||
function generateStories(announcements: Announcement[]): Story[] {
|
function generateStories(announcements: Announcement[]): Story[] {
|
||||||
return announcements.map(announcement => {
|
return announcements.map(announcement => {
|
||||||
return ({
|
return ({
|
||||||
@ -21,15 +23,15 @@ function generateStories(announcements: Announcement[]): Story[] {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function fallbackGenerateStories(announcementsFetch: ReturnType<typeof useHomeAnnouncementList>) {
|
function fallbackGenerateStories(announcementsFetch: ReturnType<typeof useAnnouncements>) {
|
||||||
const stories = generateStories(announcementsFetch.data)
|
|
||||||
|
|
||||||
if (announcementsFetch.loading)
|
if (announcementsFetch.loading)
|
||||||
return fallbackStory()
|
return fallbackStory()
|
||||||
|
|
||||||
if (announcementsFetch.error)
|
if (gotError(announcementsFetch))
|
||||||
return fallbackStory(announcementsFetch.error, true)
|
return fallbackStory(announcementsFetch.error, true)
|
||||||
|
|
||||||
|
const stories = generateStories(announcementsFetch.data)
|
||||||
|
|
||||||
if (stories.length === 0)
|
if (stories.length === 0)
|
||||||
return fallbackStory('Здесь пока пусто')
|
return fallbackStory('Здесь пока пусто')
|
||||||
|
|
||||||
@ -49,19 +51,25 @@ const fallbackStory = (text = '', isError = false): Story[] => [{
|
|||||||
},
|
},
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
const storiesContainerCSS = {
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
backgroundColor: 'rgb(17, 17, 17)'
|
||||||
|
}
|
||||||
|
|
||||||
function HomePage() {
|
function HomePage() {
|
||||||
const { height, width } = useStoryDimensions(16 / 10)
|
const { height, width } = useStoryDimensions(16 / 10)
|
||||||
|
|
||||||
const [filterShown, setFilterShown] = useState(false)
|
const [filterShown, setFilterShown] = useState(false)
|
||||||
const [filter, setFilter] = useState(defaultFilters)
|
const [filter, setFilter] = useState(defaultFilters)
|
||||||
|
|
||||||
const announcementsFetch = useHomeAnnouncementList(filter)
|
const announcementsFetch = useAnnouncements(filter)
|
||||||
|
|
||||||
const stories = fallbackGenerateStories(announcementsFetch)
|
const stories = fallbackGenerateStories(announcementsFetch)
|
||||||
|
|
||||||
return (<>
|
return (<>
|
||||||
<Filters filter={filter} setFilter={setFilter} filterShown={filterShown} setFilterShown={setFilterShown} />
|
<Filters filter={filter} setFilter={setFilter} filterShown={filterShown} setFilterShown={setFilterShown} />
|
||||||
<div style={{ display: 'flex', justifyContent: 'center', backgroundColor: 'rgb(17, 17, 17)' }}>
|
<div style={storiesContainerCSS}>
|
||||||
<Stories
|
<Stories
|
||||||
stories={stories}
|
stories={stories}
|
||||||
defaultInterval={11000}
|
defaultInterval={11000}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Announcement } from '../hooks/api/useHomeAnnouncementList'
|
import { Announcement } from '../api/announcement/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]
|
||||||
@ -7,5 +7,11 @@ type FiltersType = Partial<Pick<Announcement, FilterNames>>
|
|||||||
|
|
||||||
const defaultFilters: FiltersType = { userId: undefined, category: undefined, metro: undefined, bookedBy: undefined }
|
const defaultFilters: FiltersType = { userId: undefined, category: undefined, metro: undefined, bookedBy: undefined }
|
||||||
|
|
||||||
|
const URLEncodeFilters = (filters: FiltersType) => Object.fromEntries(
|
||||||
|
filterNames.map(
|
||||||
|
fName => [fName, filters[fName]?.toString()]
|
||||||
|
).filter((p): p is [string, string] => typeof p[1] !== 'undefined')
|
||||||
|
)
|
||||||
|
|
||||||
export type { FilterNames, FiltersType }
|
export type { FilterNames, FiltersType }
|
||||||
export { defaultFilters, filterNames }
|
export { defaultFilters, filterNames, URLEncodeFilters }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user