Mercurial > code > home > repos > light9
view web/ascoltami/Light9AscoltamiUi.ts @ 2450:a4052905ca7d default tip
notes about how rdfdb syncs, or should sync
author | drewp@bigasterisk.com |
---|---|
date | Mon, 03 Jun 2024 23:01:54 -0700 |
parents | 2ce77421c0b7 |
children |
line wrap: on
line source
import debug from "debug"; import { css, html, LitElement, PropertyValues } from "lit"; import { customElement, property } from "lit/decorators.js"; import { NamedNode } from "n3"; import { PlainViewState } from "../Light9CursorCanvas"; import { getTopGraph } from "../RdfdbSyncedGraph"; import { SyncedGraph } from "../SyncedGraph"; import { PlayerState } from "./PlayerState"; export { RdfdbSyncedGraph } from "../RdfdbSyncedGraph"; export { ResourceDisplay } from "../ResourceDisplay"; export { Light9AscoltamiTimeline } from "./Light9AscoltamiTimeline"; export { Light9SongListing } from "./Light9SongListing"; debug.enable("*"); const log = debug("asco"); async function postJson(url: string, jsBody: Object) { return fetch(url, { method: "POST", headers: { "Content-Type": "applcation/json" }, body: JSON.stringify(jsBody), }); } @customElement("light9-ascoltami-ui") export class Light9AscoltamiUi extends LitElement { graph!: SyncedGraph; @property() show: NamedNode | null = null; @property() song: NamedNode | null = null; @property() selectedSong: NamedNode | null = null; @property() viewState: PlainViewState | null = null; @property() host: any; @property() playerState: PlayerState = { duration: null, endOfSong: null, pausedSongTime: null, playing: null, song: null, wallStartTime: null }; @property() playerTime: number = 0; static styles = [ css` :host { display: flex; flex-direction: column; } .keyCap { color: #ccc; background: #525252; display: inline-block; border: 1px outset #b3b3b3; padding: 2px 3px; margin: 3px 0; margin-left: 0.4em; font-size: 16px; box-shadow: 0.9px 0.9px 0px 2px #565656; border-radius: 2px; } button { min-height: 48pt; min-width: 65pt; } #mainRow { display: flex; flex-direction: row; } light9-song-listing { flex-grow: 1; } th { text-align: right; } #bigTime { color: green; font-size: 60pt; } `, ]; constructor() { super(); getTopGraph().then((g) => { this.graph = g; this.graph.runHandler(this.updatePlayState.bind(this), "playstate-ui"); }); setInterval(this.updateT.bind(this), 100); } protected async firstUpdated(_changedProperties: PropertyValues<this>) { this.bindKeys(); const config = await (await fetch("/service/ascoltami/config")).json(); document.title = document.title.replace("{{host}}", config.host); this.host = config.host; } updatePlayState() { const U = this.graph.U(); const asco = U(":ascoltami"); this.playerState = { duration: this.graph.optionalFloatValue(asco, U(":duration")), endOfSong: this.graph.optionalBooleanValue(asco, U(":endOfSong")), pausedSongTime: this.graph.optionalFloatValue(asco, U(":pausedSongTime")), wallStartTime: this.graph.optionalFloatValue(asco, U(":wallStartTime")), playing: this.graph.optionalBooleanValue(asco, U(":playing")), song: this.graph.optionalUriValue(asco, U(":song")), }; this.updateT(); } updateT() { if (this.playerState.wallStartTime !== null) { this.playerTime = Date.now() / 1000 - this.playerState.wallStartTime; } else if (this.playerState.pausedSongTime !== null) { this.playerTime = this.playerState.pausedSongTime; } else { this.playerTime = 0; } } render() { return html` <h1>ascoltami on ${this.host}</h1> <span><rdfdb-synced-graph></rdfdb-synced-graph></span> <div id="mainRow"> <div> <light9-song-listing @selectsong=${(ev: any) => { this.selectedSong = ev.detail.song; }} .selectedSong=${this.selectedSong} ></light9-song-listing> <div id="bigTime">t=${this.playerTime.toFixed(1)}</div> </div> <div> <light9-ascoltami-timeline .playerState=${this.playerState} .playerTime=${this.playerTime}></light9-ascoltami-timeline> <div> <button ?disabled=${this.selectedSong == null} @click=${this.onLoadSelected}>Change to and play selected <span class="keyCap">l</span></button> <button ?disabled=${false} @click=${this.onCmdZero}>Seek to zero <span class="keyCap">z</span></button> <button ?disabled=${this.playerState.playing || this.playerState.song == null} @click=${this.onCmdPlay}>Play <span class="keyCap">p</span></button> <button ?disabled=${!this.playerState.playing} @click=${this.onCmdStop}>Stop <span class="keyCap">s</span></button> <button ?disabled=${true} @click=${this.onCmdGo}>Go <span class="keyCap">g</span></button> </div> ${this.renderPlayerStateTable()} </div> </div> `; } renderPlayerStateTable() { return html` <table> <tr> <th>duration</th> <td>${this.playerState.duration}</td> </tr> <tr> <th>endOfSong</th> <td>${this.playerState.endOfSong}</td> </tr> <tr> <th>pausedSongTime</th> <td>${this.playerState.pausedSongTime?.toFixed(3)}</td> </tr> <tr> <th>playing</th> <td>${this.playerState.playing}</td> </tr> <tr> <th>song</th> <td>${this.playerState.song?.value}</td> </tr> <tr> <th>wallStartTime</th> <td>${this.playerState.wallStartTime}</td> </tr> <tr> <th>t</th> <td>${this.playerTime.toFixed(3)}</td> </tr> </table>`; } onSelectSong(song: NamedNode, ev: MouseEvent) { if (this.selectedSong && song.equals(this.selectedSong)) { this.selectedSong = null; } else { this.selectedSong = song; } } async onLoadSelected() { if (!this.selectedSong) { return; } await fetch("/service/ascoltami/song", { method: "POST", body: this.selectedSong.value }); this.selectedSong = null; } onCmdStop(ev?: MouseEvent): void { postJson("/service/ascoltami/time", { pause: true }); } onCmdPlay(ev?: MouseEvent): void { postJson("/service/ascoltami/time", { resume: true }); } onCmdGo(ev?: MouseEvent): void { postJson("/service/ascoltami/go", {}); } onCmdZero(ev?: MouseEvent): void { postJson("/service/ascoltami/time", { t: 0 }); } bindKeys() { document.addEventListener("keypress", (ev) => { if (ev.key == "l") { this.onLoadSelected(); } else if (ev.key == "z") { this.onCmdZero(); } else if (ev.key == "p") { this.onCmdPlay(); } else if (ev.key == "s") { this.onCmdStop(); } else if (ev.key == "g") { this.onCmdGo(); } else { return true; } }); } }