From cac178f06d1975ea460b5acbf7ef7b43191fe09a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ab=C3=A1sz?= <>
Date: Wed, 22 Mar 2023 13:44:34 +0100
Subject: [PATCH] Add settable metric tiles and settings persistence
Make the metric tiles settable via the settings dialog and implement
persistence of these settings to the browser localStorage (partially
fix #131)
---
app/client/components/PerformanceDashboard.js | 62 ++++++++++++-------
app/client/index.js | 16 ++++-
app/client/lib/app.js | 4 +-
app/client/store/appState.js | 5 +-
4 files changed, 59 insertions(+), 28 deletions(-)
diff --git a/app/client/components/PerformanceDashboard.js b/app/client/components/PerformanceDashboard.js
index 4e5c913..91492b6 100644
--- a/app/client/components/PerformanceDashboard.js
+++ b/app/client/components/PerformanceDashboard.js
@@ -34,7 +34,7 @@ export class PerformanceDashboard extends AppElement {
}
}
- dashboard-metric, dashboard-actions,dashboard-force-curve {
+ dashboard-metric, dashboard-actions, dashboard-force-curve {
background: var(--theme-widget-color);
text-align: center;
position: relative;
@@ -63,6 +63,32 @@ export class PerformanceDashboard extends AppElement {
filter: brightness(150%);
}
`
+ dashboardMetricComponents = (formattedMetrics, appState) => ({
+ distance: html``,
+
+ pace: html``,
+
+ power: html``,
+
+ stkRate: html``,
+
+ heartRate: html`
+ ${formattedMetrics?.heartrateBatteryLevel?.value
+ ? html``
+ : ''}
+ `,
+
+ totalStk: html``,
+
+ calories: html``,
+
+ timer: html``,
+
+ forceCurve: html``,
+
+ actions: html``
+ })
+
@state({ type: Object })
dialog
@@ -73,31 +99,19 @@ export class PerformanceDashboard extends AppElement {
appState = APP_STATE
render () {
- const metrics = this.calculateFormattedMetrics(this.appState.metrics)
+ const metricConfig = [...new Set(this.appState.config.guiConfigs.dashboardMetrics)].reduce((prev, metricName) => {
+ prev.push(this.dashboardMetricComponents(this.metrics, this.appState)[metricName])
+ return prev
+ }, [])
+
+ this.metrics = this.calculateFormattedMetrics(this.appState.metrics)
return html`
${icon_settings}
${this.dialog ? this.dialog : ''}
-
-
-
-
- ${metrics?.heartrate?.value
- ? html`
-
- ${metrics?.heartrateBatteryLevel?.value
- ? html`
-
- `
- : ''
- }
- `
- : html``}
-
-
-
-
+
+ ${metricConfig}
`
}
@@ -124,10 +138,10 @@ export class PerformanceDashboard extends AppElement {
const formattedMetrics = {}
for (const [key, value] of Object.entries(metrics)) {
const valueFormatted = fieldFormatter[key] ? fieldFormatter[key](value) : value
- if (valueFormatted.value !== undefined && valueFormatted.unit !== undefined) {
+ if (valueFormatted?.value !== undefined && valueFormatted?.unit !== undefined) {
formattedMetrics[key] = {
- value: valueFormatted.value,
- unit: valueFormatted.unit
+ value: valueFormatted?.value,
+ unit: valueFormatted?.unit
}
} else {
formattedMetrics[key] = {
diff --git a/app/client/index.js b/app/client/index.js
index b26dfcd..d81f2cd 100644
--- a/app/client/index.js
+++ b/app/client/index.js
@@ -28,6 +28,11 @@ export class App extends LitElement {
// todo: we also want a mechanism here to get notified of state changes
})
+ const config = this.appState.config.guiConfigs
+ Object.keys(config).forEach(key => {
+ config[key] = JSON.parse(localStorage.getItem(key)) ?? config[key]
+ })
+
// this is how we implement changes to the global state:
// once any child component sends this CustomEvent we update the global state according
// to the changes that were passed to us
@@ -39,13 +44,22 @@ export class App extends LitElement {
this.addEventListener('triggerAction', (event) => {
this.app.handleAction(event.detail)
})
+
+ // notify the app about the triggered action
+ this.addEventListener('changeGuiSetting', (event) => {
+ Object.keys(event.detail.config.guiConfigs).forEach(key => {
+ localStorage.setItem(key, JSON.stringify(event.detail.config.guiConfigs[key]))
+ })
+
+ this.updateState(event.detail)
+ })
}
// the global state is updated by replacing the appState with a copy of the new state
// todo: maybe it is more convenient to just pass the state elements that should be changed?
// i.e. do something like this.appState = { ..this.appState, ...newState }
updateState = (newState) => {
- this.appState = { ...newState }
+ this.appState = { ...this.appState, ...newState }
}
// return a deep copy of the state to other components to minimize risk of side effects
diff --git a/app/client/lib/app.js b/app/client/lib/app.js
index fff8a1b..7404998 100644
--- a/app/client/lib/app.js
+++ b/app/client/lib/app.js
@@ -38,7 +38,7 @@ export function createApp (app) {
let initialWebsocketOpenend = true
function initWebsocket () {
// use the native websocket implementation of browser to communicate with backend
- socket = new WebSocket(`ws://${location.host}/websocket`)
+ socket = new WebSocket('ws://localhost:100/websocket')
socket.addEventListener('open', (event) => {
console.log('websocket opened')
@@ -71,7 +71,7 @@ export function createApp (app) {
const data = message.data
switch (message.type) {
case 'config': {
- app.updateState({ ...app.getState(), config: data })
+ app.updateState({ ...app.getState(), config: { ...app.getState().config, ...data } })
break
}
case 'metrics': {
diff --git a/app/client/store/appState.js b/app/client/store/appState.js
index 77c78e2..454a0cd 100644
--- a/app/client/store/appState.js
+++ b/app/client/store/appState.js
@@ -20,6 +20,9 @@ export const APP_STATE = {
// true if upload to strava is enabled
stravaUploadEnabled: false,
// true if remote device shutdown is enabled
- shutdownEnabled: false
+ shutdownEnabled: false,
+ guiConfigs: {
+ dashboardMetrics: ['distance', 'timer', 'pace', 'power', 'stkRate', 'totalStk', 'calories', 'actions']
+ }
}
}