Skip to content

Commit

Permalink
Merge pull request #50 from TurtIeSocks/fix-route-exporting
Browse files Browse the repository at this point in the history
fix route exporting page
  • Loading branch information
TurtIeSocks authored Jan 1, 2023
2 parents 1a645c8 + bb28929 commit 538b560
Show file tree
Hide file tree
Showing 20 changed files with 312 additions and 123 deletions.
41 changes: 40 additions & 1 deletion client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,28 @@ import { CssBaseline, ThemeProvider } from '@mui/material'
import { createBrowserRouter, RouterProvider } from 'react-router-dom'

import createTheme from '@assets/theme'
import { Config } from '@assets/types'
import { usePersist } from '@hooks/usePersist'
import { useStatic } from '@hooks/useStatic'
import { getData } from '@services/fetches'

import Home from '@pages/home'
import Home from '@pages/Home'
import Map from '@pages/map'
import AdminPanel from '@pages/admin'
import ErrorPage from '@pages/Error'
import Login from '@pages/Login'

const router = createBrowserRouter([
{
path: '/',
element: <Home />,
errorElement: <ErrorPage error="500" />,
},
{
path: '/login',
element: <Login />,
errorElement: <ErrorPage error="500" />,
},
{
path: '/map',
element: <Map />,
Expand All @@ -42,10 +51,40 @@ export default function App() {
return newTheme
}, [darkMode])

const { location, setStore } = usePersist.getState()
const { setStatic } = useStatic.getState()

const [fetched, setFetched] = React.useState<boolean>(false)
const [error, setError] = React.useState<string>('')

React.useEffect(() => {
getData<Config>('/config/').then((res) => {
if (res) {
if (res.logged_in) {
if (location[0] === 0 && location[1] === 0) {
setStore('location', [res.start_lat, res.start_lon])
}
setStatic('scannerType', res.scanner_type)
if (res.tile_server) {
setStatic('tileServer', res.tile_server)
}
} else {
router.navigate('/login')
}
setFetched(true)
} else {
setError('Unable to fetch config, try again later')
}
})
}, [])

if (!fetched) return null

return (
<ThemeProvider theme={theme}>
<CssBaseline />
<RouterProvider router={router} />
{error && <ErrorPage error={error} />}
</ThemeProvider>
)
}
16 changes: 13 additions & 3 deletions client/src/components/Loading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,19 @@ export default function Loading() {
</Grid2>
) : (
<Grid2 xs={6}>
<CircularProgress
size={`calc(100vh / ${Object.keys(loading).length * 3})`}
/>
{typeof value === 'boolean' ? (
<>
<Typography color="error">Failed</Typography>
<Typography>
Press F12 to open the browser console to see the logged
error
</Typography>
</>
) : (
<CircularProgress
size={`calc(100vh / ${Object.keys(loading).length * 3})`}
/>
)}
</Grid2>
)}
<Divider
Expand Down
145 changes: 103 additions & 42 deletions client/src/components/dialogs/ExportRoute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,75 @@ import {
} from '@mui/material'
import Grid2 from '@mui/material/Unstable_Grid2/Grid2'
import ContentCopy from '@mui/icons-material/ContentCopy'
import type { Feature, FeatureCollection } from 'geojson'
import useDeepCompareEffect from 'use-deep-compare-effect'
import distance from '@turf/distance'

import { useStatic } from '@hooks/useStatic'
import { convert } from '@services/fetches'

import { usePersist } from '@hooks/usePersist'
import DialogHeader from './Header'

interface Props {
open: string
setOpen: (open: string) => void
geojson: FeatureCollection
}

