Skip to content

Commit

Permalink
Merge pull request #39 from kieler/36-track-selection-and-init
Browse files Browse the repository at this point in the history
Track selection form and fetching of init data
  • Loading branch information
n1kPLV authored Jul 26, 2023
2 parents 7ddcc24 + bde3566 commit 652388a
Show file tree
Hide file tree
Showing 19 changed files with 432 additions and 235 deletions.
58 changes: 33 additions & 25 deletions Website/src/app/map/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,42 @@

import DynamicMap from '@/components/dynmap';
import {cookies} from 'next/headers';
import {getVehicleData} from '@/lib/data';
import {getInitData, getVehicleData} from '@/lib/data';
import {LoginDialog} from "@/components/login";
import LoginMapWrapper from "@/components/login_map";
import LoginWrapper from "@/components/login_wrap";
import {InitResponse, Vehicle} from "@/lib/api.website";
import {nanToUndefined} from "@/lib/helpers";

const getInitData = async (context: {token?: string, track_id: number}) => {
// TODO:
}
export default async function Home({searchParams}: { searchParams: { focus?: string, success?: string } }) {

export default async function Home() {
console.log('params', searchParams);

const token = cookies().get("token")?.value;
const track_id = 0
const server_vehicles = token ? await getVehicleData(token, track_id) : [];
const token = cookies().get("token")?.value;
const track_id = parseInt(cookies().get("track_id")?.value ?? '', 10)
const track_selected = !isNaN(track_id);
let server_vehicles: Vehicle[];
let init_data: InitResponse | undefined;
try {
init_data = (token && track_selected) ? await getInitData(token, track_id) : undefined;
server_vehicles = (token && track_selected) ? await getVehicleData(token, track_id) : [];
} catch (e) {
console.log('Catched e:', e);
init_data = undefined;
server_vehicles = []
}
const focus = nanToUndefined(parseInt(searchParams.focus ?? '', 10));

console.log("server vehicles", server_vehicles)
return (
<div className='h-full min-h-screen'>
<LoginMapWrapper logged_in={token !== undefined} map_conf={
{
position: {lat: 54.2333, lng: 10.6024},
zoom_level: 11,
server_vehicles,
track_id
}
} />
<footer>
Foo Bar Baz - Footer Text
</footer>
</div>
)
console.log("server vehicles", server_vehicles)
return (
<LoginWrapper logged_in={token !== undefined} track_selected={track_selected} map_conf={
{
position: {lat: 54.2333, lng: 10.6024},
zoom_level: 11,
server_vehicles,
track_id,
init_data,
focus
}
} child={DynamicMap}/>
)
}
5 changes: 2 additions & 3 deletions Website/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import Image from 'next/image'
import Footer from "@/app/components/footer";
import Link from "next/link";
import Header from "@/app/components/header";

export default function Home() {
return (
Expand All @@ -12,6 +9,8 @@ export default function Home() {
href={'/map'}>here</Link></li>
<li>Add a track <Link className="text-blue-600 visited:text-purple-700"
href={'/add_track'}>here</Link></li>
<li>Select a different track <Link className="text-blue-600 visited:text-purple-700"
href={'/select_track'}>here</Link></li>
<li>Login <Link className="text-blue-600 visited:text-purple-700"
href={'/login'}>here</Link></li>
<li>Logout <Link className="text-blue-600 visited:text-purple-700"
Expand Down
12 changes: 12 additions & 0 deletions Website/src/app/select_track/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Selection from "@/components/track_selection";

export default function Page() {
return (
// <div className='h-full min-h-screen'>
<main className="container mx-auto max-w-2xl grow">
<div className={'bg-white p-4 rounded'}>
<Selection/>
</div>
</main>
);
}
38 changes: 24 additions & 14 deletions Website/src/app/webapi/auth/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import {AuthenticationRequest, AuthenticationResponse} from "@/lib/api.website";
import {cookies} from "next/headers";
import {NextRequest, NextResponse} from "next/server";
import {authenticate} from "@/lib/data";
Expand All @@ -10,7 +9,10 @@ import {NextURL} from "next/dist/server/web/next-url";

export async function POST(request: NextRequest) {
const url = request.nextUrl.clone();
const base_host = request.headers.get('host') ?? request.headers.get('x-forwarded-host')
const base_host = request.headers.get('x-forwarded-host')

console.log('request headers:', request.headers);

// console.log('baz', request.destination);
const data = await request.formData();
// console.log('foo', data);
Expand All @@ -19,19 +21,27 @@ export async function POST(request: NextRequest) {
const username = data.get("username")?.toString()
const password = data.get("password")?.toString()
if (username && password) {
const token = await authenticate(username, password, data.get('signup')?.toString());
if (token) {
cookies().set({
name: 'token',
value: token,
sameSite: 'lax',
httpOnly: true
});
url.searchParams.set('success', 'true')
}
else {
return new NextResponse("failed!")
try {
const token = await authenticate(username, password, data.get('signup')?.toString());
if (token) {
cookies().set({
name: 'token',
value: token,
sameSite: 'lax',
httpOnly: true
});
url.searchParams.set('success', 'true')
console.log("User:", username, 'login successful.');
} else {
console.log("User:", username, 'login failed.');
return new NextResponse("failed!")
}
} catch (e: any) {
console.error("User:", username, 'server failure', e);
return new NextResponse(`server error: ${e.toString()}`, {status: 500});
}
} else {
return new NextResponse('Malformed Request', {status: 400});
}

return NextResponse.redirect(new NextURL(url, {base: base_host ?? undefined}))
Expand Down
1 change: 0 additions & 1 deletion Website/src/app/webapi/page.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { notFound } from "next/navigation";
import { NextRequest, NextResponse } from "next/server";

export default async function handler(request: any) {
console.log('foo', request, typeof request)
Expand Down
42 changes: 42 additions & 0 deletions Website/src/app/webapi/tracks/list/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* Offer an endpoint to call in order to request a track list positions. This will
* read the auth token from the cookie, so we don't need to access it on the client
*/

import {getTrackList, getVehicleData} from "@/lib/data";
import { cookies } from "next/headers";
import { NextRequest, NextResponse } from "next/server";
import {UnauthorizedError} from "@/lib/types";

export async function GET(request: NextRequest) {
// console.log("foobar", request)
const token = cookies().get("token")?.value;
// console.log("requested track_id", track_id);

if (token) {
try {
const tracks = await getTrackList(token);
// console.log("vehicles", vehicles)
return NextResponse.json(tracks)
}
catch (e: any) {
if (e instanceof UnauthorizedError) {
// token may have expired. Delete token.
cookies().set({
name: 'token',
value: '',
sameSite: 'lax',
httpOnly: true,
expires: new Date(0)
})
console.log('UnauthorizedError')
return new NextResponse('Unauthorized', {status: 401})
} else
return new NextResponse("Error" + e.toString(), {status: 500})
}
}
else {
return new NextResponse("Unauthorized", {status: 401})
}

}
2 changes: 0 additions & 2 deletions Website/src/app/webapi/tracks/new/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import {NextRequest, NextResponse} from "next/server";
import {sendTrack} from "@/lib/data";
import {cookies} from "next/headers";
import {UnauthorizedError} from "@/lib/types";
import {deleteCookie} from "cookies-next";


export async function PUT(request: NextRequest, x: any, y: any, z: any) {
const payload = await request.json()
Expand Down
3 changes: 0 additions & 3 deletions Website/src/app/webapi/update/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
* read the auth token from the cookie, so we don't need to access it on the client
*/

import { AuthenticationRequest, AuthenticationResponse } from "@/lib/api.website";
import { getVehicleData } from "@/lib/data";
import { STATUS_CODES } from "http";
import { redirect } from "next/dist/server/api-utils";
import { cookies } from "next/headers";
import { NextRequest, NextResponse } from "next/server";
import {UnauthorizedError} from "@/lib/types";
Expand Down
128 changes: 61 additions & 67 deletions Website/src/components/dynmap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,80 +2,74 @@
import dynamic from 'next/dynamic';
import LoadMapScreen from './loadmap';
import {Vehicle} from "@/lib/api.website";
import {IMapConfig, IMapRefreshConfig} from '@/lib/types';
import {useEffect, useRef, useState} from 'react';
import {clearInterval, setInterval} from 'timers';
import {IMapRefreshConfig, RevalidateError} from '@/lib/types';
import useSWR from "swr";

const _internal_DynamicMap = dynamic(() => import('@/components/map'), {
loading: LoadMapScreen,
ssr: false
loading: LoadMapScreen,
ssr: false
});

export default function DynamicMap(props: React.PropsWithChildren<IMapRefreshConfig & {logged_in: boolean}>) {

const { position, zoom_level, server_vehicles, track_id, logged_in } = props;
// console.log(props)
var i = 0
const fetcher = ([url, track_id]: [url: string, track_id: number]) => {
return fetch(url, {method: 'post', body: JSON.stringify({track_id})}).then(
async (res: Response) => {
if (!res.ok) {
// console.log('not ok!');
throw new RevalidateError('Re-Fetching unsuccessful', res.status);
}
//console.log('ok')
return res;
}
).then(res => res.json())
.then(res => {
// console.log(res);
const test_vehicle: Vehicle = {
id: 0,
pos: {
lat: 54.17 + 0.05 * Math.cos(i * Math.PI / 180),
lng: 10.56 + 0.085 * Math.sin(i * Math.PI / 180)
},
heading: i + 90,
name: 'foo',
batteryLevel: 0.5
};
// {id: 42, pos: {lat: 54.2 + 0.05 * Math.cos((i.current + 180) * Math.PI / 180), lng: 10.56 + 0.085 * Math.sin((i.current + 180) * Math.PI / 180) }, heading: i.current - 90, name: 'bar', batteryLevel: 1}
// ];
i += 5.1;
return res.concat([test_vehicle])
});
};

const [vehicles, setVehicles] = useState(server_vehicles)
// const timeoutRef = useRef(undefined as NodeJS.Timeout | undefined);
export default function DynamicMap(props: IMapRefreshConfig) {

const i = useRef(1)
async function updateVehicles() {
const test_vehicle: Vehicle = {id: 0, pos: {lat: 54.17 + 0.05 * Math.cos(i.current * Math.PI / 180), lng: 10.56 + 0.085 * Math.sin(i.current * Math.PI / 180)}, heading: i.current + 90, name: 'foo', batteryLevel: 0.5};
// {id: 42, pos: {lat: 54.2 + 0.05 * Math.cos((i.current + 180) * Math.PI / 180), lng: 10.56 + 0.085 * Math.sin((i.current + 180) * Math.PI / 180) }, heading: i.current - 90, name: 'bar', batteryLevel: 1}
// ];
i.current+=5.1;
let real_vehicles: Vehicle[]
const x = await fetch(`/webapi/update`, { cache: 'no-store', method: "POST", body: JSON.stringify({"track_id": track_id}) })
if (x.ok) {
// debugger;
real_vehicles = await x.json();
} else {
console.log("Could not fetch vehicle positions", x.status, x.statusText)
real_vehicles = []
}
// debugger;
real_vehicles = real_vehicles.concat([test_vehicle]);
console.log('Updating vehicle positions!', real_vehicles);
setVehicles(real_vehicles);
}

const {position, zoom_level, server_vehicles, track_id, logged_in, init_data, focus} = props;
// console.log(props)

useEffect(() => {
if (!logged_in) {
return;
}
console.log("Effect");
// debugger;
// if (1 || !timeoutRef.current)
// timeoutRef.current = new Promise(resolve => setTimeout(resolve, 1000)).then(
// updateVehicles
// ).then(
// async () => {
// console.log("Foo!");
// await undefined;
// }
// )
// timeoutRef.current = setTimeout(() => {
// console.log("timeout!!"); updateVehicles().catch(() => {}).then()
// }, 1000);
// return () => {
// console.log("Cancelled!");
// clearTimeout(timeoutRef.current);
// timeoutRef.current = undefined;
// };
const interval = setInterval(() => updateVehicles().catch(console.error), 1000);
return () => {
console.log("effect cancelled");
clearInterval(interval);
// const [vehicles, setVehicles] = useState(server_vehicles)
// const timeoutRef = useRef(undefined as NodeJS.Timeout | undefined);

const {data, error, isLoading} = useSWR((logged_in && track_id) ? ['/webapi/update', track_id] : null, fetcher, {
refreshInterval: 1000,
})

// console.log(data, error, isLoading);

const vehicles = (isLoading || error || !logged_in || !track_id) ? server_vehicles : data;

if (logged_in && error) {
if (error instanceof RevalidateError && error.statusCode == 401) {
console.log('Invalid token');
window.location.reload();
}
console.log("revalidate error", error)
}
})

return (
<div style={{ height: '90vh' }}>
<_internal_DynamicMap
position={position} zoom_level={zoom_level} server_vehicles={vehicles}
/>
</div>

return (
<div className={'h-96 grow'}>
<_internal_DynamicMap
position={position} zoom_level={zoom_level} server_vehicles={vehicles} init_data={init_data} focus={focus}
/>
</div>
)
}
Empty file added Website/src/components/form.tsx
Empty file.
2 changes: 0 additions & 2 deletions Website/src/components/loadmap.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import {IMapConfig} from '@/lib/types'

export default function LoadMapScreen() {


return (
<div className='grid justify-center content-center h-full' >
<div>Loading...</div>
Expand Down
19 changes: 0 additions & 19 deletions Website/src/components/login_map.tsx

This file was deleted.

Loading

0 comments on commit 652388a

Please sign in to comment.