Skip to content

Commit

Permalink
feat: interval data upload
Browse files Browse the repository at this point in the history
  • Loading branch information
felixerdy committed Oct 24, 2023
1 parent 2c0fa44 commit 1467436
Show file tree
Hide file tree
Showing 10 changed files with 132 additions and 159 deletions.
2 changes: 1 addition & 1 deletion ios/App/App/capacitor.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
"appName": "senseBox:Bike",
"webDir": "out",
"server": {
"url": "http://192.168.0.220:3000"
"url": "http://192.168.2.135:3000"
}
}
49 changes: 14 additions & 35 deletions src/components/Device/SettingsDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ import {
DialogTrigger,
} from '@/components/ui/dialog'
import { zodResolver } from '@hookform/resolvers/zod'
import { Cog, Settings2Icon, SettingsIcon, UserCog2 } from 'lucide-react'
import {
Cog,
ExternalLinkIcon,
Settings2Icon,
SettingsIcon,
UserCog2,
} from 'lucide-react'
import { useForm } from 'react-hook-form'
import * as z from 'zod'
import {
Expand Down Expand Up @@ -80,12 +86,15 @@ export default function SettingsDrawer() {

return (
<Drawer.Root open={open} onClose={() => setOpen(false)}>
<Drawer.Trigger onClick={() => setOpen(true)}>
<Drawer.Trigger
onClick={() => setOpen(true)}
className="focus:outline-none"
>
<Cog className="w-6" />
</Drawer.Trigger>
<Drawer.Portal>
<Drawer.Overlay className="fixed inset-0 bg-black/40" />
<Drawer.Content className="fixed bottom-0 left-0 right-0 z-10 mt-24 flex max-h-[75%] flex-col rounded-t-lg bg-zinc-100 pb-safe">
<Drawer.Content className="fixed bottom-0 left-0 right-0 z-10 mt-24 flex max-h-[75%] flex-col rounded-t-lg bg-zinc-100 pb-safe focus:outline-none">
<div className="flex-1 overflow-auto rounded-t-[10px] bg-white p-4">
<div className="mx-auto mb-8 h-1.5 w-12 flex-shrink-0 rounded-full bg-zinc-300" />
<div className="mx-auto max-w-md">
Expand Down Expand Up @@ -173,45 +182,15 @@ function SettingsDrawerFooter() {
target="_blank"
>
openSenseMap
<svg
fill="none"
height="16"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="16"
aria-hidden="true"
className="ml-1 h-3 w-3"
>
<path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6"></path>
<path d="M15 3h6v6"></path>
<path d="M10 14L21 3"></path>
</svg>
<ExternalLinkIcon className="ml-1 h-3 w-3" />
</a>
<a
className="gap-0.25 flex items-center text-xs text-zinc-600"
href="https://reedu.de"
target="_blank"
>
re:edu
<svg
fill="none"
height="16"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="16"
aria-hidden="true"
className="ml-1 h-3 w-3"
>
<path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6"></path>
<path d="M15 3h6v6"></path>
<path d="M10 14L21 3"></path>
</svg>
<ExternalLinkIcon className="ml-1 h-3 w-3" />
</a>
</div>
</div>
Expand Down
10 changes: 5 additions & 5 deletions src/components/Map/ControlBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import { PauseIcon, PlayIcon } from '@heroicons/react/24/outline'
import { Card } from '../ui/card'
import useSenseBox from '@/lib/useSenseBox'
import { BluetoothIcon, BluetoothOffIcon, Circle, Square } from 'lucide-react'
import useUploadToOpenSenseMap from '@/lib/useUploadToOpenSenseMap'

export default function ControlBar() {
const { connect, isConnected, disconnect, uploadData } = useSenseBox()

const isRecording = false
const { connect, isConnected, disconnect } = useSenseBox()
const { isRecording, start, stop } = useUploadToOpenSenseMap()

return (
<Card className="pointer-events-auto flex w-fit items-center gap-2 rounded-lg bg-background p-4">
Expand All @@ -21,12 +21,12 @@ export default function ControlBar() {
{isRecording ? (
<Square
className="h-8 w-8 fill-red-500 text-red-500"
// onClick={() => disconnect()}
onClick={() => stop()}
/>
) : (
<Circle
className="h-8 w-8 fill-red-500 text-red-500"
onClick={() => uploadData()}
onClick={() => start()}
/>
)}
</>
Expand Down
15 changes: 6 additions & 9 deletions src/components/Wizard/ConnectionSelection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Button } from '../ui/button'
import Logo from '../../../public/bike.png'
import Image from 'next/image'
import Link from 'next/link'
import { ArrowLeft, ArrowRight, CheckCircle } from 'lucide-react'
import { ArrowLeft, ArrowRight, CheckCircle, CircleIcon } from 'lucide-react'
import useSenseBox from '@/lib/useSenseBox'
import { cn } from '@/lib/utils'
import PreviewModal from '../Device/PreviewModal'
Expand All @@ -30,16 +30,13 @@ export default function ConnectionSelection({
className="w-fit"
onClick={() => swiper.slidePrev()}
>
<ArrowLeft className="mr-2 w-5" /> Zurück
<ArrowLeft className="mr-2 w-5" /> Bearbeiten
</Button>
<p className="mb-4 font-medium">Verknüpfung erfolgreich</p>

<div>
<p>Verknüfte Box:</p>
<div className="my-4 flex items-center gap-2">
<CheckCircle className="mr-2 h-5 w-5 text-green-500" />
{selectedBox?.name}
</div>
<p className="font-medium">Verknüpfte Box</p>
<div className="flex items-center gap-2">
<CheckCircle className="mr-2 h-5 w-5 text-green-500" />
{selectedBox?.name}
</div>

<Button
Expand Down
10 changes: 1 addition & 9 deletions src/components/Wizard/SelectDevice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,12 @@ export default function SelectDevice() {
const { boxes } = useAuthStore(state => state.boxes)
const selectedBox = useAuthStore(state => state.selectedBox)
const setSelectedBox = useAuthStore(state => state.setSelectedBox)
const { refreshBoxes } = useOpenSenseMapAuth()

const swiper = useSwiper()

return (
<WizardSlide className="flex h-full w-full flex-col items-center justify-center gap-4">
<p className="mb-4 font-medium">
Wähle bitte die openSenseMap-Box aus, die du mit dem Gerät verbinden
möchtest.
</p>
<Button onClick={refreshBoxes}>
<RefreshCcw className="mr-2 w-5" />
Neu laden
</Button>
<p className="mb-4 font-medium">Box auswählen</p>
<ScrollArea className="flex h-60 w-full flex-col gap-2">
{boxes &&
boxes.map(box => (
Expand Down
45 changes: 9 additions & 36 deletions src/components/Wizard/WizardDrawer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Drawer } from 'vaul'
import { AlertOctagon, Check, UserCog2 } from 'lucide-react'
import { AlertOctagon, Check, ExternalLinkIcon, UserCog2 } from 'lucide-react'
import ConnectionSelection from '@/components/Wizard/ConnectionSelection'
import { Swiper, SwiperSlide } from 'swiper/react'
import { Navigation } from 'swiper/modules'
Expand All @@ -26,7 +26,10 @@ export default function WizardDrawer() {

return (
<Drawer.Root open={open} onClose={() => setOpen(false)}>
<Drawer.Trigger onClick={() => setOpen(true)}>
<Drawer.Trigger
onClick={() => setOpen(true)}
className="focus:outline-none"
>
<div className="relative">
<UserCog2 className="w-6" />
{(!isLoggedIn || !selectedBox) && (
Expand All @@ -43,12 +46,12 @@ export default function WizardDrawer() {
</Drawer.Trigger>
<Drawer.Portal>
<Drawer.Overlay className="fixed inset-0 bg-black/40" />
<Drawer.Content className="fixed bottom-0 left-0 right-0 z-10 mt-24 flex max-h-[75%] flex-col rounded-t-lg bg-zinc-100 pb-safe">
<Drawer.Content className="fixed bottom-0 left-0 right-0 z-10 mt-24 flex max-h-[75%] flex-col rounded-t-lg bg-zinc-100 pb-safe focus:outline-none">
<div className="flex-1 overflow-auto rounded-t-[10px] bg-white p-4">
<div className="mx-auto mb-8 h-1.5 w-12 flex-shrink-0 rounded-full bg-zinc-300" />
<div className="mx-auto max-w-md">
<Swiper
initialSlide={isLoggedIn ? 1 : 0}
initialSlide={isLoggedIn ? (selectedBox ? 2 : 1) : 0}
spaceBetween={48}
modules={[Navigation]}
slidesPerView={1}
Expand Down Expand Up @@ -88,45 +91,15 @@ function DrawerWizardFooter({ setOpen }: { setOpen: (open: boolean) => void }) {
target="_blank"
>
openSenseMap
<svg
fill="none"
height="16"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="16"
aria-hidden="true"
className="ml-1 h-3 w-3"
>
<path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6"></path>
<path d="M15 3h6v6"></path>
<path d="M10 14L21 3"></path>
</svg>
<ExternalLinkIcon className="ml-1 h-3 w-3" />
</a>
<a
className="gap-0.25 flex items-center text-xs text-zinc-600"
href="https://reedu.de"
target="_blank"
>
re:edu
<svg
fill="none"
height="16"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="16"
aria-hidden="true"
className="ml-1 h-3 w-3"
>
<path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6"></path>
<path d="M15 3h6v6"></path>
<path d="M10 14L21 3"></path>
</svg>
<ExternalLinkIcon className="ml-1 h-3 w-3" />
</a>
{isLoggedIn && (
<p
Expand Down
4 changes: 2 additions & 2 deletions src/lib/api/openSenseMapClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ axiosApiInstance.interceptors.response.use(
},
async function (error) {
const originalRequest = error.config
if (error.response.status === 403 && !originalRequest._retry) {
if (error?.response?.status === 403 && !originalRequest._retry) {
originalRequest._retry = true
const access_token = await refreshAccessToken()
axios.defaults.headers.common['Authorization'] = 'Bearer ' + access_token
Expand Down Expand Up @@ -125,7 +125,7 @@ export async function uploadData(box: BoxEntity, data: UploadData) {
Authorization: box.access_token,
},
})
if (response.status === 200) {
if (response.status === 201) {
return true
} else {
throw new Error(response.data.message)
Expand Down
11 changes: 11 additions & 0 deletions src/lib/store/useUploadStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { create } from 'zustand'

interface UploadStoreInterface {
lastUpload: Date | undefined
setLastUpload: (lastUpload: Date) => void
}

export const useUploadStore = create<UploadStoreInterface>()(set => ({
lastUpload: undefined,
setLastUpload: lastUpload => set({ lastUpload }),
}))
34 changes: 5 additions & 29 deletions src/lib/useSenseBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ const BackgroundGeolocation = registerPlugin<BackgroundGeolocationPlugin>(
'BackgroundGeolocation',
)

/**
* Parses the data received from the SenseBox and returns an array of values.
* @param data - The data received from the SenseBox as a DataView.
* @returns An array of values parsed from the data.
*/
function parsePackages(data: DataView) {
const packages = data.byteLength / 4

Expand All @@ -47,7 +52,6 @@ export default function useSenseBox(timestampInterval: number = 500) {
namePrefix: 'senseBox',
})
const { values, setValues } = useSenseBoxValuesStore()
const { selectedBox } = useAuthStore()
const { useSenseBoxGPS } = useSettingsStore()
const useSenseBoxGPSRef = useRef<boolean>()
useSenseBoxGPSRef.current = useSenseBoxGPS
Expand All @@ -60,10 +64,6 @@ export default function useSenseBox(timestampInterval: number = 500) {
const locationRef = useRef<Location>()
locationRef.current = location

const [lastUploadTimestamp, setLastUploadTimestamp] = useState(
new Date('1970-01-01'),
)

useEffect(() => {
if (useSenseBoxGPS) {
if (watcherId)
Expand Down Expand Up @@ -103,10 +103,8 @@ export default function useSenseBox(timestampInterval: number = 500) {
})

return () => {
console.log('in unmount')
if (!watcherId) return

console.log('removing watcher', watcherId)
BackgroundGeolocation.removeWatcher({
id: watcherId,
})
Expand Down Expand Up @@ -223,34 +221,12 @@ export default function useSenseBox(timestampInterval: number = 500) {
])
}

const uploadValues = async () => {
if (!selectedBox) {
throw new Error('No box selected.')
}

const data = values
.slice(-2500)
.flatMap(record => match(selectedBox, record))
.map(record => ({
...record,
value: record.value.toFixed(2),
}))

const latestTimestamp = Math.max(
...data.map(e => new Date(e.createdAt).getTime()),
)
setLastUploadTimestamp(new Date(latestTimestamp))

uploadData(selectedBox, data)
}

return {
isConnected,
connect,
values,
disconnect,
resetValues,
send,
uploadData: uploadValues,
}
}
Loading

0 comments on commit 1467436

Please sign in to comment.