export default function ExportRoute({ open, setOpen }: Props) {
const exportSettings = usePersist((s) => s.export)
const mode = usePersist((s) => s.mode)
export default function ExportRoute({ open, setOpen, geojson }: Props) {
const scannerType = useStatic((s) => s.scannerType)
const [route, setRoute] = React.useState<number[][][]>([])
const [stats, setStats] = React.useState<{
max: number
total: number
count: number
}>({ max: 0, total: 0, count: 0 })

const total = exportSettings.route.flatMap((route) => route).length
const getRoutes = async () => {
const points = geojson.features.filter((f) => f.geometry?.type === 'Point')
const mergedPoints = points.length
? await convert<Feature[]>(
points,
'featureVec',
false,
'/api/v1/convert/merge_points',
)
: []
const newGeojson = {
...geojson,
features: [
...mergedPoints,
...geojson.features.filter((f) => f.geometry?.type !== 'Point'),
],
}
const newCode = await convert<number[][][]>(newGeojson, 'multiArray', false)
let max = 0
let total = 0
let count = 0
const newRoute = newCode.map((eachRoute) => {
return eachRoute.map((point, j) => {
const next = j ? eachRoute[j + 1] : eachRoute.at(-1)
if (next) {
const dis = distance(point, next, { units: 'meters' })
if (dis > max) max = dis
total += dis
}
count++
return [+point[0].toFixed(6), +point[1].toFixed(6)]
})
})
setStats({
max,
total,
count,
})
setRoute(newRoute)
}
useDeepCompareEffect(() => {
if (open === 'route') {
getRoutes()
}
}, [geojson, open])

return (
<Dialog open={open === 'route'} maxWidth="xl" onClose={() => setOpen('')}>
Expand All @@ -45,36 +100,40 @@ export default function ExportRoute({ open, setOpen }: Props) {
justifyContent="center"
>
<List sx={{ width: '90%', mx: 'auto' }}>
{exportSettings.route.map((route, i) => (
<React.Fragment key={i}>
<ListSubheader>
<Grid2 container justifyContent="space-around">
<Grid2 xs={3}>
<IconButton
onClick={() =>
navigator.clipboard.writeText(
route.map((p) => p.join(',')).join('\n'),
)
}
>
<ContentCopy />
</IconButton>
</Grid2>
<Grid2 xs={9}>
{mode === 'cluster' ? 'Area' : 'Device'} {i + 1}
{route.map((feat, i) => {
return (
<React.Fragment key={i}>
<ListSubheader>
<Grid2 container justifyContent="space-around">
<Grid2 xs={3}>
<IconButton
onClick={async () =>
navigator.clipboard.writeText(
await convert<string>(
feat,
scannerType === 'rdm' ? 'text' : 'altText',
false,
),
)
}
>
<ContentCopy />
</IconButton>
</Grid2>
<Grid2 xs={9}>[Geofence {i + 1}]</Grid2>
</Grid2>
</Grid2>
</ListSubheader>
{route.map((point, j) => (
<ListItemText
key={`${i}-${j}-${point.join('')}`}
primary={`${point[0]}, ${point[1]}`}
primaryTypographyProps={{ variant: 'caption' }}
sx={{ w: '100%', mx: 'auto' }}
/>
))}
</React.Fragment>
))}
</ListSubheader>
{feat.map((point, j) => (
<ListItemText
key={`${i}-${j}-${point.join('')}`}
primary={`${point[0]}, ${point[1]}`}
primaryTypographyProps={{ variant: 'caption' }}
sx={{ w: '100%', mx: 'auto' }}
/>
))}
</React.Fragment>
)
})}
</List>
</Grid2>
<Grid2
Expand All @@ -87,7 +146,7 @@ export default function ExportRoute({ open, setOpen }: Props) {
>
<Grid2>
<TextField
value={total || 0}
value={route.reduce((acc, cur) => acc + cur.length, 0)}
label="Count"
type="number"
fullWidth
Expand All @@ -96,7 +155,7 @@ export default function ExportRoute({ open, setOpen }: Props) {
</Grid2>
<Grid2>
<TextField
value={exportSettings.max?.toFixed(2) || 0}
value={stats.max?.toFixed(2) || 0}
label="Max"
type="number"
fullWidth
Expand All @@ -106,7 +165,7 @@ export default function ExportRoute({ open, setOpen }: Props) {
</Grid2>
<Grid2>
<TextField
value={(exportSettings.total / total)?.toFixed(2) || 0}
value={(stats.total / (stats.count || 1))?.toFixed(2) || 0}
label="Average"
type="number"
fullWidth
Expand All @@ -116,7 +175,7 @@ export default function ExportRoute({ open, setOpen }: Props) {
</Grid2>
<Grid2>
<TextField
value={exportSettings.total?.toFixed(2) || 0}
value={stats.total?.toFixed(2) || 0}
label="Total"
type="number"
fullWidth
Expand All @@ -129,11 +188,13 @@ export default function ExportRoute({ open, setOpen }: Props) {
</DialogContent>
<DialogActions>
<Button
onClick={() =>
onClick={async () =>
navigator.clipboard.writeText(
exportSettings.route
.map((r) => r.map((p) => p.join(',')).join('\n'))
.join('\n\n'),
await convert<string>(
geojson,
scannerType === 'rdm' ? 'text' : 'altText',
false,
),
)
}
>
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/dialogs/Polygon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export default function ExportPolygon({
}
})
}
}, [polygonExportMode])
}, [polygonExportMode, open])

React.useEffect(() => {
if (mode === 'import' && code) {
Expand Down
22 changes: 20 additions & 2 deletions client/src/components/drawer/manage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,27 @@ export default function ImportExport() {
mode={exportAll ? 'exportAll' : 'import'}
open={open}
setOpen={setOpen}
feature={geojson}
feature={{
...geojson,
features: geojson.features.filter(
(feat) =>
feat.geometry.type === 'Polygon' ||
feat.geometry.type === 'MultiPolygon',
),
}}
/>
<ExportRoute
open={open}
setOpen={setOpen}
geojson={{
...geojson,
features: geojson.features.filter(
(feat) =>
feat.geometry.type === 'Point' ||
feat.geometry.type === 'MultiPoint',
),
}}
/>
<ExportRoute open={open} setOpen={setOpen} />
<RawManager open={open} setOpen={setOpen} geojson={geojson} />
</List>
)
Expand Down
3 changes: 2 additions & 1 deletion client/src/components/drawer/settings/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react'
import { List, ListItem, ListSubheader } from '@mui/material'
import { List, ListItem, ListItemButton, ListSubheader } from '@mui/material'

import { usePersist } from '@hooks/usePersist'

Expand Down Expand Up @@ -40,6 +40,7 @@ export default function Settings() {
buttons={['all', 'bound', 'area']}
/>
</ListItem>
<ListItemButton href="/config/logout">Logout</ListItemButton>
</List>
)
}
2 changes: 2 additions & 0 deletions client/src/hooks/usePersist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ export interface UsePersist {
| 'featureCollection'
| 'featureVec'
| 'array'
| 'multiArray'
| 'struct'
| 'multiStruct'
| 'text'
| 'altText'
| 'poracle'
Expand Down
4 changes: 1 addition & 3 deletions client/src/hooks/useStatic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ import type { KojiStats, PixiMarker } from '@assets/types'
import { collectionToObject } from '@services/utils'

export interface UseStatic {
loading: Record<string, KojiStats | null>
loading: Record<string, KojiStats | null | false>
totalLoadingTime: number
loggedIn: boolean
pokestops: PixiMarker[]
gyms: PixiMarker[]
spawnpoints: PixiMarker[]
Expand Down Expand Up @@ -50,7 +49,6 @@ export interface UseStatic {
export const useStatic = create<UseStatic>((set, get) => ({
loading: {},
totalLoadingTime: 0,
loggedIn: false,
pokestops: [],
gyms: [],
spawnpoints: [],
Expand Down
Loading

0 comments on commit 538b560

Please sign in to comment.