...
 
Commits (7)
......@@ -19,6 +19,14 @@
</script>
<style>
@media (prefers-reduced-motion: reduce) {
/* lazy reduced-motion catch-all rule */
* {
animation-duration: 1ms !important;
transition-duration: 1ms !important;
}
}
.application--wrap {
padding-bottom: 120px;
}
......
<template>
<button type="button" class="player-control" @click.exact="$emit('click', $event)">
<slot>
<feather :type="icon" size="19" v-if="icon"/>
</slot>
</button>
</template>
<script>
export default {
props: {
icon: {
type: String,
default: null
}
}
}
</script>
<style>
.player-control {
border-radius: 50%;
width: 38px;
height: 38px;
display: flex;
align-items: center;
justify-content: center;
color: #b3b3b3;
transition: none 33ms cubic-bezier(.3, 0, 0, 1);
transition-property: color, transform;
transform-origin: 50% 50%;
padding: 0;
cursor: pointer;
}
.player-control:disabled {
color: #404040;
pointer-events: none;
}
.player-control:active {
transform: scale(.9) !important;
}
.player-control:hover,
.player-control:focus {
color: #fff;
outline: none;
}
.player-control.is-primary:hover,
.player-control.is-primary:focus {
transform: scale(1.06);
}
.player-control.is-danger {
color: hsl(354, 60%, 45%);
}
</style>
<template>
<div class="player-controls">
<button type="button" class="player-control" :disabled="true" :title="$t('player_prev_track')">
<feather type="skip-back" size="19"/>
</button>
<div class="player-controls mb-3">
<PlayerControl class="mx-2" :disabled="true" :title="$t('player_prev_track')" icon="skip-back"/>
<button type="button" class="player-control is-primary" @click="$player.togglePlay"
:title="playLabel">
<PlayerControl class="mx-2 is-primary" :title="playLabel" @click="$player.togglePlay">
<feather :type="playIcon" stroke-width="1" size="38"/>
</button>
</PlayerControl>
<button type="button" class="player-control" :disabled="true" :title="$t('player_next_track')">
<feather type="skip-forward" size="19"/>
</button>
<PlayerControl class="mx-2" :disabled="true" :title="$t('player_next_track')" icon="skip-forward"/>
</div>
</template>
<script>
import PlayerControl from './PlayerControl'
export default {
components: { PlayerControl },
computed: {
playIcon () {
return this.$player.isPlaying
......@@ -39,45 +37,6 @@
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 16px;
}
.player-control {
border-radius: 50%;
width: 38px;
height: 38px;
display: flex;
align-items: center;
justify-content: center;
color: #b3b3b3;
transition: none 33ms cubic-bezier(.3, 0, 0, 1);
transition-property: color, transform;
transform-origin: 50% 50%;
padding: 0;
}
.player-control:disabled {
color: #404040;
pointer-events: none;
}
.player-control:active {
transform: scale(.9) !important;
}
.player-control:hover,
.player-control:focus {
color: #fff;
outline: none;
}
.player-control:not(:last-child) {
margin-right: 24px;
}
.player-control.is-primary:hover,
.player-control.is-primary:focus {
transform: scale(1.06);
}
</style>
......
<template>
<div>
<div class="player-volume">
<button type="button" class="player-control" :title="muteLabel" @click.exact="$player.mute">
<feather :type="volumeIcon" size="19"/>
</button>
<div class="d-flex align-center">
<div class="player-volume d-flex align-center mx-2">
<PlayerControl class="mr-2" :class="$player.isMuted ? 'is-danger' : null"
:title="muteLabel" @click="$player.mute" :icon="volumeIcon"/>
<SeekableProgress v-model="$player.volume" :aria-label="$t('player_volume')"
:knob-label="$t('player_set_volume')"/>
</div>
......@@ -11,10 +10,11 @@
</template>
<script>
import PlayerControl from './PlayerControl'
import SeekableProgress from '../SeekableProgress'
export default {
components: { SeekableProgress },
components: { PlayerControl, SeekableProgress },
computed: {
muteLabel () {
return this.$t(this.$player.isMuted ? 'player_unmute' : 'player_mute')
......@@ -31,14 +31,8 @@
<style>
.player-volume {
display: flex;
align-items: center;
width: 125px;
}
.player-volume .player-control {
margin-right: 12px;
}
</style>
<i18n>
......
<template>
<div class="player-progress">
<span class="player-progress-label">{{ timeLabels.progress }}</span>
<div class="player-progress-bar">
<div class="player-progress-bar mx-2">
<SeekableProgress :min="progress.min" :max="progress.max" v-model="value"
:step="5" :step-factor="6" :knob-label="$t('player_set_seek')"
:label="`${timeLabels.progress} / ${timeLabels.duration}`"/>
......@@ -61,7 +61,6 @@
}
.player-progress-bar {
margin: 0 16px;
width: 100%;
}
</style>
......
......@@ -44,6 +44,7 @@ function createPlayer (Vue) {
currentHowler = new Howl({
// TODO implement proper src order and codec detection
src: trackData.src,
format: trackData.format,
onload: proxyEvent('load', () => {
this.currentTrack.duration = ensureNumber(currentHowler.duration())
}),
......
......@@ -10,7 +10,7 @@ export default new Router({
{
path: '/',
name: 'home',
component: () => import(/* webpackChunkName: "home" */ './views/Home.vue')
component: () => import(/* webpackChunkName: "home" */ './views/HomePage.vue')
}
]
})
......@@ -2,7 +2,7 @@
<div>
<v-container grid-list-xl>
<v-layout row wrap>
<v-flex xs12 sm6 lg4 xl3 grow
<v-flex xs12 sm4 lg3 xl2 grow
v-for="s in series" :key="s.id">
<SeriesPreview :series="s"/>
</v-flex>
......@@ -27,8 +27,28 @@
import { mapState } from 'vuex'
import SeriesPreview from '../components/SeriesPreview'
// TODO: move to a more fitting place
function createHowerTrackData (recording) {
const howlerFormat = source => {
if (source.container === null && source.codec === 'audio/mpeg') {
return 'mp3'
}
// TODO: add hower-format conversion for other source types
return null
}
return recording.audio
.flatMap(audio => audio.sources)
.reduce((result, source) => {
result.src.push(source.src)
result.format.push(howlerFormat(source))
return result
}, { src: [], format: [] })
}
export default {
name: 'Home',
name: 'HomePage',
components: { SeriesPreview },
computed: {
...mapState('api', {
......@@ -40,7 +60,7 @@
play (recording) {
this.$player.playTrack({
recording,
src: recording.audio.flatMap(audio => audio.sources.map(asrc => asrc.src))
...createHowerTrackData(recording)
})
}
}
......