cyp/app/js/elements/app.js

127 lines
3.4 KiB
JavaScript
Raw Permalink Normal View History

import MPD from "../mpd.js";
2020-03-10 05:24:31 +08:00
import * as html from "../html.js";
2020-03-09 05:11:46 +08:00
2019-03-29 05:52:57 +08:00
function initIcons() {
2020-03-09 16:26:10 +08:00
Array.from(document.querySelectorAll("[data-icon]")).forEach(/** @param {HTMLElement} node */ node => {
2020-05-06 19:55:01 +08:00
node.dataset.icon.split(" ").forEach(name => {
let icon = html.icon(name);
node.insertBefore(icon, node.firstChild);
})
2019-03-29 05:52:57 +08:00
});
}
2020-03-09 05:11:46 +08:00
class App extends HTMLElement {
2020-03-09 16:26:10 +08:00
static get observedAttributes() { return ["component"]; }
2020-03-09 05:11:46 +08:00
constructor() {
super();
2020-03-09 16:26:10 +08:00
initIcons();
}
2019-03-20 03:45:23 +08:00
2020-03-09 21:26:39 +08:00
async connectedCallback() {
await waitForChildren(this);
2020-03-09 21:26:39 +08:00
window.addEventListener("hashchange", e => this._onHashChange());
this._onHashChange();
2019-03-22 22:35:04 +08:00
await this._connect();
2020-03-09 16:26:10 +08:00
this.dispatchEvent(new CustomEvent("load"));
this._initMediaHandler();
2020-03-09 05:11:46 +08:00
}
2020-03-09 21:26:39 +08:00
attributeChangedCallback(name, oldValue, newValue) {
switch (name) {
case "component":
location.hash = newValue;
const e = new CustomEvent("component-change");
this.dispatchEvent(e);
break;
}
2020-03-09 05:11:46 +08:00
}
2020-03-13 23:52:24 +08:00
get component() { return this.getAttribute("component"); }
2020-05-08 02:06:43 +08:00
set component(component) { this.setAttribute("component", component); }
_onHashChange() {
const component = location.hash.substring(1) || "queue";
if (component != this.component) { this.component = component; }
}
_onChange(changed) { this.dispatchEvent(new CustomEvent("idle-change", {detail:changed})); }
_onClose(e) {
setTimeout(() => this._connect(), 3000);
}
async _connect() {
const attempts = 3;
for (let i=0;i<attempts;i++) {
try {
let mpd = await MPD.connect();
mpd.onChange = changed => this._onChange(changed);
mpd.onClose = e => this._onClose(e);
this.mpd = mpd;
return;
} catch (e) {
await sleep(500);
}
}
alert(`Failed to connect to MPD after ${attempts} attempts. Please reload the page to try again.`);
}
_initMediaHandler() {
// check support mediaSession
if (!('mediaSession' in navigator)) {
console.log('mediaSession is not supported');
return;
}
// DOM (using media session controls are allowed only if there is audio/video tag)
const audio = html.node("audio", {loop: true}, "", this);
html.node("source", {src: 'https://raw.githubusercontent.com/anars/blank-audio/master/10-seconds-of-silence.mp3'}, '', audio);
// Init event session (play audio) on click (because restrictions by web browsers)
window.addEventListener('click', () => {
audio.play();
}, {once: true});
// mediaSession define metadata
navigator.mediaSession.metadata = new MediaMetadata({
title: 'Control Your Player'
});
// mediaSession define action handlers
navigator.mediaSession.setActionHandler('play', () => {
this.mpd.command("play")
audio.play()
});
navigator.mediaSession.setActionHandler('pause', () => {
this.mpd.command("pause 1")
audio.pause()
});
navigator.mediaSession.setActionHandler('previoustrack', () => {
this.mpd.command("previous")
audio.play()
});
navigator.mediaSession.setActionHandler('nexttrack', () => {
this.mpd.command("next")
audio.play()
});
}
2020-03-09 01:06:54 +08:00
}
customElements.define("cyp-app", App);
function sleep(ms) { return new Promise(resolve =>setTimeout(resolve, ms)); }
function waitForChildren(app) {
const children = Array.from(app.querySelectorAll("*"));
const names = children.map(node => node.nodeName.toLowerCase())
.filter(name => name.startsWith("cyp-"));
const unique = new Set(names);
const promises = [...unique].map(name => customElements.whenDefined(name));
return Promise.all(promises);
}