5 Commits
2.5.1 ... main

Author SHA1 Message Date
Stian Fredrik Aune 5730609665 Quick and dirty calorie scale option 10 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 {useContext, useEffect, useReducer} from "react";
import RuntimeContext from "../contexts/RuntimeContext"; import RuntimeContext from "../contexts/RuntimeContext";
import {Values} from "../models/Shared";
import {WorkoutState} from "../models/Workouts"; import {WorkoutState} from "../models/Workouts";
interface UseKpmReducerState { interface UseKpmReducerState {
@ -28,7 +27,7 @@ export function useKpm() {
return { return {
lastStates: [...inRange, newState], lastStates: [...inRange, newState],
kpm: inRange.length > 30
kpm: duration >= 30
? Math.round(((newState.calories || 0) - (first.calories || 0)) * 60 / duration) ? Math.round(((newState.calories || 0) - (first.calories || 0)) * 60 / duration)
: 0, : 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 {faSpinner} from "@fortawesome/free-solid-svg-icons/faSpinner";
import {useKey} from "../hooks/keyboard"; import {useKey} from "../hooks/keyboard";
import {Boi} from "../primitives/boi/Boi"; import {Boi} from "../primitives/boi/Boi";
import useLocalStorage from "../hooks/storage";
export default function IndexPage(): JSX.Element { export default function IndexPage(): JSX.Element {
const {devices} = useContext(DeviceContext); const {devices} = useContext(DeviceContext);
const {programs} = useContext(ProgramContext); const {programs} = useContext(ProgramContext);
const {workouts, loadingWorkouts, expanded, showMoreWorkouts, refreshWorkouts} = useContext(WorkoutContext); const {workouts, loadingWorkouts, expanded, showMoreWorkouts, refreshWorkouts} = useContext(WorkoutContext);
const navigate = useNavigate(); const navigate = useNavigate();
const [statPos, setStatPos] = useLocalStorage<"left" | "right">("stats.position", "left");
const isRunning = useMemo(() => workouts.some(w => w.status !== WorkoutStatus.Disconnected), [workouts]); 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> <BlobTextLine secondary>Legg til</BlobTextLine>
</BlobText> </BlobText>
</Blob> </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> </PageFlexColumn>
</PageFlexRow> </PageFlexRow>
</PageBody> </PageBody>

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

@ -20,6 +20,7 @@ import MessageBoi from "./runtime/MessageBoi";
import ProgramBoi from "./runtime/ProgramBoi"; import ProgramBoi from "./runtime/ProgramBoi";
import MilestoneBoi from "./runtime/MilestoneBoi"; import MilestoneBoi from "./runtime/MilestoneBoi";
import {useKpm} from "../hooks/kpm"; import {useKpm} from "../hooks/kpm";
import useLocalStorage from "../hooks/storage";
function PlayPage(): JSX.Element { function PlayPage(): JSX.Element {
const {active, ready, ended, workout, reset, resume} = useContext(RuntimeContext); const {active, ready, ended, workout, reset, resume} = useContext(RuntimeContext);
@ -178,6 +179,7 @@ function RunPlayPage(): JSX.Element {
const {workout} = useContext(RuntimeContext); const {workout} = useContext(RuntimeContext);
const lastState = useLastState(); const lastState = useLastState();
const kpm = useKpm(); const kpm = useKpm();
const [statPos] = useLocalStorage<"left" | "right">("stats.position", "left");
if (!workout || workout.status === WorkoutStatus.Created) { if (!workout || workout.status === WorkoutStatus.Created) {
return <LoadingPage minimal/>; return <LoadingPage minimal/>;
@ -187,7 +189,7 @@ function RunPlayPage(): JSX.Element {
<Page title="YKonsole" background={"2046"}> <Page title="YKonsole" background={"2046"}>
<ControlsBoi/> <ControlsBoi/>
{lastState && ( {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%"}}> <span style={{fontSize: "125%"}}>
<FluffyValue raw={lastState} valueKey="time"/> <FluffyValue raw={lastState} valueKey="time"/>
</span> </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.LocalDate
import java.time.ZoneId import java.time.ZoneId
import java.util.* import java.util.*
import kotlin.math.roundToInt
class Indigo2( class Indigo2(
private val indigoHost: String, private val indigoHost: String,
private val oidcTokenEndpoint: String, private val oidcTokenEndpoint: String,
private val oidcClientId: String, private val oidcClientId: String,
private val oidcClientSecret: String, private val oidcClientSecret: String,
private val autoClaim: Boolean,
private val calorieScale: Double = 1.0,
) : ExportTarget { ) : ExportTarget {
private val om = jacksonObjectMapper().apply { private val om = jacksonObjectMapper().apply {
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
@ -67,7 +70,7 @@ class Indigo2(
"kind" to "ExerciseBike", "kind" to "ExerciseBike",
"measurements" to tm(chunks.first()), "measurements" to tm(chunks.first()),
), ),
"claimed" to true,
"claimed" to autoClaim,
"date" to workout.date.toString(), "date" to workout.date.toString(),
"tags" to listOfNotNull( "tags" to listOfNotNull(
tag("ykonsole:Version", "2"), tag("ykonsole:Version", "2"),
@ -161,7 +164,7 @@ class Indigo2(
if (ws.time.seconds > 0) mapOf( if (ws.time.seconds > 0) mapOf(
"seconds" to ws.time.toInt(), "seconds" to ws.time.toInt(),
"meters" to ws.distance?.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(), "resistance" to ws.level?.toInt(),
"rpmSpeed" to ws.rpmSpeed?.toInt(), "rpmSpeed" to ws.rpmSpeed?.toInt(),
"pulse" to ws.pulse?.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 tokenEndpoint = strEnv("INDIGO2_OIDC_TOKEN_ENDPOINT")
val clientId = strEnv("INDIGO2_OIDC_CLIENT_ID") val clientId = strEnv("INDIGO2_OIDC_CLIENT_ID")
val clientSecret = strEnv("INDIGO2_OIDC_CLIENT_SECRET") 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( return Indigo2(
indigoHost = indigo2Endpoint, indigoHost = indigo2Endpoint,
oidcTokenEndpoint = tokenEndpoint, oidcTokenEndpoint = tokenEndpoint,
oidcClientId = clientId, oidcClientId = clientId,
oidcClientSecret = clientSecret, oidcClientSecret = clientSecret,
autoClaim = autoClaim,
calorieScale = calorieScale,
) )
} }

Loading…
Cancel
Save