Verified Commit 62a7d909 authored by Agate's avatar Agate 💬

Now persist/restore queue/radio/player state automatically

parent ac136578
......@@ -23,7 +23,8 @@
"vue-resource": "^1.3.4",
"vue-router": "^2.3.1",
"vuedraggable": "^2.14.1",
"vuex": "^3.0.1"
"vuex": "^3.0.1",
"vuex-persistedstate": "^2.4.2"
},
"devDependencies": {
"autoprefixer": "^6.7.2",
......
import logger from '@/logging'
export default {
get (key, d) {
let v = localStorage.getItem(key)
if (v === null) {
return d
} else {
try {
return JSON.parse(v).value
} catch (e) {
logger.default.error('Removing unparsable cached value for key ' + key)
this.remove(key)
return d
}
}
},
set (key, value) {
return localStorage.setItem(key, JSON.stringify({value: value}))
},
remove (key) {
return localStorage.removeItem(key)
},
clear () {
localStorage.clear()
}
}
......@@ -62,7 +62,7 @@
{{ track.artist.name }}
</td>
<td>
<template v-if="favoriteTracks.objects[track.id]">
<template v-if="$store.getters['favorites/isFavorite'](track.id)">
<i class="pink heart icon"></i>
</template
</td>
......
......@@ -5,6 +5,8 @@
v-if="currentTrack"
:key="(currentIndex, currentTrack.id)"
:is-current="true"
:start-time="$store.state.player.currentTime"
:autoplay="$store.state.player.playing"
:track="currentTrack">
</audio-track>
......@@ -127,7 +129,7 @@
@keydown.ctrl.right.prevent.exact="next"
@keydown.ctrl.down.prevent.exact="$store.commit('player/incrementVolume', -0.1)"
@keydown.ctrl.up.prevent.exact="$store.commit('player/incrementVolume', 0.1)"
@keydown.f.prevent.exact="favoriteTracks.toggle(currentTrack.id)"
@keydown.f.prevent.exact="$store.dispatch('favorites/toggle', currentTrack.id)"
@keydown.l.prevent.exact="$store.commit('player/toggleLooping')"
@keydown.s.prevent.exact="shuffle"
/>
......
......@@ -18,6 +18,7 @@ const SEARCH_URL = config.API_URL + 'search?query={query}'
export default {
mounted () {
let self = this
jQuery(this.$el).search({
type: 'category',
minCharacters: 3,
......@@ -26,7 +27,7 @@ export default {
},
apiSettings: {
beforeXHR: function (xhrObject) {
xhrObject.setRequestHeader('Authorization', this.$store.getters['auth/header'])
xhrObject.setRequestHeader('Authorization', self.$store.getters['auth/header'])
return xhrObject
},
onResponse: function (initialResponse) {
......
......@@ -22,7 +22,9 @@ import url from '@/utils/url'
export default {
props: {
track: {type: Object},
isCurrent: {type: Boolean, default: false}
isCurrent: {type: Boolean, default: false},
startTime: {type: Number, default: 0},
autoplay: {type: Boolean, default: false}
},
computed: {
...mapState({
......@@ -57,8 +59,11 @@ export default {
},
loaded: function () {
this.$store.commit('player/duration', this.$refs.audio.duration)
if (this.isCurrent) {
if (this.isCurrent && this.autoplay) {
this.$store.commit('player/duration', this.$refs.audio.duration)
if (this.startTime) {
this.setCurrentTime(this.startTime)
}
this.$store.commit('player/playing', true)
this.$refs.audio.play()
}
......@@ -72,8 +77,9 @@ export default {
if (this.looping === 1) {
this.setCurrentTime(0)
this.$refs.audio.play()
} else {
this.$store.dispatch('player/trackEnded', this.track)
}
this.$store.dispatch('player/trackEnded', this.track)
},
setCurrentTime (t) {
if (t < 0 | t > this.duration) {
......
......@@ -119,7 +119,6 @@ export default {
self.results = response.data
self.nextLink = response.data.next
self.previousLink = response.data.previous
self.$store.commit('favorites/count', response.data.count)
self.results.results.forEach((track) => {
self.$store.commit('favorites/track', {id: track.id, value: true})
})
......
<template>
<button @click="$store.dispatch('favorites/set', {id: track.id, value: !isFavorite})" v-if="button" :class="['ui', 'pink', {'inverted': isFavorite}, {'favorited': isFavorite}, 'button']">
<button @click="$store.dispatch('favorites/toggle', track.id)" v-if="button" :class="['ui', 'pink', {'inverted': isFavorite}, {'favorited': isFavorite}, 'button']">
<i class="heart icon"></i>
<template v-if="isFavorite">
In favorites
......@@ -8,23 +8,16 @@
Add to favorites
</template>
</button>
<i v-else @click="$store.dispatch('favorites/set', {id: track.id, value: !isFavorite})" :class="['favorite-icon', 'heart', {'pink': isFavorite}, {'favorited': isFavorite}, 'link', 'icon']" :title="title"></i>
<i v-else @click="$store.dispatch('favorites/toggle', track.id)" :class="['favorite-icon', 'heart', {'pink': isFavorite}, {'favorited': isFavorite}, 'link', 'icon']" :title="title"></i>
</template>
<script>
import {mapState} from 'vuex'
export default {
props: {
track: {type: Object},
button: {type: Boolean, default: false}
},
computed: {
...mapState({
favorites: state => {
return state.favorites.tracks
}
}),
title () {
if (this.isFavorite) {
return 'Remove from favorites'
......
......@@ -65,7 +65,7 @@ export default {
},
apiSettings: {
beforeXHR: function (xhrObject, s) {
xhrObject.setRequestHeader('Authorization', this.$store.getters['auth/header'])
xhrObject.setRequestHeader('Authorization', self.$store.getters['auth/header'])
return xhrObject
},
onResponse: function (initialResponse) {
......
import Vue from 'vue'
import config from '@/config'
import logger from '@/logging'
import cache from '@/cache'
import router from '@/router'
const LOGIN_URL = config.API_URL + 'token/'
......@@ -45,9 +44,7 @@ export default {
return resource.save({}, credentials).then(response => {
logger.default.info('Successfully logged in as', credentials.username)
commit('token', response.data.token)
cache.set('token', response.data.token)
commit('username', credentials.username)
cache.set('username', credentials.username)
commit('authenticated', true)
dispatch('fetchProfile')
// Redirect to a specified route
......@@ -58,7 +55,6 @@ export default {
})
},
logout ({commit}) {
cache.clear()
commit('authenticated', false)
commit('profile', null)
logger.default.info('Log out, goodbye!')
......@@ -66,8 +62,8 @@ export default {
},
check ({commit, dispatch, state}) {
logger.default.info('Checking authentication...')
var jwt = cache.get('token')
var username = cache.get('username')
var jwt = state.token
var username = state.username
if (jwt) {
commit('authenticated', true)
commit('username', username)
......
......@@ -14,16 +14,16 @@ export default {
mutations: {
track: (state, {id, value}) => {
if (value) {
state.tracks.push(id)
if (state.tracks.indexOf(id) === -1) {
state.tracks.push(id)
}
} else {
let i = state.tracks.indexOf(id)
if (i > -1) {
state.tracks.splice(i, 1)
}
}
},
count: (state, value) => {
state.count = value
state.count = state.tracks.length
}
},
getters: {
......@@ -35,29 +35,25 @@ export default {
set ({commit, state}, {id, value}) {
commit('track', {id, value})
if (value) {
commit('count', state.count + 1)
let resource = Vue.resource(FAVORITES_URL)
resource.save({}, {'track': id}).then((response) => {
logger.default.info('Successfully added track to favorites')
}, (response) => {
logger.default.info('Error while adding track to favorites')
commit('track', {id, value: !value})
commit('count', state.count - 1)
})
} else {
commit('count', state.count - 1)
let resource = Vue.resource(REMOVE_URL)
resource.delete({}, {'track': id}).then((response) => {
logger.default.info('Successfully removed track from favorites')
}, (response) => {
logger.default.info('Error while removing track from favorites')
commit('track', {id, value: !value})
commit('count', state.count + 1)
})
}
},
toggle ({getters, dispatch}, id) {
dispatch('set', {id, value: getters['isFavorite'](id)})
dispatch('set', {id, value: !getters['isFavorite'](id)})
},
fetch ({dispatch, state, commit}, url) {
// will fetch favorites by batches from API to have them locally
......@@ -68,7 +64,6 @@ export default {
response.data.results.forEach(result => {
commit('track', {id: result.track, value: true})
})
commit('count', state.tracks.length)
if (response.data.next) {
dispatch('fetch', response.data.next)
}
......
import Vue from 'vue'
import Vuex from 'vuex'
import createPersistedState from 'vuex-persistedstate'
import favorites from './favorites'
import auth from './auth'
......@@ -16,5 +17,84 @@ export default new Vuex.Store({
queue,
radios,
player
}
},
plugins: [
createPersistedState({
key: 'auth',
paths: ['auth'],
filter: (mutation) => {
return mutation.type.startsWith('auth/')
}
}),
createPersistedState({
key: 'radios',
paths: ['radios'],
filter: (mutation) => {
return mutation.type.startsWith('radios/')
}
}),
createPersistedState({
key: 'player',
paths: [
'player.looping',
'player.playing',
'player.volume',
'player.duration',
'player.errored'],
filter: (mutation) => {
return mutation.type.startsWith('player/') && mutation.type !== 'player/currentTime'
}
}),
createPersistedState({
key: 'progress',
paths: ['player.currentTime'],
filter: (mutation) => {
let delay = 10
return mutation.type === 'player/currentTime' && parseInt(mutation.payload) % delay === 0
},
reducer: (state) => {
return {
player: {
currentTime: state.player.currentTime
}
}
}
}),
createPersistedState({
key: 'queue',
filter: (mutation) => {
return mutation.type.startsWith('queue/')
},
reducer: (state) => {
return {
queue: {
currentIndex: state.queue.currentIndex,
tracks: state.queue.tracks.map(track => {
// we keep only valuable fields to make the cache lighter and avoid
// cyclic value serialization errors
let artist = {
id: track.artist.id,
mbid: track.artist.mbid,
name: track.artist.name
}
return {
id: track.id,
title: track.title,
mbid: track.mbid,
album: {
id: track.album.id,
title: track.album.title,
mbid: track.album.mbid,
cover: track.album.cover,
artist: artist
},
artist: artist,
files: track.files
}
})
}
}
}
})
]
})
......@@ -111,11 +111,6 @@ export default {
}
},
next ({state, dispatch, commit, rootState}) {
if (rootState.player.looping === 1) {
// we loop on the same track, this is handled directly on the track
// component, so we do nothing.
return logger.default.info('Looping on the same track')
}
if (rootState.player.looping === 2 && state.currentIndex >= state.tracks.length - 1) {
logger.default.info('Going back to the beginning of the queue')
return dispatch('currentIndex', 0)
......@@ -130,6 +125,8 @@ export default {
},
currentIndex ({commit, state, rootState, dispatch}, index) {
commit('ended', false)
commit('player/currentTime', 0, {root: true})
commit('player/playing', true, {root: true})
commit('player/errored', false, {root: true})
commit('currentIndex', index)
if (state.tracks.length - index <= 2 && rootState.radios.running) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment