147 lines
5.0 KiB
TypeScript
147 lines
5.0 KiB
TypeScript
/*
|
|
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'
|
|
import { AppState } from '../store/appState.js'
|
|
@customElement('game-component')
|
|
export class GameComponent extends AppElement {
|
|
rowingGames: any
|
|
static get styles () {
|
|
return [
|
|
buttonStyles,
|
|
css`
|
|
:host {
|
|
width: 100vw;
|
|
height: 100vh;
|
|
display: flex;
|
|
}
|
|
@media (orientation: landscape) {
|
|
:host {
|
|
flex-direction: row
|
|
}
|
|
#arcade {
|
|
width: 100vh;
|
|
height: 100vh;
|
|
}
|
|
#controls {
|
|
width: calc(100vw - 100vh);
|
|
height: 100vh;
|
|
}
|
|
}
|
|
@media (orientation: portrait) {
|
|
:host {
|
|
flex-direction: column
|
|
}
|
|
#arcade {
|
|
width: 100vw;
|
|
height: 100vw;
|
|
}
|
|
#controls {
|
|
width: 100vw;
|
|
height: calc(100vh - 100vw);
|
|
}
|
|
}
|
|
|
|
div > .icon {
|
|
height: 0.8em;
|
|
width: 1.5em;
|
|
}
|
|
#controls {
|
|
text-align: left;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
#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`
|
|
<div id="arcade"></div>
|
|
<div id="controls">
|
|
<div id="widget">
|
|
<div>${icon_route}${metricValue(metrics, 'distanceTotal')}<span class="metric-unit">${metricUnit(metrics, 'distanceTotal')}</span></div>
|
|
<div>${icon_stopwatch}${metricValue(metrics, 'splitFormatted')}<span class="metric-unit">/500m</span></div>
|
|
<div>${icon_bolt}${metricValue(metrics, 'powerRaw')}<span class="metric-unit">watt</span></div>
|
|
<div>${icon_paddle}${metricValue(metrics, 'strokesPerMinute')}<span class="metric-unit">/min</span></div>
|
|
${metrics?.heartrate
|
|
? html`<div>${icon_heartbeat}${metricValue(metrics, 'heartrate')}<span class="metric-unit">bpm</span></div>`
|
|
: ''}
|
|
<div id='buttons'>
|
|
<button @click=${this.openDashboard}>${icon_exit}</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`
|
|
}
|
|
|
|
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 = 700
|
|
const arcade = this.renderRoot.querySelector('#arcade')
|
|
// this.rowingGames = createRowingGames(arcade, canvas.clientWidth, canvas.clientHeight)
|
|
// @ts-ignore
|
|
this.rowingGames = createRowingGames(arcade, 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 delay.
|
|
// This problem only occurs, 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 occur.
|
|
this.sendEvent('setGameStateUpdater', (appState: AppState) => { this.gameAppState(appState) })
|
|
}
|
|
|
|
gameAppState (appState: AppState) {
|
|
if (this.rowingGames) this.rowingGames.appState(appState)
|
|
}
|
|
|
|
disconnectedCallback () {
|
|
if (this.rowingGames !== undefined) {
|
|
this.rowingGames.dispose()
|
|
}
|
|
}
|
|
}
|