mm.site/src/views/audio.js

163 lines
3.9 KiB
JavaScript

import { LitElement, css, html } from 'lit'
import { Task } from '@lit-labs/task'
// components
import '../components/Header.js'
import '../components/Footer.js'
import '../components/Loading.js'
import '../components/AudioCard.js'
import '../components/ModularPlayer.js'
import Router from '../api/Router.js'
class AudioView extends LitElement {
static properties = {
path: { type: String },
tracks: { type: Array },
selected: { state: true }
}
constructor() {
super()
this.path = ''
this.tracks = []
this.selected = null
}
firstUpdated() {
this.addEventListener('select-audio', ({ detail }) => {
this.selected = { ...detail, media: `${Router.route.path}/${detail.media}` }
console.log(this.selected)
this._selectAudio.run()
})
this.addEventListener('next-track', () => {
this._skipTrack(1)
})
this.addEventListener('prev-track', () => {
this._skipTrack(-1)
})
}
_getAudio = new Task(
this,
async () => {
const res = await fetch(`/data${Router.route.path}.json`)
const json = await res.json()
this.tracks = json
// start track (0 is not currently available)
const idx = 1
this.selected = { ...this.tracks[idx], idx, media: `${Router.route.path}/${this.tracks[idx].media}` }
},
() => [ this.path ]
)
_selectAudio = new Task(
this,
{
task: async ([ selected ]) => {
const res = await fetch(`/media${selected.media}.json`)
return { ...selected, ...await res.json() }
},
args: () => [ this.selected ]
}
)
_skipTrack(direction) {
const idx = (this.selected.idx + direction) % this.tracks.length
const detail = { ...this.tracks[idx], idx }
this.selected = { ...detail, media: `${Router.route.path}/${detail.media}` }
this._selectAudio.run()
}
render() {
return html`
<mm-header></mm-header>
<main>
<nav class="tracks">
<div class="scroll-items">
${this._getAudio.render({
pending: () => html`<mm-loading style="--fill-color: grey"></mm-loading>`,
complete: () => html`${this.tracks.map((t, i) => html`<mm-acard idx=${i} icon=${t.album ? 'album' : 'play-circle'} ?selected=${t.title == this.selected.title && t.details == this.selected.details} .details=${t}></mm-acard>`)}`,
error: (err) => html`Error: ${err}`
})}
</div>
</nav>
<div class="player">
<div>
${this._selectAudio.render({
pending: () => html`<mm-loading style="--fill-color: grey"></mm-loading>`,
complete: (selected) => html`<mm-audio-player .details=${selected}></mm-audio-player>`
})}
</div>
</div>
</main>
<mm-footer></mm-footer>
`
}
static styles = css`
:host {
display: flex;
flex-direction: column;
height: 100vh;
}
main {
flex-grow: 1;
display: grid;
grid-template-columns: 0.7fr 1fr;
gap: 0.5em;
}
.tracks {
--border-radius: 0.75em;
--padding: 0.5em;
position: relative;
background: var(--neutral-400, lightgrey);
border-radius: 0 var(--border-radius) 0 0;
}
.scroll-items {
position: absolute;
inset: 0;
padding-block: 0.75em;
padding-inline-start: 0.5em;
padding-inline-end: 0.75em;
border-radius: 0 calc(var(--border-radius) * 0.5) 0 0;
display: grid;
grid-auto-flow: rows;
align-content: start;
gap: 0.75em;
overflow-x: hidden;
overflow-y: auto;
}
.player {
position: relative;
}
.player > div {
position: absolute;
inset: 0;
border-radius: 0.75em 0 0 0.75em;
background: var(--green-gradient-400, lightgrey);
margin-block-end: 1em;
}
mm-footer {
z-index: 1;
}
`
}
customElements.define('mm-audio', AudioView)