mirror of
https://github.com/jellyfin/jellyfin-vue.git
synced 2025-03-03 11:17:22 +00:00
feat(music): create full screen music player
This commit is contained in:
parent
c5e9843c90
commit
8b6de3c7a0
@ -1,52 +1,114 @@
|
||||
<template>
|
||||
<v-container fluid>
|
||||
<v-row>
|
||||
<v-col cols="9" md="3" class="d-flex flex-row pa-0">
|
||||
<v-avatar ref="albumCover" tile size="72" color="primary">
|
||||
<v-img :src="getImageUrl(getCurrentItem.AlbumId)">
|
||||
<template #placeholder>
|
||||
<v-icon dark>mdi-album</v-icon>
|
||||
</template>
|
||||
</v-img>
|
||||
</v-avatar>
|
||||
<div class="d-flex flex-column justify-center ml-4">
|
||||
<span class="font-weight-medium mt-md-n2">
|
||||
<nuxt-link
|
||||
tag="span"
|
||||
class="text-truncate link"
|
||||
:to="`/item/${getCurrentItem.AlbumId}`"
|
||||
<transition name="fade-fast" mode="in-out">
|
||||
<v-footer
|
||||
v-if="isPlaying && getCurrentlyPlayingMediaType() === 'Audio'"
|
||||
app
|
||||
:color="footerColor"
|
||||
>
|
||||
<v-container v-if="isFullScreenPlayer" fluid>
|
||||
<time-slider />
|
||||
</v-container>
|
||||
<v-container fluid>
|
||||
<v-row>
|
||||
<v-col cols="9" md="3" class="d-flex flex-row pa-0">
|
||||
<v-avatar
|
||||
v-if="!isFullScreenPlayer"
|
||||
ref="albumCover"
|
||||
tile
|
||||
size="72"
|
||||
color="primary"
|
||||
>
|
||||
{{ getCurrentItem.Name }}
|
||||
</nuxt-link>
|
||||
<v-btn class="d-none d-md-inline-flex" icon disabled>
|
||||
<v-icon size="18">{{
|
||||
getCurrentItem.UserData.IsFavorite
|
||||
? 'mdi-heart'
|
||||
: 'mdi-heart-outline'
|
||||
}}</v-icon>
|
||||
</v-btn>
|
||||
</span>
|
||||
<nuxt-link
|
||||
v-if="getCurrentItem.AlbumArtists[0].Id"
|
||||
tag="span"
|
||||
class="text--secondary text-caption text-truncate mt-md-n2 link"
|
||||
:to="`/artist/${getCurrentItem.AlbumArtists[0].Id}`"
|
||||
<v-img :src="getImageUrl(getCurrentItem().AlbumId)">
|
||||
<template #placeholder>
|
||||
<v-icon dark>mdi-album</v-icon>
|
||||
</template>
|
||||
</v-img>
|
||||
</v-avatar>
|
||||
<div class="d-flex flex-column justify-center ml-4">
|
||||
<span class="font-weight-medium mt-md-n2">
|
||||
<nuxt-link
|
||||
tag="span"
|
||||
class="text-truncate link"
|
||||
:to="`/item/${getCurrentItem().AlbumId}`"
|
||||
>
|
||||
{{ getCurrentItem().Name }}
|
||||
</nuxt-link>
|
||||
<v-btn class="d-none d-md-inline-flex" icon disabled>
|
||||
<v-icon size="18">{{
|
||||
getCurrentItem().UserData.IsFavorite
|
||||
? 'mdi-heart'
|
||||
: 'mdi-heart-outline'
|
||||
}}</v-icon>
|
||||
</v-btn>
|
||||
</span>
|
||||
<nuxt-link
|
||||
tag="span"
|
||||
class="text--secondary text-caption text-truncate mt-md-n2 link"
|
||||
:to="`/artist/${getCurrentItem().AlbumArtists[0].Id}`"
|
||||
>
|
||||
{{ getCurrentItem().AlbumArtist }}
|
||||
</nuxt-link>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="6" class="pa-0 d-none d-md-inline">
|
||||
<div class="d-flex flex-column justify-center">
|
||||
<div class="d-flex align-center justify-center">
|
||||
<v-btn disabled icon class="mx-1">
|
||||
<v-icon>mdi-shuffle</v-icon>
|
||||
</v-btn>
|
||||
<v-btn icon class="mx-1" @click="setPreviousTrack">
|
||||
<v-icon>mdi-skip-previous</v-icon>
|
||||
</v-btn>
|
||||
<v-btn icon large class="mx-1" @click="togglePause">
|
||||
<v-icon large>
|
||||
{{ isPaused ? 'mdi-play' : 'mdi-pause' }}
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
<v-btn icon class="mx-1" @click="stopPlayback">
|
||||
<v-icon>mdi-stop</v-icon>
|
||||
</v-btn>
|
||||
<v-btn icon class="mx-1" @click="setNextTrack">
|
||||
<v-icon>mdi-skip-next</v-icon>
|
||||
</v-btn>
|
||||
<v-btn disabled icon class="mx-1">
|
||||
<v-icon>mdi-repeat-off</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
<time-slider v-if="!isFullScreenPlayer" />
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="3" class="d-none d-md-flex align-center justify-end">
|
||||
<v-tooltip top>
|
||||
<template #activator="{ on, attrs }">
|
||||
<v-btn disabled icon class="mr-2" v-bind="attrs" v-on="on">
|
||||
<v-icon>mdi-playlist-play</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<span>{{ $t('queue') }}</span>
|
||||
</v-tooltip>
|
||||
<volume-slider />
|
||||
<transition name="fade-fast" mode="in-out">
|
||||
<v-tooltip v-if="!isFullScreenPlayer" top>
|
||||
<template #activator="{ on, attrs }">
|
||||
<nuxt-link tag="span" :to="'/playback'">
|
||||
<v-btn icon class="ml-2" v-bind="attrs" v-on="on">
|
||||
<v-icon>mdi-fullscreen</v-icon>
|
||||
</v-btn>
|
||||
</nuxt-link>
|
||||
</template>
|
||||
<span>{{ $t('fullScreen') }}</span>
|
||||
</v-tooltip>
|
||||
</transition>
|
||||
</v-col>
|
||||
<v-col
|
||||
cols="3"
|
||||
class="d-flex d-md-none px-0 align-center justify-end"
|
||||
>
|
||||
{{ getCurrentItem.AlbumArtist }}
|
||||
</nuxt-link>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="6" class="pa-0 d-none d-md-inline">
|
||||
<div class="d-flex flex-column justify-center">
|
||||
<div class="d-flex align-center justify-center">
|
||||
<v-btn disabled icon class="mx-1">
|
||||
<v-icon>mdi-shuffle</v-icon>
|
||||
<v-btn icon>
|
||||
<v-icon>mdi-heart</v-icon>
|
||||
</v-btn>
|
||||
<v-btn icon class="mx-1" @click="setPreviousTrack">
|
||||
<v-icon>mdi-skip-previous</v-icon>
|
||||
</v-btn>
|
||||
<v-btn icon large class="mx-1" @click="togglePause">
|
||||
<v-icon large>
|
||||
<v-btn icon @click="togglePause">
|
||||
<v-icon>
|
||||
{{ isPaused ? 'mdi-play' : 'mdi-pause' }}
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
@ -59,45 +121,23 @@
|
||||
<v-btn disabled icon class="mx-1">
|
||||
<v-icon>mdi-repeat-off</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
<time-slider />
|
||||
</v-col>
|
||||
</v-row>
|
||||
<div
|
||||
v-if="isFullScreenPlayer && nextTrackName"
|
||||
class="d-flex justify-center align-center"
|
||||
>
|
||||
<h4 class="text-h6 font-weight-thin font-italic">
|
||||
{{ $t('upNext') }}: {{ nextTrackName }}
|
||||
</h4>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="3" class="d-none d-md-flex align-center justify-end">
|
||||
<v-tooltip top>
|
||||
<template #activator="{ on, attrs }">
|
||||
<v-btn disabled icon class="mr-2" v-bind="attrs" v-on="on">
|
||||
<v-icon>mdi-playlist-play</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<span>{{ $t('queue') }}</span>
|
||||
</v-tooltip>
|
||||
<volume-slider />
|
||||
<v-tooltip top>
|
||||
<template #activator="{ on, attrs }">
|
||||
<v-btn disabled icon class="ml-2" v-bind="attrs" v-on="on">
|
||||
<v-icon>mdi-fullscreen</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<span>{{ $t('fullScreen') }}</span>
|
||||
</v-tooltip>
|
||||
</v-col>
|
||||
<v-col cols="3" class="d-flex d-md-none px-0 align-center justify-end">
|
||||
<v-btn icon>
|
||||
<v-icon>mdi-heart</v-icon>
|
||||
</v-btn>
|
||||
<v-btn icon @click="togglePause">
|
||||
<v-icon>
|
||||
{{ isPaused ? 'mdi-play' : 'mdi-pause' }}
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-container>
|
||||
</v-footer>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ImageType } from '@jellyfin/client-axios';
|
||||
import { ImageType, RepeatMode } from '@jellyfin/client-axios';
|
||||
import Vue from 'vue';
|
||||
import { mapActions, mapGetters } from 'vuex';
|
||||
import timeUtils from '~/mixins/timeUtils';
|
||||
@ -107,9 +147,39 @@ import { PlaybackStatus } from '~/store/playbackManager';
|
||||
export default Vue.extend({
|
||||
mixins: [timeUtils, imageHelper],
|
||||
computed: {
|
||||
...mapGetters('playbackManager', ['getCurrentItem']),
|
||||
isPaused(): boolean {
|
||||
return this.$store.state.playbackManager.status === PlaybackStatus.paused;
|
||||
},
|
||||
isPlaying(): boolean {
|
||||
return (
|
||||
this.$store.state.playbackManager.status !== PlaybackStatus.stopped
|
||||
);
|
||||
},
|
||||
isFullScreenPlayer(): boolean {
|
||||
return !this.$store.state.playbackManager.isMinimized;
|
||||
},
|
||||
footerColor(): string | undefined {
|
||||
if (this.isFullScreenPlayer) {
|
||||
return 'rgba(0,0,0,0.15)';
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
nextTrackName(): string | undefined {
|
||||
const state = this.$store.state.playbackManager;
|
||||
const queue = this.$store.state.playbackManager.queue;
|
||||
if (
|
||||
state.currentItemIndex !== null &&
|
||||
state.currentItemIndex + 1 < state.queue.length
|
||||
) {
|
||||
return queue[state.currentItemIndex + 1].Name;
|
||||
} else if (
|
||||
this.$store.state.playbackManager.repeatMode === RepeatMode.RepeatAll
|
||||
) {
|
||||
return queue[0].Name;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -121,6 +191,10 @@ export default Vue.extend({
|
||||
'unpause',
|
||||
'pause'
|
||||
]),
|
||||
...mapGetters('playbackManager', [
|
||||
'getCurrentItem',
|
||||
'getCurrentlyPlayingMediaType'
|
||||
]),
|
||||
getImageUrl(itemId: string): string | undefined {
|
||||
const element = this.$refs.albumCover as HTMLElement;
|
||||
return this.getImageUrlForElement(ImageType.Primary, {
|
||||
|
@ -3,11 +3,10 @@
|
||||
<v-slider
|
||||
hide-details
|
||||
thumb-label
|
||||
min="0"
|
||||
max="100"
|
||||
:value="currentvolume"
|
||||
validate-on-blur
|
||||
prepend-icon="mdi-volume-high"
|
||||
:prepend-icon="icon"
|
||||
@input="onVolumeChange"
|
||||
>
|
||||
</v-slider>
|
||||
@ -22,6 +21,17 @@ export default Vue.extend({
|
||||
computed: {
|
||||
currentvolume(): number {
|
||||
return this.$store.state.playbackManager.currentVolume;
|
||||
},
|
||||
icon(): string {
|
||||
if (this.currentvolume >= 80) {
|
||||
return 'mdi-volume-high';
|
||||
} else if (this.currentvolume < 80 && this.currentvolume >= 25) {
|
||||
return 'mdi-volume-medium';
|
||||
} else if (this.currentvolume < 25 && this.currentvolume >= 1) {
|
||||
return 'mdi-volume-low';
|
||||
} else {
|
||||
return 'mdi-volume-mute';
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -2,6 +2,7 @@
|
||||
<v-app ref="app">
|
||||
<backdrop />
|
||||
<v-navigation-drawer
|
||||
v-if="$store.state.page.showNavDrawer"
|
||||
v-model="drawer"
|
||||
:temporary="$vuetify.breakpoint.mobile"
|
||||
:permanent="!$vuetify.breakpoint.mobile"
|
||||
@ -54,7 +55,7 @@
|
||||
:class="{ opaque: opaqueAppBar || $vuetify.breakpoint.xsOnly }"
|
||||
>
|
||||
<v-app-bar-nav-icon
|
||||
v-if="$vuetify.breakpoint.mobile"
|
||||
v-if="$vuetify.breakpoint.mobile && $store.state.page.showNavDrawer"
|
||||
@click.stop="drawer = !drawer"
|
||||
/>
|
||||
<v-btn
|
||||
@ -94,14 +95,7 @@
|
||||
<v-main>
|
||||
<nuxt />
|
||||
</v-main>
|
||||
<transition name="fade" mode="in-out">
|
||||
<v-footer
|
||||
v-if="isPlaying && getCurrentlyPlayingMediaType() === 'Audio'"
|
||||
app
|
||||
>
|
||||
<audio-controls />
|
||||
</v-footer>
|
||||
</transition>
|
||||
<audio-controls />
|
||||
<!-- Utilities and global systems -->
|
||||
<snackbar />
|
||||
<player-manager />
|
||||
@ -112,9 +106,8 @@
|
||||
import { BaseItemDto } from '@jellyfin/client-axios';
|
||||
import { stringify } from 'qs';
|
||||
import Vue from 'vue';
|
||||
import { mapActions, mapGetters, mapState } from 'vuex';
|
||||
import { mapActions, mapState } from 'vuex';
|
||||
import { AppState } from '~/store';
|
||||
import { PlaybackStatus } from '~/store/playbackManager';
|
||||
import { getLibraryIcon } from '~/utils/items';
|
||||
|
||||
interface WebSocketMessage {
|
||||
@ -149,11 +142,6 @@ export default Vue.extend({
|
||||
};
|
||||
})
|
||||
}),
|
||||
isPlaying(): boolean {
|
||||
return (
|
||||
this.$store.state.playbackManager.status !== PlaybackStatus.stopped
|
||||
);
|
||||
},
|
||||
items(): LayoutButton[] {
|
||||
return [
|
||||
{
|
||||
@ -193,7 +181,6 @@ export default Vue.extend({
|
||||
methods: {
|
||||
...mapActions('userViews', ['refreshUserViews']),
|
||||
...mapActions('displayPreferences', ['callAllCallbacks']),
|
||||
...mapGetters('playbackManager', ['getCurrentlyPlayingMediaType']),
|
||||
handleKeepAlive(): void {
|
||||
this.$store.subscribe((mutation, state) => {
|
||||
if (
|
||||
|
@ -132,7 +132,7 @@ export default Vue.extend({
|
||||
|
||||
if (
|
||||
this.collectionInfo &&
|
||||
['CollectionFolder', 'Folder', 'UserView'].includes(
|
||||
['CollectionFolder', 'Folder', 'UserView', 'playlists'].includes(
|
||||
this.collectionInfo.Type || ''
|
||||
)
|
||||
) {
|
||||
|
161
pages/playback/index.vue
Normal file
161
pages/playback/index.vue
Normal file
@ -0,0 +1,161 @@
|
||||
<template>
|
||||
<v-container fluid>
|
||||
<swiper
|
||||
ref="playbackSwiper"
|
||||
class="swiper"
|
||||
:options="swiperOptions"
|
||||
@slideChange="onSlideChange"
|
||||
>
|
||||
<swiper-slide v-for="item in currentQueue" :key="item.Id">
|
||||
<div class="d-flex flex-column justify-center">
|
||||
<div class="d-flex align-center justify-center">
|
||||
<v-avatar ref="albumCover" tile size="65vh" color="primary">
|
||||
<v-img :src="getImageUrl(item)">
|
||||
<template #placeholder>
|
||||
<v-icon dark>mdi-album</v-icon>
|
||||
</template>
|
||||
</v-img>
|
||||
</v-avatar>
|
||||
</div>
|
||||
</div>
|
||||
</swiper-slide>
|
||||
</swiper>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { BaseItemDto, ImageType } from '@jellyfin/client-axios';
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import Swiper, { SwiperOptions } from 'swiper';
|
||||
import { PlaybackStatus, RepeatMode } from '~/store/playbackManager';
|
||||
import imageHelper from '~/mixins/imageHelper';
|
||||
|
||||
export default Vue.extend({
|
||||
mixins: [imageHelper],
|
||||
data() {
|
||||
return {
|
||||
swiperOptions: {
|
||||
slidesPerView: 4,
|
||||
centeredSlides: true,
|
||||
initialSlide: 0,
|
||||
loop: true,
|
||||
parallax: true,
|
||||
autoplay: false,
|
||||
effect: 'coverflow',
|
||||
coverflowEffect: {
|
||||
depth: 500,
|
||||
slideShadows: false,
|
||||
stretch: -200,
|
||||
rotate: 0
|
||||
},
|
||||
keyboard: true,
|
||||
a11y: true
|
||||
} as SwiperOptions,
|
||||
swiper: undefined as Swiper | undefined
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
currentItemIndex: {
|
||||
get(): number {
|
||||
return this.$store.state.playbackManager.currentItemIndex;
|
||||
}
|
||||
},
|
||||
currentQueue: {
|
||||
get(): BaseItemDto[] {
|
||||
return this.$store.state.playbackManager.queue;
|
||||
}
|
||||
},
|
||||
backdropHash: {
|
||||
get(): string {
|
||||
return (
|
||||
this.getBlurhashHash(this.getCurrentItem(), ImageType.Primary) || ''
|
||||
);
|
||||
}
|
||||
},
|
||||
isPaused: {
|
||||
get(): boolean {
|
||||
return (
|
||||
this.$store.state.playbackManager.status === PlaybackStatus.paused
|
||||
);
|
||||
}
|
||||
},
|
||||
isPlaying: {
|
||||
get(): boolean {
|
||||
return (
|
||||
this.$store.state.playbackManager.status !== PlaybackStatus.stopped
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
currentItemIndex(newIndex: number): void {
|
||||
if (this.swiperOptions.loop) {
|
||||
this.swiper?.slideToLoop(newIndex);
|
||||
} else {
|
||||
this.swiper?.slideTo(newIndex);
|
||||
}
|
||||
this.setBackdrop({ hash: this.backdropHash });
|
||||
},
|
||||
isPlaying(newValue: boolean): void {
|
||||
if (!newValue) {
|
||||
this.$router.back();
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
if (this.$store.state.playbackManager.repeatMode !== RepeatMode.all) {
|
||||
this.swiperOptions.loop = false;
|
||||
}
|
||||
this.showNavDrawer({ showNavDrawer: false });
|
||||
this.setAppBarOpacity({ opaqueAppBar: false });
|
||||
this.setBackdropOpacity({ value: 0.5 });
|
||||
if (!this.isPlaying) {
|
||||
this.$router.back();
|
||||
}
|
||||
this.setMinimized({ minimized: false });
|
||||
},
|
||||
mounted() {
|
||||
this.swiper = (this.$refs.playbackSwiper as Vue).$swiper as Swiper;
|
||||
this.setBackdrop({ hash: this.backdropHash });
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.clearBackdrop();
|
||||
this.resetBackdropOpacity();
|
||||
this.setMinimized({ minimized: true });
|
||||
},
|
||||
destroyed() {
|
||||
this.showNavDrawer({ showNavDrawer: true });
|
||||
this.setAppBarOpacity({ opaqueAppBar: true });
|
||||
},
|
||||
methods: {
|
||||
...mapGetters('playbackManager', ['getCurrentItem']),
|
||||
...mapActions('playbackManager', ['setCurrentIndex', 'setMinimized']),
|
||||
...mapActions('page', ['showNavDrawer', 'setAppBarOpacity']),
|
||||
...mapActions('backdrop', [
|
||||
'setBackdrop',
|
||||
'setBackdropOpacity',
|
||||
'resetBackdropOpacity',
|
||||
'clearBackdrop'
|
||||
]),
|
||||
onSlideChange(): void {
|
||||
const index = this.swiper?.realIndex || 0;
|
||||
this.setCurrentIndex({ index });
|
||||
},
|
||||
getImageUrl(item: BaseItemDto): string | undefined {
|
||||
const tag = this.getImageTag(item, ImageType.Primary);
|
||||
if (tag) {
|
||||
return this.getImageUrlForElement(ImageType.Primary, { item });
|
||||
}
|
||||
|
||||
const albumTag = this.getImageTag(item.AlbumId, ImageType.Primary);
|
||||
if (albumTag) {
|
||||
return this.getImageUrlForElement(ImageType.Primary, {
|
||||
item: item.AlbumId
|
||||
});
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
@ -3,11 +3,13 @@ import { ActionTree, MutationTree } from 'vuex';
|
||||
export interface PageState {
|
||||
title: string;
|
||||
opaqueAppBar: boolean;
|
||||
showNavDrawer: boolean;
|
||||
}
|
||||
|
||||
export const state = (): PageState => ({
|
||||
title: 'Jellyfin',
|
||||
opaqueAppBar: true
|
||||
opaqueAppBar: true,
|
||||
showNavDrawer: true
|
||||
});
|
||||
|
||||
interface TitleMutationPayload {
|
||||
@ -18,6 +20,10 @@ interface AppBarMutationPayload {
|
||||
opaqueAppBar: boolean;
|
||||
}
|
||||
|
||||
interface NavDrawerMutationPayload {
|
||||
showNavDrawer: boolean;
|
||||
}
|
||||
|
||||
export const mutations: MutationTree<PageState> = {
|
||||
SET_PAGE_TITLE(state: PageState, { title }: TitleMutationPayload) {
|
||||
state.title = title;
|
||||
@ -27,6 +33,12 @@ export const mutations: MutationTree<PageState> = {
|
||||
{ opaqueAppBar }: AppBarMutationPayload
|
||||
) {
|
||||
state.opaqueAppBar = opaqueAppBar;
|
||||
},
|
||||
SET_NAVDRAWER_VISIBILITY(
|
||||
state: PageState,
|
||||
{ showNavDrawer }: NavDrawerMutationPayload
|
||||
) {
|
||||
state.showNavDrawer = showNavDrawer;
|
||||
}
|
||||
};
|
||||
|
||||
@ -36,5 +48,8 @@ export const actions: ActionTree<PageState, PageState> = {
|
||||
},
|
||||
setAppBarOpacity({ commit }, { opaqueAppBar }: AppBarMutationPayload) {
|
||||
commit('SET_APPBAR_OPACITY', { opaqueAppBar });
|
||||
},
|
||||
showNavDrawer({ commit }, { showNavDrawer }: NavDrawerMutationPayload) {
|
||||
commit('SET_NAVDRAWER_VISIBILITY', { showNavDrawer });
|
||||
}
|
||||
};
|
||||
|
@ -269,6 +269,9 @@ export const actions: ActionTree<PlaybackManagerState, PlaybackManagerState> = {
|
||||
setVolume({ commit }, { volume }: { volume: number }) {
|
||||
commit('SET_VOLUME', { volume });
|
||||
},
|
||||
setCurrentIndex({ commit }, { index }: { index: number }) {
|
||||
commit('SET_CURRENT_ITEM_INDEX', { currentItemIndex: index });
|
||||
},
|
||||
setCurrentTime({ commit }, { time }: { time: number | null }) {
|
||||
commit('SET_CURRENT_TIME', { time });
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user