'use strict' /* Open Rowing Monitor, https://github.com/laberning/openrowingmonitor Wrapper for the Open Rowing Monitor rowing games */ import { customElement } from 'lit/decorators.js' import { createRowingGames } from '../arcade/RowingGames.js' import { icon_bolt, icon_exit, icon_heartbeat, icon_paddle, icon_route, icon_stopwatch } from '../lib/icons.js' import { metricValue, metricUnit } from '../lib/helper.js' import { buttonStyles } from '../lib/styles.js' import { AppElement, css, html } from './AppElement.js' @customElement('game-component') export class GameComponent extends AppElement { static get styles () { return [ buttonStyles, css` :host { width: 100vw; height: 100vh; display: flex; } #arcade { width: 100vh !important; height: 100vh !important; } @media (orientation: portrait) { :host { flex-direction: column } #arcade { width: 100vw; height: 100vw; } } div > .icon { height: 0.8em; width: 1.5em; } #container { width: 100%; height: 100%; text-align: left; position: relative; } #widget { position: absolute; bottom: 0; display: flex; flex-direction: row; flex-wrap: wrap; padding: 0.5em; margin: 1vw; background: var(--theme-background-color); border-radius: var(--theme-border-radius); } .metric-unit { font-size: 80% } #widget div { width: 6.5em; white-space: nowrap; } #buttons { padding: 0.5em 0.5em 0 0.5em; flex-basis: 100%; } ` ] } render () { const metrics = this.appState.metrics return html`
${icon_route}${metricValue(metrics, 'distanceTotal')}${metricUnit(metrics, 'distanceTotal')}
${icon_stopwatch}${metricValue(metrics, 'splitFormatted')}/500m
${icon_bolt}${metricValue(metrics, 'powerRaw')}watt
${icon_paddle}${metricValue(metrics, 'strokesPerMinute')}/min
${metrics?.heartrate ? html`
${icon_heartbeat}${metricValue(metrics, 'heartrate')}bpm
` : ''}
${icon_bolt}${metricValue(metrics, 'instantaneousTorque')}trq
${icon_bolt}${metricValue(metrics, 'powerRatio')}ratio
${icon_bolt}${metricValue(metrics, 'strokeState')}
` } openDashboard () { this.sendEvent('triggerAction', { command: 'openDashboard' }) } firstUpdated () { // todo: haven't decided at what resolution to render the game. // might use the screen resolution for this, but then the game would look and behave differently // depending on the resolution // using a square screen has the advantage that it works well on portrait and landscape screens // for now will set it to a fixed square resolution and let css take care of scaling it const gameSize = 600 const canvas = this.renderRoot.querySelector('#arcade') // this.rowingGames = createRowingGames(canvas, canvas.clientWidth, canvas.clientHeight) // @ts-ignore this.rowingGames = createRowingGames(canvas, gameSize, gameSize) // This mitigates a problem with delayed app state updates in the kaboom game. // If we use the change events from our Web Component to notify the game (i.e. by using the // change notifiers available in this component), then the state changes will be processed by the // game with a certain delay. This is pretty weird, since they are processed by this component at // the correct time. Also when we look at timestamps in the games callback, then it seems that they // are called completely in sync with the event and without dely. // This problem only occures, when the update events are created from a web request (i.e. by receiving // new rowing metrics via web socket). // By delivering the app state updates directly here from index.js, this problem does not occure. this.sendEvent('setGameStateUpdater', (appState) => { this.gameAppState(appState) }) } gameAppState (appState) { if (this.rowingGames) this.rowingGames.appState(appState) } disconnectedCallback () { if (this.rowingGames !== undefined) { this.rowingGames.dispose() } } }