5 Commits
2.5.1 ... main

Author SHA1 Message Date
Stian Fredrik Aune 5730609665 Quick and dirty calorie scale option 9 months ago
Stian Fredrik Aune fc177c16a5 Statistikkblokk på Venstre/høyre 1 year ago
Stian Fredrik Aune c867638b9a git add . 1 year ago
Stian Fredrik Aune 34c96e12e0 Merge branch 'main' of git.aiterp.net:ykonsole2/ykonsole2 1 year ago
Stian Fredrik Aune e516c32ede Indigo2 claim option 1 year ago
  1. 3
      webui-react/src/hooks/kpm.ts
  2. 31
      webui-react/src/hooks/storage.ts
  3. 14
      webui-react/src/pages/IndexPage.tsx
  4. 4
      webui-react/src/pages/PlayPage.tsx
  5. 7
      ykonsole-exporter/src/main/kotlin/net/aiterp/git/ykonsole2/infrastructure/indigo2/Indigo2.kt
  6. 4
      ykonsole-server/src/main/kotlin/net/aiterp/git/ykonsole2/Server.kt

3
webui-react/src/hooks/kpm.ts

@ -1,6 +1,5 @@
import {useContext, useEffect, useReducer} from "react";
import RuntimeContext from "../contexts/RuntimeContext";
import {Values} from "../models/Shared";
import {WorkoutState} from "../models/Workouts";
interface UseKpmReducerState {
@ -28,7 +27,7 @@ export function useKpm() {
return {
lastStates: [...inRange, newState],
kpm: inRange.length > 30
kpm: duration >= 30
? Math.round(((newState.calories || 0) - (first.calories || 0)) * 60 / duration)
: 0,
}

31
webui-react/src/hooks/storage.ts

@ -0,0 +1,31 @@
import {Dispatch, SetStateAction, useEffect, useState} from "react";
function loadData<T>(key: string, defValue: T): T {
const newData = window.localStorage.getItem(`ykonsole2.${key}`);
if (newData === null) {
return defValue;
}
return JSON.parse(newData) as T;
}
export default function useLocalStorage<T>(key: string, defValue: T): [T, Dispatch<SetStateAction<T>>] {
const [data, setData] = useState(() => loadData(key, defValue));
useEffect(() => {
window.localStorage.setItem(`ykonsole2.${key}`, JSON.stringify(data));
}, [key, data]);
useEffect(() => {
const callback = () => {
setData(loadData(key, defValue));
};
window.addEventListener("storage", callback);
return () => {
window.removeEventListener("storage", callback);
};
}, [key, defValue]);
return [data, setData];
}

14
webui-react/src/pages/IndexPage.tsx

@ -17,12 +17,14 @@ import {colorOf, WorkoutStatus} from "../models/Workouts";
import {faSpinner} from "@fortawesome/free-solid-svg-icons/faSpinner";
import {useKey} from "../hooks/keyboard";
import {Boi} from "../primitives/boi/Boi";
import useLocalStorage from "../hooks/storage";
export default function IndexPage(): JSX.Element {
const {devices} = useContext(DeviceContext);
const {programs} = useContext(ProgramContext);
const {workouts, loadingWorkouts, expanded, showMoreWorkouts, refreshWorkouts} = useContext(WorkoutContext);
const navigate = useNavigate();
const [statPos, setStatPos] = useLocalStorage<"left" | "right">("stats.position", "left");
const isRunning = useMemo(() => workouts.some(w => w.status !== WorkoutStatus.Disconnected), [workouts]);
@ -112,6 +114,18 @@ export default function IndexPage(): JSX.Element {
<BlobTextLine secondary>Legg til</BlobTextLine>
</BlobText>
</Blob>
<TitleLine>Plassering statistikblokk</TitleLine>
<Blob onClick={() => setStatPos("left")} color={statPos === "left" ? "blue" : "gray"}>
<BlobText>
<BlobTextLine>Til venstre</BlobTextLine>
</BlobText>
</Blob>
<Blob onClick={() => setStatPos("right")} color={statPos === "right" ? "blue" : "gray"}>
<BlobText>
<BlobTextLine>Til høyre</BlobTextLine>
</BlobText>
</Blob>
</PageFlexColumn>
</PageFlexRow>
</PageBody>

4
webui-react/src/pages/PlayPage.tsx

@ -20,6 +20,7 @@ import MessageBoi from "./runtime/MessageBoi";
import ProgramBoi from "./runtime/ProgramBoi";
import MilestoneBoi from "./runtime/MilestoneBoi";
import {useKpm} from "../hooks/kpm";
import useLocalStorage from "../hooks/storage";
function PlayPage(): JSX.Element {
const {active, ready, ended, workout, reset, resume} = useContext(RuntimeContext);
@ -178,6 +179,7 @@ function RunPlayPage(): JSX.Element {
const {workout} = useContext(RuntimeContext);
const lastState = useLastState();
const kpm = useKpm();
const [statPos] = useLocalStorage<"left" | "right">("stats.position", "left");
if (!workout || workout.status === WorkoutStatus.Created) {
return <LoadingPage minimal/>;
@ -187,7 +189,7 @@ function RunPlayPage(): JSX.Element {
<Page title="YKonsole" background={"2046"}>
<ControlsBoi/>
{lastState && (
<Boi vertical="center" horizontal="left" style={{padding: "0.5vmax", paddingBottom: "0"}}>
<Boi vertical="center" horizontal={statPos} style={{padding: "0.5vmax", paddingBottom: "0"}}>
<span style={{fontSize: "125%"}}>
<FluffyValue raw={lastState} valueKey="time"/>
</span>

7
ykonsole-exporter/src/main/kotlin/net/aiterp/git/ykonsole2/infrastructure/indigo2/Indigo2.kt

@ -20,12 +20,15 @@ import java.time.Instant
import java.time.LocalDate
import java.time.ZoneId
import java.util.*
import kotlin.math.roundToInt
class Indigo2(
private val indigoHost: String,
private val oidcTokenEndpoint: String,
private val oidcClientId: String,
private val oidcClientSecret: String,
private val autoClaim: Boolean,
private val calorieScale: Double = 1.0,
) : ExportTarget {
private val om = jacksonObjectMapper().apply {
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
@ -67,7 +70,7 @@ class Indigo2(
"kind" to "ExerciseBike",
"measurements" to tm(chunks.first()),
),
"claimed" to true,
"claimed" to autoClaim,
"date" to workout.date.toString(),
"tags" to listOfNotNull(
tag("ykonsole:Version", "2"),
@ -161,7 +164,7 @@ class Indigo2(
if (ws.time.seconds > 0) mapOf(
"seconds" to ws.time.toInt(),
"meters" to ws.distance?.toInt(),
"calories" to ws.calories?.toInt(),
"calories" to ws.calories?.toInt()?.let { (it * calorieScale).roundToInt() },
"resistance" to ws.level?.toInt(),
"rpmSpeed" to ws.rpmSpeed?.toInt(),
"pulse" to ws.pulse?.toInt(),

4
ykonsole-server/src/main/kotlin/net/aiterp/git/ykonsole2/Server.kt

@ -111,12 +111,16 @@ private data class RepositorySet(
val tokenEndpoint = strEnv("INDIGO2_OIDC_TOKEN_ENDPOINT")
val clientId = strEnv("INDIGO2_OIDC_CLIENT_ID")
val clientSecret = strEnv("INDIGO2_OIDC_CLIENT_SECRET")
val autoClaim = (optStrEnv("INDIGO2_OIDC_AUTO_CLAIM")?.lowercase() ?: "false") in arrayOf("yes", "1", "true")
val calorieScale = (optStrEnv("INDIGO2_EXPORT_CALORIE_SCALE")?.toDoubleOrNull()) ?: 1.0
return Indigo2(
indigoHost = indigo2Endpoint,
oidcTokenEndpoint = tokenEndpoint,
oidcClientId = clientId,
oidcClientSecret = clientSecret,
autoClaim = autoClaim,
calorieScale = calorieScale,
)
}

Loading…
Cancel
Save