Skip to content

Commit

Permalink
Merge pull request #33 from reedu-reengineering-education/feat/track-…
Browse files Browse the repository at this point in the history
…store

Feat/track store
  • Loading branch information
felixerdy authored Nov 20, 2023
2 parents 7567526 + 9390acf commit 9fa9d0e
Show file tree
Hide file tree
Showing 23 changed files with 2,147 additions and 312 deletions.
4 changes: 1 addition & 3 deletions ios/App/App/capacitor.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,5 @@
"appId": "de.reedu.senseboxbike",
"appName": "senseBox:Bike",
"webDir": "out",
"server": {
"url": "http://192.168.2.135:3000"
}
"server": {}
}
19 changes: 11 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"@capacitor/haptics": "^5.0.6",
"@capacitor/ios": "^5.5.1",
"@capacitor/preferences": "^5.0.6",
"@felixerdy/background-geolocation": "1.2.17",
"@felixerdy/background-geolocation": "1.2.18",
"@heroicons/react": "^2.0.18",
"@hookform/resolvers": "^3.3.1",
"@radix-ui/react-dialog": "^1.0.4",
Expand All @@ -36,19 +36,20 @@
"@radix-ui/react-toggle": "^1.0.3",
"@react-spring/web": "^9.7.3",
"@tremor/react": "^3.11.1",
"@turf/circle": "^6.5.0",
"@turf/helpers": "^6.5.0",
"@turf/turf": "^6.5.0",
"autoprefixer": "10.4.16",
"axios": "^1.6.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"eslint": "8.53.0",
"eslint-config-next": "14.0.2",
"date-fns": "^2.30.0",
"eslint": "8.54.0",
"eslint-config-next": "14.0.3",
"eslint-config-prettier": "^9.0.0",
"geist": "^1.1.0",
"lodash.debounce": "^4.0.8",
"lucide-react": "^0.292.0",
"maplibre-gl": "^3.6.1",
"next": "14.0.2",
"next": "14.0.3",
"next-themes": "^0.2.1",
"postcss": "8.4.31",
"react": "18.2.0",
Expand All @@ -60,6 +61,7 @@
"tailwindcss": "3.3.5",
"tailwindcss-animate": "^1.0.7",
"typescript": "5.2.2",
"uuid": "^9.0.1",
"vaul": "^0.7.9",
"zod": "^3.22.2",
"zustand": "^4.4.6"
Expand All @@ -68,10 +70,11 @@
"@capacitor/assets": "^3.0.1",
"@capacitor/cli": "^5.5.1",
"@types/lodash.debounce": "^4.0.9",
"@types/node": "20.9.0",
"@types/node": "20.9.2",
"@types/react": "18.2.37",
"@types/react-dom": "18.2.15",
"eslint-config-prettier": "^9.0.0",
"@types/turf": "^3.5.32",
"@types/uuid": "^9.0.7",
"eslint-plugin-unused-imports": "^3.0.0",
"husky": "^8.0.3",
"prettier": "^3.1.0",
Expand Down
9 changes: 9 additions & 0 deletions src/app/tracks/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import TrackWrapper from '@/components/Tracks/track-wrapper'

export default function TracksPage() {
return (
<div className="h-full w-full p-4">
<TrackWrapper />
</div>
)
}
44 changes: 2 additions & 42 deletions src/components/Device/DeviceMapWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,54 +1,14 @@
'use client'

import MapComponent from '@/components/Map/Map'
import useSenseBox from '@/lib/useSenseBox'
import LocationMarker from '@/components/Map/LocationMarker'
import LocationHistory from '@/components/Map/LocationHistory'
import MeasurementsGrid from '../Map/MeasurementsGrid'
import { useEffect, useRef } from 'react'
import { MapRef } from 'react-map-gl/dist/esm/exports-maplibre'
import TrajectoryMap from './TrajectoryMap'

