mirror of
https://github.com/owntone/owntone-server.git
synced 2025-01-27 06:33:21 -05:00
[web-src] Restyling of navbars and now playing page
This commit is contained in:
parent
1d24622c99
commit
b298fc1170
@ -8,7 +8,10 @@
|
|||||||
</transition>
|
</transition>
|
||||||
<modal-dialog-remote-pairing :show="pairing_active" @close="pairing_active = false" />
|
<modal-dialog-remote-pairing :show="pairing_active" @close="pairing_active = false" />
|
||||||
<notifications v-show="!show_burger_menu" />
|
<notifications v-show="!show_burger_menu" />
|
||||||
<navbar-bottom v-show="!show_burger_menu" />
|
<navbar-bottom />
|
||||||
|
<div class="is-overlay" v-show="show_burger_menu || show_player_menu"
|
||||||
|
style="z-index:25; width: 100vw; height:100vh;background-color: rgba(10, 10, 10, 0.2);"
|
||||||
|
@click="show_burger_menu = show_player_menu = false"></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -35,8 +38,21 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
show_burger_menu () {
|
show_burger_menu: {
|
||||||
return this.$store.state.show_burger_menu
|
get () {
|
||||||
|
return this.$store.state.show_burger_menu
|
||||||
|
},
|
||||||
|
set (value) {
|
||||||
|
this.$store.commit(types.SHOW_BURGER_MENU, value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
show_player_menu: {
|
||||||
|
get () {
|
||||||
|
return this.$store.state.show_player_menu
|
||||||
|
},
|
||||||
|
set (value) {
|
||||||
|
this.$store.commit(types.SHOW_PLAYER_MENU, value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -209,16 +225,23 @@ export default {
|
|||||||
this.$store.commit(types.UPDATE_PAIRING, data)
|
this.$store.commit(types.UPDATE_PAIRING, data)
|
||||||
this.pairing_active = data.active
|
this.pairing_active = data.active
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
update_is_clipped: function () {
|
||||||
|
if (this.show_burger_menu || this.show_player_menu) {
|
||||||
|
document.querySelector('html').classList.add('is-clipped')
|
||||||
|
} else {
|
||||||
|
document.querySelector('html').classList.remove('is-clipped')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
'show_burger_menu' () {
|
'show_burger_menu' () {
|
||||||
if (this.show_burger_menu) {
|
this.update_is_clipped()
|
||||||
document.querySelector('html').classList.add('is-clipped')
|
},
|
||||||
} else {
|
'show_player_menu' () {
|
||||||
document.querySelector('html').classList.remove('is-clipped')
|
this.update_is_clipped()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,40 +1,380 @@
|
|||||||
<template>
|
<template>
|
||||||
<nav class="navbar is-dark is-fixed-bottom" role="navigation" aria-label="player controls">
|
<nav class="navbar is-white is-fixed-bottom" :style="zindex" :class="{ 'is-transparent': is_now_playing_page, 'is-dark': !is_now_playing_page }" role="navigation" aria-label="player controls">
|
||||||
<div class="navbar-brand fd-expanded">
|
<div class="navbar-brand fd-expanded">
|
||||||
<router-link to="/" class="navbar-item" active-class="is-active" exact>
|
|
||||||
|
<!-- Link to queue -->
|
||||||
|
<navbar-item-link to="/" exact>
|
||||||
<span class="icon"><i class="mdi mdi-24px mdi-playlist-play"></i></span>
|
<span class="icon"><i class="mdi mdi-24px mdi-playlist-play"></i></span>
|
||||||
</router-link>
|
</navbar-item-link>
|
||||||
<router-link to="/now-playing" class="navbar-item is-expanded is-clipped" active-class="is-active" exact>
|
|
||||||
<div>
|
<!-- Now playing artist/title (not visible on "now playing" page) -->
|
||||||
|
<router-link to="/now-playing" v-if="!is_now_playing_page" class="navbar-item is-expanded is-clipped" active-class="is-active" exact>
|
||||||
|
<div class="is-clipped">
|
||||||
<p class="is-size-7 fd-is-text-clipped">
|
<p class="is-size-7 fd-is-text-clipped">
|
||||||
<strong>{{ now_playing.title }}</strong><br>
|
<strong>{{ now_playing.title }}</strong><br>
|
||||||
{{ now_playing.artist }}<span v-if="now_playing.data_kind === 'url'"> - {{ now_playing.album }}</span>
|
{{ now_playing.artist }}<span v-if="now_playing.data_kind === 'url'"> - {{ now_playing.album }}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</router-link>
|
</router-link>
|
||||||
<player-button-play-pause class="navbar-item fd-margin-left-auto" icon_style="mdi-36px" show_disabled_message></player-button-play-pause>
|
|
||||||
|
<!-- Skip previous (not visible on "now playing" page) -->
|
||||||
|
<player-button-previous v-if="is_now_playing_page" class="navbar-item fd-margin-left-auto" icon_style="mdi-24px"></player-button-previous>
|
||||||
|
<!-- Play/pause -->
|
||||||
|
<player-button-play-pause class="navbar-item" icon_style="mdi-36px" show_disabled_message></player-button-play-pause>
|
||||||
|
<!-- Skip next (not visible on "now playing" page) -->
|
||||||
|
<player-button-next v-if="is_now_playing_page" class="navbar-item" icon_style="mdi-24px"></player-button-next>
|
||||||
|
|
||||||
|
<!-- Player menu button (only visible on mobile and tablet) -->
|
||||||
|
<a class="navbar-item fd-margin-left-auto is-hidden-desktop" @click="show_player_menu = !show_player_menu">
|
||||||
|
<span class="icon"><i class="mdi mdi-18px" :class="{ 'mdi-chevron-up': !show_player_menu, 'mdi-chevron-down': show_player_menu }"></i></span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- Player menu dropup menu (only visible on desktop) -->
|
||||||
|
<div class="navbar-item has-dropdown has-dropdown-up fd-margin-left-auto is-hidden-touch"
|
||||||
|
:class="{ 'is-active': show_player_menu }">
|
||||||
|
<a class="navbar-link is-arrowless"
|
||||||
|
@click="show_player_menu = !show_player_menu">
|
||||||
|
<span class="icon"><i class="mdi mdi-18px"
|
||||||
|
:class="{ 'mdi-chevron-up': !show_player_menu, 'mdi-chevron-down': show_player_menu }"></i></span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="navbar-dropdown is-right is-boxed" style="margin-right: 6px; margin-bottom: 6px; border-radius: 6px;">
|
||||||
|
<div class="navbar-item">
|
||||||
|
<!-- Outputs: master volume -->
|
||||||
|
<div class="level is-mobile">
|
||||||
|
<div class="level-left fd-expanded">
|
||||||
|
<div class="level-item" style="flex-grow: 0;">
|
||||||
|
<a class="button is-white is-small" @click="toggle_mute_volume">
|
||||||
|
<span class="icon"><i class="mdi mdi-18px" :class="{ 'mdi-volume-off': player.volume <= 0, 'mdi-volume-high': player.volume > 0 }"></i></span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="level-item fd-expanded">
|
||||||
|
<div class="fd-expanded">
|
||||||
|
<p class="heading">Volume</p>
|
||||||
|
<range-slider
|
||||||
|
class="slider fd-has-action"
|
||||||
|
min="0"
|
||||||
|
max="100"
|
||||||
|
step="1"
|
||||||
|
:value="player.volume"
|
||||||
|
@change="set_volume">
|
||||||
|
</range-slider>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Outputs: master volume -->
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<navbar-item-output v-for="output in outputs" :key="output.id" :output="output"></navbar-item-output>
|
||||||
|
|
||||||
|
<!-- Outputs: stream volume -->
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<div class="navbar-item">
|
||||||
|
<div class="level is-mobile">
|
||||||
|
<div class="level-left fd-expanded">
|
||||||
|
<div class="level-item" style="flex-grow: 0;">
|
||||||
|
<a class="button is-white is-small" :class="{ 'is-loading': loading }"><span class="icon fd-has-action" :class="{ 'has-text-grey-light': !playing && !loading, 'is-loading': loading }" @click="togglePlay"><i class="mdi mdi-18px mdi-radio-tower"></i></span></a>
|
||||||
|
</div>
|
||||||
|
<div class="level-item fd-expanded">
|
||||||
|
<div class="fd-expanded">
|
||||||
|
<p class="heading" :class="{ 'has-text-grey-light': !playing }">HTTP stream <a href="/stream.mp3"><span class="is-lowercase">(stream.mp3)</span></a></p>
|
||||||
|
<range-slider
|
||||||
|
class="slider fd-has-action"
|
||||||
|
min="0"
|
||||||
|
max="100"
|
||||||
|
step="1"
|
||||||
|
:disabled="!playing"
|
||||||
|
:value="stream_volume"
|
||||||
|
@change="set_stream_volume">
|
||||||
|
</range-slider>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Playback controls -->
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<div class="navbar-item">
|
||||||
|
<div class="level is-mobile fd-expanded">
|
||||||
|
<div class="level-left">
|
||||||
|
<div class="level-item">
|
||||||
|
<div class="buttons has-addons">
|
||||||
|
<player-button-previous class="button"></player-button-previous>
|
||||||
|
<player-button-play-pause class="button"></player-button-play-pause>
|
||||||
|
<player-button-next class="button"></player-button-next>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="level-right">
|
||||||
|
<div class="level-item">
|
||||||
|
<div class="buttons has-addons">
|
||||||
|
<player-button-repeat class="button"></player-button-repeat>
|
||||||
|
<player-button-shuffle class="button"></player-button-shuffle>
|
||||||
|
<player-button-consume class="button"></player-button-consume>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Player menu (only visible on mobile and tablet) -->
|
||||||
|
<div class="navbar-menu is-hidden-desktop" style="max-height: calc(100vh - 3.25rem); overflow: scroll"
|
||||||
|
:class="{ 'is-active': show_player_menu }">
|
||||||
|
<div class="navbar-start">
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<!-- Repeat/shuffle/consume -->
|
||||||
|
<div class="navbar-item">
|
||||||
|
<div class="buttons is-centered">
|
||||||
|
<player-button-repeat class="button" icon_style="mdi-18px"></player-button-repeat>
|
||||||
|
<player-button-shuffle class="button" icon_style="mdi-18px"></player-button-shuffle>
|
||||||
|
<player-button-consume class="button" icon_style="mdi-18px"></player-button-consume>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr style="margin: 12px 0;">
|
||||||
|
|
||||||
|
<!-- Outputs: master volume -->
|
||||||
|
<div class="navbar-item">
|
||||||
|
<div class="level is-mobile">
|
||||||
|
<div class="level-left fd-expanded">
|
||||||
|
<div class="level-item" style="flex-grow: 0;">
|
||||||
|
<a class="button is-white is-small" @click="toggle_mute_volume">
|
||||||
|
<span class="icon"><i class="mdi mdi-18px" :class="{ 'mdi-volume-off': player.volume <= 0, 'mdi-volume-high': player.volume > 0 }"></i></span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="level-item fd-expanded">
|
||||||
|
<div class="fd-expanded">
|
||||||
|
<p class="heading">Volume</p>
|
||||||
|
<range-slider
|
||||||
|
class="slider fd-has-action"
|
||||||
|
min="0"
|
||||||
|
max="100"
|
||||||
|
step="1"
|
||||||
|
:value="player.volume"
|
||||||
|
@change="set_volume">
|
||||||
|
</range-slider>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Outputs dropdown menu
|
||||||
|
<div class="navbar-item has-dropdown"
|
||||||
|
:class="{ 'is-active': show_outputs_menu }">
|
||||||
|
<a class="navbar-link is-arrowless has-text-centered is-size-7" @click="show_outputs_menu = !show_outputs_menu">
|
||||||
|
<span class="icon"><i class="mdi mdi-18px" :class="{ 'mdi-chevron-up': !show_outputs_menu, 'mdi-chevron-down': show_outputs_menu }"></i></span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="navbar-dropdown is-right" v-show="show_outputs_menu">
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
-->
|
||||||
|
<!-- Outputs: speaker volumes -->
|
||||||
|
<navbar-item-output v-for="output in outputs" :key="output.id" :output="output"></navbar-item-output>
|
||||||
|
|
||||||
|
<!-- Outputs: stream volume -->
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<div class="navbar-item">
|
||||||
|
<div class="level is-mobile">
|
||||||
|
<div class="level-left fd-expanded">
|
||||||
|
<div class="level-item" style="flex-grow: 0;">
|
||||||
|
<a class="button is-white is-small" :class="{ 'is-loading': loading }"><span class="icon fd-has-action" :class="{ 'has-text-grey-light': !playing && !loading, 'is-loading': loading }" @click="togglePlay"><i class="mdi mdi-18px mdi-radio-tower"></i></span></a>
|
||||||
|
</div>
|
||||||
|
<div class="level-item fd-expanded">
|
||||||
|
<div class="fd-expanded">
|
||||||
|
<p class="heading" :class="{ 'has-text-grey-light': !playing }">HTTP stream <a href="/stream.mp3"><span class="is-lowercase">(stream.mp3)</span></a></p>
|
||||||
|
<range-slider
|
||||||
|
class="slider fd-has-action"
|
||||||
|
min="0"
|
||||||
|
max="100"
|
||||||
|
step="1"
|
||||||
|
:disabled="!playing"
|
||||||
|
:value="stream_volume"
|
||||||
|
@change="set_stream_volume">
|
||||||
|
</range-slider>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- </div>
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import PlayerButtonPlayPause from './PlayerButtonPlayPause'
|
import webapi from '@/webapi'
|
||||||
|
import _audio from '@/audio'
|
||||||
|
import NavbarItemLink from './NavbarItemLink'
|
||||||
|
import NavbarItemOutput from './NavbarItemOutput'
|
||||||
|
import PlayerButtonPlayPause from '@/components/PlayerButtonPlayPause'
|
||||||
|
import PlayerButtonNext from '@/components/PlayerButtonNext'
|
||||||
|
import PlayerButtonPrevious from '@/components/PlayerButtonPrevious'
|
||||||
|
import PlayerButtonShuffle from '@/components/PlayerButtonShuffle'
|
||||||
|
import PlayerButtonConsume from '@/components/PlayerButtonConsume'
|
||||||
|
import PlayerButtonRepeat from '@/components/PlayerButtonRepeat'
|
||||||
|
import RangeSlider from 'vue-range-slider'
|
||||||
|
import * as types from '@/store/mutation_types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'NavbarBottom',
|
name: 'NavbarBottom',
|
||||||
components: { PlayerButtonPlayPause },
|
components: { NavbarItemLink, NavbarItemOutput, RangeSlider, PlayerButtonPlayPause, PlayerButtonNext, PlayerButtonPrevious, PlayerButtonShuffle, PlayerButtonConsume, PlayerButtonRepeat },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return { }
|
return {
|
||||||
|
old_volume: 0,
|
||||||
|
|
||||||
|
playing: false,
|
||||||
|
loading: false,
|
||||||
|
stream_volume: 10,
|
||||||
|
|
||||||
|
show_outputs_menu: false,
|
||||||
|
show_desktop_outputs_menu: false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
show_player_menu: {
|
||||||
|
get () {
|
||||||
|
return this.$store.state.show_player_menu
|
||||||
|
},
|
||||||
|
set (value) {
|
||||||
|
this.$store.commit(types.SHOW_PLAYER_MENU, value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
show_burger_menu () {
|
||||||
|
return this.$store.state.show_burger_menu
|
||||||
|
},
|
||||||
|
|
||||||
|
zindex () {
|
||||||
|
if (this.show_burger_menu) {
|
||||||
|
return 'z-index: 20'
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
},
|
||||||
|
|
||||||
state () {
|
state () {
|
||||||
return this.$store.state.player
|
return this.$store.state.player
|
||||||
},
|
},
|
||||||
now_playing () {
|
now_playing () {
|
||||||
return this.$store.getters.now_playing
|
return this.$store.getters.now_playing
|
||||||
|
},
|
||||||
|
is_now_playing_page () {
|
||||||
|
return this.$route.path === '/now-playing'
|
||||||
|
},
|
||||||
|
outputs () {
|
||||||
|
return this.$store.state.outputs
|
||||||
|
},
|
||||||
|
|
||||||
|
player () {
|
||||||
|
return this.$store.state.player
|
||||||
|
},
|
||||||
|
|
||||||
|
config () {
|
||||||
|
return this.$store.state.config
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
on_click_outside_outputs () {
|
||||||
|
this.show_outputs_menu = false
|
||||||
|
},
|
||||||
|
|
||||||
|
set_volume: function (newVolume) {
|
||||||
|
webapi.player_volume(newVolume)
|
||||||
|
},
|
||||||
|
|
||||||
|
toggle_mute_volume: function () {
|
||||||
|
if (this.player.volume > 0) {
|
||||||
|
this.set_volume(0)
|
||||||
|
} else {
|
||||||
|
this.set_volume(this.old_volume)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setupAudio: function () {
|
||||||
|
const a = _audio.setupAudio()
|
||||||
|
|
||||||
|
a.addEventListener('waiting', e => {
|
||||||
|
this.playing = false
|
||||||
|
this.loading = true
|
||||||
|
})
|
||||||
|
a.addEventListener('playing', e => {
|
||||||
|
this.playing = true
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
a.addEventListener('ended', e => {
|
||||||
|
this.playing = false
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
a.addEventListener('error', e => {
|
||||||
|
this.closeAudio()
|
||||||
|
this.$store.dispatch('add_notification', { text: 'HTTP stream error: failed to load stream or stopped loading due to network problem', type: 'danger' })
|
||||||
|
this.playing = false
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// close active audio
|
||||||
|
closeAudio: function () {
|
||||||
|
_audio.stopAudio()
|
||||||
|
this.playing = false
|
||||||
|
},
|
||||||
|
|
||||||
|
playChannel: function () {
|
||||||
|
if (this.playing) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const channel = '/stream.mp3'
|
||||||
|
this.loading = true
|
||||||
|
_audio.playSource(channel)
|
||||||
|
_audio.setVolume(this.stream_volume / 100)
|
||||||
|
},
|
||||||
|
|
||||||
|
togglePlay: function () {
|
||||||
|
if (this.loading) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (this.playing) {
|
||||||
|
return this.closeAudio()
|
||||||
|
}
|
||||||
|
return this.playChannel()
|
||||||
|
},
|
||||||
|
|
||||||
|
set_stream_volume: function (newVolume) {
|
||||||
|
this.stream_volume = newVolume
|
||||||
|
_audio.setVolume(this.stream_volume / 100)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
'$store.state.player.volume' () {
|
||||||
|
if (this.player.volume > 0) {
|
||||||
|
this.old_volume = this.player.volume
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// on app mounted
|
||||||
|
mounted () {
|
||||||
|
this.setupAudio()
|
||||||
|
},
|
||||||
|
|
||||||
|
// on app destroyed
|
||||||
|
destroyed () {
|
||||||
|
this.closeAudio()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<a class="navbar-item" :class="{ 'is-active': is_active }" @click.prevent="open_link()" :href="full_path()">
|
<a class="navbar-item" :class="{ 'is-active': is_active }" @click.stop.prevent="open_link()" :href="full_path()">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
@ -9,17 +9,46 @@ import * as types from '@/store/mutation_types'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'NavbarItemLink',
|
name: 'NavbarItemLink',
|
||||||
props: ['to'],
|
props: {
|
||||||
|
to: String,
|
||||||
|
exact: Boolean
|
||||||
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
is_active () {
|
is_active () {
|
||||||
|
if (this.exact) {
|
||||||
|
return this.$route.path === this.to
|
||||||
|
}
|
||||||
return this.$route.path.startsWith(this.to)
|
return this.$route.path.startsWith(this.to)
|
||||||
|
},
|
||||||
|
|
||||||
|
show_player_menu: {
|
||||||
|
get () {
|
||||||
|
return this.$store.state.show_player_menu
|
||||||
|
},
|
||||||
|
set (value) {
|
||||||
|
this.$store.commit(types.SHOW_PLAYER_MENU, value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
show_burger_menu: {
|
||||||
|
get () {
|
||||||
|
return this.$store.state.show_burger_menu
|
||||||
|
},
|
||||||
|
set (value) {
|
||||||
|
this.$store.commit(types.SHOW_BURGER_MENU, value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
open_link: function () {
|
open_link: function () {
|
||||||
this.$store.commit(types.SHOW_BURGER_MENU, false)
|
if (this.show_burger_menu) {
|
||||||
|
this.$store.commit(types.SHOW_BURGER_MENU, false)
|
||||||
|
}
|
||||||
|
if (this.show_player_menu) {
|
||||||
|
this.$store.commit(types.SHOW_PLAYER_MENU, false)
|
||||||
|
}
|
||||||
this.$router.push({ path: this.to })
|
this.$router.push({ path: this.to })
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<nav class="navbar is-light is-fixed-top" role="navigation" aria-label="main navigation">
|
<nav class="navbar is-light is-fixed-top" :style="zindex" role="navigation" aria-label="main navigation">
|
||||||
<div class="navbar-brand">
|
<div class="navbar-brand">
|
||||||
<navbar-item-link to="/playlists">
|
<navbar-item-link to="/playlists">
|
||||||
<span class="icon"><i class="mdi mdi-library-music"></i></span>
|
<span class="icon"><i class="mdi mdi-library-music"></i></span>
|
||||||
@ -20,7 +20,7 @@
|
|||||||
<span class="icon"><i class="mdi mdi-magnify"></i></span>
|
<span class="icon"><i class="mdi mdi-magnify"></i></span>
|
||||||
</navbar-item-link>
|
</navbar-item-link>
|
||||||
|
|
||||||
<div class="navbar-burger" @click="update_show_burger_menu" :class="{ 'is-active': show_burger_menu }">
|
<div class="navbar-burger" @click="show_burger_menu = !show_burger_menu" :class="{ 'is-active': show_burger_menu }">
|
||||||
<span></span>
|
<span></span>
|
||||||
<span></span>
|
<span></span>
|
||||||
<span></span>
|
<span></span>
|
||||||
@ -33,150 +33,59 @@
|
|||||||
|
|
||||||
<div class="navbar-end">
|
<div class="navbar-end">
|
||||||
|
|
||||||
<!-- Outputs dropdown -->
|
|
||||||
<div class="navbar-item has-dropdown"
|
|
||||||
:class="{ 'is-active': show_outputs_menu, 'is-hoverable': !show_outputs_menu && !show_settings_menu }"
|
|
||||||
@click="show_outputs_menu = !show_outputs_menu"
|
|
||||||
v-click-outside="on_click_outside_outputs">
|
|
||||||
<a class="navbar-link is-arrowless"><span class="icon is-hidden-mobile is-hidden-tablet-only"><i class="mdi mdi-volume-high"></i></span> <span class="is-hidden-desktop has-text-weight-bold">Volume</span></a>
|
|
||||||
|
|
||||||
<div class="navbar-dropdown is-right">
|
|
||||||
<div class="navbar-item">
|
|
||||||
<!-- Outputs: master volume -->
|
|
||||||
<div class="level is-mobile">
|
|
||||||
<div class="level-left fd-expanded">
|
|
||||||
<div class="level-item" style="flex-grow: 0;">
|
|
||||||
<a class="button is-white is-small" @click="toggle_mute_volume">
|
|
||||||
<span class="icon"><i class="mdi mdi-18px" :class="{ 'mdi-volume-off': player.volume <= 0, 'mdi-volume-high': player.volume > 0 }"></i></span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="level-item fd-expanded">
|
|
||||||
<div class="fd-expanded">
|
|
||||||
<p class="heading">Volume</p>
|
|
||||||
<range-slider
|
|
||||||
class="slider fd-has-action"
|
|
||||||
min="0"
|
|
||||||
max="100"
|
|
||||||
step="1"
|
|
||||||
:value="player.volume"
|
|
||||||
@change="set_volume">
|
|
||||||
</range-slider>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Outputs: master volume -->
|
|
||||||
<hr class="navbar-divider">
|
|
||||||
<navbar-item-output v-for="output in outputs" :key="output.id" :output="output"></navbar-item-output>
|
|
||||||
|
|
||||||
<!-- Outputs: stream volume -->
|
|
||||||
<hr class="navbar-divider">
|
|
||||||
<div class="navbar-item">
|
|
||||||
<div class="level is-mobile">
|
|
||||||
<div class="level-left fd-expanded">
|
|
||||||
<div class="level-item" style="flex-grow: 0;">
|
|
||||||
<a class="button is-white is-small" :class="{ 'is-loading': loading }"><span class="icon fd-has-action" :class="{ 'has-text-grey-light': !playing && !loading, 'is-loading': loading }" @click="togglePlay"><i class="mdi mdi-18px mdi-radio-tower"></i></span></a>
|
|
||||||
</div>
|
|
||||||
<div class="level-item fd-expanded">
|
|
||||||
<div class="fd-expanded">
|
|
||||||
<p class="heading" :class="{ 'has-text-grey-light': !playing }">HTTP stream <a href="/stream.mp3"><span class="is-lowercase">(stream.mp3)</span></a></p>
|
|
||||||
<range-slider
|
|
||||||
class="slider fd-has-action"
|
|
||||||
min="0"
|
|
||||||
max="100"
|
|
||||||
step="1"
|
|
||||||
:disabled="!playing"
|
|
||||||
:value="stream_volume"
|
|
||||||
@change="set_stream_volume">
|
|
||||||
</range-slider>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Playback controls -->
|
|
||||||
<hr class="navbar-divider">
|
|
||||||
<div class="navbar-item">
|
|
||||||
<div class="level is-mobile">
|
|
||||||
<div class="level-left">
|
|
||||||
<div class="level-item">
|
|
||||||
<div class="buttons has-addons">
|
|
||||||
<player-button-previous class="button"></player-button-previous>
|
|
||||||
<player-button-play-pause class="button"></player-button-play-pause>
|
|
||||||
<player-button-next class="button"></player-button-next>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="level-item">
|
|
||||||
<div class="buttons has-addons">
|
|
||||||
<player-button-repeat class="button is-light"></player-button-repeat>
|
|
||||||
<player-button-shuffle class="button is-light"></player-button-shuffle>
|
|
||||||
<player-button-consume class="button is-light"></player-button-consume>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Settings drop down -->
|
<!-- Settings drop down -->
|
||||||
<div class="navbar-item has-dropdown"
|
<div class="navbar-item has-dropdown is-hoverable"
|
||||||
:class="{ 'is-active': show_settings_menu, 'is-hoverable': !show_outputs_menu && !show_settings_menu }"
|
:class="{ 'is-active': show_settings_menu }"
|
||||||
@click="show_settings_menu = !show_settings_menu"
|
@click="on_click_outside_settings">
|
||||||
v-click-outside="on_click_outside_settings">
|
<a class="navbar-link is-arrowless">
|
||||||
<a class="navbar-link is-arrowless"><span class="icon is-hidden-mobile is-hidden-tablet-only"><i class="mdi mdi-settings"></i></span> <span class="is-hidden-desktop has-text-weight-bold">forked-daapd</span></a>
|
<span class="icon is-hidden-touch"><i class="mdi mdi-24px mdi-menu"></i></span>
|
||||||
|
<span class="is-hidden-desktop has-text-weight-bold">forked-daapd</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
<div class="navbar-dropdown is-right">
|
<div class="navbar-dropdown is-right">
|
||||||
|
|
||||||
|
<navbar-item-link to="/playlists"><span class="icon"><i class="mdi mdi-library-music"></i></span> <b>Playlists</b></navbar-item-link>
|
||||||
|
<navbar-item-link to="/music" exact><span class="icon"><i class="mdi mdi-music"></i></span> <b>Music</b></navbar-item-link>
|
||||||
|
<navbar-item-link to="/music/artists"><span style="padding-left: 1.5rem;">Artists</span></navbar-item-link>
|
||||||
|
<navbar-item-link to="/music/albums"><span style="padding-left: 1.5rem;">Albums</span></navbar-item-link>
|
||||||
|
<navbar-item-link to="/music/genres"><span style="padding-left: 1.5rem;">Genres</span></navbar-item-link>
|
||||||
|
<navbar-item-link to="/music/spotify" v-if="spotify_enabled"><span style="padding-left: 1.5rem;">Spotify</span></navbar-item-link>
|
||||||
|
<navbar-item-link to="/podcasts"><span class="icon"><i class="mdi mdi-microphone"></i></span> <b>Podcasts</b></navbar-item-link>
|
||||||
|
<navbar-item-link to="/audiobooks"><span class="icon"><i class="mdi mdi-book-open-variant"></i></span> <b>Audiobooks</b></navbar-item-link>
|
||||||
|
<navbar-item-link to="/files"><span class="icon"><i class="mdi mdi-folder-open"></i></span> <b>Files</b></navbar-item-link>
|
||||||
|
<navbar-item-link to="/search"><span class="icon"><i class="mdi mdi-magnify"></i></span> <b>Search</b></navbar-item-link>
|
||||||
|
<hr style="margin: 12px 0;">
|
||||||
|
|
||||||
<a class="navbar-item" href="/admin.html">Admin</a>
|
<a class="navbar-item" href="/admin.html">Admin</a>
|
||||||
<hr class="navbar-divider">
|
<hr style="margin: 12px 0;">
|
||||||
<navbar-item-link to="/settings/webinterface">Settings</navbar-item-link>
|
<navbar-item-link to="/settings/webinterface">Settings</navbar-item-link>
|
||||||
<navbar-item-link to="/about">About</navbar-item-link>
|
<navbar-item-link to="/about">About</navbar-item-link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="is-overlay" v-show="show_settings_menu"
|
||||||
|
style="z-index:10; width: 100vw; height:100vh;"
|
||||||
|
@click="show_settings_menu = false"></div>
|
||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import webapi from '@/webapi'
|
|
||||||
import _audio from '@/audio'
|
|
||||||
import NavbarItemLink from './NavbarItemLink'
|
import NavbarItemLink from './NavbarItemLink'
|
||||||
import NavbarItemOutput from './NavbarItemOutput'
|
|
||||||
import PlayerButtonPlayPause from './PlayerButtonPlayPause'
|
|
||||||
import PlayerButtonNext from './PlayerButtonNext'
|
|
||||||
import PlayerButtonPrevious from './PlayerButtonPrevious'
|
|
||||||
import PlayerButtonShuffle from './PlayerButtonShuffle'
|
|
||||||
import PlayerButtonConsume from './PlayerButtonConsume'
|
|
||||||
import PlayerButtonRepeat from './PlayerButtonRepeat'
|
|
||||||
import RangeSlider from 'vue-range-slider'
|
|
||||||
import * as types from '@/store/mutation_types'
|
import * as types from '@/store/mutation_types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'NavbarTop',
|
name: 'NavbarTop',
|
||||||
components: { NavbarItemLink, NavbarItemOutput, PlayerButtonPlayPause, PlayerButtonNext, PlayerButtonPrevious, PlayerButtonShuffle, PlayerButtonConsume, PlayerButtonRepeat, RangeSlider },
|
components: { NavbarItemLink },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
old_volume: 0,
|
|
||||||
|
|
||||||
playing: false,
|
|
||||||
loading: false,
|
|
||||||
stream_volume: 10,
|
|
||||||
|
|
||||||
show_outputs_menu: false,
|
|
||||||
show_settings_menu: false
|
show_settings_menu: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
outputs () {
|
|
||||||
return this.$store.state.outputs
|
|
||||||
},
|
|
||||||
|
|
||||||
player () {
|
player () {
|
||||||
return this.$store.state.player
|
return this.$store.state.player
|
||||||
},
|
},
|
||||||
@ -197,108 +106,43 @@ export default {
|
|||||||
return this.$store.state.podcasts_count
|
return this.$store.state.podcasts_count
|
||||||
},
|
},
|
||||||
|
|
||||||
show_burger_menu () {
|
spotify_enabled () {
|
||||||
return this.$store.state.show_burger_menu
|
return this.$store.state.spotify.webapi_token_valid
|
||||||
|
},
|
||||||
|
|
||||||
|
show_burger_menu: {
|
||||||
|
get () {
|
||||||
|
return this.$store.state.show_burger_menu
|
||||||
|
},
|
||||||
|
set (value) {
|
||||||
|
this.$store.commit(types.SHOW_BURGER_MENU, value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
show_player_menu () {
|
||||||
|
return this.$store.state.show_player_menu
|
||||||
|
},
|
||||||
|
|
||||||
|
zindex () {
|
||||||
|
if (this.show_player_menu) {
|
||||||
|
return 'z-index: 20'
|
||||||
|
}
|
||||||
|
return ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
update_show_burger_menu: function () {
|
|
||||||
this.$store.commit(types.SHOW_BURGER_MENU, !this.show_burger_menu)
|
|
||||||
},
|
|
||||||
|
|
||||||
on_click_outside_outputs () {
|
|
||||||
this.show_outputs_menu = false
|
|
||||||
},
|
|
||||||
|
|
||||||
on_click_outside_settings () {
|
on_click_outside_settings () {
|
||||||
this.show_settings_menu = false
|
console.log('yyyyy')
|
||||||
},
|
this.show_settings_menu = !this.show_settings_menu
|
||||||
|
|
||||||
set_volume: function (newVolume) {
|
|
||||||
webapi.player_volume(newVolume)
|
|
||||||
},
|
|
||||||
|
|
||||||
toggle_mute_volume: function () {
|
|
||||||
if (this.player.volume > 0) {
|
|
||||||
this.set_volume(0)
|
|
||||||
} else {
|
|
||||||
this.set_volume(this.old_volume)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setupAudio: function () {
|
|
||||||
const a = _audio.setupAudio()
|
|
||||||
|
|
||||||
a.addEventListener('waiting', e => {
|
|
||||||
this.playing = false
|
|
||||||
this.loading = true
|
|
||||||
})
|
|
||||||
a.addEventListener('playing', e => {
|
|
||||||
this.playing = true
|
|
||||||
this.loading = false
|
|
||||||
})
|
|
||||||
a.addEventListener('ended', e => {
|
|
||||||
this.playing = false
|
|
||||||
this.loading = false
|
|
||||||
})
|
|
||||||
a.addEventListener('error', e => {
|
|
||||||
this.closeAudio()
|
|
||||||
this.$store.dispatch('add_notification', { text: 'HTTP stream error: failed to load stream or stopped loading due to network problem', type: 'danger' })
|
|
||||||
this.playing = false
|
|
||||||
this.loading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// close active audio
|
|
||||||
closeAudio: function () {
|
|
||||||
_audio.stopAudio()
|
|
||||||
this.playing = false
|
|
||||||
},
|
|
||||||
|
|
||||||
playChannel: function () {
|
|
||||||
if (this.playing) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const channel = '/stream.mp3'
|
|
||||||
this.loading = true
|
|
||||||
_audio.playSource(channel)
|
|
||||||
_audio.setVolume(this.stream_volume / 100)
|
|
||||||
},
|
|
||||||
|
|
||||||
togglePlay: function () {
|
|
||||||
if (this.loading) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (this.playing) {
|
|
||||||
return this.closeAudio()
|
|
||||||
}
|
|
||||||
return this.playChannel()
|
|
||||||
},
|
|
||||||
|
|
||||||
set_stream_volume: function (newVolume) {
|
|
||||||
this.stream_volume = newVolume
|
|
||||||
_audio.setVolume(this.stream_volume / 100)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
'$store.state.player.volume' () {
|
$route (to, from) {
|
||||||
if (this.player.volume > 0) {
|
console.log('xxxx')
|
||||||
this.old_volume = this.player.volume
|
this.show_settings_menu = false
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
// on app mounted
|
|
||||||
mounted () {
|
|
||||||
this.setupAudio()
|
|
||||||
},
|
|
||||||
|
|
||||||
// on app destroyed
|
|
||||||
destroyed () {
|
|
||||||
this.closeAudio()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<a v-on:click="toggle_consume_mode" v-bind:class="{ 'is-warning': is_consume }">
|
<a @click="toggle_consume_mode" :class="{ 'is-warning': is_consume }">
|
||||||
<span class="icon"><i class="mdi mdi-fire"></i></span>
|
<span class="icon"><i class="mdi mdi-fire" :class="icon_style"></i></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -10,6 +10,10 @@ import webapi from '@/webapi'
|
|||||||
export default {
|
export default {
|
||||||
name: 'PlayerButtonConsume',
|
name: 'PlayerButtonConsume',
|
||||||
|
|
||||||
|
props: {
|
||||||
|
icon_style: String
|
||||||
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
is_consume () {
|
is_consume () {
|
||||||
return this.$store.state.player.consume
|
return this.$store.state.player.consume
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<a v-on:click="play_next" :disabled="disabled">
|
<a @click="play_next" :disabled="disabled">
|
||||||
<span class="icon"><i class="mdi mdi-skip-forward"></i></span>
|
<span class="icon"><i class="mdi mdi-skip-forward" :class="icon_style"></i></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -10,6 +10,10 @@ import webapi from '@/webapi'
|
|||||||
export default {
|
export default {
|
||||||
name: 'PlayerButtonNext',
|
name: 'PlayerButtonNext',
|
||||||
|
|
||||||
|
props: {
|
||||||
|
icon_style: String
|
||||||
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
disabled () {
|
disabled () {
|
||||||
return !this.$store.state.queue || this.$store.state.queue.count <= 0
|
return !this.$store.state.queue || this.$store.state.queue.count <= 0
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<a @click="toggle_play_pause" :disabled="disabled">
|
<a @click="toggle_play_pause" :disabled="disabled">
|
||||||
<span class="icon"><i class="mdi" v-bind:class="[icon_style, { 'mdi-play': !is_playing, 'mdi-pause': is_playing && is_pause_allowed, 'mdi-stop': is_playing && !is_pause_allowed }]"></i></span>
|
<span class="icon"><i class="mdi" :class="[icon_style, { 'mdi-play': !is_playing, 'mdi-pause': is_playing && is_pause_allowed, 'mdi-stop': is_playing && !is_pause_allowed }]"></i></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<a v-on:click="play_previous" :disabled="disabled">
|
<a @click="play_previous" :disabled="disabled">
|
||||||
<span class="icon"><i class="mdi mdi-skip-backward"></i></span>
|
<span class="icon"><i class="mdi mdi-skip-backward" :class="icon_style"></i></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -10,6 +10,10 @@ import webapi from '@/webapi'
|
|||||||
export default {
|
export default {
|
||||||
name: 'PlayerButtonPrevious',
|
name: 'PlayerButtonPrevious',
|
||||||
|
|
||||||
|
props: {
|
||||||
|
icon_style: String
|
||||||
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
disabled () {
|
disabled () {
|
||||||
return !this.$store.state.queue || this.$store.state.queue.count <= 0
|
return !this.$store.state.queue || this.$store.state.queue.count <= 0
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<a v-on:click="toggle_repeat_mode" v-bind:class="{ 'is-warning': !is_repeat_off }">
|
<a @click="toggle_repeat_mode" :class="{ 'is-warning': !is_repeat_off }">
|
||||||
<span class="icon"><i class="mdi" v-bind:class="{ 'mdi-repeat': is_repeat_all, 'mdi-repeat-once': is_repeat_single, 'mdi-repeat-off': is_repeat_off }"></i></span>
|
<span class="icon"><i class="mdi" :class="[icon_style, { 'mdi-repeat': is_repeat_all, 'mdi-repeat-once': is_repeat_single, 'mdi-repeat-off': is_repeat_off }]"></i></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -10,8 +10,8 @@ import webapi from '@/webapi'
|
|||||||
export default {
|
export default {
|
||||||
name: 'PlayerButtonRepeat',
|
name: 'PlayerButtonRepeat',
|
||||||
|
|
||||||
data () {
|
props: {
|
||||||
return { }
|
icon_style: String
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<a v-on:click="toggle_shuffle_mode" v-bind:class="{ 'is-warning': is_shuffle }">
|
<a @click="toggle_shuffle_mode" :class="{ 'is-warning': is_shuffle }">
|
||||||
<span class="icon"><i class="mdi" v-bind:class="{ 'mdi-shuffle': is_shuffle, 'mdi-shuffle-disabled': !is_shuffle }"></i></span>
|
<span class="icon"><i class="mdi" :class="[icon_style, { 'mdi-shuffle': is_shuffle, 'mdi-shuffle-disabled': !is_shuffle }]"></i></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -10,6 +10,10 @@ import webapi from '@/webapi'
|
|||||||
export default {
|
export default {
|
||||||
name: 'PlayerButtonShuffle',
|
name: 'PlayerButtonShuffle',
|
||||||
|
|
||||||
|
props: {
|
||||||
|
icon_style: String
|
||||||
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
is_shuffle () {
|
is_shuffle () {
|
||||||
return this.$store.state.player.shuffle
|
return this.$store.state.player.shuffle
|
||||||
|
@ -1,8 +1,51 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="hero fd-is-fullheight">
|
<section class="fd-is-fullheight" style="display: flex; flex-direction: column;">
|
||||||
|
<div class="" style="max-height: calc(100vh - 25rem); padding: 1.5rem; overflow: hidden; flex-grow: 1;flex-shrink: 1;" v-show="artwork_visible">
|
||||||
|
<img :src="artwork_url"
|
||||||
|
class="fd-has-action"
|
||||||
|
style="width: 100%;height: 100%;object-fit: contain;filter: drop-shadow(0px 0px 1px rgba(0,0,0,.3)) drop-shadow(0px 0px 10px rgba(0,0,0,.3));"
|
||||||
|
@load="artwork_loaded"
|
||||||
|
@error="artwork_error"
|
||||||
|
@click="open_dialog(now_playing)">
|
||||||
|
</div>
|
||||||
|
<div class="fd-has-padding-left-right">
|
||||||
|
<div class="container has-text-centered">
|
||||||
|
<p class="control has-text-centered fd-progress-now-playing">
|
||||||
|
<range-slider
|
||||||
|
class="seek-slider fd-has-action"
|
||||||
|
min="0"
|
||||||
|
:max="state.item_length_ms"
|
||||||
|
:value="item_progress_ms"
|
||||||
|
:disabled="state.state === 'stop'"
|
||||||
|
step="1000"
|
||||||
|
@change="seek" >
|
||||||
|
</range-slider>
|
||||||
|
</p>
|
||||||
|
<p class="content">
|
||||||
|
<span>{{ item_progress_ms | duration }} / {{ now_playing.length_ms | duration }}</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="fd-has-padding-left-right">
|
||||||
|
<div class="container has-text-centered fd-has-margin-top">
|
||||||
|
<h1 class="title is-5">
|
||||||
|
{{ now_playing.title }}
|
||||||
|
</h1>
|
||||||
|
<h2 class="title is-6">
|
||||||
|
{{ now_playing.artist }}
|
||||||
|
</h2>
|
||||||
|
<h2 class="subtitle is-6 has-text-grey has-text-weight-bold" v-if="composer">
|
||||||
|
{{ composer }}
|
||||||
|
</h2>
|
||||||
|
<h3 class="subtitle is-6">
|
||||||
|
{{ now_playing.album }}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--
|
||||||
<div class="hero-head fd-has-padding-left-right">
|
<div class="hero-head fd-has-padding-left-right">
|
||||||
<div class="container has-text-centered fd-has-margin-top">
|
<div class="container has-text-centered fd-has-margin-top">
|
||||||
<h1 class="title is-4">
|
<h1 class="title is-5">
|
||||||
{{ now_playing.title }}
|
{{ now_playing.title }}
|
||||||
</h1>
|
</h1>
|
||||||
<h2 class="title is-6">
|
<h2 class="title is-6">
|
||||||
@ -17,10 +60,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="hero-body fd-is-fullheight-body has-text-centered" v-show="artwork_visible">
|
<div class="hero-body fd-is-fullheight-body has-text-centered" v-show="artwork_visible">
|
||||||
<img :src="artwork_url" class="fd-has-shadow fd-image-fullheight fd-has-action"
|
<figure class="image is-square">
|
||||||
@load="artwork_loaded"
|
<img :src="artwork_url"
|
||||||
@error="artwork_error"
|
class="fd-has-shadow fd-image-fullheight fd-has-action"
|
||||||
@click="open_dialog(now_playing)">
|
style="width: auto; max-height: calc(100vh - 25rem); margin: 0 auto;"
|
||||||
|
@load="artwork_loaded"
|
||||||
|
@error="artwork_error"
|
||||||
|
@click="open_dialog(now_playing)">
|
||||||
|
</figure>
|
||||||
</div>
|
</div>
|
||||||
<div class="hero-body fd-is-fullheight-body has-text-centered" v-show="!artwork_visible">
|
<div class="hero-body fd-is-fullheight-body has-text-centered" v-show="!artwork_visible">
|
||||||
<a @click="open_dialog(now_playing)" class="button is-white is-medium">
|
<a @click="open_dialog(now_playing)" class="button is-white is-medium">
|
||||||
@ -43,35 +90,22 @@
|
|||||||
<p class="content">
|
<p class="content">
|
||||||
<span>{{ item_progress_ms | duration }} / {{ now_playing.length_ms | duration }}</span>
|
<span>{{ item_progress_ms | duration }} / {{ now_playing.length_ms | duration }}</span>
|
||||||
</p>
|
</p>
|
||||||
<div class="buttons has-addons is-centered">
|
|
||||||
<player-button-previous class="button is-medium"></player-button-previous>
|
|
||||||
<player-button-play-pause class="button is-medium" icon_style="mdi-36px"></player-button-play-pause>
|
|
||||||
<player-button-next class="button is-medium"></player-button-next>
|
|
||||||
<player-button-repeat class="button is-medium is-light"></player-button-repeat>
|
|
||||||
<player-button-shuffle class="button is-medium is-light"></player-button-shuffle>
|
|
||||||
<player-button-consume class="button is-medium is-light"></player-button-consume>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<modal-dialog-queue-item :show="show_details_modal" :item="selected_item" @close="show_details_modal = false" />
|
|
||||||
</div>
|
</div>
|
||||||
|
-->
|
||||||
|
<modal-dialog-queue-item :show="show_details_modal" :item="selected_item" @close="show_details_modal = false" />
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ModalDialogQueueItem from '@/components/ModalDialogQueueItem'
|
import ModalDialogQueueItem from '@/components/ModalDialogQueueItem'
|
||||||
import PlayerButtonPlayPause from '@/components/PlayerButtonPlayPause'
|
|
||||||
import PlayerButtonNext from '@/components/PlayerButtonNext'
|
|
||||||
import PlayerButtonPrevious from '@/components/PlayerButtonPrevious'
|
|
||||||
import PlayerButtonShuffle from '@/components/PlayerButtonShuffle'
|
|
||||||
import PlayerButtonConsume from '@/components/PlayerButtonConsume'
|
|
||||||
import PlayerButtonRepeat from '@/components/PlayerButtonRepeat'
|
|
||||||
import RangeSlider from 'vue-range-slider'
|
import RangeSlider from 'vue-range-slider'
|
||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
import * as types from '@/store/mutation_types'
|
import * as types from '@/store/mutation_types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PageNowPlaying',
|
name: 'PageNowPlaying',
|
||||||
components: { ModalDialogQueueItem, PlayerButtonPlayPause, PlayerButtonNext, PlayerButtonPrevious, PlayerButtonShuffle, PlayerButtonConsume, PlayerButtonRepeat, RangeSlider },
|
components: { ModalDialogQueueItem, RangeSlider },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
@ -275,9 +275,15 @@ export const router = new VueRouter({
|
|||||||
})
|
})
|
||||||
|
|
||||||
router.beforeEach((to, from, next) => {
|
router.beforeEach((to, from, next) => {
|
||||||
const burgerMenuVisible = store.state.show_burger_menu
|
if (store.state.show_burger_menu) {
|
||||||
if (burgerMenuVisible) {
|
|
||||||
store.commit(types.SHOW_BURGER_MENU, false)
|
store.commit(types.SHOW_BURGER_MENU, false)
|
||||||
|
next(false)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
next(!burgerMenuVisible)
|
if (store.state.show_player_menu) {
|
||||||
|
store.commit(types.SHOW_PLAYER_MENU, false)
|
||||||
|
next(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
next(true)
|
||||||
})
|
})
|
||||||
|
@ -54,7 +54,8 @@ export default new Vuex.Store({
|
|||||||
|
|
||||||
hide_singles: false,
|
hide_singles: false,
|
||||||
show_only_next_items: false,
|
show_only_next_items: false,
|
||||||
show_burger_menu: false
|
show_burger_menu: false,
|
||||||
|
show_player_menu: false
|
||||||
},
|
},
|
||||||
|
|
||||||
getters: {
|
getters: {
|
||||||
@ -175,6 +176,9 @@ export default new Vuex.Store({
|
|||||||
},
|
},
|
||||||
[types.SHOW_BURGER_MENU] (state, showBurgerMenu) {
|
[types.SHOW_BURGER_MENU] (state, showBurgerMenu) {
|
||||||
state.show_burger_menu = showBurgerMenu
|
state.show_burger_menu = showBurgerMenu
|
||||||
|
},
|
||||||
|
[types.SHOW_PLAYER_MENU] (state, showPlayerMenu) {
|
||||||
|
state.show_player_menu = showPlayerMenu
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -21,3 +21,4 @@ export const ADD_RECENT_SEARCH = 'ADD_RECENT_SEARCH'
|
|||||||
export const HIDE_SINGLES = 'HIDE_SINGLES'
|
export const HIDE_SINGLES = 'HIDE_SINGLES'
|
||||||
export const SHOW_ONLY_NEXT_ITEMS = 'SHOW_ONLY_NEXT_ITEMS'
|
export const SHOW_ONLY_NEXT_ITEMS = 'SHOW_ONLY_NEXT_ITEMS'
|
||||||
export const SHOW_BURGER_MENU = 'SHOW_BURGER_MENU'
|
export const SHOW_BURGER_MENU = 'SHOW_BURGER_MENU'
|
||||||
|
export const SHOW_PLAYER_MENU = 'SHOW_PLAYER_MENU'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user