adds cooldown, fixed stats bug and fixes middle layout
This commit is contained in:
Generated
+29
@@ -1288,6 +1288,16 @@
|
||||
"loader-utils": "^1.2.3"
|
||||
}
|
||||
},
|
||||
"@testing-library/react-hooks": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-3.2.1.tgz",
|
||||
"integrity": "sha512-1OB6Ksvlk6BCJA1xpj8/WWz0XVd1qRcgqdaFAq+xeC6l61Ucj0P6QpA5u+Db/x9gU4DCX8ziR5b66Mlfg0M2RA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.5.4",
|
||||
"@types/testing-library__react-hooks": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"@types/babel__core": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.3.tgz",
|
||||
@@ -1378,11 +1388,30 @@
|
||||
"csstype": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"@types/react-test-renderer": {
|
||||
"version": "16.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-16.9.1.tgz",
|
||||
"integrity": "sha512-nCXQokZN1jp+QkoDNmDZwoWpKY8HDczqevIDO4Uv9/s9rbGPbSpy8Uaxa5ixHKkcm/Wt0Y9C3wCxZivh4Al+rQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/stack-utils": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz",
|
||||
"integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw=="
|
||||
},
|
||||
"@types/testing-library__react-hooks": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/testing-library__react-hooks/-/testing-library__react-hooks-3.1.0.tgz",
|
||||
"integrity": "sha512-QJc1sgH9DD6jbfybzugnP0sY8wPzzIq8sHDBuThzCr2ZEbyHIaAvN9ytx/tHzcWL5MqmeZJqiUm/GsythaGx3g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/react": "*",
|
||||
"@types/react-test-renderer": "*"
|
||||
}
|
||||
},
|
||||
"@types/yargs": {
|
||||
"version": "13.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz",
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
"workbox-webpack-plugin": "4.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/react-hooks": "^3.2.1",
|
||||
"@types/react": "^16.9.11"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -2,11 +2,11 @@ import React, {useContext, useEffect, useState} from 'react';
|
||||
|
||||
import "./Bois.css";
|
||||
import {StatusContext} from "./Contexts";
|
||||
import {CalorieScore, CpmScore, DistanceScore, RpmScore, Timer} from "./Score";
|
||||
import {CalorieScore, CpmScore, LevelScore, RpmScore, Timer} from "./Score";
|
||||
import calculateDiff from "../helpers/diff";
|
||||
import useKey from "../hooks/useKey";
|
||||
import {Milestones} from "./Milestones";
|
||||
import {Info, InfoTable, StateFilter, Warning} from "./Misc";
|
||||
import {Info, InfoTable, StateFilter, Differ} from "./Misc";
|
||||
|
||||
const Boi = ({type, children}) => {
|
||||
return (
|
||||
@@ -17,22 +17,23 @@ const Boi = ({type, children}) => {
|
||||
};
|
||||
|
||||
export const LeftBoi = () => {
|
||||
const {prevLongDiff, workoutStatus, program, hidden} = useContext(StatusContext);
|
||||
if (workoutStatus === null || program === null || hidden) {
|
||||
const {prevLongDiff, workout, workoutStatus, program, hidden} = useContext(StatusContext);
|
||||
if (workoutStatus === null || workout === null || program === null || hidden) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const {minutes, seconds, calories, distance, rpm} = workoutStatus;
|
||||
const diff = calculateDiff(program, minutes, seconds, calories);
|
||||
const {cooldownMin} = workout;
|
||||
const {minutes, seconds, calories, level, rpm} = workoutStatus;
|
||||
const diff = calculateDiff({program, cooldownMin, minutes, seconds, calories});
|
||||
const cpm = calories / (minutes + (seconds / 60));
|
||||
|
||||
return (
|
||||
<Boi type="left">
|
||||
<Timer minutes={minutes} seconds={seconds}/>
|
||||
<Timer minutes={minutes} seconds={seconds} cooldownMin={cooldownMin}/>
|
||||
<CalorieScore calories={calories} diff={diff} prevDiff={prevLongDiff}/>
|
||||
<DistanceScore distance={distance}/>
|
||||
<RpmScore rpm={rpm}/>
|
||||
<CpmScore cpm={cpm}/>
|
||||
<LevelScore level={level}/>
|
||||
</Boi>
|
||||
);
|
||||
};
|
||||
@@ -42,6 +43,9 @@ export const CentreBoi = () => {
|
||||
state, program, setProgram, bike, setBike, bikes, programs,
|
||||
start, pause, stop, create, workoutStatus,
|
||||
hidden, setHidden,
|
||||
workout,
|
||||
prevLongDiff,
|
||||
toggleCooldown,
|
||||
} = useContext(StatusContext);
|
||||
const [options, setOptions] = useState(null);
|
||||
const [current, setCurrent] = useState(0);
|
||||
@@ -115,6 +119,12 @@ export const CentreBoi = () => {
|
||||
|
||||
useKey(["H", "h"], () => showHide());
|
||||
|
||||
useKey("*", () => {
|
||||
if (state === "started") {
|
||||
toggleCooldown();
|
||||
}
|
||||
});
|
||||
|
||||
function showHide() {
|
||||
setHidden(!hidden);
|
||||
}
|
||||
@@ -124,13 +134,14 @@ export const CentreBoi = () => {
|
||||
}
|
||||
|
||||
if (state === "started" && workoutStatus !== null) {
|
||||
const {cooldownMin} = workout;
|
||||
const {minutes, seconds, calories} = workoutStatus;
|
||||
const diff = calculateDiff(program, minutes, seconds, calories);
|
||||
const diff = calculateDiff({program, cooldownMin, minutes, seconds, calories});
|
||||
|
||||
if (diff < 0) {
|
||||
if (diff < 0 || (minutes > 0 && seconds === 0)) {
|
||||
return (
|
||||
<Boi type="centre">
|
||||
<Warning>{diff}</Warning>
|
||||
<Differ diff={diff} prevDiff={prevLongDiff} />
|
||||
</Boi>
|
||||
);
|
||||
} else {
|
||||
@@ -147,15 +158,13 @@ export const CentreBoi = () => {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (options[current] === void(0)) {
|
||||
if (options[current] === void (0)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return options[current].name;
|
||||
}
|
||||
|
||||
console.log(state);
|
||||
|
||||
return (
|
||||
<Boi type="centre">
|
||||
<StateFilter current={state} required="stopped">
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import React, {createContext, useEffect, useState} from "react";
|
||||
import React, {createContext, useCallback, useEffect, useState} from "react";
|
||||
import {
|
||||
connectWorkout,
|
||||
createNewWorkout,
|
||||
fetchActiveWorkouts, fetchBikes, fetchPrograms,
|
||||
openWebsocket, pauseWorkout,
|
||||
fetchActiveWorkouts,
|
||||
openWebsocket,
|
||||
pauseWorkout,
|
||||
startWorkout,
|
||||
stopWorkout
|
||||
stopWorkout, updateCooldownMins
|
||||
} from "../hooks/net";
|
||||
import useMilestones from "../hooks/milestones";
|
||||
import useOptions from "../hooks/options";
|
||||
|
||||
export const StatusContext = createContext({
|
||||
bike: null,
|
||||
@@ -28,10 +30,9 @@ export const StatusContext = createContext({
|
||||
});
|
||||
|
||||
export const StatusContextProvider = ({children}) => {
|
||||
const {bikes, programs} = useOptions();
|
||||
const [bike, setBike] = useState(null);
|
||||
const [program, setProgram] = useState(null);
|
||||
const [bikes, setBikes] = useState(null);
|
||||
const [programs, setPrograms] = useState(null);
|
||||
const [workout, setWorkout] = useState(null);
|
||||
const [workoutStatus, setWorkoutStatus] = useState(null);
|
||||
const [state, setState] = useState("offline");
|
||||
@@ -39,21 +40,24 @@ export const StatusContextProvider = ({children}) => {
|
||||
const [socket, setSocket] = useState(null);
|
||||
const [hidden, setHidden] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (programs === null) {
|
||||
fetchPrograms().then(newPrograms => setPrograms(newPrograms));
|
||||
const toggleCooldown = useCallback(async () => {
|
||||
if (workout === null || workoutStatus === null) {
|
||||
return;
|
||||
}
|
||||
}, [programs]);
|
||||
|
||||
const {cooldownMin} = workout;
|
||||
const {minutes} = workoutStatus;
|
||||
|
||||
if (cooldownMin === -1) {
|
||||
setWorkout(await updateCooldownMins(workout, minutes + 1));
|
||||
} else if (minutes < cooldownMin) {
|
||||
setWorkout(await updateCooldownMins(workout, -1));
|
||||
}
|
||||
}, [workout, workoutStatus]);
|
||||
|
||||
useEffect(() => {
|
||||
if (bikes === null) {
|
||||
fetchBikes().then(newBikes => {
|
||||
setBikes(newBikes);
|
||||
|
||||
if (newBikes.length === 1) {
|
||||
setBike(newBikes[0]);
|
||||
}
|
||||
});
|
||||
if (bikes !== null && bikes.length === 1) {
|
||||
setBike(bikes[0]);
|
||||
}
|
||||
}, [bikes]);
|
||||
|
||||
@@ -94,15 +98,17 @@ export const StatusContextProvider = ({children}) => {
|
||||
}
|
||||
|
||||
if (typeof body.workoutStatusBackfill !== "undefined") {
|
||||
const cooldownMin = workout.cooldownMin;
|
||||
body.workoutStatusBackfill.forEach(wsbf => {
|
||||
setWorkoutStatus(wsbf);
|
||||
msDispatch({type: "measure", payload: {...wsbf, program}});
|
||||
msDispatch({type: "measure", payload: {...wsbf, program, cooldownMin}});
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof body.workoutStatus !== "undefined") {
|
||||
const cooldownMin = workout.cooldownMin;
|
||||
setWorkoutStatus(body.workoutStatus);
|
||||
msDispatch({type: "measure", payload: {...body.workoutStatus, program}});
|
||||
msDispatch({type: "measure", payload: {...body.workoutStatus, program, cooldownMin}});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -162,6 +168,7 @@ export const StatusContextProvider = ({children}) => {
|
||||
programs, bikes,
|
||||
prevDiff, prevLongDiff,
|
||||
hidden, setHidden,
|
||||
toggleCooldown,
|
||||
}}>
|
||||
{children}
|
||||
</StatusContext.Provider>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.Warning {
|
||||
margin-top: 1em;
|
||||
margin-top: 0;
|
||||
font-weight: 800;
|
||||
font-size: 150%;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React from 'react';
|
||||
import {COLOR_VERY_BAD} from "../helpers/color";
|
||||
import {COLOR_VERY_BAD, colorByDiff} from "../helpers/color";
|
||||
|
||||
import "./Misc.css";
|
||||
import {diffString} from "../helpers/diff";
|
||||
|
||||
const Filter = ({bool, children}) => bool ? <>{children}</> : null;
|
||||
|
||||
@@ -9,9 +10,9 @@ export const StateFilter = ({current, required, children}) => (
|
||||
<Filter bool={current === required}>{children}</Filter>
|
||||
);
|
||||
|
||||
export const Warning = ({children}) => (
|
||||
<div className="Warning" style={{color: COLOR_VERY_BAD}}>
|
||||
{children}
|
||||
export const Differ = ({diff, prevDiff}) => (
|
||||
<div className="Warning" style={{color: colorByDiff(diff, prevDiff)}}>
|
||||
{diffString(diff)}
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
@@ -9,4 +9,12 @@
|
||||
.Timer-number {
|
||||
font-size: 125%;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.Timer-cooldown {
|
||||
color: #0BF;
|
||||
}
|
||||
|
||||
.Timer-awaits-cooldown {
|
||||
color: #8FF;
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import "./Score.css";
|
||||
|
||||
const Score = ({value, suffix = null, color = COLOR_NEUTRAL}) => (
|
||||
<div className="Score">
|
||||
<span className="Score-value" style={{color}}>{value}</span>
|
||||
<span className="Score-value" style={{color}}>{!isNaN(value) ? value : 0}</span>
|
||||
{suffix !== null && (
|
||||
<>
|
||||
|
||||
@@ -15,13 +15,24 @@ const Score = ({value, suffix = null, color = COLOR_NEUTRAL}) => (
|
||||
</div>
|
||||
);
|
||||
|
||||
export const Timer = ({minutes, seconds}) => {
|
||||
export const Timer = ({minutes, seconds, cooldownMin}) => {
|
||||
function pad(number) {
|
||||
return number >= 10 ? `${number}` : `0${number}`;
|
||||
}
|
||||
|
||||
const hasCooldown = cooldownMin >= 0 && cooldownMin !== void(0);
|
||||
const isCooldown = hasCooldown && minutes >= cooldownMin;
|
||||
const awaitsCooldown = hasCooldown && !isCooldown;
|
||||
|
||||
const classes = ["Timer"];
|
||||
if (isCooldown) {
|
||||
classes.push("Timer-cooldown");
|
||||
} else if (awaitsCooldown) {
|
||||
classes.push("Timer-awaits-cooldown");
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="Timer">
|
||||
<div className={classes.join(" ")}>
|
||||
<span className="Timer-number">{pad(minutes)}</span>
|
||||
{" : "}
|
||||
<span className="Timer-number">{pad(seconds)}</span>
|
||||
@@ -33,7 +44,7 @@ export const CalorieScore = ({calories, diff, prevDiff}) => (
|
||||
<Score value={calories} color={colorByDiff(diff, prevDiff)} suffix="kal"/>
|
||||
);
|
||||
|
||||
export const DistanceScore = ({distance}) => <Score value={distance.toFixed(1)} suffix="km"/>;
|
||||
export const LevelScore = ({level}) => <Score value={level} suffix="lvl"/>;
|
||||
|
||||
export const RpmScore = ({rpm}) => <Score value={rpm} suffix="rpm"/>;
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import {COLOR_BAD, COLOR_GOOD, COLOR_VERY_BAD, COLOR_VERY_GOOD, colorByDiff} from "./color";
|
||||
|
||||
describe("colorByDiff", function () {
|
||||
it("should show dark red for worse bad", () => {
|
||||
expect(colorByDiff(-5, -2)).toBe(COLOR_VERY_BAD);
|
||||
});
|
||||
|
||||
it("should show bright red for equal bad", () => {
|
||||
expect(colorByDiff(-11, -11)).toBe(COLOR_BAD);
|
||||
});
|
||||
|
||||
it("should show bright red for better bad", () => {
|
||||
expect(colorByDiff(-1, -7)).toBe(COLOR_BAD);
|
||||
});
|
||||
|
||||
it("should show dark green for better good", () => {
|
||||
expect(colorByDiff(33, 25)).toBe(COLOR_VERY_GOOD);
|
||||
});
|
||||
|
||||
it("should show bright green for equal good", () => {
|
||||
expect(colorByDiff(10, 10)).toBe(COLOR_GOOD);
|
||||
});
|
||||
|
||||
it("should show bright green for worse good", () => {
|
||||
expect(colorByDiff(5, 7)).toBe(COLOR_GOOD);
|
||||
});
|
||||
});
|
||||
+31
-16
@@ -2,24 +2,39 @@ export function diffString(diff) {
|
||||
return diff < 0 ? `${diff}` : `+${diff}`
|
||||
}
|
||||
|
||||
export default function calculateDiff(program, minutes, seconds, calories) {
|
||||
const {warmupMin, warmupCpm, cpm} = program;
|
||||
export default function calculateDiff({program, cooldownMin = null, minutes, seconds, calories}) {
|
||||
const {warmupMin, warmupCpm, cpm, cooldownCpm} = program;
|
||||
const actualWarmup = cooldownMin !== null && cooldownMin > 0
|
||||
? Math.min(warmupMin, cooldownMin)
|
||||
: warmupMin;
|
||||
|
||||
let preWarmup = 0;
|
||||
if (warmupMin > 0) {
|
||||
// Pre-warmup
|
||||
const warmedUpMinutes = Math.min(minutes, warmupMin);
|
||||
const warmedUpSeconds = minutes >= warmupMin ? 0 : seconds;
|
||||
preWarmup = Math.round((warmupCpm * (warmedUpMinutes + (warmedUpSeconds / 60))));
|
||||
// Minutes in each section
|
||||
const minWarmup = calculateMins(0, actualWarmup, minutes, seconds);
|
||||
const minMain = calculateMins(actualWarmup, cooldownMin, minutes, seconds);
|
||||
const minCooldown = calculateMins(cooldownMin, null, minutes, seconds);
|
||||
|
||||
// Expected calories in each section
|
||||
const calWarmup = minWarmup * warmupCpm;
|
||||
const calMain = minMain * cpm;
|
||||
const calCooldown = minCooldown * cooldownCpm;
|
||||
|
||||
return Math.round(calories - (calWarmup + calMain + calCooldown));
|
||||
}
|
||||
|
||||
function calculateMins(minMinutes, maxMinutes, minutes, seconds) {
|
||||
const fraction = seconds / 60;
|
||||
|
||||
if (minMinutes === null || minMinutes < 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// Post-warmup
|
||||
const trainedMinutes = Math.max(0, minutes - warmupMin);
|
||||
const trainedSeconds = minutes >= warmupMin ? seconds : 0;
|
||||
const postWarmup = Math.round((cpm * (trainedMinutes + (trainedSeconds / 60))));
|
||||
if (minutes >= minMinutes) {
|
||||
if (maxMinutes !== null && minutes >= maxMinutes) {
|
||||
return maxMinutes - minMinutes;
|
||||
}
|
||||
|
||||
// Sum
|
||||
const target = Math.round(preWarmup + postWarmup);
|
||||
|
||||
return calories - target;
|
||||
return (minutes - minMinutes) + fraction;
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import calculateDiff, {diffString} from "./diff";
|
||||
|
||||
const programWithoutWarmup = {
|
||||
warmupMin: 0,
|
||||
warmupCpm: 0,
|
||||
cpm: 30,
|
||||
cooldownCpm: 20,
|
||||
};
|
||||
|
||||
const programWithWarmup = {
|
||||
warmupMin: 10,
|
||||
warmupCpm: 25,
|
||||
cpm: 30,
|
||||
cooldownCpm: 20,
|
||||
};
|
||||
|
||||
describe('diffString', () => {
|
||||
it("should show a plus before zero", () => {
|
||||
expect(diffString(0)).toBe("+0");
|
||||
});
|
||||
|
||||
it("should show a plus before a positive number", () => {
|
||||
expect(diffString(44)).toBe("+44");
|
||||
});
|
||||
|
||||
it("should show a minus when negatives", () => {
|
||||
expect(diffString(-12)).toBe("-12");
|
||||
});
|
||||
});
|
||||
|
||||
describe("calculateDiff", () => {
|
||||
it("should return zero at start", () => {
|
||||
const diff = calculateDiff({
|
||||
program: programWithoutWarmup,
|
||||
minutes: 0,
|
||||
seconds: 0,
|
||||
calories: 0,
|
||||
});
|
||||
|
||||
expect(diff).toBe(0);
|
||||
});
|
||||
|
||||
it("should expect 300 calories after 0' / 10'", () => {
|
||||
const diff = calculateDiff({
|
||||
program: programWithoutWarmup,
|
||||
minutes: 10,
|
||||
seconds: 0,
|
||||
calories: 305,
|
||||
});
|
||||
|
||||
expect(diff).toBe(5);
|
||||
});
|
||||
|
||||
it("should expect 865 calories after 10' / 20'50", () => {
|
||||
const diff = calculateDiff({
|
||||
program: programWithWarmup,
|
||||
minutes: 30,
|
||||
seconds: 50,
|
||||
calories: 250 + 600 + 25 - 17,
|
||||
});
|
||||
|
||||
// Aim: 875
|
||||
expect(diff).toBe(-17);
|
||||
});
|
||||
|
||||
it("should expect 755 calories after 10' / 15' / 7'45", () => {
|
||||
const diff = calculateDiff({
|
||||
program: programWithWarmup,
|
||||
cooldownMin: 25,
|
||||
minutes: 32,
|
||||
seconds: 45,
|
||||
calories: 250 + 450 + 140 + 15,
|
||||
});
|
||||
|
||||
expect(diff).toBe(0);
|
||||
});
|
||||
|
||||
it("should expect 325 calories after 5' (half) / 0' / 10'", () => {
|
||||
const diff = calculateDiff({
|
||||
program: programWithWarmup,
|
||||
cooldownMin: 5,
|
||||
minutes: 15,
|
||||
seconds: 0,
|
||||
calories: 325
|
||||
});
|
||||
|
||||
expect(diff).toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -11,19 +11,21 @@ function reducer(state, {type, payload}) {
|
||||
switch (type) {
|
||||
case "measure":
|
||||
let {prevDiff, prevLongDiff, milestones} = state;
|
||||
const {minutes, seconds, calories, program} = payload;
|
||||
const {minutes, seconds, calories, program, cooldownMin} = payload;
|
||||
|
||||
if (minutes === 0 || seconds !== 0) {
|
||||
return state;
|
||||
}
|
||||
|
||||
const isFive = minutes % 5 === 0;
|
||||
const diff = calculateDiff(program, minutes, seconds, calories);
|
||||
let newMilestones = milestones.filter(m => m.minutes !== minutes);
|
||||
newMilestones.push({
|
||||
minutes, seconds, calories, diff,
|
||||
prevDiff: isFive ? prevLongDiff : prevDiff
|
||||
});
|
||||
const diff = calculateDiff({program, minutes, seconds, calories, cooldownMin});
|
||||
let newMilestones = [...milestones];
|
||||
if (newMilestones.find(m => m.minutes === minutes) === void(0)) {
|
||||
newMilestones.push({
|
||||
minutes, seconds, calories, diff,
|
||||
prevDiff: isFive ? prevLongDiff : prevDiff
|
||||
});
|
||||
}
|
||||
|
||||
if (isFive) {
|
||||
newMilestones = newMilestones.filter(m => m.minutes % 5 === 0);
|
||||
|
||||
@@ -33,6 +33,10 @@ export async function pauseWorkout(workout) {
|
||||
return await post(`/workout/${workout.id}/pause`);
|
||||
}
|
||||
|
||||
export async function updateCooldownMins(workout, cooldownMin) {
|
||||
return await put(`/workout/${workout.id}`, {cooldownMin});
|
||||
}
|
||||
|
||||
export function openWebsocket(workout) {
|
||||
return new WebSocket(url(`/workout/${workout.id}/subscribe`, "ws"));
|
||||
}
|
||||
@@ -53,6 +57,16 @@ function post(path, data = {}) {
|
||||
}).then(r => r.json());
|
||||
}
|
||||
|
||||
function put(path, data = {}) {
|
||||
return fetch(url(path), {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
}).then(r => r.json());
|
||||
}
|
||||
|
||||
function url(path, prefix = "http") {
|
||||
return `${prefix}://127.0.0.1:9999/api${path}`;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {fetchBikes, fetchPrograms} from "./net";
|
||||
|
||||
export default function useOptions() {
|
||||
const [bikes, setBikes] = useState(null);
|
||||
const [programs, setPrograms] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (programs === null) {
|
||||
fetchPrograms().then(newPrograms => setPrograms(newPrograms));
|
||||
}
|
||||
}, [programs]);
|
||||
|
||||
useEffect(() => {
|
||||
if (bikes === null) {
|
||||
fetchBikes().then(newBikes => {
|
||||
setBikes(newBikes);
|
||||
});
|
||||
}
|
||||
}, [bikes]);
|
||||
|
||||
return {bikes, programs};
|
||||
}
|
||||
Reference in New Issue
Block a user