-
-
Notifications
You must be signed in to change notification settings - Fork 5
WIP Explore map + dynamic device routes + bottom bar #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Changes from 9 commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
f212de2
make ENV variables accesible
freds-dev 6b9f3f8
add map to `/explore` route
freds-dev 69b76bd
add nested route for `/explore/$deviceId`
freds-dev 42b5334
use osem-API
freds-dev c7696b1
add heroicons and some mobile styling
freds-dev 961615f
check if lastMeasurement exists on sensor
freds-dev 81d0eeb
Merge branch 'dev' into explore
freds-dev b5566ef
delete unnecessary `/explore/index.tsx`
freds-dev 1d712ba
added CatchBoundary for unknown `$deviceId`
freds-dev 782aa1b
edit .env variables
freds-dev c1e9e54
redirect from bottom bar to `/explore`
freds-dev 1db9352
header component (#6)
fab-scm File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| import React, { useRef, useEffect, useState } from "react"; | ||
| import type { Map } from "maplibre-gl"; | ||
| import maplibregl from "maplibre-gl"; | ||
|
|
||
| interface MapProps extends React.InputHTMLAttributes<HTMLInputElement> { | ||
| latitude?: number; | ||
| longitude?: number; | ||
| } | ||
|
|
||
| export type MapContextValue = { | ||
| map: Map | null; | ||
| }; | ||
|
|
||
| export const MapContext = React.createContext<MapContextValue>({ | ||
| map: null, | ||
| }); | ||
|
|
||
| export default function MyMap({ latitude = 7, longitude = 52 }: MapProps) { | ||
| const mapContainer = useRef<HTMLDivElement>(null); | ||
| const [mapInstance, setMapInstance] = useState<Map | null>(null); | ||
|
|
||
| const { current: contextValue } = useRef<MapContextValue>({ map: null }); | ||
|
|
||
| useEffect(() => { | ||
| if (contextValue.map) return; //stops map from intializing more than once | ||
|
|
||
| let map: Map; | ||
|
|
||
| const initialState = { | ||
| lng: longitude, | ||
| lat: latitude, | ||
| zoom: 2, | ||
| }; | ||
|
|
||
| if (mapContainer.current) { | ||
| map = new maplibregl.Map({ | ||
| container: mapContainer.current, | ||
| style: `https://api.maptiler.com/maps/streets/style.json?key=${ENV.MAPTILER_KEY}`, | ||
| center: [initialState.lng, initialState.lat], | ||
| zoom: initialState.zoom, | ||
| }); | ||
|
|
||
| contextValue.map = map; | ||
|
|
||
| setMapInstance(map); | ||
| } | ||
|
|
||
| return () => { | ||
| if (mapInstance) { | ||
| map.remove(); | ||
| } | ||
| }; | ||
| }, [contextValue, longitude, latitude]); | ||
|
|
||
| return ( | ||
| <div className="h-full min-h-full w-full"> | ||
| <div ref={mapContainer} className="h-full w-full"> | ||
| <MapContext.Provider value={contextValue}></MapContext.Provider> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| import { useState } from "react"; | ||
| import SingleValue from "./SingleValue"; | ||
| import { ChevronDoubleUpIcon } from "@heroicons/react/24/solid"; | ||
| import { XCircleIcon } from "@heroicons/react/24/solid"; | ||
|
|
||
| interface BottomBarProps { | ||
| id: string; | ||
| name: string; | ||
| sensors: Array<SensorProps>; | ||
| lastUpdate: string; | ||
| } | ||
|
|
||
| interface SensorProps { | ||
| _id: string; | ||
| icon: string; | ||
| lastMeasurement: LastMeasurementProps; | ||
| sensorType: string; | ||
| title: string; | ||
| unit: string; | ||
| } | ||
|
|
||
| interface LastMeasurementProps { | ||
| createdAt: string; | ||
| value: string; | ||
| } | ||
|
|
||
| export default function BottomBar(device: BottomBarProps) { | ||
| const [isOpen, setIsOpen] = useState<Boolean>(true); | ||
| return ( | ||
| <div> | ||
| <div className={"bg-white " + (isOpen ? "animate-fade-in-up" : "hidden")}> | ||
| <div className="flex"> | ||
| <div className="text-l basis-1/4 bg-green-300 pt-6 pb-6 text-center font-bold text-green-900 lg:text-3xl"> | ||
| <p>{device.name}</p> | ||
| </div> | ||
| <div className="grid basis-3/4 content-center bg-green-900 pr-2 text-right text-sm text-white"> | ||
| <div> | ||
| <p className="lg:inline text-xs lg:text-sm">Letzte Messung:</p> | ||
| <p className="lg:inline text-xs lg:text-sm"> {device.lastUpdate}</p> | ||
| </div> | ||
| </div> | ||
| <div className="flex items-center bg-green-900 pr-2"> | ||
| <XCircleIcon | ||
| onClick={() => { | ||
| setIsOpen(!isOpen); | ||
| }} | ||
| className="h-6 w-6 lg:h-8 lg:w-8 cursor-pointer text-white" | ||
| /> | ||
| </div> | ||
| </div> | ||
| <div className="flex justify-center overflow-auto"> | ||
| {device.sensors.map((sensor: SensorProps) => { | ||
| return ( | ||
| <SingleValue | ||
| key={sensor._id} | ||
| _id={sensor._id} | ||
| icon={sensor.icon} | ||
| sensorType={sensor.sensorType} | ||
| title={sensor.title} | ||
| unit={sensor.unit} | ||
| lastMeasurement={sensor.lastMeasurement} | ||
| /> | ||
| ); | ||
| })} | ||
| </div> | ||
| </div> | ||
| <div | ||
| onClick={() => { | ||
| setIsOpen(!isOpen); | ||
| }} | ||
| className={ | ||
| "absolute bottom-5 left-1/2 cursor-pointer rounded-full bg-white p-2 hover:animate-bounce " + | ||
| (!isOpen ? "visible" : "hidden") | ||
| } | ||
| > | ||
| <ChevronDoubleUpIcon className="h-6 w-6 text-green-900" /> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| interface SingleValueProps { | ||
| _id: string; | ||
| icon: string; | ||
| lastMeasurement: LastMeasurementProps; | ||
| sensorType: string; | ||
| title: string; | ||
| unit: string; | ||
| } | ||
|
|
||
| interface LastMeasurementProps { | ||
| createdAt: string; | ||
| value: string; | ||
| } | ||
|
|
||
| export default function SingleValue(sensor: SingleValueProps) { | ||
| return ( | ||
| <div className="border-grey-300 lg:mb-3 mt-3 flex-1 border-r border-l border-solid pl-3 pr-3 text-center text-l lg:text-2xl"> | ||
| <div className="flex justify-center"> | ||
| {sensor.lastMeasurement ? (<b>{sensor.lastMeasurement.value}</b>) : (<b>xx</b>)} | ||
| <p>{sensor.unit}</p> | ||
| </div> | ||
| <p className="text-sm lg:text-xl">{sensor.title}</p> | ||
| </div> | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| export function getEnv() { | ||
| return { | ||
| MAPTILER_KEY: process.env.MAPTILER_KEY, | ||
| }; | ||
| } | ||
|
|
||
| type ENV = ReturnType<typeof getEnv>; | ||
|
|
||
| declare global { | ||
| var ENV: ENV; | ||
| interface Window { | ||
| ENV: ENV; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| import { LinksFunction } from "@remix-run/node"; | ||
| import { Outlet } from "@remix-run/react"; | ||
| import Map from "~/components/Map"; | ||
| import maplibregl from "maplibre-gl/dist/maplibre-gl.css"; | ||
|
|
||
| export const links: LinksFunction = () => { | ||
| return [ | ||
| { | ||
| rel: "stylesheet", | ||
| href: maplibregl, | ||
| }, | ||
| ]; | ||
| }; | ||
|
|
||
| export default function Explore() { | ||
| return ( | ||
| <div className="h-full w-full"> | ||
| <Map /> | ||
| <main className="absolute bottom-0 z-10 w-full"> | ||
| <Outlet /> | ||
| </main> | ||
| </div> | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| import { json, LoaderArgs } from "@remix-run/node"; | ||
| import { useCatch, useLoaderData } from "@remix-run/react"; | ||
| import BottomBar from "~/components/bottomBar/BottomBar"; | ||
|
|
||
| export async function loader({ params }: LoaderArgs) { | ||
| // request to API with deviceID | ||
| const response = await fetch( | ||
| "https://api.opensensemap.org/boxes/" + params.deviceId | ||
|
freds-dev marked this conversation as resolved.
Outdated
|
||
| ); | ||
| const data = await response.json(); | ||
| if (data.code === "UnprocessableEntity") { | ||
| throw new Response("Device not found", { status: 502 }); | ||
| } | ||
| return json(data); | ||
| } | ||
|
|
||
| export default function DeviceId() { | ||
| const data = useLoaderData<typeof loader>(); | ||
| return ( | ||
| <BottomBar | ||
| id={data._id} | ||
| name={data.name} | ||
| sensors={data.sensors} | ||
| lastUpdate={data.updatedAt} | ||
| /> | ||
| ); | ||
| } | ||
|
|
||
| export function CatchBoundary() { | ||
| const caught = useCatch(); | ||
| if (caught.status === 502) { | ||
| return ( | ||
| <div> | ||
| <div className="flex animate-fade-in-up items-center justify-center bg-white py-10"> | ||
| <div className="text-red-500">Oh no, we could not find this Device ID. Are you sure it exists?</div> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } | ||
| throw new Error(`Unsupported thrown response status code: ${caught.status}`); | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.