You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

358 lines
10 KiB

import Page, {PageBody, PageFlexColumn, PageFlexRow} from "../primitives/page/Page";
import Header, {HeaderButton, HeaderTitle} from "../primitives/header/Header";
import {TitleLine} from "../primitives/misc/Misc";
import {Size} from "../models/Shared";
import Blob, {BlobInput, BlobText} from "../primitives/blob/Blob";
import {Icon} from "../primitives/Shared";
import {
faChain,
faCheck, faChevronDown,
faChevronLeft,
faChevronUp, faMessage,
faPencilAlt,
faTag,
faTrashCan
} from "@fortawesome/free-solid-svg-icons";
import {useCallback, useContext, useEffect, useMemo, useState} from "react";
import LoadingPage from "./LoadingPage";
import {useNavigate, useParams} from "react-router";
import DeviceContext from "../contexts/DeviceContext";
import {useSearchParams} from "react-router-dom";
import {Device} from "../models/Devices";
import deviceRepo from "../actions/devices";
import RuntimeContext from "../contexts/RuntimeContext";
interface DevicePageProps {
edit?: boolean
}
export default function DevicePage({edit}: DevicePageProps): JSX.Element {
const {devices} = useContext(DeviceContext);
const navigate = useNavigate();
const {id} = useParams();
const [search] = useSearchParams();
const device = useMemo(() => devices?.find(d => d.id === id) || null, [devices]);
if (edit && id === undefined) {
return <NewDevicePage/>;
} else if (edit && device) {
return <EditDevicePage device={device}/>;
} else if (device !== null) {
return <ViewDevicePage device={device}/>;
} else {
if (devices !== null) {
navigate("/");
}
return <LoadingPage text="Henter enhet"/>;
}
}
function NewDevicePage(): JSX.Element {
const navigate = useNavigate();
const {refreshDevices} = useContext(DeviceContext);
const [name, setName] = useState<string>("");
const [connectionString, setConnectionString] = useState<string>("");
const [wait, setWait] = useState<boolean>();
const onSave = useCallback(async () => {
setWait(true);
if (await deviceRepo().save({name, connectionString})) {
refreshDevices();
navigate("/");
} else {
setWait(false);
}
}, [name, connectionString]);
if (wait) {
return <LoadingPage text="Legger til enhet"/>;
}
return (
<Page>
<Header>
<HeaderButton onClick={() => navigate("/")}>
<Icon value={faChevronLeft}/>
</HeaderButton>
<HeaderTitle>Ny enhet</HeaderTitle>
</Header>
<PageBody>
<PageFlexRow collapseOn={Size.Mobile}>
<PageFlexColumn flex={1}>
<TitleLine>Enhet</TitleLine>
<Blob fillOn={Size.Any}>
<BlobText>Navn</BlobText>
<BlobInput flex={1} type="text" value={name} onChange={setName}/>
</Blob>
<Blob fillOn={Size.Any}>
<BlobText>Adresse</BlobText>
<BlobInput flex={1} type="text" value={connectionString} onChange={setConnectionString}/>
</Blob>
<Blob color="green" onClick={onSave}>
<BlobText>
<Icon value={faCheck}/> Lagre
</BlobText>
</Blob>
</PageFlexColumn>
<PageFlexColumn flex={1}/>
</PageFlexRow>
</PageBody>
</Page>
);
}
interface EditDevicePageProps {
device: Device
}
function EditDevicePage({device}: EditDevicePageProps): JSX.Element {
const navigate = useNavigate();
const {refreshDevices} = useContext(DeviceContext);
const [name, setName] = useState<string>(device.name);
const [connectionString, setConnectionString] = useState<string>(device.connectionString);
const [wait, setWait] = useState<boolean>();
const onSave = useCallback(async () => {
setWait(true);
if (await deviceRepo().save({id: device.id, name, connectionString})) {
refreshDevices();
navigate(`/devices/${device.id}`);
} else {
setWait(false);
}
}, [device, name, connectionString]);
if (wait) {
return <LoadingPage text="Oppdaterer enhet"/>;
}
return (
<Page>
<Header>
<HeaderButton onClick={() => navigate(`/devices/${device.id}`)}>
<Icon value={faChevronLeft}/>
</HeaderButton>
<HeaderTitle>Endre «{device.name}»</HeaderTitle>
</Header>
<PageBody>
<PageFlexRow collapseOn={Size.Mobile}>
<PageFlexColumn flex={1}>
<TitleLine>Enhet</TitleLine>
<Blob fillOn={Size.Any}>
<BlobText>Navn</BlobText>
<BlobInput flex={1} type="text" value={name} onChange={setName}/>
</Blob>
<Blob fillOn={Size.Any}>
<BlobText>Adresse</BlobText>
<BlobInput flex={1} type="text" value={connectionString} onChange={setConnectionString}/>
</Blob>
<Blob color="green" onClick={onSave}>
<BlobText>
<Icon value={faCheck}/> Lagre
</BlobText>
</Blob>
</PageFlexColumn>
<PageFlexColumn flex={1}/>
</PageFlexRow>
</PageBody>
</Page>
);
}
interface ViewDevicePageProps {
device: Device
}
function ViewDevicePage({device}: ViewDevicePageProps): JSX.Element {
const navigate = useNavigate();
const {refreshDevices} = useContext(DeviceContext);
const {active, ended, error, create} = useContext(RuntimeContext);
const onDelete = useCallback(() => {
if (!window.confirm("Vil du fjerne denne enheten?")) return;
deviceRepo().delete(device.id).then(() => {
refreshDevices();
navigate("/");
})
}, [device, navigate, refreshDevices]);
return (
<Page>
<Header>
<HeaderButton onClick={() => navigate("/")}>
<Icon value={faChevronLeft}/>
</HeaderButton>
<HeaderTitle>{device.name}</HeaderTitle>
</Header>
<PageBody>
<PageFlexRow collapseOn={Size.Mobile}>
<PageFlexColumn flex={1}>
<TitleLine>Enhet</TitleLine>
<Blob>
<BlobText>
<Icon value={faTag}/> {device.name}
</BlobText>
</Blob>
<Blob>
<BlobText>
<Icon value={faChain}/> {device.connectionString}
</BlobText>
</Blob>
<Blob color="indigo" onClick={() => navigate(`/devices/${device.id}/edit`)}>
<BlobText>
<Icon value={faPencilAlt}/>
</BlobText>
</Blob>
<Blob color="red" onClick={onDelete}>
<BlobText>
<Icon value={faTrashCan}/>
</BlobText>
</Blob>
</PageFlexColumn>
<PageFlexColumn flex={1}>
<TitleLine>Test</TitleLine>
{active && !ended ? <TestSection/> : (
<Blob
color={error ? "red" : (ended ? "green" : "yellow")}
onClick={() => create({deviceId: device.id, test: true})}
>
{ended && error && <BlobText>Prøv igjen</BlobText>}
{ended && !error && <BlobText>Vellykket</BlobText>}
{!ended && <BlobText>Kjør</BlobText>}
</Blob>
)}
</PageFlexColumn>
</PageFlexRow>
</PageBody>
</Page>
);
}
interface Event {
createdAt: string,
type: "up" | "down" | "log",
message: string
}
function currentTime(): string {
return (new Date().toTimeString().substring(0, 8));
}
function TestSection(): JSX.Element {
const {lastEvent, connect, disconnect, start, stop} = useContext(RuntimeContext);
const [events, setEvents] = useState<Event[]>([]);
useEffect(() => {
const timeouts: number[] = [];
if (lastEvent) {
const createdAt = (new Date().toTimeString().substring(0, 8));
if (lastEvent.workout) {
setEvents(prev => [
...prev,
{createdAt, type: "down", message: `Opprettet økt ${lastEvent.workout!.id}`},
{createdAt, type: "log", message: "Vil koble til umiddelbart"},
]);
}
if (lastEvent.workoutStates && lastEvent.workoutStates.length > 0) {
const last = lastEvent.workoutStates[lastEvent.workoutStates.length - 1];
setEvents(prev => [...prev, {
createdAt, type: "down", message: `Ny tilstand: ${last.time} s, ${last.calories} kcal`,
}])
}
if (lastEvent.event) {
setEvents(prev => [
...prev,
{createdAt, type: "down", message: `Ny hendelse: ${lastEvent.event!.name}`},
]);
if (lastEvent.event.name === "Connected") {
setEvents(prev => [
...prev,
{createdAt, type: "log", message: "Vil starte om 5 sekunder..."},
]);
timeouts.push(setTimeout(() => {
setEvents(prev => [
...prev,
{createdAt: currentTime(), type: "up", message: "Starter"},
]);
start();
}, 5000));
}
if (lastEvent.event.name === "Started") {
setEvents(prev => [
...prev,
{createdAt, type: "log", message: "Vil stoppe om 30 sekunder..."},
]);
timeouts.push(setTimeout(() => {
setEvents(prev => [
...prev,
{createdAt: currentTime(), type: "up", message: "Stopper"},
]);
stop();
}, 30000));
}
if (lastEvent.event.name === "Stopped") {
setEvents(prev => [
...prev,
{createdAt, type: "log", message: "Vil koble fra om 5 sekunder..."},
]);
timeouts.push(setTimeout(() => {
setEvents(prev => [
...prev,
{createdAt: currentTime(), type: "up", message: "Kobler fra"},
]);
disconnect();
}, 5000));
}
if (lastEvent.event.name === "Disconnected") {
setEvents(prev => [
...prev,
{createdAt, type: "log", message: "Frakoblet"},
]);
}
}
} else {
setEvents([]);
}
}, [lastEvent]);
return (
<>
{events.map((e, i) => (
<PageFlexRow key={i}>
<Blob>
<BlobText>{e.createdAt}</BlobText>
</Blob>
<Blob>
<BlobText>
{e.type === "up" && <Icon value={faChevronUp}/>}
{e.type === "down" && <Icon value={faChevronDown}/>}
{e.type === "log" && <Icon value={faMessage}/>}
</BlobText>
</Blob>
<Blob flex={1}>
<BlobText>{e.message}</BlobText>
</Blob>
</PageFlexRow>
))}
<div style={{height: "100px"}}/>
</>
);
}