export default function DeviceMapWrapper() {
const { values } = useSenseBox()

const mapRef = useRef<MapRef>(null)

useEffect(() => {
const latestValue = values.at(-1)
if (latestValue && latestValue.gps_lat && latestValue.gps_lng) {
mapRef.current?.flyTo({
center: [latestValue.gps_lng, latestValue.gps_lat],
zoom: mapRef.current?.getZoom() > 10 ? mapRef.current.getZoom() : 15,
})
}
}, [values])

return (
<div className="flex h-full w-full portrait:flex-col">
<div className="portrait:border-b landscape:border-r-2">
<MeasurementsGrid />
</div>
<div className="relative h-full w-full">
<MapComponent ref={mapRef}>
{values && values.length > 0 && (
<>
<LocationHistory values={values} />
<LocationMarker
location={{
latitude: values.at(-1)?.gps_lat || 0,
longitude: values.at(-1)?.gps_lng || 0,
accuracy: 10,
simulated: false,
altitude: null,
altitudeAccuracy: null,
bearing: null,
speed: null,
time: null,
}}
/>
</>
)}
</MapComponent>
<TrajectoryMap />
</div>
</div>
)
Expand Down
53 changes: 45 additions & 8 deletions src/components/Device/SettingsDrawer.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
'use client'

import { useSettingsStore } from '@/lib/store/useSettingsStore'
import { useUIStore } from '@/lib/store/useUIStore'
import useSenseBox, { BackgroundGeolocation } from '@/lib/useSenseBox'
import { App } from '@capacitor/app'
import { PluginListenerHandle } from '@capacitor/core'
import { zodResolver } from '@hookform/resolvers/zod'
import { DialogClose } from '@radix-ui/react-dialog'
import { Cog, ExternalLinkIcon } from 'lucide-react'
import { useEffect, useState } from 'react'
import { useForm } from 'react-hook-form'
import { Drawer } from 'vaul'
import * as z from 'zod'
import { Button } from '../ui/button'
import {
Form,
FormControl,
Expand All @@ -14,30 +23,39 @@ import {
} from '../ui/form'
import { Slider } from '../ui/slider'
import { Switch } from '../ui/switch'
import { useSettingsStore } from '@/lib/store/useSettingsStore'
import { Button } from '../ui/button'
import { DialogClose } from '@radix-ui/react-dialog'
import useSenseBox, { BackgroundGeolocation } from '@/lib/useSenseBox'
import { Drawer } from 'vaul'
import { useEffect, useState } from 'react'

const formSchema = z.object({
uploadInterval: z.number().min(1).max(60),
switchUseSmartphoneGPS: z.boolean(),
switchLiveMode: z.boolean(),
switchReducedMotion: z.boolean(),
})

export default function SettingsDrawer() {
const uploadInterval = useSettingsStore(state => state.uploadInterval)
const useDeviceGPS = useSettingsStore(state => state.useSenseBoxGPS)
const { reducedMotion, setReducedMotion } = useUIStore()
const { send } = useSenseBox()

useEffect(() => {
let listener: PluginListenerHandle | undefined

App.addListener('backButton', () => {
setOpen(false)
}).then(l => (listener = l))

return () => {
if (listener) listener.remove()
}
}, [])

const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
uploadInterval: uploadInterval,
switchUseSmartphoneGPS: !useDeviceGPS,
switchLiveMode: false,
switchReducedMotion: reducedMotion,
},
})

