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.
140 lines
3.8 KiB
140 lines
3.8 KiB
import {useContext, useEffect, useMemo, useState} from "react";
|
|
import RuntimeContext from "../../contexts/RuntimeContext";
|
|
import {WorkoutStatus} from "../../models/Workouts";
|
|
import {faPause, faPlay} from "@fortawesome/free-solid-svg-icons";
|
|
import {useKey, usePlusMinus} from "../../hooks/keyboard";
|
|
import {Boi} from "../../primitives/boi/Boi";
|
|
import {TitleLine} from "../../primitives/misc/Misc";
|
|
import Blob, {BlobText} from "../../primitives/blob/Blob";
|
|
import {Icon} from "../../primitives/Shared";
|
|
import {useLastState} from "./hooks";
|
|
import {IconDefinition} from "@fortawesome/fontawesome-svg-core";
|
|
import MessageBoi from "./MessageBoi";
|
|
|
|
interface Option {
|
|
icon?: IconDefinition
|
|
text?: string
|
|
warning?: boolean
|
|
onClick(): void
|
|
}
|
|
|
|
const skipMessages = ["Ikke hopp over", "Hopp over straks", "Hopp over nå"];
|
|
|
|
export function ControlsBoi() {
|
|
const {workout, disconnect, start, stop, setLevel, skip} = useContext(RuntimeContext);
|
|
const lastState = useLastState();
|
|
|
|
const [mode, setMode] = useState<"default" | "level" | "skip">("default");
|
|
const [nextSkip, setNextSkip] = useState(-1);
|
|
const [lastTime, setLastTime] = useState(0);
|
|
|
|
const options: Option[] = useMemo(() => {
|
|
if (!workout) return [];
|
|
|
|
const isStopped = workout.status === WorkoutStatus.Connected || workout.status === WorkoutStatus.Stopped;
|
|
|
|
const btnList: Option[] = [];
|
|
if (isStopped) {
|
|
btnList.push({icon: faPlay, onClick: start})
|
|
}
|
|
|
|
if (workout.status === WorkoutStatus.Started) {
|
|
btnList.push({icon: faPause, onClick: stop});
|
|
|
|
if (workout.program) {
|
|
const text = nextSkip > 0 ? `Hopper om ${nextSkip - lastTime} sek.` : "Hopp over steg";
|
|
|
|
btnList.push({text, warning: nextSkip >= 0, onClick: () => setMode("skip")})
|
|
} else {
|
|
btnList.push({text: "Motstand", onClick: () => setMode("level")});
|
|
}
|
|
}
|
|
|
|
if (isStopped) {
|
|
btnList.push({text: "Avslutt", onClick: disconnect});
|
|
}
|
|
|
|
return btnList;
|
|
}, [workout, start, stop, disconnect, skip, nextSkip, lastTime]);
|
|
|
|
let length = options.length;
|
|
if (mode === "level") length = 32;
|
|
if (mode === "skip") length = 3;
|
|
|
|
const [sel, setSel] = usePlusMinus(length);
|
|
|
|
useKey("Enter", () => {
|
|
if (mode !== "default") {
|
|
if (mode === "level") {
|
|
setLevel(sel + 1);
|
|
} else {
|
|
if (sel === 1) {
|
|
setNextSkip(lastTime + 60 - (lastTime % 60));
|
|
} else if (sel === 2) {
|
|
setNextSkip(0);
|
|
} else {
|
|
setNextSkip(-1);
|
|
}
|
|
}
|
|
|
|
setSel(0);
|
|
setMode("default");
|
|
} else {
|
|
if (options[sel]) {
|
|
options[sel].onClick();
|
|
}
|
|
}
|
|
|
|
setSel(0);
|
|
}, [options, lastTime, sel]);
|
|
|
|
useKey("Escape", () => {
|
|
if (!workout) return;
|
|
|
|
const isStopped = workout.status === WorkoutStatus.Connected || workout.status === WorkoutStatus.Stopped;
|
|
|
|
if (isStopped) {
|
|
disconnect();
|
|
}
|
|
|
|
}, [workout, disconnect]);
|
|
|
|
useEffect(() => {
|
|
if (lastState?.level && mode === "level") {
|
|
setSel(lastState.level - 1);
|
|
}
|
|
}, [mode]);
|
|
|
|
useEffect(() => {
|
|
if (lastState?.time) {
|
|
setLastTime(lastState.time!);
|
|
}
|
|
}, [lastState]);
|
|
|
|
useEffect(() => {
|
|
if (nextSkip !== -1 && (nextSkip === 0 || nextSkip <= lastTime)) {
|
|
setNextSkip(-1);
|
|
skip();
|
|
}
|
|
}, [lastTime, nextSkip]);
|
|
|
|
if (mode === "level") {
|
|
return <MessageBoi text={`${sel + 1} +/-`}/>;
|
|
}
|
|
|
|
if (mode === "skip") {
|
|
return <MessageBoi text={`${skipMessages[sel]} +/-`}/>;
|
|
}
|
|
|
|
return (
|
|
<Boi vertical="top" horizontal="right" style={{fontSize: "2vmax"}}>
|
|
{options.map((o, i) => (
|
|
<Blob key={i} color={(o.warning && lastTime % 2 === 0) ? "yellow" : (sel === i ? "indigo" : "gray")} onClick={o.onClick}>
|
|
<BlobText>
|
|
{o.icon && <Icon value={o.icon}/>} {o.text}
|
|
</BlobText>
|
|
</Blob>
|
|
))}
|
|
</Boi>
|
|
);
|
|
}
|