Expand All @@ -54,6 +72,7 @@ export default function SettingsDrawer() {
uploadInterval: values.uploadInterval,
useSenseBoxGPS: !values.switchUseSmartphoneGPS,
})
setReducedMotion(values.switchReducedMotion)
setOpen(false)
}
const [open, setOpen] = useState(false)
Expand All @@ -79,9 +98,9 @@ export default function SettingsDrawer() {
<Drawer.Portal>
<Drawer.Overlay className="fixed inset-0 z-20 bg-black/60" />
<Drawer.Content className="fixed bottom-0 left-0 right-0 z-30 mt-24 flex max-h-[75%] flex-col rounded-t-lg border-t bg-background focus:outline-none">
<div className="mx-auto my-4 h-1.5 w-12 flex-shrink-0 rounded-full bg-muted" />
<div className="flex-1 overflow-auto rounded-t-[10px] p-4">
<div className="mx-auto mb-8 h-1.5 w-12 flex-shrink-0 rounded-full bg-muted" />
<div className="mx-auto max-w-md">
<div className="mx-auto max-w-md overflow-y-auto">
<p className="mb-4 font-medium">Einstellungen</p>
<Button onClick={() => BackgroundGeolocation.openSettings()}>
Geolocation Settings
Expand Down Expand Up @@ -140,6 +159,24 @@ export default function SettingsDrawer() {
</FormItem>
)}
/>
<FormField
name="switchReducedMotion"
control={form.control}
render={({ field }) => (
<FormItem>
<FormLabel>Reduced Motion</FormLabel>
<FormDescription>
Enable this setting to reduce animations
</FormDescription>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
</div>
</div>
<DialogClose className="float-right">
Expand Down
86 changes: 86 additions & 0 deletions src/components/Device/TrajectoryMap.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
'use client'

import { useUIStore } from '@/lib/store/useUIStore'
import useSenseBox from '@/lib/useSenseBox'
import { bearing, point } from '@turf/turf'
import { LngLatLike } from 'maplibre-gl'
import { useEffect, useRef } from 'react'
import { MapRef } from 'react-map-gl/dist/esm/exports-maplibre'
import LocationHistory from '../Map/LocationHistory'
import LocationMarker from '../Map/LocationMarker'
import MapComponent from '../Map/Map'

export default function TrajectoryMap() {
const { values } = useSenseBox()
const { reducedMotion } = useUIStore()

const mapRef = useRef<MapRef>(null)

useEffect(() => {
const latestValue = values.at(-1)
if (
latestValue &&
latestValue.gps_lat &&
latestValue.gps_lng &&
mapRef.current
) {
const center = [latestValue.gps_lng, latestValue.gps_lat] as LngLatLike
const zoom =
mapRef.current?.getZoom() > 10 ? mapRef.current.getZoom() : 18
let mapBearing = mapRef.current?.getBearing()
let pitch = mapRef.current?.getPitch()
const valueBefore = values.at(-2)
if (
valueBefore?.gps_lat &&
valueBefore?.gps_lng &&
(latestValue?.gps_spd ?? 0) > 5
) {
mapBearing = bearing(
point([valueBefore.gps_lng, valueBefore.gps_lat]),
point([latestValue.gps_lng, latestValue.gps_lat]),
)
pitch = 60
} else {
mapBearing = 0
pitch = 0
}

if (reducedMotion) {
mapRef.current?.setCenter(center)
mapRef.current?.setZoom(zoom)
mapRef.current?.setBearing(mapBearing)
mapRef.current?.setPitch(pitch)
} else {
mapRef.current?.flyTo({
center,
zoom,
bearing: mapBearing,
pitch,
})
}
}
}, [values])

return (
<MapComponent ref={mapRef}>
{values && values.length > 0 && (
<>
<LocationHistory values={values} />
<LocationMarker
location={{
latitude: values.at(-1)?.gps_lat || 0,
longitude: values.at(-1)?.gps_lng || 0,
accuracy: 10,
simulated: false,
altitude: null,
altitudeAccuracy: null,
bearing: null,
speed: null,
time: null,
}}
/>
</>
)}
</MapComponent>
)
}
13 changes: 6 additions & 7 deletions src/components/Map/LocationMarker.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { Location } from '@felixerdy/background-geolocation'
import { Source, Layer } from 'react-map-gl/maplibre'

// @ts-ignore
import circle from '@turf/circle'
import { point, circle } from '@turf/turf'

export default function LocationMarker({ location }: { location: Location }) {
const circlePoly = circle(
[location?.longitude, location?.latitude],
location.accuracy,
{
units: 'meters',
},
point([location?.longitude, location?.latitude]),
location.accuracy / 1000,
64,
'kilometers',
)

return (
Expand Down Expand Up @@ -41,6 +39,7 @@ export default function LocationMarker({ location }: { location: Location }) {
'circle-color': '#007cbf',
'circle-stroke-width': 2,
'circle-stroke-color': '#fff',
'circle-pitch-alignment': 'map',
}}
/>
</Source>
Expand Down
Loading

0 comments on commit 9fa9d0e

Please sign in to comment.