mirror of
https://github.com/koel/koel
synced 2024-11-10 06:34:14 +00:00
Lint everything
This commit is contained in:
parent
449fac97fc
commit
adfebd0167
104 changed files with 2770 additions and 2764 deletions
2
.eslintignore
Normal file
2
.eslintignore
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
libs
|
||||||
|
tests
|
6
.eslintrc
Normal file
6
.eslintrc
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"extends": "vue",
|
||||||
|
"rules": {
|
||||||
|
"no-multi-str": "off"
|
||||||
|
}
|
||||||
|
}
|
|
@ -50,13 +50,16 @@
|
||||||
"browserify-hmr": "^0.3.1",
|
"browserify-hmr": "^0.3.1",
|
||||||
"chai": "^3.4.1",
|
"chai": "^3.4.1",
|
||||||
"chalk": "^1.1.3",
|
"chalk": "^1.1.3",
|
||||||
|
"eslint": "^3.10.2",
|
||||||
|
"eslint-config-vue": "^2.0.1",
|
||||||
|
"eslint-plugin-vue": "^1.0.0",
|
||||||
"jsdom": "^9.2.1",
|
"jsdom": "^9.2.1",
|
||||||
"mocha": "^2.3.4",
|
"mocha": "^2.3.4",
|
||||||
"sinon": "^1.17.2"
|
"sinon": "^1.17.2"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"postinstall": "cross-env NODE_ENV=production && gulp --production",
|
"postinstall": "cross-env NODE_ENV=production && gulp --production",
|
||||||
"test": "mocha --compilers js:babel-register --require resources/assets/js/tests/helper.js resources/assets/js/tests/**/*Test.js",
|
"test": "eslint resources/assets/js --ext=js,vue && mocha --compilers js:babel-register --require resources/assets/js/tests/helper.js resources/assets/js/tests/**/*Test.js",
|
||||||
"e2e": "gulp e2e"
|
"e2e": "gulp e2e"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,77 +23,79 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Vue from 'vue';
|
import Vue from 'vue'
|
||||||
import $ from 'jquery';
|
import $ from 'jquery'
|
||||||
|
|
||||||
import siteHeader from './components/site-header/index.vue';
|
import siteHeader from './components/site-header/index.vue'
|
||||||
import siteFooter from './components/site-footer/index.vue';
|
import siteFooter from './components/site-footer/index.vue'
|
||||||
import mainWrapper from './components/main-wrapper/index.vue';
|
import mainWrapper from './components/main-wrapper/index.vue'
|
||||||
import overlay from './components/shared/overlay.vue';
|
import overlay from './components/shared/overlay.vue'
|
||||||
import loginForm from './components/auth/login-form.vue';
|
import loginForm from './components/auth/login-form.vue'
|
||||||
import editSongsForm from './components/modals/edit-songs-form.vue';
|
import editSongsForm from './components/modals/edit-songs-form.vue'
|
||||||
|
|
||||||
import { event, showOverlay, hideOverlay, forceReloadWindow, url } from './utils';
|
import { event, showOverlay, hideOverlay, forceReloadWindow } from './utils'
|
||||||
import { sharedStore, songStore, userStore, preferenceStore as preferences } from './stores';
|
import { sharedStore, userStore, preferenceStore as preferences } from './stores'
|
||||||
import { playback, ls } from './services';
|
import { playback, ls } from './services'
|
||||||
import { focusDirective, clickawayDirective } from './directives';
|
import { focusDirective, clickawayDirective } from './directives'
|
||||||
import router from './router';
|
import router from './router'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { siteHeader, siteFooter, mainWrapper, overlay, loginForm, editSongsForm },
|
components: { siteHeader, siteFooter, mainWrapper, overlay, loginForm, editSongsForm },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
authenticated: false,
|
authenticated: false
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted () {
|
mounted () {
|
||||||
// The app has just been initialized, check if we can get the user data with an already existing token
|
// The app has just been initialized, check if we can get the user data with an already existing token
|
||||||
const token = ls.get('jwt-token');
|
const token = ls.get('jwt-token')
|
||||||
if (token) {
|
if (token) {
|
||||||
this.authenticated = true;
|
this.authenticated = true
|
||||||
this.init();
|
this.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the element to be the ghost drag image.
|
// Create the element to be the ghost drag image.
|
||||||
$('<div id="dragGhost"></div>').appendTo('body');
|
$('<div id="dragGhost"></div>').appendTo('body')
|
||||||
|
|
||||||
// And the textarea to copy stuff
|
// And the textarea to copy stuff
|
||||||
$('<textarea id="copyArea"></textarea>').appendTo('body');
|
$('<textarea id="copyArea"></textarea>').appendTo('body')
|
||||||
|
|
||||||
// Add an ugly mac/non-mac class for OS-targeting styles.
|
// Add an ugly mac/non-mac class for OS-targeting styles.
|
||||||
// I'm crying inside.
|
// I'm crying inside.
|
||||||
$('html').addClass(navigator.userAgent.indexOf('Mac') !== -1 ? 'mac' : 'non-mac');
|
$('html').addClass(navigator.userAgent.indexOf('Mac') !== -1 ? 'mac' : 'non-mac')
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
init () {
|
init () {
|
||||||
showOverlay();
|
showOverlay()
|
||||||
|
|
||||||
// Make the most important HTTP request to get all necessary data from the server.
|
// Make the most important HTTP request to get all necessary data from the server.
|
||||||
// Afterwards, init all mandatory stores and services.
|
// Afterwards, init all mandatory stores and services.
|
||||||
sharedStore.init().then(() => {
|
sharedStore.init().then(() => {
|
||||||
playback.init();
|
playback.init()
|
||||||
hideOverlay();
|
hideOverlay()
|
||||||
|
|
||||||
// Ask for user's notification permission.
|
// Ask for user's notification permission.
|
||||||
this.requestNotifPermission();
|
this.requestNotifPermission()
|
||||||
|
|
||||||
// To confirm or not to confirm closing, it's a question.
|
// To confirm or not to confirm closing, it's a question.
|
||||||
window.onbeforeunload = e => {
|
window.onbeforeunload = e => {
|
||||||
if (!preferences.confirmClosing) {
|
if (!preferences.confirmClosing) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notice that a custom message like this has ceased to be supported
|
// Notice that a custom message like this has ceased to be supported
|
||||||
// starting from Chrome 51.
|
// starting from Chrome 51.
|
||||||
return 'You asked Koel to confirm before closing, so here it is.';
|
return 'You asked Koel to confirm before closing, so here it is.'
|
||||||
};
|
}
|
||||||
|
|
||||||
// Let all other components know we're ready.
|
// Let all other components know we're ready.
|
||||||
event.emit('koel:ready');
|
event.emit('koel:ready')
|
||||||
}).catch(() => this.authenticated = false);
|
}).catch(() => {
|
||||||
|
this.authenticated = false
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -103,12 +105,12 @@ export default {
|
||||||
*/
|
*/
|
||||||
togglePlayback (e) {
|
togglePlayback (e) {
|
||||||
if ($(e.target).is('input,textarea,button,select')) {
|
if ($(e.target).is('input,textarea,button,select')) {
|
||||||
return true;
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ah... Good ol' jQuery. Whatever play/pause control is there, we blindly click it.
|
// Ah... Good ol' jQuery. Whatever play/pause control is there, we blindly click it.
|
||||||
$('#mainFooter .play:visible, #mainFooter .pause:visible').click();
|
$('#mainFooter .play:visible, #mainFooter .pause:visible').click()
|
||||||
e.preventDefault();
|
e.preventDefault()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -118,11 +120,11 @@ export default {
|
||||||
*/
|
*/
|
||||||
playPrev (e) {
|
playPrev (e) {
|
||||||
if ($(e.target).is('input,textarea')) {
|
if ($(e.target).is('input,textarea')) {
|
||||||
return true;
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
playback.playPrev();
|
playback.playPrev()
|
||||||
e.preventDefault();
|
e.preventDefault()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -132,11 +134,11 @@ export default {
|
||||||
*/
|
*/
|
||||||
playNext (e) {
|
playNext (e) {
|
||||||
if ($(e.target).is('input,textarea')) {
|
if ($(e.target).is('input,textarea')) {
|
||||||
return true;
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
playback.playNext();
|
playback.playNext()
|
||||||
e.preventDefault();
|
e.preventDefault()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -146,25 +148,25 @@ export default {
|
||||||
*/
|
*/
|
||||||
search (e) {
|
search (e) {
|
||||||
if ($(e.target).is('input,textarea') || e.metaKey || e.ctrlKey) {
|
if ($(e.target).is('input,textarea') || e.metaKey || e.ctrlKey) {
|
||||||
return true;
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#searchForm input[type="search"]').focus().select();
|
$('#searchForm input[type="search"]').focus().select()
|
||||||
e.preventDefault();
|
e.preventDefault()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request for notification permission if it's not provided and the user is OK with notifs.
|
* Request for notification permission if it's not provided and the user is OK with notifs.
|
||||||
*/
|
*/
|
||||||
requestNotifPermission () {
|
requestNotifPermission () {
|
||||||
if (window.Notification && preferences.notify && Notification.permission !== 'granted') {
|
if (window.Notification && preferences.notify && window.Notification.permission !== 'granted') {
|
||||||
Notification.requestPermission(result => {
|
window.Notification.requestPermission(result => {
|
||||||
if (result === 'denied') {
|
if (result === 'denied') {
|
||||||
preferences.notify = false;
|
preferences.notify = false
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
created () {
|
created () {
|
||||||
|
@ -173,8 +175,8 @@ export default {
|
||||||
* When the user logs in, set the whole app to be "authenticated" and initialize it.
|
* When the user logs in, set the whole app to be "authenticated" and initialize it.
|
||||||
*/
|
*/
|
||||||
'user:loggedin': () => {
|
'user:loggedin': () => {
|
||||||
this.authenticated = true;
|
this.authenticated = true
|
||||||
this.init();
|
this.init()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -189,20 +191,20 @@ export default {
|
||||||
*/
|
*/
|
||||||
logout () {
|
logout () {
|
||||||
userStore.logout().then((r) => {
|
userStore.logout().then((r) => {
|
||||||
ls.remove('jwt-token');
|
ls.remove('jwt-token')
|
||||||
forceReloadWindow();
|
forceReloadWindow()
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init our basic, custom router on ready to determine app state.
|
* Init our basic, custom router on ready to determine app state.
|
||||||
*/
|
*/
|
||||||
'koel:ready': () => {
|
'koel:ready': () => {
|
||||||
router.init();
|
router.init()
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
// Register our custom key codes
|
// Register our custom key codes
|
||||||
Vue.config.keyCodes = {
|
Vue.config.keyCodes = {
|
||||||
|
@ -213,11 +215,11 @@ Vue.config.keyCodes = {
|
||||||
mediaNext: 176,
|
mediaNext: 176,
|
||||||
mediaPrev: 177,
|
mediaPrev: 177,
|
||||||
mediaToggle: 179
|
mediaToggle: 179
|
||||||
};
|
}
|
||||||
|
|
||||||
// …and the global directives
|
// …and the global directives
|
||||||
Vue.directive('koel-focus', focusDirective);
|
Vue.directive('koel-focus', focusDirective)
|
||||||
Vue.directive('koel-clickaway',clickawayDirective);
|
Vue.directive('koel-clickaway', clickawayDirective)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -7,33 +7,35 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { userStore } from '../../stores';
|
import { userStore } from '../../stores'
|
||||||
import { event } from '../../utils';
|
import { event } from '../../utils'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
email: '',
|
email: '',
|
||||||
password: '',
|
password: '',
|
||||||
failed: false,
|
failed: false
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
login () {
|
login () {
|
||||||
this.failed = false;
|
this.failed = false
|
||||||
|
|
||||||
userStore.login(this.email, this.password).then(() => {
|
userStore.login(this.email, this.password).then(() => {
|
||||||
this.failed = false;
|
this.failed = false
|
||||||
|
|
||||||
// Reset the password so that the next login will have this field empty.
|
// Reset the password so that the next login will have this field empty.
|
||||||
this.password = '';
|
this.password = ''
|
||||||
|
|
||||||
event.emit('user:loggedin');
|
event.emit('user:loggedin')
|
||||||
}).catch(() => this.failed = true);
|
}).catch(() => {
|
||||||
},
|
this.failed = true
|
||||||
},
|
})
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass" scoped>
|
<style lang="sass" scoped>
|
||||||
|
|
|
@ -37,31 +37,31 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { playback } from '../../../services';
|
import { playback } from '../../../services'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['album', 'mode'],
|
props: ['album', 'mode'],
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
showingFullWiki: false,
|
showingFullWiki: false
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
album () {
|
album () {
|
||||||
this.showingFullWiki = false;
|
this.showingFullWiki = false
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
showSummary () {
|
showSummary () {
|
||||||
return this.mode !== 'full' && !this.showingFullWiki;
|
return this.mode !== 'full' && !this.showingFullWiki
|
||||||
},
|
},
|
||||||
|
|
||||||
showFull () {
|
showFull () {
|
||||||
return this.mode === 'full' || this.showingFullWiki;
|
return this.mode === 'full' || this.showingFullWiki
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -69,10 +69,10 @@ export default {
|
||||||
* Shuffle all songs in the current album.
|
* Shuffle all songs in the current album.
|
||||||
*/
|
*/
|
||||||
shuffleAll () {
|
shuffleAll () {
|
||||||
playback.playAllInAlbum(this.album);
|
playback.playAllInAlbum(this.album)
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -29,30 +29,30 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { playback } from '../../../services';
|
import { playback } from '../../../services'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['artist', 'mode'],
|
props: ['artist', 'mode'],
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
showingFullBio: false,
|
showingFullBio: false
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
artist () {
|
artist () {
|
||||||
this.showingFullBio = false;
|
this.showingFullBio = false
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
showSummary () {
|
showSummary () {
|
||||||
return this.mode !== 'full' && !this.showingFullBio;
|
return this.mode !== 'full' && !this.showingFullBio
|
||||||
},
|
},
|
||||||
|
|
||||||
showFull () {
|
showFull () {
|
||||||
return this.mode === 'full' || this.showingFullBio;
|
return this.mode === 'full' || this.showingFullBio
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -61,10 +61,10 @@ export default {
|
||||||
* Shuffle all songs performed by the current song's artist.
|
* Shuffle all songs performed by the current song's artist.
|
||||||
*/
|
*/
|
||||||
shuffleAll () {
|
shuffleAll () {
|
||||||
playback.playAllByArtist(this.artist);
|
playback.playAllByArtist(this.artist)
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -35,18 +35,17 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import isMobile from 'ismobilejs';
|
import isMobile from 'ismobilejs'
|
||||||
import { invokeMap } from 'lodash';
|
import $ from 'jquery'
|
||||||
import $ from 'jquery';
|
|
||||||
|
|
||||||
import { event } from '../../../utils';
|
import { event } from '../../../utils'
|
||||||
import { sharedStore, songStore, preferenceStore as preferences } from '../../../stores';
|
import { sharedStore, songStore, preferenceStore as preferences } from '../../../stores'
|
||||||
import { songInfo } from '../../../services';
|
import { songInfo } from '../../../services'
|
||||||
|
|
||||||
import lyrics from './lyrics.vue';
|
import lyrics from './lyrics.vue'
|
||||||
import artistInfo from './artist-info.vue';
|
import artistInfo from './artist-info.vue'
|
||||||
import albumInfo from './album-info.vue';
|
import albumInfo from './album-info.vue'
|
||||||
import youtube from './youtube.vue';
|
import youtube from './youtube.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'main-wrapper--extra--index',
|
name: 'main-wrapper--extra--index',
|
||||||
|
@ -57,8 +56,8 @@ export default {
|
||||||
song: songStore.stub,
|
song: songStore.stub,
|
||||||
state: preferences.state,
|
state: preferences.state,
|
||||||
sharedState: sharedStore.state,
|
sharedState: sharedStore.state,
|
||||||
currentView: 'lyrics',
|
currentView: 'lyrics'
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -67,25 +66,25 @@ export default {
|
||||||
* to/from the html tag.
|
* to/from the html tag.
|
||||||
* Some element's CSS can then be controlled based on this class.
|
* Some element's CSS can then be controlled based on this class.
|
||||||
*/
|
*/
|
||||||
'state.showExtraPanel': function (newVal) {
|
'state.showExtraPanel' (newVal) {
|
||||||
if (newVal && !isMobile.any) {
|
if (newVal && !isMobile.any) {
|
||||||
$('html').addClass('with-extra-panel');
|
$('html').addClass('with-extra-panel')
|
||||||
} else {
|
} else {
|
||||||
$('html').removeClass('with-extra-panel');
|
$('html').removeClass('with-extra-panel')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted () {
|
mounted () {
|
||||||
// On ready, add 'with-extra-panel' class.
|
// On ready, add 'with-extra-panel' class.
|
||||||
if (!isMobile.any) {
|
if (!isMobile.any) {
|
||||||
$('html').addClass('with-extra-panel');
|
$('html').addClass('with-extra-panel')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMobile.phone) {
|
if (isMobile.phone) {
|
||||||
// On a mobile device, we always hide the panel initially regardless of
|
// On a mobile device, we always hide the panel initially regardless of
|
||||||
// the saved preference.
|
// the saved preference.
|
||||||
preferences.showExtraPanel = false;
|
preferences.showExtraPanel = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -94,9 +93,9 @@ export default {
|
||||||
* Reset all self and applicable child components' states.
|
* Reset all self and applicable child components' states.
|
||||||
*/
|
*/
|
||||||
resetState () {
|
resetState () {
|
||||||
this.currentView = 'lyrics';
|
this.currentView = 'lyrics'
|
||||||
this.song = songStore.stub;
|
this.song = songStore.stub
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
created () {
|
created () {
|
||||||
|
@ -104,20 +103,20 @@ export default {
|
||||||
'main-content-view:load': view => {
|
'main-content-view:load': view => {
|
||||||
// Hide the panel away if a main view is triggered on mobile.
|
// Hide the panel away if a main view is triggered on mobile.
|
||||||
if (isMobile.phone) {
|
if (isMobile.phone) {
|
||||||
preferences.showExtraPanel = false;
|
preferences.showExtraPanel = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
'song:played': song => {
|
'song:played': song => {
|
||||||
songInfo.fetch(song).then(song => {
|
songInfo.fetch(song).then(song => {
|
||||||
this.song = song;
|
this.song = song
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
'koel:teardown': () => this.resetState(),
|
'koel:teardown': () => this.resetState()
|
||||||
});
|
})
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: ['song'],
|
props: ['song']
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -18,8 +18,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { event } from '../../../utils';
|
import { youtube as youtubeService } from '../../../services'
|
||||||
import { youtube as youtubeService } from '../../../services';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'main-wrapper--extra--youtube',
|
name: 'main-wrapper--extra--youtube',
|
||||||
|
@ -28,33 +27,33 @@ export default {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
videos: [],
|
videos: []
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
song (val) {
|
song (val) {
|
||||||
this.videos = val.youtube ? val.youtube.items : [];
|
this.videos = val.youtube ? val.youtube.items : []
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
playYouTube (id) {
|
playYouTube (id) {
|
||||||
youtubeService.play(id);
|
youtubeService.play(id)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load more videos.
|
* Load more videos.
|
||||||
*/
|
*/
|
||||||
loadMore () {
|
loadMore () {
|
||||||
this.loading = true;
|
this.loading = true
|
||||||
youtubeService.searchVideosRelatedToSong(this.song, () => {
|
youtubeService.searchVideosRelatedToSong(this.song, () => {
|
||||||
this.videos = this.song.youtube.items;
|
this.videos = this.song.youtube.items
|
||||||
this.loading = false;
|
this.loading = false
|
||||||
});
|
})
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass" scoped>
|
<style lang="sass" scoped>
|
||||||
|
|
|
@ -7,13 +7,13 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import sidebar from './sidebar/index.vue';
|
import sidebar from './sidebar/index.vue'
|
||||||
import mainContent from './main-content/index.vue';
|
import mainContent from './main-content/index.vue'
|
||||||
import extra from './extra/index.vue';
|
import extra from './extra/index.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { sidebar, mainContent, extra },
|
components: { sidebar, mainContent, extra }
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -46,15 +46,13 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import isMobile from 'ismobilejs';
|
import { pluralize, event } from '../../../utils'
|
||||||
|
import { albumStore, artistStore, sharedStore } from '../../../stores'
|
||||||
import { pluralize, event } from '../../../utils';
|
import { playback, download, albumInfo as albumInfoService } from '../../../services'
|
||||||
import { albumStore, artistStore, sharedStore } from '../../../stores';
|
import router from '../../../router'
|
||||||
import { playback, download, albumInfo as albumInfoService } from '../../../services';
|
import hasSongList from '../../../mixins/has-song-list'
|
||||||
import router from '../../../router';
|
import albumInfo from '../extra/album-info.vue'
|
||||||
import hasSongList from '../../../mixins/has-song-list';
|
import soundBar from '../../shared/sound-bar.vue'
|
||||||
import albumInfo from '../extra/album-info.vue';
|
|
||||||
import soundBar from '../../shared/sound-bar.vue';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'main-wrapper--main-content--album',
|
name: 'main-wrapper--main-content--album',
|
||||||
|
@ -68,16 +66,16 @@ export default {
|
||||||
album: albumStore.stub,
|
album: albumStore.stub,
|
||||||
info: {
|
info: {
|
||||||
showing: false,
|
showing: false,
|
||||||
loading: true,
|
loading: true
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
isNormalArtist () {
|
isNormalArtist () {
|
||||||
return !artistStore.isVariousArtists(this.album.artist)
|
return !artistStore.isVariousArtists(this.album.artist) &&
|
||||||
&& !artistStore.isUnknownArtist(this.album.artist);
|
!artistStore.isUnknownArtist(this.album.artist)
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -89,9 +87,9 @@ export default {
|
||||||
*/
|
*/
|
||||||
'album.songs.length' (newVal) {
|
'album.songs.length' (newVal) {
|
||||||
if (!newVal) {
|
if (!newVal) {
|
||||||
router.go('albums');
|
router.go('albums')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
created () {
|
created () {
|
||||||
|
@ -104,10 +102,10 @@ export default {
|
||||||
*/
|
*/
|
||||||
event.on('main-content-view:load', (view, album) => {
|
event.on('main-content-view:load', (view, album) => {
|
||||||
if (view === 'album') {
|
if (view === 'album') {
|
||||||
this.info.showing = false;
|
this.info.showing = false
|
||||||
this.album = album;
|
this.album = album
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -115,27 +113,29 @@ export default {
|
||||||
* Shuffle the songs in the current album.
|
* Shuffle the songs in the current album.
|
||||||
*/
|
*/
|
||||||
shuffle () {
|
shuffle () {
|
||||||
playback.queueAndPlay(this.album.songs, true);
|
playback.queueAndPlay(this.album.songs, true)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Download all songs from the album.
|
* Download all songs from the album.
|
||||||
*/
|
*/
|
||||||
download () {
|
download () {
|
||||||
download.fromAlbum(this.album);
|
download.fromAlbum(this.album)
|
||||||
},
|
},
|
||||||
|
|
||||||
showInfo () {
|
showInfo () {
|
||||||
this.info.showing = true;
|
this.info.showing = true
|
||||||
if (!this.album.info) {
|
if (!this.album.info) {
|
||||||
this.info.loading = true;
|
this.info.loading = true
|
||||||
albumInfoService.fetch(this.album).then(() => this.info.loading = false);
|
albumInfoService.fetch(this.album).then(() => {
|
||||||
|
this.info.loading = false
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
this.info.loading = false;
|
this.info.loading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass" scoped>
|
<style lang="sass" scoped>
|
||||||
|
|
|
@ -14,11 +14,11 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { filterBy, limitBy, event } from '../../../utils';
|
import { filterBy, limitBy, event } from '../../../utils'
|
||||||
import { albumStore } from '../../../stores';
|
import { albumStore } from '../../../stores'
|
||||||
import albumItem from '../../shared/album-item.vue';
|
import albumItem from '../../shared/album-item.vue'
|
||||||
import viewModeSwitch from '../../shared/view-mode-switch.vue';
|
import viewModeSwitch from '../../shared/view-mode-switch.vue'
|
||||||
import infiniteScroll from '../../../mixins/infinite-scroll';
|
import infiniteScroll from '../../../mixins/infinite-scroll'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [infiniteScroll],
|
mixins: [infiniteScroll],
|
||||||
|
@ -29,8 +29,8 @@ export default {
|
||||||
perPage: 9,
|
perPage: 9,
|
||||||
numOfItems: 9,
|
numOfItems: 9,
|
||||||
q: '',
|
q: '',
|
||||||
viewMode: null,
|
viewMode: null
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -38,14 +38,14 @@ export default {
|
||||||
return limitBy(
|
return limitBy(
|
||||||
filterBy(albumStore.all, this.q, 'name', 'artist.name'),
|
filterBy(albumStore.all, this.q, 'name', 'artist.name'),
|
||||||
this.numOfItems
|
this.numOfItems
|
||||||
);
|
)
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
changeViewMode (mode) {
|
changeViewMode (mode) {
|
||||||
this.viewMode = mode;
|
this.viewMode = mode
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
created () {
|
created () {
|
||||||
|
@ -56,14 +56,16 @@ export default {
|
||||||
'koel:ready': () => this.displayMore(),
|
'koel:ready': () => this.displayMore(),
|
||||||
|
|
||||||
'koel:teardown': () => {
|
'koel:teardown': () => {
|
||||||
this.q = '';
|
this.q = ''
|
||||||
this.numOfItems = 9;
|
this.numOfItems = 9
|
||||||
},
|
},
|
||||||
|
|
||||||
'filter:changed': q => this.q = q,
|
'filter:changed': q => {
|
||||||
});
|
this.q = q
|
||||||
},
|
}
|
||||||
};
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -45,15 +45,13 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import isMobile from 'ismobilejs';
|
import { pluralize, event } from '../../../utils'
|
||||||
|
import { sharedStore, artistStore } from '../../../stores'
|
||||||
import { pluralize, event } from '../../../utils';
|
import { playback, download, artistInfo as artistInfoService } from '../../../services'
|
||||||
import { sharedStore, artistStore } from '../../../stores';
|
import router from '../../../router'
|
||||||
import { playback, download, artistInfo as artistInfoService } from '../../../services';
|
import hasSongList from '../../../mixins/has-song-list'
|
||||||
import router from '../../../router';
|
import artistInfo from '../extra/artist-info.vue'
|
||||||
import hasSongList from '../../../mixins/has-song-list';
|
import soundBar from '../../shared/sound-bar.vue'
|
||||||
import artistInfo from '../extra/artist-info.vue';
|
|
||||||
import soundBar from '../../shared/sound-bar.vue';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'main-wrapper--main-content--artist',
|
name: 'main-wrapper--main-content--artist',
|
||||||
|
@ -67,9 +65,9 @@ export default {
|
||||||
artist: artistStore.stub,
|
artist: artistStore.stub,
|
||||||
info: {
|
info: {
|
||||||
showing: false,
|
showing: false,
|
||||||
loading: true,
|
loading: true
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -81,9 +79,9 @@ export default {
|
||||||
*/
|
*/
|
||||||
'artist.albums.length' (newVal) {
|
'artist.albums.length' (newVal) {
|
||||||
if (!newVal) {
|
if (!newVal) {
|
||||||
router.go('artists');
|
router.go('artists')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
created () {
|
created () {
|
||||||
|
@ -96,10 +94,10 @@ export default {
|
||||||
*/
|
*/
|
||||||
event.on('main-content-view:load', (view, artist) => {
|
event.on('main-content-view:load', (view, artist) => {
|
||||||
if (view === 'artist') {
|
if (view === 'artist') {
|
||||||
this.info.showing = false;
|
this.info.showing = false
|
||||||
this.artist = artist;
|
this.artist = artist
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -107,27 +105,29 @@ export default {
|
||||||
* Shuffle the songs by the current artist.
|
* Shuffle the songs by the current artist.
|
||||||
*/
|
*/
|
||||||
shuffle () {
|
shuffle () {
|
||||||
playback.queueAndPlay(this.artist.songs, true);
|
playback.queueAndPlay(this.artist.songs, true)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Download all songs by the artist.
|
* Download all songs by the artist.
|
||||||
*/
|
*/
|
||||||
download () {
|
download () {
|
||||||
download.fromArtist(this.artist);
|
download.fromArtist(this.artist)
|
||||||
},
|
},
|
||||||
|
|
||||||
showInfo () {
|
showInfo () {
|
||||||
this.info.showing = true;
|
this.info.showing = true
|
||||||
if (!this.artist.info) {
|
if (!this.artist.info) {
|
||||||
this.info.loading = true;
|
this.info.loading = true
|
||||||
artistInfoService.fetch(this.artist).then(() => this.info.loading = false);
|
artistInfoService.fetch(this.artist).then(() => {
|
||||||
|
this.info.loading = false
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
this.info.loading = false;
|
this.info.loading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass" scoped>
|
<style lang="sass" scoped>
|
||||||
|
|
|
@ -14,12 +14,12 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { filterBy, limitBy, event } from '../../../utils';
|
import { filterBy, limitBy, event } from '../../../utils'
|
||||||
import { artistStore } from '../../../stores';
|
import { artistStore } from '../../../stores'
|
||||||
|
|
||||||
import artistItem from '../../shared/artist-item.vue';
|
import artistItem from '../../shared/artist-item.vue'
|
||||||
import viewModeSwitch from '../../shared/view-mode-switch.vue';
|
import viewModeSwitch from '../../shared/view-mode-switch.vue'
|
||||||
import infiniteScroll from '../../../mixins/infinite-scroll';
|
import infiniteScroll from '../../../mixins/infinite-scroll'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [infiniteScroll],
|
mixins: [infiniteScroll],
|
||||||
|
@ -31,8 +31,8 @@ export default {
|
||||||
perPage: 9,
|
perPage: 9,
|
||||||
numOfItems: 9,
|
numOfItems: 9,
|
||||||
q: '',
|
q: '',
|
||||||
viewMode: null,
|
viewMode: null
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -40,14 +40,14 @@ export default {
|
||||||
return limitBy(
|
return limitBy(
|
||||||
filterBy(artistStore.all, this.q, 'name'),
|
filterBy(artistStore.all, this.q, 'name'),
|
||||||
this.numOfItems
|
this.numOfItems
|
||||||
);
|
)
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
changeViewMode (mode) {
|
changeViewMode (mode) {
|
||||||
this.viewMode = mode;
|
this.viewMode = mode
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
created () {
|
created () {
|
||||||
|
@ -58,14 +58,16 @@ export default {
|
||||||
'koel:ready': () => this.displayMore(),
|
'koel:ready': () => this.displayMore(),
|
||||||
|
|
||||||
'koel:teardown': () => {
|
'koel:teardown': () => {
|
||||||
this.q = '';
|
this.q = ''
|
||||||
this.numOfItems = 9;
|
this.numOfItems = 9
|
||||||
},
|
},
|
||||||
|
|
||||||
'filter:changed': q => this.q = q,
|
'filter:changed': q => {
|
||||||
});
|
this.q = q
|
||||||
},
|
}
|
||||||
};
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -35,12 +35,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import isMobile from 'ismobilejs';
|
import { pluralize } from '../../../utils'
|
||||||
|
import { favoriteStore, sharedStore } from '../../../stores'
|
||||||
import { pluralize } from '../../../utils';
|
import { download } from '../../../services'
|
||||||
import { favoriteStore, sharedStore } from '../../../stores';
|
import hasSongList from '../../../mixins/has-song-list'
|
||||||
import { playback, download } from '../../../services';
|
|
||||||
import hasSongList from '../../../mixins/has-song-list';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'main-wrapper--main-content--favorites',
|
name: 'main-wrapper--main-content--favorites',
|
||||||
|
@ -50,8 +48,8 @@ export default {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
state: favoriteStore.state,
|
state: favoriteStore.state,
|
||||||
sharedState: sharedStore.state,
|
sharedState: sharedStore.state
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -59,10 +57,10 @@ export default {
|
||||||
* Download all favorite songs.
|
* Download all favorite songs.
|
||||||
*/
|
*/
|
||||||
download () {
|
download () {
|
||||||
download.fromFavorites();
|
download.fromFavorites()
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -74,15 +74,15 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { sample } from 'lodash';
|
import { sample } from 'lodash'
|
||||||
|
|
||||||
import { event } from '../../../utils';
|
import { event } from '../../../utils'
|
||||||
import { songStore, albumStore, artistStore, userStore, preferenceStore } from '../../../stores';
|
import { songStore, albumStore, artistStore, userStore, preferenceStore } from '../../../stores'
|
||||||
import infiniteScroll from '../../../mixins/infinite-scroll';
|
import infiniteScroll from '../../../mixins/infinite-scroll'
|
||||||
|
|
||||||
import albumItem from '../../shared/album-item.vue';
|
import albumItem from '../../shared/album-item.vue'
|
||||||
import artistItem from '../../shared/artist-item.vue';
|
import artistItem from '../../shared/artist-item.vue'
|
||||||
import songItem from '../../shared/home-song-item.vue';
|
import songItem from '../../shared/home-song-item.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { albumItem, artistItem, songItem },
|
components: { albumItem, artistItem, songItem },
|
||||||
|
@ -103,31 +103,31 @@ export default {
|
||||||
'Sup, %s?',
|
'Sup, %s?',
|
||||||
'How’s life, %s?',
|
'How’s life, %s?',
|
||||||
'How’s your day, %s?',
|
'How’s your day, %s?',
|
||||||
'How have you been, %s?',
|
'How have you been, %s?'
|
||||||
],
|
],
|
||||||
recentSongs: [],
|
recentSongs: [],
|
||||||
top: {
|
top: {
|
||||||
songs: [],
|
songs: [],
|
||||||
albums: [],
|
albums: [],
|
||||||
artists: [],
|
artists: []
|
||||||
},
|
},
|
||||||
recentlyAdded: {
|
recentlyAdded: {
|
||||||
albums: [],
|
albums: [],
|
||||||
songs: [],
|
songs: []
|
||||||
},
|
},
|
||||||
|
|
||||||
preferences: preferenceStore.state,
|
preferences: preferenceStore.state
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
greeting () {
|
greeting () {
|
||||||
return sample(this.greetings).replace('%s', userStore.current.name);
|
return sample(this.greetings).replace('%s', userStore.current.name)
|
||||||
},
|
},
|
||||||
|
|
||||||
showRecentlyAddedSection () {
|
showRecentlyAddedSection () {
|
||||||
return this.recentlyAdded.albums.length || this.recentlyAdded.songs.length;
|
return this.recentlyAdded.albums.length || this.recentlyAdded.songs.length
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -135,23 +135,23 @@ export default {
|
||||||
* Refresh the dashboard with latest data.
|
* Refresh the dashboard with latest data.
|
||||||
*/
|
*/
|
||||||
refreshDashboard () {
|
refreshDashboard () {
|
||||||
this.top.songs = songStore.getMostPlayed(7);
|
this.top.songs = songStore.getMostPlayed(7)
|
||||||
this.top.albums = albumStore.getMostPlayed(6);
|
this.top.albums = albumStore.getMostPlayed(6)
|
||||||
this.top.artists = artistStore.getMostPlayed(6);
|
this.top.artists = artistStore.getMostPlayed(6)
|
||||||
this.recentlyAdded.albums = albumStore.getRecentlyAdded(6);
|
this.recentlyAdded.albums = albumStore.getRecentlyAdded(6)
|
||||||
this.recentlyAdded.songs = songStore.getRecentlyAdded(10);
|
this.recentlyAdded.songs = songStore.getRecentlyAdded(10)
|
||||||
this.recentSongs = songStore.getRecent(7);
|
this.recentSongs = songStore.getRecent(7)
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
created () {
|
created () {
|
||||||
event.on({
|
event.on({
|
||||||
'koel:ready': () => this.refreshDashboard(),
|
'koel:ready': () => this.refreshDashboard(),
|
||||||
|
|
||||||
'song:played': () => this.refreshDashboard(),
|
'song:played': () => this.refreshDashboard()
|
||||||
});
|
})
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -18,22 +18,22 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { event } from '../../../utils';
|
import { event } from '../../../utils'
|
||||||
import { albumStore, sharedStore } from '../../../stores';
|
import { albumStore, sharedStore } from '../../../stores'
|
||||||
|
|
||||||
import albums from './albums.vue';
|
import albums from './albums.vue'
|
||||||
import album from './album.vue';
|
import album from './album.vue'
|
||||||
import artists from './artists.vue';
|
import artists from './artists.vue'
|
||||||
import artist from './artist.vue';
|
import artist from './artist.vue'
|
||||||
import songs from './songs.vue';
|
import songs from './songs.vue'
|
||||||
import settings from './settings.vue';
|
import settings from './settings.vue'
|
||||||
import users from './users.vue';
|
import users from './users.vue'
|
||||||
import queue from './queue.vue';
|
import queue from './queue.vue'
|
||||||
import home from './home.vue';
|
import home from './home.vue'
|
||||||
import playlist from './playlist.vue';
|
import playlist from './playlist.vue'
|
||||||
import favorites from './favorites.vue';
|
import favorites from './favorites.vue'
|
||||||
import profile from './profile.vue';
|
import profile from './profile.vue'
|
||||||
import youtubePlayer from './youtube-player.vue';
|
import youtubePlayer from './youtube-player.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { albums, album, artists, artist, songs, settings,
|
components: { albums, album, artists, artist, songs, settings,
|
||||||
|
@ -43,13 +43,15 @@ export default {
|
||||||
return {
|
return {
|
||||||
view: 'home', // The default view
|
view: 'home', // The default view
|
||||||
albumCover: null,
|
albumCover: null,
|
||||||
sharedState: sharedStore.state,
|
sharedState: sharedStore.state
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
created () {
|
created () {
|
||||||
event.on({
|
event.on({
|
||||||
'main-content-view:load': view => this.view = view,
|
'main-content-view:load': view => {
|
||||||
|
this.view = view
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When a new song is played, find its cover for the translucent effect.
|
* When a new song is played, find its cover for the translucent effect.
|
||||||
|
@ -59,11 +61,11 @@ export default {
|
||||||
* @return {Boolean}
|
* @return {Boolean}
|
||||||
*/
|
*/
|
||||||
'song:played': song => {
|
'song:played': song => {
|
||||||
this.albumCover = song.album.cover === albumStore.stub.cover ? null : song.album.cover;
|
this.albumCover = song.album.cover === albumStore.stub.cover ? null : song.album.cover
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -37,14 +37,13 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import isMobile from 'ismobilejs';
|
import swal from 'sweetalert'
|
||||||
import swal from 'sweetalert';
|
|
||||||
|
|
||||||
import { pluralize, event } from '../../../utils';
|
import { pluralize, event } from '../../../utils'
|
||||||
import { playlistStore, sharedStore } from '../../../stores';
|
import { playlistStore, sharedStore } from '../../../stores'
|
||||||
import { playback, download } from '../../../services';
|
import { playback, download } from '../../../services'
|
||||||
import router from '../../../router';
|
import router from '../../../router'
|
||||||
import hasSongList from '../../../mixins/has-song-list';
|
import hasSongList from '../../../mixins/has-song-list'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'main-wrapper--main-content--playlist',
|
name: 'main-wrapper--main-content--playlist',
|
||||||
|
@ -56,9 +55,9 @@ export default {
|
||||||
playlist: playlistStore.stub,
|
playlist: playlistStore.stub,
|
||||||
sharedState: sharedStore.state,
|
sharedState: sharedStore.state,
|
||||||
songListControlConfig: {
|
songListControlConfig: {
|
||||||
deletePlaylist: true,
|
deletePlaylist: true
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
created () {
|
created () {
|
||||||
|
@ -71,9 +70,9 @@ export default {
|
||||||
*/
|
*/
|
||||||
event.on('main-content-view:load', (view, playlist) => {
|
event.on('main-content-view:load', (view, playlist) => {
|
||||||
if (view === 'playlist') {
|
if (view === 'playlist') {
|
||||||
this.playlist = playlist;
|
this.playlist = playlist
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -82,7 +81,7 @@ export default {
|
||||||
* Overriding the mixin.
|
* Overriding the mixin.
|
||||||
*/
|
*/
|
||||||
shuffleAll () {
|
shuffleAll () {
|
||||||
playback.queueAndPlay(this.playlist.songs, true);
|
playback.queueAndPlay(this.playlist.songs, true)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -91,9 +90,8 @@ export default {
|
||||||
confirmDelete () {
|
confirmDelete () {
|
||||||
// If the playlist is empty, just go ahead and delete it.
|
// If the playlist is empty, just go ahead and delete it.
|
||||||
if (!this.playlist.songs.length) {
|
if (!this.playlist.songs.length) {
|
||||||
this.del();
|
this.del()
|
||||||
|
return
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
swal({
|
swal({
|
||||||
|
@ -101,8 +99,8 @@ export default {
|
||||||
text: 'Once it’s gone, it’s gone, and there’s no turning back.',
|
text: 'Once it’s gone, it’s gone, and there’s no turning back.',
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
showCancelButton: true,
|
showCancelButton: true,
|
||||||
confirmButtonText: 'Yes, go ahead',
|
confirmButtonText: 'Yes, go ahead'
|
||||||
}, this.del);
|
}, this.del)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -112,21 +110,21 @@ export default {
|
||||||
playlistStore.delete(this.playlist).then(() => {
|
playlistStore.delete(this.playlist).then(() => {
|
||||||
// Reset the current playlist to our stub, so that we don't encounter
|
// Reset the current playlist to our stub, so that we don't encounter
|
||||||
// any property reference error.
|
// any property reference error.
|
||||||
this.playlist = playlistStore.stub;
|
this.playlist = playlistStore.stub
|
||||||
|
|
||||||
// Switch back to Home screen
|
// Switch back to Home screen
|
||||||
router.go('home');
|
router.go('home')
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Download all songs in the current playlist.
|
* Download all songs in the current playlist.
|
||||||
*/
|
*/
|
||||||
download () {
|
download () {
|
||||||
return download.fromPlaylist(this.playlist);
|
return download.fromPlaylist(this.playlist)
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -104,12 +104,12 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import $ from 'jquery';
|
import $ from 'jquery'
|
||||||
import swal from 'sweetalert';
|
import swal from 'sweetalert'
|
||||||
|
|
||||||
import { userStore, preferenceStore, sharedStore } from '../../../stores';
|
import { userStore, preferenceStore, sharedStore } from '../../../stores'
|
||||||
import { forceReloadWindow } from '../../../utils';
|
import { forceReloadWindow } from '../../../utils'
|
||||||
import { http, ls } from '../../../services';
|
import { http, ls } from '../../../services'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data () {
|
data () {
|
||||||
|
@ -119,8 +119,8 @@ export default {
|
||||||
pwd: '',
|
pwd: '',
|
||||||
confirmPwd: '',
|
confirmPwd: '',
|
||||||
prefs: preferenceStore.state,
|
prefs: preferenceStore.state,
|
||||||
sharedState: sharedStore.state,
|
sharedState: sharedStore.state
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -130,31 +130,30 @@ export default {
|
||||||
update () {
|
update () {
|
||||||
// A little validation put in a small place.
|
// A little validation put in a small place.
|
||||||
if ((this.pwd || this.confirmPwd) && this.pwd !== this.confirmPwd) {
|
if ((this.pwd || this.confirmPwd) && this.pwd !== this.confirmPwd) {
|
||||||
$('#inputProfilePassword, #inputProfileConfirmPassword').addClass('error');
|
$('#inputProfilePassword, #inputProfileConfirmPassword').addClass('error')
|
||||||
|
return
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#inputProfilePassword, #inputProfileConfirmPassword').removeClass('error');
|
$('#inputProfilePassword, #inputProfileConfirmPassword').removeClass('error')
|
||||||
|
|
||||||
userStore.updateProfile(this.pwd).then(() => {
|
userStore.updateProfile(this.pwd).then(() => {
|
||||||
this.pwd = '';
|
this.pwd = ''
|
||||||
this.confirmPwd = '';
|
this.confirmPwd = ''
|
||||||
|
|
||||||
swal({
|
swal({
|
||||||
title: 'Done!',
|
title: 'Done!',
|
||||||
text: 'Profile saved.',
|
text: 'Profile saved.',
|
||||||
type: 'success',
|
type: 'success',
|
||||||
allowOutsideClick: true,
|
allowOutsideClick: true
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save the current user's preference.
|
* Save the current user's preference.
|
||||||
*/
|
*/
|
||||||
savePreference () {
|
savePreference () {
|
||||||
this.$nextTick(() => preferenceStore.save());
|
this.$nextTick(() => preferenceStore.save())
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -167,7 +166,7 @@ export default {
|
||||||
`/api/lastfm/connect?jwt-token=${ls.get('jwt-token')}`,
|
`/api/lastfm/connect?jwt-token=${ls.get('jwt-token')}`,
|
||||||
'_blank',
|
'_blank',
|
||||||
'toolbar=no,titlebar=no,location=no,width=1024,height=640'
|
'toolbar=no,titlebar=no,location=no,width=1024,height=640'
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -181,10 +180,10 @@ export default {
|
||||||
// - Nope. Users should be grown-ass adults who take responsibilty of their actions.
|
// - Nope. Users should be grown-ass adults who take responsibilty of their actions.
|
||||||
// But one of my users is my new born kid!
|
// But one of my users is my new born kid!
|
||||||
// - Then? Kids will fuck things up anyway.
|
// - Then? Kids will fuck things up anyway.
|
||||||
http.delete('lastfm/disconnect', {}, forceReloadWindow);
|
http.delete('lastfm/disconnect', {}, forceReloadWindow)
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -34,10 +34,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { pluralize } from '../../../utils';
|
import { pluralize } from '../../../utils'
|
||||||
import { queueStore, songStore } from '../../../stores';
|
import { queueStore, songStore } from '../../../stores'
|
||||||
import { playback } from '../../../services';
|
import { playback } from '../../../services'
|
||||||
import hasSongList from '../../../mixins/has-song-list';
|
import hasSongList from '../../../mixins/has-song-list'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'main-wrapper--main-content--queue',
|
name: 'main-wrapper--main-content--queue',
|
||||||
|
@ -48,9 +48,9 @@ export default {
|
||||||
return {
|
return {
|
||||||
state: queueStore.state,
|
state: queueStore.state,
|
||||||
songListControlConfig: {
|
songListControlConfig: {
|
||||||
clearQueue: true,
|
clearQueue: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -58,8 +58,8 @@ export default {
|
||||||
* Determine if we should display a "Shuffle All" link.
|
* Determine if we should display a "Shuffle All" link.
|
||||||
*/
|
*/
|
||||||
showShufflingAllOption () {
|
showShufflingAllOption () {
|
||||||
return songStore.all.length;
|
return songStore.all.length
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -68,17 +68,17 @@ export default {
|
||||||
* Overriding the mixin.
|
* Overriding the mixin.
|
||||||
*/
|
*/
|
||||||
shuffleAll () {
|
shuffleAll () {
|
||||||
playback.queueAndPlay(this.state.songs.length ? this.state.songs : songStore.all, true);
|
playback.queueAndPlay(this.state.songs.length ? this.state.songs : songStore.all, true)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear the queue.
|
* Clear the queue.
|
||||||
*/
|
*/
|
||||||
clearQueue () {
|
clearQueue () {
|
||||||
queueStore.clear();
|
queueStore.clear()
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -24,18 +24,18 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import swal from 'sweetalert';
|
import swal from 'sweetalert'
|
||||||
|
|
||||||
import { settingStore, sharedStore } from '../../../stores';
|
import { settingStore, sharedStore } from '../../../stores'
|
||||||
import { parseValidationError, forceReloadWindow, event, showOverlay, hideOverlay } from '../../../utils';
|
import { parseValidationError, forceReloadWindow, showOverlay, hideOverlay } from '../../../utils'
|
||||||
import router from '../../../router';
|
import router from '../../../router'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
state: settingStore.state,
|
state: settingStore.state,
|
||||||
sharedState: sharedStore.state,
|
sharedState: sharedStore.state
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -47,8 +47,8 @@ export default {
|
||||||
shouldWarn () {
|
shouldWarn () {
|
||||||
// Warn the user if the media path is not empty and about to change.
|
// Warn the user if the media path is not empty and about to change.
|
||||||
return this.sharedState.originalMediaPath &&
|
return this.sharedState.originalMediaPath &&
|
||||||
this.sharedState.originalMediaPath !== this.state.settings.media_path.trim();
|
this.sharedState.originalMediaPath !== this.state.settings.media_path.trim()
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -61,10 +61,10 @@ export default {
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
showCancelButton: true,
|
showCancelButton: true,
|
||||||
confirmButtonText: 'I know. Go ahead.',
|
confirmButtonText: 'I know. Go ahead.',
|
||||||
confirmButtonColor: '#c34848',
|
confirmButtonColor: '#c34848'
|
||||||
}, this.save);
|
}, this.save)
|
||||||
} else {
|
} else {
|
||||||
this.save();
|
this.save()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -72,31 +72,31 @@ export default {
|
||||||
* Save the settings.
|
* Save the settings.
|
||||||
*/
|
*/
|
||||||
save () {
|
save () {
|
||||||
showOverlay();
|
showOverlay()
|
||||||
|
|
||||||
settingStore.update().then(() => {
|
settingStore.update().then(() => {
|
||||||
// Make sure we're back to home first.
|
// Make sure we're back to home first.
|
||||||
router.go('home');
|
router.go('home')
|
||||||
forceReloadWindow();
|
forceReloadWindow()
|
||||||
}).catch(r => {
|
}).catch(r => {
|
||||||
let msg = 'Unknown error.';
|
let msg = 'Unknown error.'
|
||||||
|
|
||||||
if (r.status === 422) {
|
if (r.status === 422) {
|
||||||
msg = parseValidationError(r.responseJSON)[0];
|
msg = parseValidationError(r.responseJSON)[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
hideOverlay();
|
hideOverlay()
|
||||||
|
|
||||||
swal({
|
swal({
|
||||||
title: 'Something went wrong',
|
title: 'Something went wrong',
|
||||||
text: msg,
|
text: msg,
|
||||||
type: 'error',
|
type: 'error',
|
||||||
allowOutsideClick: true,
|
allowOutsideClick: true
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -25,12 +25,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import isMobile from 'ismobilejs';
|
import { pluralize } from '../../../utils'
|
||||||
|
import { songStore } from '../../../stores'
|
||||||
import { pluralize } from '../../../utils';
|
import hasSongList from '../../../mixins/has-song-list'
|
||||||
import { songStore } from '../../../stores';
|
|
||||||
import { playback } from '../../../services';
|
|
||||||
import hasSongList from '../../../mixins/has-song-list';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'main-wrapper--main-content--songs',
|
name: 'main-wrapper--main-content--songs',
|
||||||
|
@ -40,9 +37,9 @@ export default {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
state: songStore.state
|
state: songStore.state
|
||||||
};
|
}
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -44,11 +44,11 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { clone } from 'lodash';
|
import { clone } from 'lodash'
|
||||||
import isMobile from 'ismobilejs';
|
import isMobile from 'ismobilejs'
|
||||||
|
|
||||||
import { userStore } from '../../../stores';
|
import { userStore } from '../../../stores'
|
||||||
import userItem from '../../shared/user-item.vue';
|
import userItem from '../../shared/user-item.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { userItem },
|
components: { userItem },
|
||||||
|
@ -59,8 +59,8 @@ export default {
|
||||||
isPhone: isMobile.phone,
|
isPhone: isMobile.phone,
|
||||||
showingControls: false,
|
showingControls: false,
|
||||||
creating: false,
|
creating: false,
|
||||||
newUser: {},
|
newUser: {}
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -69,12 +69,12 @@ export default {
|
||||||
*/
|
*/
|
||||||
store () {
|
store () {
|
||||||
userStore.store(this.newUser.name, this.newUser.email, this.newUser.password).then(u => {
|
userStore.store(this.newUser.name, this.newUser.email, this.newUser.password).then(u => {
|
||||||
this.newUser = clone(userStore.stub);
|
this.newUser = clone(userStore.stub)
|
||||||
this.creating = false;
|
this.creating = false
|
||||||
});
|
})
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -11,11 +11,11 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { event } from '../../../utils';
|
import { event } from '../../../utils'
|
||||||
import { playback } from '../../../services';
|
import { playback } from '../../../services'
|
||||||
import YouTubePlayer from 'youtube-player';
|
import YouTubePlayer from 'youtube-player'
|
||||||
|
|
||||||
let player;
|
let player
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'main-wrapper--main-content--youtube-player',
|
name: 'main-wrapper--main-content--youtube-player',
|
||||||
|
@ -28,32 +28,32 @@ export default {
|
||||||
if (!player) {
|
if (!player) {
|
||||||
player = YouTubePlayer('player', {
|
player = YouTubePlayer('player', {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '100%',
|
height: '100%'
|
||||||
});
|
})
|
||||||
|
|
||||||
player.on('stateChange', event => {
|
player.on('stateChange', event => {
|
||||||
// Pause song playback when video is played
|
// Pause song playback when video is played
|
||||||
event.data === 1 && playback.pause();
|
event.data === 1 && playback.pause()
|
||||||
});
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
created () {
|
created () {
|
||||||
event.on({
|
event.on({
|
||||||
'youtube:play': id => {
|
'youtube:play': id => {
|
||||||
this.initPlayer();
|
this.initPlayer()
|
||||||
player.loadVideoById(id);
|
player.loadVideoById(id)
|
||||||
player.playVideo();
|
player.playVideo()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop video playback when a song is played/resumed.
|
* Stop video playback when a song is played/resumed.
|
||||||
*/
|
*/
|
||||||
'song:played': () => player && player.pauseVideo(),
|
'song:played': () => player && player.pauseVideo()
|
||||||
});
|
})
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass" scoped>
|
<style lang="sass" scoped>
|
||||||
|
|
|
@ -55,12 +55,12 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import isMobile from 'ismobilejs';
|
import isMobile from 'ismobilejs'
|
||||||
import $ from 'jquery';
|
import $ from 'jquery'
|
||||||
|
|
||||||
import { event } from '../../../utils';
|
import { event } from '../../../utils'
|
||||||
import { sharedStore, userStore, songStore, queueStore } from '../../../stores';
|
import { sharedStore, userStore, songStore, queueStore } from '../../../stores'
|
||||||
import playlists from './playlists.vue';
|
import playlists from './playlists.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { playlists },
|
components: { playlists },
|
||||||
|
@ -70,14 +70,14 @@ export default {
|
||||||
currentView: 'home',
|
currentView: 'home',
|
||||||
user: userStore.state,
|
user: userStore.state,
|
||||||
showing: !isMobile.phone,
|
showing: !isMobile.phone,
|
||||||
sharedState: sharedStore.state,
|
sharedState: sharedStore.state
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
latestVersionUrl () {
|
latestVersionUrl () {
|
||||||
return 'https://github.com/phanan/koel/releases/tag/' + this.sharedState.latestVersion;
|
return 'https://github.com/phanan/koel/releases/tag/' + this.sharedState.latestVersion
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -87,7 +87,7 @@ export default {
|
||||||
* @param {Object} e The dragleave event.
|
* @param {Object} e The dragleave event.
|
||||||
*/
|
*/
|
||||||
removeDroppableState (e) {
|
removeDroppableState (e) {
|
||||||
$(e.target).removeClass('droppable');
|
$(e.target).removeClass('droppable')
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -96,10 +96,10 @@ export default {
|
||||||
* @param {Object} e The dragover event.
|
* @param {Object} e The dragover event.
|
||||||
*/
|
*/
|
||||||
allowDrop (e) {
|
allowDrop (e) {
|
||||||
$(e.target).addClass('droppable');
|
$(e.target).addClass('droppable')
|
||||||
e.dataTransfer.dropEffect = 'move';
|
e.dataTransfer.dropEffect = 'move'
|
||||||
|
|
||||||
return false;
|
return false
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -110,41 +110,43 @@ export default {
|
||||||
* @return {Boolean}
|
* @return {Boolean}
|
||||||
*/
|
*/
|
||||||
handleDrop (e) {
|
handleDrop (e) {
|
||||||
this.removeDroppableState(e);
|
this.removeDroppableState(e)
|
||||||
|
|
||||||
if (!e.dataTransfer.getData('application/x-koel.text+plain')) {
|
if (!e.dataTransfer.getData('application/x-koel.text+plain')) {
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const songs = songStore.byIds(e.dataTransfer.getData('application/x-koel.text+plain').split(','));
|
const songs = songStore.byIds(e.dataTransfer.getData('application/x-koel.text+plain').split(','))
|
||||||
|
|
||||||
if (!songs.length) {
|
if (!songs.length) {
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
queueStore.queue(songs);
|
queueStore.queue(songs)
|
||||||
|
|
||||||
return false;
|
return false
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
created () {
|
created () {
|
||||||
event.on('main-content-view:load', view => {
|
event.on('main-content-view:load', view => {
|
||||||
this.currentView = view;
|
this.currentView = view
|
||||||
|
|
||||||
// Hide the sidebar if on mobile
|
// Hide the sidebar if on mobile
|
||||||
if (isMobile.phone) {
|
if (isMobile.phone) {
|
||||||
this.showing = false;
|
this.showing = false
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listen to sidebar:toggle event to show or hide the sidebar.
|
* Listen to sidebar:toggle event to show or hide the sidebar.
|
||||||
* This should only be triggered on a mobile device.
|
* This should only be triggered on a mobile device.
|
||||||
*/
|
*/
|
||||||
event.on('sidebar:toggle', () => this.showing = !this.showing);
|
event.on('sidebar:toggle', () => {
|
||||||
},
|
this.showing = !this.showing
|
||||||
};
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -20,10 +20,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import $ from 'jquery';
|
import $ from 'jquery'
|
||||||
|
|
||||||
import { event } from '../../../utils';
|
import { event } from '../../../utils'
|
||||||
import { songStore, playlistStore, favoriteStore } from '../../../stores';
|
import { songStore, playlistStore, favoriteStore } from '../../../stores'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['playlist', 'type'],
|
props: ['playlist', 'type'],
|
||||||
|
@ -32,8 +32,8 @@ export default {
|
||||||
return {
|
return {
|
||||||
newName: '',
|
newName: '',
|
||||||
editing: false,
|
editing: false,
|
||||||
active: false,
|
active: false
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -43,12 +43,12 @@ export default {
|
||||||
* @return {Boolean}
|
* @return {Boolean}
|
||||||
*/
|
*/
|
||||||
isFavorites () {
|
isFavorites () {
|
||||||
return this.type === 'favorites';
|
return this.type === 'favorites'
|
||||||
},
|
},
|
||||||
|
|
||||||
playlistUrl () {
|
playlistUrl () {
|
||||||
return this.isFavorites ? '/#!/favorites' : `/#!/playlist/${this.playlist.id}`;
|
return this.isFavorites ? '/#!/favorites' : `/#!/playlist/${this.playlist.id}`
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -57,11 +57,11 @@ export default {
|
||||||
*/
|
*/
|
||||||
edit () {
|
edit () {
|
||||||
if (this.isFavorites) {
|
if (this.isFavorites) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.beforeEditCache = this.playlist.name;
|
this.beforeEditCache = this.playlist.name
|
||||||
this.editing = true;
|
this.editing = true
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -69,26 +69,26 @@ export default {
|
||||||
*/
|
*/
|
||||||
update () {
|
update () {
|
||||||
if (this.isFavorites || !this.editing) {
|
if (this.isFavorites || !this.editing) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.editing = false;
|
this.editing = false
|
||||||
|
|
||||||
this.playlist.name = this.playlist.name.trim();
|
this.playlist.name = this.playlist.name.trim()
|
||||||
if (!this.playlist.name) {
|
if (!this.playlist.name) {
|
||||||
this.playlist.name = this.beforeEditCache;
|
this.playlist.name = this.beforeEditCache
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
playlistStore.update(this.playlist);
|
playlistStore.update(this.playlist)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancel editing.
|
* Cancel editing.
|
||||||
*/
|
*/
|
||||||
cancelEdit () {
|
cancelEdit () {
|
||||||
this.editing = false;
|
this.editing = false
|
||||||
this.playlist.name = this.beforeEditCache;
|
this.playlist.name = this.beforeEditCache
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -97,7 +97,7 @@ export default {
|
||||||
* @param {Object} e The dragleave event.
|
* @param {Object} e The dragleave event.
|
||||||
*/
|
*/
|
||||||
removeDroppableState (e) {
|
removeDroppableState (e) {
|
||||||
$(e.target).removeClass('droppable');
|
$(e.target).removeClass('droppable')
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -107,10 +107,10 @@ export default {
|
||||||
* @param {Object} e The dragover event.
|
* @param {Object} e The dragover event.
|
||||||
*/
|
*/
|
||||||
allowDrop (e) {
|
allowDrop (e) {
|
||||||
$(e.target).addClass('droppable');
|
$(e.target).addClass('droppable')
|
||||||
e.dataTransfer.dropEffect = 'move';
|
e.dataTransfer.dropEffect = 'move'
|
||||||
|
|
||||||
return false;
|
return false
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -121,40 +121,40 @@ export default {
|
||||||
* @return {Boolean}
|
* @return {Boolean}
|
||||||
*/
|
*/
|
||||||
handleDrop (e) {
|
handleDrop (e) {
|
||||||
this.removeDroppableState(e);
|
this.removeDroppableState(e)
|
||||||
|
|
||||||
if (!e.dataTransfer.getData('application/x-koel.text+plain')) {
|
if (!e.dataTransfer.getData('application/x-koel.text+plain')) {
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const songs = songStore.byIds(e.dataTransfer.getData('application/x-koel.text+plain').split(','));
|
const songs = songStore.byIds(e.dataTransfer.getData('application/x-koel.text+plain').split(','))
|
||||||
|
|
||||||
if (!songs.length) {
|
if (!songs.length) {
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.type === 'favorites') {
|
if (this.type === 'favorites') {
|
||||||
favoriteStore.like(songs);
|
favoriteStore.like(songs)
|
||||||
} else {
|
} else {
|
||||||
playlistStore.addSongs(this.playlist, songs);
|
playlistStore.addSongs(this.playlist, songs)
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
created () {
|
created () {
|
||||||
event.on('main-content-view:load', (view, playlist) => {
|
event.on('main-content-view:load', (view, playlist) => {
|
||||||
if (view === 'favorites') {
|
if (view === 'favorites') {
|
||||||
this.active = this.isFavorites;
|
this.active = this.isFavorites
|
||||||
} else if (view === 'playlist') {
|
} else if (view === 'playlist') {
|
||||||
this.active = this.playlist === playlist;
|
this.active = this.playlist === playlist
|
||||||
} else {
|
} else {
|
||||||
this.active = false;
|
this.active = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass" scoped>
|
<style lang="sass" scoped>
|
||||||
|
|
|
@ -22,10 +22,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { playlistStore, favoriteStore } from '../../../stores';
|
import { playlistStore, favoriteStore } from '../../../stores'
|
||||||
import router from '../../../router';
|
import router from '../../../router'
|
||||||
|
|
||||||
import playlistItem from './playlist-item.vue';
|
import playlistItem from './playlist-item.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'sidebar--playlists',
|
name: 'sidebar--playlists',
|
||||||
|
@ -37,8 +37,8 @@ export default {
|
||||||
playlistState: playlistStore.state,
|
playlistState: playlistStore.state,
|
||||||
favoriteState: favoriteStore.state,
|
favoriteState: favoriteStore.state,
|
||||||
creating: false,
|
creating: false,
|
||||||
newName: '',
|
newName: ''
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -46,16 +46,16 @@ export default {
|
||||||
* Store/create a new playlist.
|
* Store/create a new playlist.
|
||||||
*/
|
*/
|
||||||
store () {
|
store () {
|
||||||
this.creating = false;
|
this.creating = false
|
||||||
|
|
||||||
playlistStore.store(this.newName).then(p => {
|
playlistStore.store(this.newName).then(p => {
|
||||||
this.newName = '';
|
this.newName = ''
|
||||||
// Activate the new playlist right away
|
// Activate the new playlist right away
|
||||||
this.$nextTick(() => router.go(`playlist/${p.id}`));
|
this.$nextTick(() => router.go(`playlist/${p.id}`))
|
||||||
});
|
})
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -76,20 +76,20 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { every, filter } from 'lodash';
|
import { every, filter } from 'lodash'
|
||||||
|
|
||||||
import { br2nl } from '../../utils';
|
import { br2nl, forceReloadWindow } from '../../utils'
|
||||||
import { songInfo } from '../../services/info';
|
import { songInfo } from '../../services/info'
|
||||||
import { artistStore, albumStore, songStore } from '../../stores';
|
import { artistStore, albumStore, songStore } from '../../stores'
|
||||||
|
|
||||||
import soundBar from '../shared/sound-bar.vue';
|
import soundBar from '../shared/sound-bar.vue'
|
||||||
import typeahead from '../shared/typeahead.vue';
|
import typeahead from '../shared/typeahead.vue'
|
||||||
|
|
||||||
const COMPILATION_STATES = {
|
const COMPILATION_STATES = {
|
||||||
NONE: 0, // No songs belong to a compilation album
|
NONE: 0, // No songs belong to a compilation album
|
||||||
ALL: 1, // All songs belong to compilation album(s)
|
ALL: 1, // All songs belong to compilation album(s)
|
||||||
SOME: 2, // Some of the songs belong to compilation album(s)
|
SOME: 2 // Some of the songs belong to compilation album(s)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { soundBar, typeahead },
|
components: { soundBar, typeahead },
|
||||||
|
@ -105,13 +105,13 @@
|
||||||
artistState: artistStore.state,
|
artistState: artistStore.state,
|
||||||
artistTypeaheadOptions: {
|
artistTypeaheadOptions: {
|
||||||
displayKey: 'name',
|
displayKey: 'name',
|
||||||
filterKey: 'name',
|
filterKey: 'name'
|
||||||
},
|
},
|
||||||
|
|
||||||
albumState: albumStore.state,
|
albumState: albumStore.state,
|
||||||
albumTypeaheadOptions: {
|
albumTypeaheadOptions: {
|
||||||
displayKey: 'name',
|
displayKey: 'name',
|
||||||
filterKey: 'name',
|
filterKey: 'name'
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -126,9 +126,9 @@
|
||||||
artistName: '',
|
artistName: '',
|
||||||
lyrics: '',
|
lyrics: '',
|
||||||
track: '',
|
track: '',
|
||||||
compilationState: null,
|
compilationState: null
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -138,7 +138,7 @@
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
editSingle () {
|
editSingle () {
|
||||||
return this.songs.length === 1;
|
return this.songs.length === 1
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -147,7 +147,7 @@
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
bySameArtist () {
|
bySameArtist () {
|
||||||
return every(this.songs, song => song.artist.id === this.songs[0].artist.id);
|
return every(this.songs, song => song.artist.id === this.songs[0].artist.id)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -156,7 +156,7 @@
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
inSameAlbum () {
|
inSameAlbum () {
|
||||||
return every(this.songs, song => song.album.id === this.songs[0].album.id);
|
return every(this.songs, song => song.album.id === this.songs[0].album.id)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -165,7 +165,7 @@
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
coverUrl () {
|
coverUrl () {
|
||||||
return this.inSameAlbum ? this.songs[0].album.cover : '/public/img/covers/unknown-album.png';
|
return this.inSameAlbum ? this.songs[0].album.cover : '/public/img/covers/unknown-album.png'
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -179,12 +179,12 @@
|
||||||
if (!contributedSongs.length) {
|
if (!contributedSongs.length) {
|
||||||
this.formData.compilationState = COMPILATION_STATES.NONE
|
this.formData.compilationState = COMPILATION_STATES.NONE
|
||||||
} else if (contributedSongs.length === this.songs.length) {
|
} else if (contributedSongs.length === this.songs.length) {
|
||||||
this.formData.compilationState = COMPILATION_STATES.ALL;
|
this.formData.compilationState = COMPILATION_STATES.ALL
|
||||||
} else {
|
} else {
|
||||||
this.formData.compilationState = COMPILATION_STATES.SOME;
|
this.formData.compilationState = COMPILATION_STATES.SOME
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.formData.compilationState;
|
return this.formData.compilationState
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -193,7 +193,7 @@
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
displayedTitle () {
|
displayedTitle () {
|
||||||
return this.editSingle ? this.formData.title : `${this.songs.length} songs selected`;
|
return this.editSingle ? this.formData.title : `${this.songs.length} songs selected`
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -203,9 +203,9 @@
|
||||||
*/
|
*/
|
||||||
displayedAlbum () {
|
displayedAlbum () {
|
||||||
if (this.editSingle) {
|
if (this.editSingle) {
|
||||||
return this.formData.albumName;
|
return this.formData.albumName
|
||||||
} else {
|
} else {
|
||||||
return this.formData.albumName ? this.formData.albumName : 'Mixed Albums';
|
return this.formData.albumName ? this.formData.albumName : 'Mixed Albums'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -216,46 +216,46 @@
|
||||||
*/
|
*/
|
||||||
displayedArtist () {
|
displayedArtist () {
|
||||||
if (this.editSingle) {
|
if (this.editSingle) {
|
||||||
return this.formData.artistName;
|
return this.formData.artistName
|
||||||
} else {
|
} else {
|
||||||
return this.formData.artistName ? this.formData.artistName : 'Mixed Artists';
|
return this.formData.artistName ? this.formData.artistName : 'Mixed Artists'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
open (songs) {
|
open (songs) {
|
||||||
this.shown = true;
|
this.shown = true
|
||||||
this.songs = songs;
|
this.songs = songs
|
||||||
this.currentView = 'details';
|
this.currentView = 'details'
|
||||||
this.needsReload = false;
|
this.needsReload = false
|
||||||
|
|
||||||
if (this.editSingle) {
|
if (this.editSingle) {
|
||||||
this.formData.title = this.songs[0].title;
|
this.formData.title = this.songs[0].title
|
||||||
this.formData.albumName = this.songs[0].album.name;
|
this.formData.albumName = this.songs[0].album.name
|
||||||
this.formData.artistName = this.songs[0].artist.name;
|
this.formData.artistName = this.songs[0].artist.name
|
||||||
|
|
||||||
// If we're editing only one song and the song's info (including lyrics)
|
// If we're editing only one song and the song's info (including lyrics)
|
||||||
// hasn't been loaded, load it now.
|
// hasn't been loaded, load it now.
|
||||||
if (!this.songs[0].infoRetrieved) {
|
if (!this.songs[0].infoRetrieved) {
|
||||||
this.loading = true;
|
this.loading = true
|
||||||
|
|
||||||
songInfo.fetch(this.songs[0]).then(r => {
|
songInfo.fetch(this.songs[0]).then(r => {
|
||||||
this.loading = false;
|
this.loading = false
|
||||||
this.formData.lyrics = br2nl(this.songs[0].lyrics);
|
this.formData.lyrics = br2nl(this.songs[0].lyrics)
|
||||||
this.formData.track = this.songs[0].track;
|
this.formData.track = this.songs[0].track
|
||||||
this.initCompilationStateCheckbox();
|
this.initCompilationStateCheckbox()
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
this.formData.lyrics = br2nl(this.songs[0].lyrics);
|
this.formData.lyrics = br2nl(this.songs[0].lyrics)
|
||||||
this.formData.track = this.songs[0].track;
|
this.formData.track = this.songs[0].track
|
||||||
this.initCompilationStateCheckbox();
|
this.initCompilationStateCheckbox()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.formData.albumName = this.inSameAlbum ? this.songs[0].album.name : '';
|
this.formData.albumName = this.inSameAlbum ? this.songs[0].album.name : ''
|
||||||
this.formData.artistName = this.bySameArtist ? this.songs[0].artist.name : '';
|
this.formData.artistName = this.bySameArtist ? this.songs[0].artist.name : ''
|
||||||
this.loading = false;
|
this.loading = false
|
||||||
this.initCompilationStateCheckbox();
|
this.initCompilationStateCheckbox()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -266,23 +266,23 @@
|
||||||
// This must be wrapped in a $nextTick callback, because the form is dynamically
|
// This must be wrapped in a $nextTick callback, because the form is dynamically
|
||||||
// attached into DOM in conjunction with `this.loading` data binding.
|
// attached into DOM in conjunction with `this.loading` data binding.
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
const chk = this.$refs.compilationStateChk;
|
const chk = this.$refs.compilationStateChk
|
||||||
|
|
||||||
switch (this.compilationState) {
|
switch (this.compilationState) {
|
||||||
case COMPILATION_STATES.ALL:
|
case COMPILATION_STATES.ALL:
|
||||||
chk.checked = true;
|
chk.checked = true
|
||||||
chk.indeterminate = false;
|
chk.indeterminate = false
|
||||||
break;
|
break
|
||||||
case COMPILATION_STATES.NONE:
|
case COMPILATION_STATES.NONE:
|
||||||
chk.checked = false;
|
chk.checked = false
|
||||||
chk.indeterminate = false;
|
chk.indeterminate = false
|
||||||
break;
|
break
|
||||||
default:
|
default:
|
||||||
chk.checked = false;
|
chk.checked = false
|
||||||
chk.indeterminate = true;
|
chk.indeterminate = true
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -292,33 +292,33 @@
|
||||||
* once the user clicks the checkbox, there's no going back to indeterminate state.
|
* once the user clicks the checkbox, there's no going back to indeterminate state.
|
||||||
*/
|
*/
|
||||||
changeCompilationState (e) {
|
changeCompilationState (e) {
|
||||||
this.formData.compilationState = e.target.checked ? COMPILATION_STATES.ALL : COMPILATION_STATES.NONE;
|
this.formData.compilationState = e.target.checked ? COMPILATION_STATES.ALL : COMPILATION_STATES.NONE
|
||||||
this.needsReload = true;
|
this.needsReload = true
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close the modal.
|
* Close the modal.
|
||||||
*/
|
*/
|
||||||
close () {
|
close () {
|
||||||
this.shown = false;
|
this.shown = false
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Submit the form.
|
* Submit the form.
|
||||||
*/
|
*/
|
||||||
submit () {
|
submit () {
|
||||||
this.loading = true;
|
this.loading = true
|
||||||
|
|
||||||
songStore.update(this.songs, this.formData).then(r => {
|
songStore.update(this.songs, this.formData).then(r => {
|
||||||
this.loading = false;
|
this.loading = false
|
||||||
this.close();
|
this.close()
|
||||||
if (this.needsReload) {
|
this.needsReload && forceReloadWindow()
|
||||||
forceReloadWindow();
|
}).catch(r => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}).catch(r => this.loading = false);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -27,12 +27,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { assign } from 'lodash';
|
import { pluralize } from '../../utils'
|
||||||
|
import { playlistStore } from '../../stores'
|
||||||
import { pluralize, event } from '../../utils';
|
import router from '../../router'
|
||||||
import { playlistStore } from '../../stores';
|
import songMenuMethods from '../../mixins/song-menu-methods'
|
||||||
import router from '../../router';
|
|
||||||
import songMenuMethods from '../../mixins/song-menu-methods';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'shared--add-to-menu',
|
name: 'shared--add-to-menu',
|
||||||
|
@ -43,16 +41,16 @@ export default {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
newPlaylistName: '',
|
newPlaylistName: '',
|
||||||
playlistState: playlistStore.state,
|
playlistState: playlistStore.state
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
songs () {
|
songs () {
|
||||||
if (!this.songs.length) {
|
if (!this.songs.length) {
|
||||||
this.close();
|
this.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -61,26 +59,26 @@ export default {
|
||||||
* As of current we don't have selective save.
|
* As of current we don't have selective save.
|
||||||
*/
|
*/
|
||||||
createNewPlaylistFromSongs () {
|
createNewPlaylistFromSongs () {
|
||||||
this.newPlaylistName = this.newPlaylistName.trim();
|
this.newPlaylistName = this.newPlaylistName.trim()
|
||||||
|
|
||||||
if (!this.newPlaylistName) {
|
if (!this.newPlaylistName) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
playlistStore.store(this.newPlaylistName, this.songs).then(p => {
|
playlistStore.store(this.newPlaylistName, this.songs).then(p => {
|
||||||
this.newPlaylistName = '';
|
this.newPlaylistName = ''
|
||||||
// Activate the new playlist right away
|
// Activate the new playlist right away
|
||||||
this.$nextTick(() => router.go(`playlist/${p.id}`));
|
this.$nextTick(() => router.go(`playlist/${p.id}`))
|
||||||
});
|
})
|
||||||
|
|
||||||
this.close();
|
this.close()
|
||||||
},
|
},
|
||||||
|
|
||||||
close () {
|
close () {
|
||||||
this.$parent.closeAddToMenu();
|
this.$parent.closeAddToMenu()
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass" scoped>
|
<style lang="sass" scoped>
|
||||||
|
|
|
@ -36,12 +36,12 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { map } from 'lodash';
|
import { map } from 'lodash'
|
||||||
import $ from 'jquery';
|
import $ from 'jquery'
|
||||||
|
|
||||||
import { pluralize } from '../../utils';
|
import { pluralize } from '../../utils'
|
||||||
import { queueStore, artistStore, sharedStore } from '../../stores';
|
import { queueStore, artistStore, sharedStore } from '../../stores'
|
||||||
import { playback, download } from '../../services';
|
import { playback, download } from '../../services'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'shared--album-item',
|
name: 'shared--album-item',
|
||||||
|
@ -50,15 +50,15 @@ export default {
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
sharedState: sharedStore.state,
|
sharedState: sharedStore.state
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
isNormalArtist () {
|
isNormalArtist () {
|
||||||
return !artistStore.isVariousArtists(this.album.artist)
|
return !artistStore.isVariousArtists(this.album.artist) &&
|
||||||
&& !artistStore.isUnknownArtist(this.album.artist);
|
!artistStore.isUnknownArtist(this.album.artist)
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -68,9 +68,9 @@ export default {
|
||||||
*/
|
*/
|
||||||
play (e) {
|
play (e) {
|
||||||
if (e.metaKey || e.ctrlKey) {
|
if (e.metaKey || e.ctrlKey) {
|
||||||
queueStore.queue(this.album.songs);
|
queueStore.queue(this.album.songs)
|
||||||
} else {
|
} else {
|
||||||
playback.playAllInAlbum(this.album, false);
|
playback.playAllInAlbum(this.album, false)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -78,30 +78,30 @@ export default {
|
||||||
* Shuffle all songs in album.
|
* Shuffle all songs in album.
|
||||||
*/
|
*/
|
||||||
shuffle () {
|
shuffle () {
|
||||||
playback.playAllInAlbum(this.album, true);
|
playback.playAllInAlbum(this.album, true)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Download all songs in album.
|
* Download all songs in album.
|
||||||
*/
|
*/
|
||||||
download () {
|
download () {
|
||||||
download.fromAlbum(this.album);
|
download.fromAlbum(this.album)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allow dragging the album (actually, its songs).
|
* Allow dragging the album (actually, its songs).
|
||||||
*/
|
*/
|
||||||
dragStart (e) {
|
dragStart (e) {
|
||||||
const songIds = map(this.album.songs, 'id');
|
const songIds = map(this.album.songs, 'id')
|
||||||
e.dataTransfer.setData('application/x-koel.text+plain', songIds);
|
e.dataTransfer.setData('application/x-koel.text+plain', songIds)
|
||||||
e.dataTransfer.effectAllowed = 'move';
|
e.dataTransfer.effectAllowed = 'move'
|
||||||
|
|
||||||
// Set a fancy drop image using our ghost element.
|
// Set a fancy drop image using our ghost element.
|
||||||
const $ghost = $('#dragGhost').text(`All ${songIds.length} song${songIds.length === 1 ? '' : 's'} in ${this.album.name}`);
|
const $ghost = $('#dragGhost').text(`All ${songIds.length} song${songIds.length === 1 ? '' : 's'} in ${this.album.name}`)
|
||||||
e.dataTransfer.setDragImage($ghost[0], 0, 0);
|
e.dataTransfer.setDragImage($ghost[0], 0, 0)
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -28,12 +28,12 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { map } from 'lodash';
|
import { map } from 'lodash'
|
||||||
import $ from 'jquery';
|
import $ from 'jquery'
|
||||||
|
|
||||||
import { pluralize } from '../../utils';
|
import { pluralize } from '../../utils'
|
||||||
import { artistStore, queueStore, sharedStore } from '../../stores';
|
import { artistStore, queueStore, sharedStore } from '../../stores'
|
||||||
import { playback, download } from '../../services';
|
import { playback, download } from '../../services'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'shared--artist-item',
|
name: 'shared--artist-item',
|
||||||
|
@ -42,8 +42,8 @@ export default {
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
sharedState: sharedStore.state,
|
sharedState: sharedStore.state
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -54,7 +54,7 @@ export default {
|
||||||
* @return {Boolean}
|
* @return {Boolean}
|
||||||
*/
|
*/
|
||||||
showing () {
|
showing () {
|
||||||
return this.artist.songCount && !artistStore.isVariousArtists(this.artist);
|
return this.artist.songCount && !artistStore.isVariousArtists(this.artist)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -64,9 +64,9 @@ export default {
|
||||||
*/
|
*/
|
||||||
play (e) {
|
play (e) {
|
||||||
if (e.metaKey || e.ctrlKey) {
|
if (e.metaKey || e.ctrlKey) {
|
||||||
queueStore.queue(this.artist.songs);
|
queueStore.queue(this.artist.songs)
|
||||||
} else {
|
} else {
|
||||||
playback.playAllByArtist(this.artist);
|
playback.playAllByArtist(this.artist)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -74,23 +74,23 @@ export default {
|
||||||
* Download all songs by artist.
|
* Download all songs by artist.
|
||||||
*/
|
*/
|
||||||
download () {
|
download () {
|
||||||
download.fromArtist(this.artist);
|
download.fromArtist(this.artist)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allow dragging the artist (actually, their songs).
|
* Allow dragging the artist (actually, their songs).
|
||||||
*/
|
*/
|
||||||
dragStart (e) {
|
dragStart (e) {
|
||||||
const songIds = map(this.artist.songs, 'id');
|
const songIds = map(this.artist.songs, 'id')
|
||||||
e.dataTransfer.setData('application/x-koel.text+plain', songIds);
|
e.dataTransfer.setData('application/x-koel.text+plain', songIds)
|
||||||
e.dataTransfer.effectAllowed = 'move';
|
e.dataTransfer.effectAllowed = 'move'
|
||||||
|
|
||||||
// Set a fancy drop image using our ghost element.
|
// Set a fancy drop image using our ghost element.
|
||||||
const $ghost = $('#dragGhost').text(`All ${songIds.length} song${songIds.length === 1 ? '' : 's'} by ${this.artist.name}`);
|
const $ghost = $('#dragGhost').text(`All ${songIds.length} song${songIds.length === 1 ? '' : 's'} by ${this.artist.name}`)
|
||||||
e.dataTransfer.setDragImage($ghost[0], 0, 0);
|
e.dataTransfer.setDragImage($ghost[0], 0, 0)
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -21,9 +21,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { pluralize } from '../../utils';
|
import { pluralize } from '../../utils'
|
||||||
import { queueStore } from '../../stores';
|
import { queueStore } from '../../stores'
|
||||||
import { playback } from '../../services';
|
import { playback } from '../../services'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'shared--home-song-item',
|
name: 'shared--home-song-item',
|
||||||
|
@ -32,30 +32,30 @@ export default {
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
showPlayCount () {
|
showPlayCount () {
|
||||||
return this.topPlayCount && this.song.playCount;
|
return this.topPlayCount && this.song.playCount
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
play () {
|
play () {
|
||||||
if (!queueStore.contains(this.song)) {
|
if (!queueStore.contains(this.song)) {
|
||||||
queueStore.queueAfterCurrent(this.song);
|
queueStore.queueAfterCurrent(this.song)
|
||||||
}
|
}
|
||||||
|
|
||||||
playback.play(this.song);
|
playback.play(this.song)
|
||||||
},
|
},
|
||||||
|
|
||||||
changeSongState () {
|
changeSongState () {
|
||||||
if (this.song.playbackState === 'stopped') {
|
if (this.song.playbackState === 'stopped') {
|
||||||
this.play(this.song);
|
this.play(this.song)
|
||||||
} else if (this.song.playbackState === 'paused') {
|
} else if (this.song.playbackState === 'paused') {
|
||||||
playback.resume();
|
playback.resume()
|
||||||
} else {
|
} else {
|
||||||
playback.pause();
|
playback.pause()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass" scoped>
|
<style lang="sass" scoped>
|
||||||
|
|
|
@ -15,10 +15,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { assign } from 'lodash';
|
import { assign } from 'lodash'
|
||||||
|
|
||||||
import { event } from '../../utils';
|
import { event } from '../../utils'
|
||||||
import soundBar from './sound-bar.vue';
|
import soundBar from './sound-bar.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { soundBar },
|
components: { soundBar },
|
||||||
|
@ -35,9 +35,9 @@ export default {
|
||||||
* @type {String}
|
* @type {String}
|
||||||
*/
|
*/
|
||||||
type: 'loading',
|
type: 'loading',
|
||||||
message: '',
|
message: ''
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -49,15 +49,15 @@ export default {
|
||||||
* @param {Boolean} dismissable Whether to show the Close button
|
* @param {Boolean} dismissable Whether to show the Close button
|
||||||
*/
|
*/
|
||||||
show (options) {
|
show (options) {
|
||||||
assign(this.state, options);
|
assign(this.state, options)
|
||||||
this.state.showing = true;
|
this.state.showing = true
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hide the overlay.
|
* Hide the overlay.
|
||||||
*/
|
*/
|
||||||
hide () {
|
hide () {
|
||||||
this.state.showing = false;
|
this.state.showing = false
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -67,17 +67,17 @@ export default {
|
||||||
* @param {Boolean} dismissable
|
* @param {Boolean} dismissable
|
||||||
*/
|
*/
|
||||||
setDimissable (dismissable = true) {
|
setDimissable (dismissable = true) {
|
||||||
this.state.dismissable = dismissable;
|
this.state.dismissable = dismissable
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
created () {
|
created () {
|
||||||
event.on({
|
event.on({
|
||||||
'overlay:show': options => this.show(options),
|
'overlay:show': options => this.show(options),
|
||||||
'overlay:hide': () => this.hide(),
|
'overlay:hide': () => this.hide()
|
||||||
});
|
})
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -25,22 +25,22 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { playback } from '../../services';
|
import { playback } from '../../services'
|
||||||
import { queueStore } from '../../stores';
|
import { queueStore } from '../../stores'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['song'],
|
props: ['song'],
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
selected: false,
|
selected: false
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
playing () {
|
playing () {
|
||||||
return this.song.playbackState === 'playing' || this.song.playbackState === 'paused';
|
return this.song.playbackState === 'playing' || this.song.playbackState === 'paused'
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -49,10 +49,10 @@ export default {
|
||||||
*/
|
*/
|
||||||
playRightAwayyyyyyy () {
|
playRightAwayyyyyyy () {
|
||||||
if (!queueStore.contains(this.song)) {
|
if (!queueStore.contains(this.song)) {
|
||||||
queueStore.queueAfterCurrent(this.song);
|
queueStore.queueAfterCurrent(this.song)
|
||||||
}
|
}
|
||||||
|
|
||||||
playback.play(this.song);
|
playback.play(this.song)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -61,37 +61,37 @@ export default {
|
||||||
doPlayback () {
|
doPlayback () {
|
||||||
switch (this.song.playbackState) {
|
switch (this.song.playbackState) {
|
||||||
case 'playing':
|
case 'playing':
|
||||||
playback.pause();
|
playback.pause()
|
||||||
break;
|
break
|
||||||
case 'paused':
|
case 'paused':
|
||||||
playback.resume();
|
playback.resume()
|
||||||
break;
|
break
|
||||||
default:
|
default:
|
||||||
this.playRightAwayyyyyyy();
|
this.playRightAwayyyyyyy()
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
clicked ($e) {
|
clicked ($e) {
|
||||||
this.$emit('itemClicked', this.song.id, $e);
|
this.$emit('itemClicked', this.song.id, $e)
|
||||||
},
|
},
|
||||||
|
|
||||||
select () {
|
select () {
|
||||||
this.selected = true;
|
this.selected = true
|
||||||
},
|
},
|
||||||
|
|
||||||
deselect () {
|
deselect () {
|
||||||
this.selected = false;
|
this.selected = false
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle the "selected" state of the current component.
|
* Toggle the "selected" state of the current component.
|
||||||
*/
|
*/
|
||||||
toggleSelectedState () {
|
toggleSelectedState () {
|
||||||
this.selected = !this.selected;
|
this.selected = !this.selected
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import isMobile from 'ismobilejs';
|
import isMobile from 'ismobilejs'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'shared--song-list-controls-toggler',
|
name: 'shared--song-list-controls-toggler',
|
||||||
|
@ -20,7 +20,7 @@ export default {
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
toggleControls () {
|
toggleControls () {
|
||||||
this.$emit('toggleControls');
|
this.$emit('toggleControls')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,9 +39,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { assign } from 'lodash';
|
import { assign } from 'lodash'
|
||||||
import { queueStore } from '../../stores';
|
import addToMenu from './add-to-menu.vue'
|
||||||
import addToMenu from './add-to-menu.vue';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'shared--song-list-controls',
|
name: 'shared--song-list-controls',
|
||||||
|
@ -57,52 +56,52 @@ export default {
|
||||||
queue: true,
|
queue: true,
|
||||||
favorites: true,
|
favorites: true,
|
||||||
playlists: true,
|
playlists: true,
|
||||||
newPlaylist: true,
|
newPlaylist: true
|
||||||
},
|
},
|
||||||
clearQueue: false,
|
clearQueue: false,
|
||||||
deletePlaylist: false
|
deletePlaylist: false
|
||||||
},
|
},
|
||||||
showingAddToMenu: false,
|
showingAddToMenu: false,
|
||||||
numberOfQueuedSongs: 0
|
numberOfQueuedSongs: 0
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
showClearQueueButton () {
|
showClearQueueButton () {
|
||||||
return this.fullConfig.clearQueue;
|
return this.fullConfig.clearQueue
|
||||||
},
|
},
|
||||||
|
|
||||||
showDeletePlaylistButton () {
|
showDeletePlaylistButton () {
|
||||||
return this.fullConfig.deletePlaylist;
|
return this.fullConfig.deletePlaylist
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted () {
|
mounted () {
|
||||||
assign(this.fullConfig, this.config);
|
assign(this.fullConfig, this.config)
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
shuffle () {
|
shuffle () {
|
||||||
this.$emit('shuffleAll');
|
this.$emit('shuffleAll')
|
||||||
},
|
},
|
||||||
|
|
||||||
shuffleSelected () {
|
shuffleSelected () {
|
||||||
this.$emit('shuffleSelected');
|
this.$emit('shuffleSelected')
|
||||||
},
|
},
|
||||||
|
|
||||||
clearQueue () {
|
clearQueue () {
|
||||||
this.$emit('clearQueue');
|
this.$emit('clearQueue')
|
||||||
},
|
},
|
||||||
|
|
||||||
deletePlaylist () {
|
deletePlaylist () {
|
||||||
this.$emit('deletePlaylist');
|
this.$emit('deletePlaylist')
|
||||||
},
|
},
|
||||||
|
|
||||||
closeAddToMenu () {
|
closeAddToMenu () {
|
||||||
this.showingAddToMenu = false;
|
this.showingAddToMenu = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass"></style>
|
<style lang="sass"></style>
|
||||||
|
|
|
@ -50,17 +50,17 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { find, invokeMap, filter, map } from 'lodash';
|
import { find, invokeMap, filter, map } from 'lodash'
|
||||||
import isMobile from 'ismobilejs';
|
import isMobile from 'ismobilejs'
|
||||||
import $ from 'jquery';
|
import $ from 'jquery'
|
||||||
|
|
||||||
import { filterBy, orderBy, limitBy, event } from '../../utils';
|
import { filterBy, orderBy, limitBy, event } from '../../utils'
|
||||||
import { playlistStore, queueStore, songStore, favoriteStore } from '../../stores';
|
import { playlistStore, queueStore, songStore, favoriteStore } from '../../stores'
|
||||||
import { playback } from '../../services';
|
import { playback } from '../../services'
|
||||||
import router from '../../router';
|
import router from '../../router'
|
||||||
import songItem from './song-item.vue';
|
import songItem from './song-item.vue'
|
||||||
import songMenu from './song-menu.vue';
|
import songMenu from './song-menu.vue'
|
||||||
import infiniteScroll from '../../mixins/infinite-scroll';
|
import infiniteScroll from '../../mixins/infinite-scroll'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'song-list',
|
name: 'song-list',
|
||||||
|
@ -77,8 +77,8 @@ export default {
|
||||||
sortingByAlbum: false,
|
sortingByAlbum: false,
|
||||||
sortingByArtist: false,
|
sortingByArtist: false,
|
||||||
selectedSongs: [],
|
selectedSongs: [],
|
||||||
mutatedItems: [],
|
mutatedItems: []
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -87,21 +87,21 @@ export default {
|
||||||
*/
|
*/
|
||||||
items () {
|
items () {
|
||||||
if (this.sortable === false) {
|
if (this.sortable === false) {
|
||||||
this.sortKey = '';
|
this.sortKey = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
this.mutatedItems = this.items;
|
this.mutatedItems = this.items
|
||||||
|
|
||||||
// Update the song count and duration status on parent.
|
// Update the song count and duration status on parent.
|
||||||
this.$parent.updateMeta({
|
this.$parent.updateMeta({
|
||||||
songCount: this.items.length,
|
songCount: this.items.length,
|
||||||
totalLength: songStore.getLength(this.items, true),
|
totalLength: songStore.getLength(this.items, true)
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
selectedSongs (val) {
|
selectedSongs (val) {
|
||||||
this.$parent.setSelectedSongs(val);
|
this.$parent.setSelectedSongs(val)
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -113,8 +113,8 @@ export default {
|
||||||
'title', 'album.name', 'artist.name'
|
'title', 'album.name', 'artist.name'
|
||||||
),
|
),
|
||||||
this.numOfItems
|
this.numOfItems
|
||||||
);
|
)
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -125,41 +125,41 @@ export default {
|
||||||
*/
|
*/
|
||||||
sort (key) {
|
sort (key) {
|
||||||
if (this.sortable === false) {
|
if (this.sortable === false) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.sortKey = key;
|
this.sortKey = key
|
||||||
this.order = 0 - this.order;
|
this.order = 0 - this.order
|
||||||
this.sortingByAlbum = Array.isArray(this.sortKey) && this.sortKey[0] === 'album.name';
|
this.sortingByAlbum = Array.isArray(this.sortKey) && this.sortKey[0] === 'album.name'
|
||||||
this.sortingByArtist = Array.isArray(this.sortKey) && this.sortKey[0] === 'album.artist.name';
|
this.sortingByArtist = Array.isArray(this.sortKey) && this.sortKey[0] === 'album.artist.name'
|
||||||
this.mutatedItems = orderBy(this.items, this.sortKey, this.order);
|
this.mutatedItems = orderBy(this.items, this.sortKey, this.order)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the corresponding reaction(s) when the user presses Delete.
|
* Execute the corresponding reaction(s) when the user presses Delete.
|
||||||
*/
|
*/
|
||||||
handleDelete () {
|
handleDelete () {
|
||||||
const songs = this.selectedSongs;
|
const songs = this.selectedSongs
|
||||||
|
|
||||||
if (!songs.length) {
|
if (!songs.length) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case 'queue':
|
case 'queue':
|
||||||
queueStore.unqueue(songs);
|
queueStore.unqueue(songs)
|
||||||
break;
|
break
|
||||||
case 'favorites':
|
case 'favorites':
|
||||||
favoriteStore.unlike(songs);
|
favoriteStore.unlike(songs)
|
||||||
break;
|
break
|
||||||
case 'playlist':
|
case 'playlist':
|
||||||
playlistStore.removeSongs(this.playlist, songs);
|
playlistStore.removeSongs(this.playlist, songs)
|
||||||
break;
|
break
|
||||||
default:
|
default:
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
this.clearSelection();
|
this.clearSelection()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -168,24 +168,24 @@ export default {
|
||||||
* @param {Object} e The keydown event.
|
* @param {Object} e The keydown event.
|
||||||
*/
|
*/
|
||||||
handleEnter (e) {
|
handleEnter (e) {
|
||||||
const songs = this.selectedSongs;
|
const songs = this.selectedSongs
|
||||||
|
|
||||||
if (!songs.length) {
|
if (!songs.length) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (songs.length === 1) {
|
if (songs.length === 1) {
|
||||||
// Just play the song
|
// Just play the song
|
||||||
playback.play(songs[0]);
|
playback.play(songs[0])
|
||||||
|
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case 'queue':
|
case 'queue':
|
||||||
// Play the first song selected if we're in Queue screen.
|
// Play the first song selected if we're in Queue screen.
|
||||||
playback.play(songs[0]);
|
playback.play(songs[0])
|
||||||
break;
|
break
|
||||||
case 'favorites':
|
case 'favorites':
|
||||||
case 'playlist':
|
case 'playlist':
|
||||||
default:
|
default:
|
||||||
|
@ -201,20 +201,20 @@ export default {
|
||||||
// Also, if there's only one song selected, play it right away.
|
// Also, if there's only one song selected, play it right away.
|
||||||
// --------------------------------------------------------------------
|
// --------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
queueStore.queue(songs, false, e.shiftKey);
|
queueStore.queue(songs, false, e.shiftKey)
|
||||||
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
router.go('queue');
|
router.go('queue')
|
||||||
|
|
||||||
if (e.ctrlKey || e.metaKey || songs.length === 1) {
|
if (e.ctrlKey || e.metaKey || songs.length === 1) {
|
||||||
playback.play(songs[0]);
|
playback.play(songs[0])
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
this.clearSelection();
|
this.clearSelection()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -225,7 +225,7 @@ export default {
|
||||||
* @return {Object} The Vue compoenent
|
* @return {Object} The Vue compoenent
|
||||||
*/
|
*/
|
||||||
getComponentBySongId (id) {
|
getComponentBySongId (id) {
|
||||||
return find(this.$refs.rows, { song: { id } });
|
return find(this.$refs.rows, { song: { id }})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -235,11 +235,11 @@ export default {
|
||||||
*/
|
*/
|
||||||
handleA (e) {
|
handleA (e) {
|
||||||
if (!e.metaKey && !e.ctrlKey) {
|
if (!e.metaKey && !e.ctrlKey) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
invokeMap(this.$refs.rows, 'select');
|
invokeMap(this.$refs.rows, 'select')
|
||||||
this.gatherSelected();
|
this.gatherSelected()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -248,10 +248,10 @@ export default {
|
||||||
* @return {Array.<Object>} An array of Song objects
|
* @return {Array.<Object>} An array of Song objects
|
||||||
*/
|
*/
|
||||||
gatherSelected () {
|
gatherSelected () {
|
||||||
const selectedRows = filter(this.$refs.rows, { selected: true });
|
const selectedRows = filter(this.$refs.rows, { selected: true })
|
||||||
const ids = map(selectedRows, row => row.song.id);
|
const ids = map(selectedRows, row => row.song.id)
|
||||||
|
|
||||||
this.selectedSongs = songStore.byIds(ids);
|
this.selectedSongs = songStore.byIds(ids)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -269,32 +269,32 @@ export default {
|
||||||
* @param {Object} e
|
* @param {Object} e
|
||||||
*/
|
*/
|
||||||
itemClicked (songId, e) {
|
itemClicked (songId, e) {
|
||||||
const row = this.getComponentBySongId(songId);
|
const row = this.getComponentBySongId(songId)
|
||||||
|
|
||||||
// If we're on a touch device, or if Ctrl/Cmd key is pressed, just toggle selection.
|
// If we're on a touch device, or if Ctrl/Cmd key is pressed, just toggle selection.
|
||||||
if (isMobile.any) {
|
if (isMobile.any) {
|
||||||
this.toggleRow(row);
|
this.toggleRow(row)
|
||||||
this.gatherSelected();
|
this.gatherSelected()
|
||||||
|
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.ctrlKey || e.metaKey) {
|
if (e.ctrlKey || e.metaKey) {
|
||||||
this.toggleRow(row);
|
this.toggleRow(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.button === 0) {
|
if (e.button === 0) {
|
||||||
if (!e.ctrlKey && !e.metaKey && !e.shiftKey) {
|
if (!e.ctrlKey && !e.metaKey && !e.shiftKey) {
|
||||||
this.clearSelection();
|
this.clearSelection()
|
||||||
this.toggleRow(row);
|
this.toggleRow(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.shiftKey && this.lastSelectedRow && this.lastSelectedRow.$el) {
|
if (e.shiftKey && this.lastSelectedRow && this.lastSelectedRow.$el) {
|
||||||
this.selectRowsBetweenIndexes([this.lastSelectedRow.$el.rowIndex, row.$el.rowIndex]);
|
this.selectRowsBetweenIndexes([this.lastSelectedRow.$el.rowIndex, row.$el.rowIndex])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.gatherSelected();
|
this.gatherSelected()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -303,17 +303,17 @@ export default {
|
||||||
* @param {Object} row The song-item component
|
* @param {Object} row The song-item component
|
||||||
*/
|
*/
|
||||||
toggleRow (row) {
|
toggleRow (row) {
|
||||||
row.toggleSelectedState();
|
row.toggleSelectedState()
|
||||||
this.lastSelectedRow = row;
|
this.lastSelectedRow = row
|
||||||
},
|
},
|
||||||
|
|
||||||
selectRowsBetweenIndexes (indexes) {
|
selectRowsBetweenIndexes (indexes) {
|
||||||
indexes.sort((a, b) => a - b);
|
indexes.sort((a, b) => a - b)
|
||||||
|
|
||||||
const rows = $(this.$refs.wrapper).find('tbody tr');
|
const rows = $(this.$refs.wrapper).find('tbody tr')
|
||||||
|
|
||||||
for (let i = indexes[0]; i <= indexes[1]; ++i) {
|
for (let i = indexes[0]; i <= indexes[1]; ++i) {
|
||||||
this.getComponentBySongId($(rows[i - 1]).data('song-id')).select();
|
this.getComponentBySongId($(rows[i - 1]).data('song-id')).select()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -321,8 +321,8 @@ export default {
|
||||||
* Clear the current selection on this song list.
|
* Clear the current selection on this song list.
|
||||||
*/
|
*/
|
||||||
clearSelection () {
|
clearSelection () {
|
||||||
invokeMap(this.$refs.rows, 'deselect');
|
invokeMap(this.$refs.rows, 'deselect')
|
||||||
this.gatherSelected();
|
this.gatherSelected()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -334,22 +334,22 @@ export default {
|
||||||
*/
|
*/
|
||||||
dragStart (songId, e) {
|
dragStart (songId, e) {
|
||||||
// If the user is dragging an unselected row, clear the current selection.
|
// If the user is dragging an unselected row, clear the current selection.
|
||||||
const currentRow = this.getComponentBySongId(songId);
|
const currentRow = this.getComponentBySongId(songId)
|
||||||
if (!currentRow.selected) {
|
if (!currentRow.selected) {
|
||||||
this.clearSelection();
|
this.clearSelection()
|
||||||
currentRow.select();
|
currentRow.select()
|
||||||
this.gatherSelected();
|
this.gatherSelected()
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
const songIds = map(this.selectedSongs, 'id');
|
const songIds = map(this.selectedSongs, 'id')
|
||||||
e.dataTransfer.setData('application/x-koel.text+plain', songIds);
|
e.dataTransfer.setData('application/x-koel.text+plain', songIds)
|
||||||
e.dataTransfer.effectAllowed = 'move';
|
e.dataTransfer.effectAllowed = 'move'
|
||||||
|
|
||||||
// Set a fancy drop image using our ghost element.
|
// Set a fancy drop image using our ghost element.
|
||||||
const $ghost = $('#dragGhost').text(`${songIds.length} song${songIds.length === 1 ? '' : 's'}`);
|
const $ghost = $('#dragGhost').text(`${songIds.length} song${songIds.length === 1 ? '' : 's'}`)
|
||||||
e.dataTransfer.setDragImage($ghost[0], 0, 0);
|
e.dataTransfer.setDragImage($ghost[0], 0, 0)
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -360,13 +360,13 @@ export default {
|
||||||
*/
|
*/
|
||||||
allowDrop (songId, e) {
|
allowDrop (songId, e) {
|
||||||
if (this.type !== 'queue') {
|
if (this.type !== 'queue') {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
$(e.target).parents('tr').addClass('droppable');
|
$(e.target).parents('tr').addClass('droppable')
|
||||||
e.dataTransfer.dropEffect = 'move';
|
e.dataTransfer.dropEffect = 'move'
|
||||||
|
|
||||||
return false;
|
return false
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -376,25 +376,23 @@ export default {
|
||||||
* @param {Object} e
|
* @param {Object} e
|
||||||
*/
|
*/
|
||||||
handleDrop (songId, e) {
|
handleDrop (songId, e) {
|
||||||
console.log('dropping into', songId);
|
|
||||||
if (this.type !== 'queue') {
|
if (this.type !== 'queue') {
|
||||||
return this.removeDroppableState(e) && false;
|
return this.removeDroppableState(e) && false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!e.dataTransfer.getData('application/x-koel.text+plain')) {
|
if (!e.dataTransfer.getData('application/x-koel.text+plain')) {
|
||||||
return this.removeDroppableState(e) && false;
|
return this.removeDroppableState(e) && false
|
||||||
}
|
}
|
||||||
|
|
||||||
const songs = this.selectedSongs;
|
const songs = this.selectedSongs
|
||||||
console.log('selected songs after drop:', songs);
|
|
||||||
|
|
||||||
if (!songs.length) {
|
if (!songs.length) {
|
||||||
return this.removeDroppableState(e) && false;
|
return this.removeDroppableState(e) && false
|
||||||
}
|
}
|
||||||
|
|
||||||
queueStore.move(songs, songStore.byId(songId));
|
queueStore.move(songs, songStore.byId(songId))
|
||||||
|
|
||||||
return this.removeDroppableState(e) && false;
|
return this.removeDroppableState(e) && false
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -403,21 +401,21 @@ export default {
|
||||||
* @param {Object} e
|
* @param {Object} e
|
||||||
*/
|
*/
|
||||||
removeDroppableState (e) {
|
removeDroppableState (e) {
|
||||||
return $(e.target).parents('tr').removeClass('droppable');
|
return $(e.target).parents('tr').removeClass('droppable')
|
||||||
},
|
},
|
||||||
|
|
||||||
openContextMenu (songId, e) {
|
openContextMenu (songId, e) {
|
||||||
// If the user is right-clicking an unselected row,
|
// If the user is right-clicking an unselected row,
|
||||||
// clear the current selection and select it instead.
|
// clear the current selection and select it instead.
|
||||||
const currentRow = this.getComponentBySongId(songId);
|
const currentRow = this.getComponentBySongId(songId)
|
||||||
if (!currentRow.selected) {
|
if (!currentRow.selected) {
|
||||||
this.clearSelection();
|
this.clearSelection()
|
||||||
currentRow.select();
|
currentRow.select()
|
||||||
this.gatherSelected();
|
this.gatherSelected()
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$nextTick(() => this.$refs.contextMenu.open(e.pageY, e.pageX));
|
this.$nextTick(() => this.$refs.contextMenu.open(e.pageY, e.pageX))
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
created () {
|
created () {
|
||||||
|
@ -430,21 +428,21 @@ export default {
|
||||||
'song:played': song => {
|
'song:played': song => {
|
||||||
// If the song is at the end of the current displayed items, load more.
|
// If the song is at the end of the current displayed items, load more.
|
||||||
if (this.type === 'queue' && this.items.indexOf(song) >= this.numOfItems) {
|
if (this.type === 'queue' && this.items.indexOf(song) >= this.numOfItems) {
|
||||||
this.displayMore();
|
this.displayMore()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scroll the item into view if it's lost into oblivion.
|
// Scroll the item into view if it's lost into oblivion.
|
||||||
if (this.type === 'queue') {
|
if (this.type === 'queue') {
|
||||||
const $wrapper = $(this.$refs.wrapper);
|
const $wrapper = $(this.$refs.wrapper)
|
||||||
const $row = $wrapper.find(`.song-item[data-song-id="${song.id}"]`);
|
const $row = $wrapper.find(`.song-item[data-song-id="${song.id}"]`)
|
||||||
|
|
||||||
if (!$row.length) {
|
if (!$row.length) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($wrapper[0].getBoundingClientRect().top + $wrapper[0].getBoundingClientRect().height <
|
if ($wrapper[0].getBoundingClientRect().top + $wrapper[0].getBoundingClientRect().height <
|
||||||
$row[0].getBoundingClientRect().top) {
|
$row[0].getBoundingClientRect().top) {
|
||||||
$wrapper.scrollTop($wrapper.scrollTop() + $row.position().top);
|
$wrapper.scrollTop($wrapper.scrollTop() + $row.position().top)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -452,7 +450,9 @@ export default {
|
||||||
/**
|
/**
|
||||||
* Listen to 'filter:changed' event to filter the current list.
|
* Listen to 'filter:changed' event to filter the current list.
|
||||||
*/
|
*/
|
||||||
'filter:changed': q => this.q = q,
|
'filter:changed': q => {
|
||||||
|
this.q = q
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears the current list's selection if the user has switched to another view.
|
* Clears the current list's selection if the user has switched to another view.
|
||||||
|
@ -463,10 +463,10 @@ export default {
|
||||||
* Listen to 'song:selection-clear' (often broadcasted from the direct parent)
|
* Listen to 'song:selection-clear' (often broadcasted from the direct parent)
|
||||||
* to clear the selected songs.
|
* to clear the selected songs.
|
||||||
*/
|
*/
|
||||||
'song:selection-clear': () => this.clearSelection(),
|
'song:selection-clear': () => this.clearSelection()
|
||||||
});
|
})
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -30,14 +30,14 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import $ from 'jquery';
|
import $ from 'jquery'
|
||||||
|
|
||||||
import songMenuMethods from '../../mixins/song-menu-methods';
|
import songMenuMethods from '../../mixins/song-menu-methods'
|
||||||
|
|
||||||
import { event, isClipboardSupported, copyText } from '../../utils';
|
import { event, isClipboardSupported, copyText } from '../../utils'
|
||||||
import { sharedStore, songStore, queueStore, userStore, playlistStore } from '../../stores';
|
import { sharedStore, songStore, queueStore, userStore, playlistStore } from '../../stores'
|
||||||
import { playback, download } from '../../services';
|
import { playback, download } from '../../services'
|
||||||
import router from '../../router';
|
import router from '../../router'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'song-menu',
|
name: 'song-menu',
|
||||||
|
@ -48,50 +48,50 @@ export default {
|
||||||
return {
|
return {
|
||||||
playlistState: playlistStore.state,
|
playlistState: playlistStore.state,
|
||||||
sharedState: sharedStore.state,
|
sharedState: sharedStore.state,
|
||||||
copyable: isClipboardSupported(),
|
copyable: isClipboardSupported()
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
onlyOneSongSelected () {
|
onlyOneSongSelected () {
|
||||||
return this.songs.length === 1;
|
return this.songs.length === 1
|
||||||
},
|
},
|
||||||
|
|
||||||
firstSongPlaying () {
|
firstSongPlaying () {
|
||||||
return this.songs[0] ? this.songs[0].playbackState === 'playing' : false;
|
return this.songs[0] ? this.songs[0].playbackState === 'playing' : false
|
||||||
},
|
},
|
||||||
|
|
||||||
isAdmin () {
|
isAdmin () {
|
||||||
return userStore.current.is_admin;
|
return userStore.current.is_admin
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
open (top = 0, left = 0) {
|
open (top = 0, left = 0) {
|
||||||
if (!this.songs.length) {
|
if (!this.songs.length) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.top = top;
|
this.top = top
|
||||||
this.left = left;
|
this.left = left
|
||||||
this.shown = true;
|
this.shown = true
|
||||||
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
// Make sure the menu isn't off-screen
|
// Make sure the menu isn't off-screen
|
||||||
if (this.$el.getBoundingClientRect().bottom > window.innerHeight) {
|
if (this.$el.getBoundingClientRect().bottom > window.innerHeight) {
|
||||||
$(this.$el).css({
|
$(this.$el).css({
|
||||||
top: 'auto',
|
top: 'auto',
|
||||||
bottom: 0,
|
bottom: 0
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
$(this.$el).css({
|
$(this.$el).css({
|
||||||
top: this.top,
|
top: this.top,
|
||||||
bottom: 'auto',
|
bottom: 'auto'
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$refs.menu.focus();
|
this.$refs.menu.focus()
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -100,21 +100,21 @@ export default {
|
||||||
doPlayback () {
|
doPlayback () {
|
||||||
switch (this.songs[0].playbackState) {
|
switch (this.songs[0].playbackState) {
|
||||||
case 'playing':
|
case 'playing':
|
||||||
playback.pause();
|
playback.pause()
|
||||||
break;
|
break
|
||||||
case 'paused':
|
case 'paused':
|
||||||
playback.resume();
|
playback.resume()
|
||||||
break;
|
break
|
||||||
default:
|
default:
|
||||||
if (!queueStore.contains(this.songs[0])) {
|
if (!queueStore.contains(this.songs[0])) {
|
||||||
queueStore.queueAfterCurrent(this.songs[0]);
|
queueStore.queueAfterCurrent(this.songs[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
playback.play(this.songs[0]);
|
playback.play(this.songs[0])
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
this.close();
|
this.close()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -122,36 +122,36 @@ export default {
|
||||||
*/
|
*/
|
||||||
openEditForm () {
|
openEditForm () {
|
||||||
if (this.songs.length) {
|
if (this.songs.length) {
|
||||||
event.emit('songs:edit', this.songs);
|
event.emit('songs:edit', this.songs)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.close();
|
this.close()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load the album details screen.
|
* Load the album details screen.
|
||||||
*/
|
*/
|
||||||
viewAlbumDetails (album) {
|
viewAlbumDetails (album) {
|
||||||
router.go(`album/${album.id}`);
|
router.go(`album/${album.id}`)
|
||||||
this.close();
|
this.close()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load the artist details screen.
|
* Load the artist details screen.
|
||||||
*/
|
*/
|
||||||
viewArtistDetails (artist) {
|
viewArtistDetails (artist) {
|
||||||
router.go(`artist/${artist.id}`);
|
router.go(`artist/${artist.id}`)
|
||||||
this.close();
|
this.close()
|
||||||
},
|
},
|
||||||
|
|
||||||
download () {
|
download () {
|
||||||
download.fromSongs(this.songs);
|
download.fromSongs(this.songs)
|
||||||
this.close();
|
this.close()
|
||||||
},
|
},
|
||||||
|
|
||||||
copyUrl () {
|
copyUrl () {
|
||||||
copyText(songStore.getShareableUrl(this.songs[0]));
|
copyText(songStore.getShareableUrl(this.songs[0]))
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -161,28 +161,28 @@ export default {
|
||||||
*/
|
*/
|
||||||
mounted () {
|
mounted () {
|
||||||
$(this.$el).find('.has-sub').hover(e => {
|
$(this.$el).find('.has-sub').hover(e => {
|
||||||
const $submenu = $(e.target).find('.submenu:first');
|
const $submenu = $(e.target).find('.submenu:first')
|
||||||
if (!$submenu.length) {
|
if (!$submenu.length) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
$submenu.show();
|
$submenu.show()
|
||||||
|
|
||||||
// Make sure the submenu isn't off-screen
|
// Make sure the submenu isn't off-screen
|
||||||
if ($submenu[0].getBoundingClientRect().bottom > window.innerHeight) {
|
if ($submenu[0].getBoundingClientRect().bottom > window.innerHeight) {
|
||||||
$submenu.css({
|
$submenu.css({
|
||||||
top: 'auto',
|
top: 'auto',
|
||||||
bottom: 0,
|
bottom: 0
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}, e => {
|
}, e => {
|
||||||
$(e.target).find('.submenu:first').hide().css({
|
$(e.target).find('.submenu:first').hide().css({
|
||||||
top: 0,
|
top: 0,
|
||||||
bottom: 'auto',
|
bottom: 'auto'
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass" scoped>
|
<style lang="sass" scoped>
|
||||||
|
|
|
@ -42,7 +42,7 @@ export default {
|
||||||
// They call the Rising Sun
|
// They call the Rising Sun
|
||||||
// And it's been the ruin of many a poor boy
|
// And it's been the ruin of many a poor boy
|
||||||
// And God, I know I'm one.
|
// And God, I know I'm one.
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: ['showing'],
|
props: ['showing']
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -21,8 +21,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import $ from 'jquery';
|
import $ from 'jquery'
|
||||||
import { filterBy } from '../../utils';
|
import { filterBy } from '../../utils'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['options', 'value', 'items'],
|
props: ['options', 'value', 'items'],
|
||||||
|
@ -31,14 +31,14 @@ export default {
|
||||||
return {
|
return {
|
||||||
filter: '',
|
filter: '',
|
||||||
showingResult: false,
|
showingResult: false,
|
||||||
mutatedValue: this.value,
|
mutatedValue: this.value
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
displayedItems () {
|
displayedItems () {
|
||||||
return filterBy(this.items, this.filter, this.options.filterKey);
|
return filterBy(this.items, this.filter, this.options.filterKey)
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -46,36 +46,36 @@ export default {
|
||||||
* Navigate down the result list.
|
* Navigate down the result list.
|
||||||
*/
|
*/
|
||||||
down (e) {
|
down (e) {
|
||||||
const selected = $(this.$el).find('.result li.selected');
|
const selected = $(this.$el).find('.result li.selected')
|
||||||
|
|
||||||
if (!selected.length || !selected.removeClass('selected').next('li').addClass('selected').length) {
|
if (!selected.length || !selected.removeClass('selected').next('li').addClass('selected').length) {
|
||||||
$(this.$el).find('.result li:first').addClass('selected');
|
$(this.$el).find('.result li:first').addClass('selected')
|
||||||
}
|
}
|
||||||
|
|
||||||
this.scrollSelectedIntoView(false);
|
this.scrollSelectedIntoView(false)
|
||||||
this.apply();
|
this.apply()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Navigate up the result list.
|
* Navigate up the result list.
|
||||||
*/
|
*/
|
||||||
up (e) {
|
up (e) {
|
||||||
const selected = $(this.$el).find('.result li.selected');
|
const selected = $(this.$el).find('.result li.selected')
|
||||||
|
|
||||||
if (!selected.length || !selected.removeClass('selected').prev('li').addClass('selected').length) {
|
if (!selected.length || !selected.removeClass('selected').prev('li').addClass('selected').length) {
|
||||||
$(this.$el).find('.result li:last').addClass('selected');
|
$(this.$el).find('.result li:last').addClass('selected')
|
||||||
}
|
}
|
||||||
|
|
||||||
this.scrollSelectedIntoView(true);
|
this.scrollSelectedIntoView(true)
|
||||||
this.apply();
|
this.apply()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle ENTER or TAB keydown events.
|
* Handle ENTER or TAB keydown events.
|
||||||
*/
|
*/
|
||||||
enter () {
|
enter () {
|
||||||
this.apply();
|
this.apply()
|
||||||
this.showingResult = false;
|
this.showingResult = false
|
||||||
},
|
},
|
||||||
|
|
||||||
keyup (e) {
|
keyup (e) {
|
||||||
|
@ -84,41 +84,41 @@ export default {
|
||||||
* The actually result navigation is handled by this.up() and this.down().
|
* The actually result navigation is handled by this.up() and this.down().
|
||||||
*/
|
*/
|
||||||
if (e.keyCode === 38 || e.keyCode === 40) {
|
if (e.keyCode === 38 || e.keyCode === 40) {
|
||||||
e.stopPropagation();
|
e.stopPropagation()
|
||||||
e.preventDefault();
|
e.preventDefault()
|
||||||
|
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it's an ENTER or TAB key, don't do anything.
|
// If it's an ENTER or TAB key, don't do anything.
|
||||||
// We've handled ENTER & TAB on keydown.
|
// We've handled ENTER & TAB on keydown.
|
||||||
if (e.keyCode === 13 || e.keyCode === 9) {
|
if (e.keyCode === 13 || e.keyCode === 9) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hide the typeahead results and reset the value if ESC is pressed.
|
// Hide the typeahead results and reset the value if ESC is pressed.
|
||||||
if (e.keyCode === 27) {
|
if (e.keyCode === 27) {
|
||||||
this.showingResult = false;
|
this.showingResult = false
|
||||||
|
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.filter = this.mutatedValue;
|
this.filter = this.mutatedValue
|
||||||
this.showingResult = true;
|
this.showingResult = true
|
||||||
},
|
},
|
||||||
|
|
||||||
resultClick (e) {
|
resultClick (e) {
|
||||||
$(this.$el).find('.result li.selected').removeClass('selected');
|
$(this.$el).find('.result li.selected').removeClass('selected')
|
||||||
$(e.target).addClass('selected');
|
$(e.target).addClass('selected')
|
||||||
|
|
||||||
this.apply();
|
this.apply()
|
||||||
this.showingResult = false;
|
this.showingResult = false
|
||||||
},
|
},
|
||||||
|
|
||||||
apply () {
|
apply () {
|
||||||
this.mutatedValue = $(this.$el).find('.result li.selected').text().trim() || this.mutatedValue;
|
this.mutatedValue = $(this.$el).find('.result li.selected').text().trim() || this.mutatedValue
|
||||||
// In Vue 2.0, we can use v-model on custom components like this.
|
// In Vue 2.0, we can use v-model on custom components like this.
|
||||||
this.$emit('input', this.mutatedValue);
|
this.$emit('input', this.mutatedValue)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -127,24 +127,24 @@ export default {
|
||||||
* @param {boolean} alignTop Whether the item should be aligned to top of its container.
|
* @param {boolean} alignTop Whether the item should be aligned to top of its container.
|
||||||
*/
|
*/
|
||||||
scrollSelectedIntoView (alignTop) {
|
scrollSelectedIntoView (alignTop) {
|
||||||
const elem = $(this.$el).find('.result li.selected')[0];
|
const elem = $(this.$el).find('.result li.selected')[0]
|
||||||
if (!elem) {
|
if (!elem) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const elemRect = elem.getBoundingClientRect();
|
const elemRect = elem.getBoundingClientRect()
|
||||||
const containerRect = elem.offsetParent.getBoundingClientRect();
|
const containerRect = elem.offsetParent.getBoundingClientRect()
|
||||||
|
|
||||||
if (elemRect.bottom > containerRect.bottom || elemRect.top < containerRect.top) {
|
if (elemRect.bottom > containerRect.bottom || elemRect.top < containerRect.top) {
|
||||||
elem.scrollIntoView(alignTop);
|
elem.scrollIntoView(alignTop)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
hideResults () {
|
hideResults () {
|
||||||
this.showingResult = false;
|
this.showingResult = false
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass" scoped>
|
<style lang="sass" scoped>
|
||||||
|
|
|
@ -43,11 +43,11 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { clone, assign } from 'lodash';
|
import { clone, assign } from 'lodash'
|
||||||
import swal from 'sweetalert';
|
import swal from 'sweetalert'
|
||||||
|
|
||||||
import { userStore } from '../../stores';
|
import { userStore } from '../../stores'
|
||||||
import router from '../../router';
|
import router from '../../router'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['user'],
|
props: ['user'],
|
||||||
|
@ -56,8 +56,8 @@ export default {
|
||||||
return {
|
return {
|
||||||
editing: false,
|
editing: false,
|
||||||
confirmingDelete: false,
|
confirmingDelete: false,
|
||||||
cached: {},
|
cached: {}
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -67,8 +67,8 @@ export default {
|
||||||
* @return {Boolean}
|
* @return {Boolean}
|
||||||
*/
|
*/
|
||||||
isCurrentUser () {
|
isCurrentUser () {
|
||||||
return this.user.id === userStore.current.id;
|
return this.user.id === userStore.current.id
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -78,14 +78,14 @@ export default {
|
||||||
*/
|
*/
|
||||||
edit () {
|
edit () {
|
||||||
if (this.isCurrentUser) {
|
if (this.isCurrentUser) {
|
||||||
router.go('profile');
|
router.go('profile')
|
||||||
|
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep a cached version of the user for rolling back.
|
// Keep a cached version of the user for rolling back.
|
||||||
this.cached = clone(this.user);
|
this.cached = clone(this.user)
|
||||||
this.editing = true;
|
this.editing = true
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -93,8 +93,8 @@ export default {
|
||||||
*/
|
*/
|
||||||
cancelEdit () {
|
cancelEdit () {
|
||||||
// Restore the original user's properties
|
// Restore the original user's properties
|
||||||
assign(this.user, this.cached);
|
assign(this.user, this.cached)
|
||||||
this.editing = false;
|
this.editing = false
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -102,8 +102,8 @@ export default {
|
||||||
*/
|
*/
|
||||||
update () {
|
update () {
|
||||||
userStore.update(this.user, this.user.name, this.user.email, this.user.password).then(u => {
|
userStore.update(this.user, this.user.name, this.user.email, this.user.password).then(u => {
|
||||||
this.editing = false;
|
this.editing = false
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -116,15 +116,15 @@ export default {
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
showCancelButton: true,
|
showCancelButton: true,
|
||||||
confirmButtonText: 'Certainly',
|
confirmButtonText: 'Certainly',
|
||||||
cancelButtonText: 'Oops',
|
cancelButtonText: 'Oops'
|
||||||
}, () => {
|
}, () => {
|
||||||
userStore.destroy(this.user).then(() => {
|
userStore.destroy(this.user).then(() => {
|
||||||
this.$destroy(true);
|
this.$destroy(true)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -10,18 +10,18 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import isMobile from 'ismobilejs';
|
import isMobile from 'ismobilejs'
|
||||||
|
|
||||||
import { event } from '../../utils';
|
import { event } from '../../utils'
|
||||||
import { preferenceStore as preferences } from '../../stores';
|
import { preferenceStore as preferences } from '../../stores'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['mode', 'for'],
|
props: ['mode', 'for'],
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
mutatedMode: this.mode,
|
mutatedMode: this.mode
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -31,32 +31,32 @@ export default {
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
preferenceKey () {
|
preferenceKey () {
|
||||||
return `${this.for}ViewMode`;
|
return `${this.for}ViewMode`
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
setMode (mode) {
|
setMode (mode) {
|
||||||
preferences[this.preferenceKey] = this.mutatedMode = mode;
|
preferences[this.preferenceKey] = this.mutatedMode = mode
|
||||||
this.$parent.changeViewMode(mode);
|
this.$parent.changeViewMode(mode)
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
created () {
|
created () {
|
||||||
event.on('koel:ready', () => {
|
event.on('koel:ready', () => {
|
||||||
this.mutatedMode = preferences[this.preferenceKey];
|
this.mutatedMode = preferences[this.preferenceKey]
|
||||||
|
|
||||||
// If the value is empty, we set a default mode.
|
// If the value is empty, we set a default mode.
|
||||||
// On mobile, the mode should be 'listing'.
|
// On mobile, the mode should be 'listing'.
|
||||||
// For desktop, 'thumbnails'.
|
// For desktop, 'thumbnails'.
|
||||||
if (!this.mutatedMode) {
|
if (!this.mutatedMode) {
|
||||||
this.mutatedMode = isMobile.phone ? 'list' : 'thumbnails';
|
this.mutatedMode = isMobile.phone ? 'list' : 'thumbnails'
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setMode(this.mutatedMode);
|
this.setMode(this.mutatedMode)
|
||||||
});
|
})
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass" scoped>
|
<style lang="sass" scoped>
|
||||||
|
|
|
@ -40,12 +40,13 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { map, cloneDeep } from 'lodash';
|
import { map, cloneDeep } from 'lodash'
|
||||||
import $ from 'jquery';
|
import $ from 'jquery'
|
||||||
import rangeslider from 'rangeslider.js';
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
import rangeslider from 'rangeslider.js'
|
||||||
|
|
||||||
import { isAudioContextSupported, event } from '../../utils';
|
import { isAudioContextSupported, event } from '../../utils'
|
||||||
import { equalizerStore, preferenceStore as preferences } from '../../stores';
|
import { equalizerStore, preferenceStore as preferences } from '../../stores'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data () {
|
data () {
|
||||||
|
@ -53,20 +54,20 @@ export default {
|
||||||
idx: 0,
|
idx: 0,
|
||||||
bands: [],
|
bands: [],
|
||||||
preampGainValue: 0,
|
preampGainValue: 0,
|
||||||
selectedPresetIndex: -1,
|
selectedPresetIndex: -1
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
presets () {
|
presets () {
|
||||||
let clonedPreset = cloneDeep(equalizerStore.presets);
|
const clonedPreset = cloneDeep(equalizerStore.presets)
|
||||||
// Prepend an empty option for instruction purpose.
|
// Prepend an empty option for instruction purpose.
|
||||||
clonedPreset.unshift({
|
clonedPreset.unshift({
|
||||||
id: -1,
|
id: -1,
|
||||||
name: 'Preset',
|
name: 'Preset'
|
||||||
});
|
})
|
||||||
return clonedPreset;
|
return clonedPreset
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -78,12 +79,12 @@ export default {
|
||||||
/**
|
/**
|
||||||
* Save the selected preset (index) into local storage every time the value's changed.
|
* Save the selected preset (index) into local storage every time the value's changed.
|
||||||
*/
|
*/
|
||||||
preferences.selectedPreset = val;
|
preferences.selectedPreset = val
|
||||||
|
|
||||||
if (~~val !== -1) {
|
if (~~val !== -1) {
|
||||||
this.loadPreset(equalizerStore.getPresetById(val));
|
this.loadPreset(equalizerStore.getPresetById(val))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -93,55 +94,56 @@ export default {
|
||||||
* @param {Element} player The audio player's DOM.
|
* @param {Element} player The audio player's DOM.
|
||||||
*/
|
*/
|
||||||
init (player) {
|
init (player) {
|
||||||
const settings = equalizerStore.get();
|
const settings = equalizerStore.get()
|
||||||
|
|
||||||
const AudioContext = window.AudioContext ||
|
const AudioContext = window.AudioContext ||
|
||||||
window.webkitAudioContext ||
|
window.webkitAudioContext ||
|
||||||
window.mozAudioContext ||
|
window.mozAudioContext ||
|
||||||
window.oAudioContext ||
|
window.oAudioContext ||
|
||||||
window.msAudioContext;
|
window.msAudioContext
|
||||||
|
|
||||||
const context = new AudioContext();
|
const context = new AudioContext()
|
||||||
|
|
||||||
this.preampGainNode = context.createGain();
|
this.preampGainNode = context.createGain()
|
||||||
this.changePreampGain(settings.preamp);
|
this.changePreampGain(settings.preamp)
|
||||||
|
|
||||||
const source = context.createMediaElementSource(player);
|
const source = context.createMediaElementSource(player)
|
||||||
source.connect(this.preampGainNode);
|
source.connect(this.preampGainNode)
|
||||||
|
|
||||||
let prevFilter = null;
|
let prevFilter = null
|
||||||
|
|
||||||
// Create 10 bands with the frequencies similar to those of Winamp and connect them together.
|
// Create 10 bands with the frequencies similar to those of Winamp and connect them together.
|
||||||
[60, 170, 310, 600, 1000, 3000, 6000, 12000, 14000, 16000].forEach((f, i) => {
|
const freqs = [60, 170, 310, 600, 1000, 3000, 6000, 12000, 14000, 16000]
|
||||||
const filter = context.createBiquadFilter();
|
freqs.forEach((f, i) => {
|
||||||
|
const filter = context.createBiquadFilter()
|
||||||
|
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
filter.type = 'lowshelf';
|
filter.type = 'lowshelf'
|
||||||
} else if (i === 9) {
|
} else if (i === 9) {
|
||||||
filter.type = 'highshelf';
|
filter.type = 'highshelf'
|
||||||
} else {
|
} else {
|
||||||
filter.type = 'peaking';
|
filter.type = 'peaking'
|
||||||
}
|
}
|
||||||
|
|
||||||
filter.gain.value = settings.gains[i] ? settings.gains[i] : 0;
|
filter.gain.value = settings.gains[i] ? settings.gains[i] : 0
|
||||||
filter.Q.value = 1;
|
filter.Q.value = 1
|
||||||
filter.frequency.value = f;
|
filter.frequency.value = f
|
||||||
|
|
||||||
prevFilter ? prevFilter.connect(filter) : this.preampGainNode.connect(filter);
|
prevFilter ? prevFilter.connect(filter) : this.preampGainNode.connect(filter)
|
||||||
prevFilter = filter;
|
prevFilter = filter
|
||||||
|
|
||||||
this.bands.push({
|
this.bands.push({
|
||||||
filter,
|
filter,
|
||||||
label: (f + '').replace('000', 'K'),
|
label: (f + '').replace('000', 'K')
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
prevFilter.connect(context.destination);
|
prevFilter.connect(context.destination)
|
||||||
|
|
||||||
this.$nextTick(this.createRangeSliders);
|
this.$nextTick(this.createRangeSliders)
|
||||||
|
|
||||||
// Now we set this value to trigger the audio processing.
|
// Now we set this value to trigger the audio processing.
|
||||||
this.selectedPresetIndex = preferences.selectedPreset;
|
this.selectedPresetIndex = preferences.selectedPreset
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -165,9 +167,9 @@ export default {
|
||||||
*/
|
*/
|
||||||
onSlide: (position, value) => {
|
onSlide: (position, value) => {
|
||||||
if ($(el).parents('.band').is('.preamp')) {
|
if ($(el).parents('.band').is('.preamp')) {
|
||||||
this.changePreampGain(value);
|
this.changePreampGain(value)
|
||||||
} else {
|
} else {
|
||||||
this.changeFilterGain(this.bands[i - 1].filter, value);
|
this.changeFilterGain(this.bands[i - 1].filter, value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -175,11 +177,11 @@ export default {
|
||||||
* Save the settings and set the preset index to -1 (which is None) on slideEnd.
|
* Save the settings and set the preset index to -1 (which is None) on slideEnd.
|
||||||
*/
|
*/
|
||||||
onSlideEnd: () => {
|
onSlideEnd: () => {
|
||||||
this.selectedPresetIndex = -1;
|
this.selectedPresetIndex = -1
|
||||||
this.save();
|
this.save()
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -188,8 +190,8 @@ export default {
|
||||||
* @param {Number} dbValue The value of the gain, in dB.
|
* @param {Number} dbValue The value of the gain, in dB.
|
||||||
*/
|
*/
|
||||||
changePreampGain (dbValue) {
|
changePreampGain (dbValue) {
|
||||||
this.preampGainValue = dbValue;
|
this.preampGainValue = dbValue
|
||||||
this.preampGainNode.gain.value = Math.pow(10, dbValue / 20);
|
this.preampGainNode.gain.value = Math.pow(10, dbValue / 20)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -199,7 +201,7 @@ export default {
|
||||||
* @param {Object} value Value of the gain, in dB.
|
* @param {Object} value Value of the gain, in dB.
|
||||||
*/
|
*/
|
||||||
changeFilterGain (filter, value) {
|
changeFilterGain (filter, value) {
|
||||||
filter.gain.value = value;
|
filter.gain.value = value
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -209,37 +211,37 @@ export default {
|
||||||
$('#equalizer input[type=range]').each((i, input) => {
|
$('#equalizer input[type=range]').each((i, input) => {
|
||||||
// We treat our preamp slider differently.
|
// We treat our preamp slider differently.
|
||||||
if ($(input).parents('.band').is('.preamp')) {
|
if ($(input).parents('.band').is('.preamp')) {
|
||||||
this.changePreampGain(preset.preamp);
|
this.changePreampGain(preset.preamp)
|
||||||
} else {
|
} else {
|
||||||
this.changeFilterGain(this.bands[i - 1].filter, preset.gains[i - 1]);
|
this.changeFilterGain(this.bands[i - 1].filter, preset.gains[i - 1])
|
||||||
input.value = preset.gains[i - 1];
|
input.value = preset.gains[i - 1]
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
// Update the slider values into GUI.
|
// Update the slider values into GUI.
|
||||||
$('#equalizer input[type="range"]').rangeslider('update', true);
|
$('#equalizer input[type="range"]').rangeslider('update', true)
|
||||||
});
|
})
|
||||||
|
|
||||||
this.save();
|
this.save()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save the current user's equalizer preferences into local storage.
|
* Save the current user's equalizer preferences into local storage.
|
||||||
*/
|
*/
|
||||||
save () {
|
save () {
|
||||||
equalizerStore.set(this.preampGainValue, map(this.bands, 'filter.gain.value'));
|
equalizerStore.set(this.preampGainValue, map(this.bands, 'filter.gain.value'))
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted () {
|
mounted () {
|
||||||
event.on('equalizer:init', player => {
|
event.on('equalizer:init', player => {
|
||||||
if (isAudioContextSupported()) {
|
if (isAudioContextSupported()) {
|
||||||
this.init(player);
|
this.init(player)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -56,14 +56,13 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import config from '../../config';
|
import { playback } from '../../services'
|
||||||
import { playback } from '../../services';
|
import { isAudioContextSupported, event } from '../../utils'
|
||||||
import { isAudioContextSupported, event } from '../../utils';
|
import { songStore, favoriteStore, preferenceStore } from '../../stores'
|
||||||
import { songStore, favoriteStore, preferenceStore } from '../../stores';
|
|
||||||
|
|
||||||
import soundBar from '../shared/sound-bar.vue';
|
import soundBar from '../shared/sound-bar.vue'
|
||||||
import equalizer from './equalizer.vue';
|
import equalizer from './equalizer.vue'
|
||||||
import volume from './volume.vue';
|
import volume from './volume.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data () {
|
data () {
|
||||||
|
@ -80,8 +79,8 @@ export default {
|
||||||
*
|
*
|
||||||
* @type {Boolean}
|
* @type {Boolean}
|
||||||
*/
|
*/
|
||||||
useEqualizer: isAudioContextSupported(),
|
useEqualizer: isAudioContextSupported()
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
components: { soundBar, equalizer, volume },
|
components: { soundBar, equalizer, volume },
|
||||||
|
@ -93,7 +92,7 @@ export default {
|
||||||
* @return {?Object}
|
* @return {?Object}
|
||||||
*/
|
*/
|
||||||
prev () {
|
prev () {
|
||||||
return playback.previous;
|
return playback.previous
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -102,8 +101,8 @@ export default {
|
||||||
* @return {?Object}
|
* @return {?Object}
|
||||||
*/
|
*/
|
||||||
next () {
|
next () {
|
||||||
return playback.next;
|
return playback.next
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -111,14 +110,14 @@ export default {
|
||||||
* Play the previous song in queue.
|
* Play the previous song in queue.
|
||||||
*/
|
*/
|
||||||
playPrev () {
|
playPrev () {
|
||||||
return playback.playPrev();
|
return playback.playPrev()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Play the next song in queue.
|
* Play the next song in queue.
|
||||||
*/
|
*/
|
||||||
playNext () {
|
playNext () {
|
||||||
return playback.playNext();
|
return playback.playNext()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -127,24 +126,24 @@ export default {
|
||||||
*/
|
*/
|
||||||
resume () {
|
resume () {
|
||||||
if (!this.song.id) {
|
if (!this.song.id) {
|
||||||
return playback.playFirstInQueue();
|
return playback.playFirstInQueue()
|
||||||
}
|
}
|
||||||
|
|
||||||
playback.resume();
|
playback.resume()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pause the playback.
|
* Pause the playback.
|
||||||
*/
|
*/
|
||||||
pause () {
|
pause () {
|
||||||
playback.pause();
|
playback.pause()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change the repeat mode.
|
* Change the repeat mode.
|
||||||
*/
|
*/
|
||||||
changeRepeatMode () {
|
changeRepeatMode () {
|
||||||
return playback.changeRepeatMode();
|
return playback.changeRepeatMode()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -152,22 +151,22 @@ export default {
|
||||||
*/
|
*/
|
||||||
like () {
|
like () {
|
||||||
if (!this.song.id) {
|
if (!this.song.id) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
favoriteStore.toggleOne(this.song);
|
favoriteStore.toggleOne(this.song)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle hide or show the extra panel.
|
* Toggle hide or show the extra panel.
|
||||||
*/
|
*/
|
||||||
toggleExtraPanel () {
|
toggleExtraPanel () {
|
||||||
preferenceStore.set('showExtraPanel', !this.prefs.showExtraPanel);
|
preferenceStore.set('showExtraPanel', !this.prefs.showExtraPanel)
|
||||||
},
|
},
|
||||||
|
|
||||||
closeEqualizer () {
|
closeEqualizer () {
|
||||||
this.showEqualizer = false;
|
this.showEqualizer = false
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
created () {
|
created () {
|
||||||
|
@ -180,20 +179,24 @@ export default {
|
||||||
* @return {Boolean}
|
* @return {Boolean}
|
||||||
*/
|
*/
|
||||||
'song:played': song => {
|
'song:played': song => {
|
||||||
this.song = song;
|
this.song = song
|
||||||
this.cover = this.song.album.cover;
|
this.cover = this.song.album.cover
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listen to main-content-view:load event and highlight the Queue icon if
|
* Listen to main-content-view:load event and highlight the Queue icon if
|
||||||
* the Queue screen is being loaded.
|
* the Queue screen is being loaded.
|
||||||
*/
|
*/
|
||||||
'main-content-view:load': view => this.viewingQueue = view === 'queue',
|
'main-content-view:load': view => {
|
||||||
|
this.viewingQueue = view === 'queue'
|
||||||
'koel:teardown': () => this.song = songStore.stub,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
};
|
|
||||||
|
'koel:teardown': () => {
|
||||||
|
this.song = songStore.stub
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -7,13 +7,13 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { playback } from '../../services';
|
import { playback } from '../../services'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
muted: false,
|
muted: false
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -21,21 +21,19 @@ export default {
|
||||||
* Mute the volume.
|
* Mute the volume.
|
||||||
*/
|
*/
|
||||||
mute () {
|
mute () {
|
||||||
this.muted = true;
|
this.muted = true
|
||||||
|
return playback.mute()
|
||||||
return playback.mute();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unmute the volume.
|
* Unmute the volume.
|
||||||
*/
|
*/
|
||||||
unmute () {
|
unmute () {
|
||||||
this.muted = false;
|
this.muted = false
|
||||||
|
return playback.unmute()
|
||||||
return playback.unmute();
|
}
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -14,18 +14,18 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import config from '../../config';
|
import config from '../../config'
|
||||||
import { event } from '../../utils';
|
import { event } from '../../utils'
|
||||||
import searchForm from './search-form.vue';
|
import searchForm from './search-form.vue'
|
||||||
import userBadge from './user-badge.vue';
|
import userBadge from './user-badge.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { searchForm, userBadge },
|
components: { searchForm, userBadge },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
appTitle: config.appTitle,
|
appTitle: config.appTitle
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -33,17 +33,17 @@ export default {
|
||||||
* No I'm not documenting this.
|
* No I'm not documenting this.
|
||||||
*/
|
*/
|
||||||
toggleSidebar () {
|
toggleSidebar () {
|
||||||
event.emit('sidebar:toggle');
|
event.emit('sidebar:toggle')
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* or this.
|
* or this.
|
||||||
*/
|
*/
|
||||||
toggleSearchForm () {
|
toggleSearchForm () {
|
||||||
event.emit('search:toggle');
|
event.emit('search:toggle')
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -10,10 +10,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import isMobile from 'ismobilejs';
|
import isMobile from 'ismobilejs'
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash'
|
||||||
|
|
||||||
import { event } from '../../utils';
|
import { event } from '../../utils'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'site-header--search-form',
|
name: 'site-header--search-form',
|
||||||
|
@ -21,8 +21,8 @@ export default {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
q: '',
|
q: '',
|
||||||
showing: !isMobile.phone,
|
showing: !isMobile.phone
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -30,21 +30,21 @@ export default {
|
||||||
* Limit the filter's execution rate using lodash's debounce.
|
* Limit the filter's execution rate using lodash's debounce.
|
||||||
*/
|
*/
|
||||||
filter: debounce(function () {
|
filter: debounce(function () {
|
||||||
event.emit('filter:changed', this.q);
|
event.emit('filter:changed', this.q)
|
||||||
}, 200),
|
}, 200)
|
||||||
},
|
},
|
||||||
|
|
||||||
created () {
|
created () {
|
||||||
event.on('search:toggle', () => {
|
event.on('search:toggle', () => {
|
||||||
this.showing = !this.showing;
|
this.showing = !this.showing
|
||||||
});
|
})
|
||||||
|
|
||||||
event.on('koel:teardown', () => {
|
event.on('koel:teardown', () => {
|
||||||
this.q = '';
|
this.q = ''
|
||||||
this.filter();
|
this.filter()
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -10,24 +10,24 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { userStore } from '../../stores';
|
import { userStore } from '../../stores'
|
||||||
import { event } from '../../utils';
|
import { event } from '../../utils'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'site-header--user-badge',
|
name: 'site-header--user-badge',
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
state: userStore.state,
|
state: userStore.state
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
logout () {
|
logout () {
|
||||||
event.emit('logout');
|
event.emit('logout')
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export default {
|
export default {
|
||||||
unknownCover: (typeof window !== 'undefined' ? window.location.href : '/') + 'public/img/covers/unknown-album.png',
|
unknownCover: (typeof window !== 'undefined' ? window.location.href : '/') + 'public/img/covers/unknown-album.png',
|
||||||
appTitle: 'Koel',
|
appTitle: 'Koel'
|
||||||
};
|
}
|
||||||
|
|
|
@ -6,14 +6,14 @@
|
||||||
export const clickawayDirective = {
|
export const clickawayDirective = {
|
||||||
bind (el, { value }) {
|
bind (el, { value }) {
|
||||||
if (typeof value !== 'function') {
|
if (typeof value !== 'function') {
|
||||||
console.warn(`Expect a function, got ${value}`);
|
console.warn(`Expect a function, got ${value}`)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('click', e => {
|
document.addEventListener('click', e => {
|
||||||
if (!el.contains(e.target)) {
|
if (!el.contains(e.target)) {
|
||||||
value();
|
value()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
});
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
import Vue from 'vue';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple directive to set focus into an input field when it's shown.
|
* A simple directive to set focus into an input field when it's shown.
|
||||||
*/
|
*/
|
||||||
export const focusDirective = {
|
export const focusDirective = {
|
||||||
inserted (el) {
|
inserted (el) {
|
||||||
el.focus();
|
el.focus()
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
export * from './focus';
|
export * from './focus'
|
||||||
export * from './clickaway';
|
export * from './clickaway'
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import Vue from 'vue';
|
import Vue from 'vue'
|
||||||
|
|
||||||
import { event } from './utils';
|
import { event } from './utils'
|
||||||
import { http } from './services';
|
import { http } from './services'
|
||||||
/**
|
/**
|
||||||
* For Ancelot, the ancient cross of war
|
* For Ancelot, the ancient cross of war
|
||||||
* for the holy town of Gods
|
* for the holy town of Gods
|
||||||
|
@ -12,7 +12,7 @@ new Vue({
|
||||||
el: '#app',
|
el: '#app',
|
||||||
render: h => h(require('./app.vue')),
|
render: h => h(require('./app.vue')),
|
||||||
created () {
|
created () {
|
||||||
event.init();
|
event.init()
|
||||||
http.init();
|
http.init()
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
* Add necessary functionalities into a view that contains a song-list component.
|
* Add necessary functionalities into a view that contains a song-list component.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { assign } from 'lodash';
|
import { assign } from 'lodash'
|
||||||
import isMobile from 'ismobilejs';
|
import isMobile from 'ismobilejs'
|
||||||
|
|
||||||
import { playback } from '../services';
|
import { playback } from '../services'
|
||||||
import songList from '../components/shared/song-list.vue';
|
import songList from '../components/shared/song-list.vue'
|
||||||
import songListControls from '../components/shared/song-list-controls.vue';
|
import songListControls from '../components/shared/song-list-controls.vue'
|
||||||
import controlsToggler from '../components/shared/song-list-controls-toggler.vue';
|
import controlsToggler from '../components/shared/song-list-controls-toggler.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { songList, songListControls, controlsToggler },
|
components: { songList, songListControls, controlsToggler },
|
||||||
|
@ -18,34 +18,34 @@ export default {
|
||||||
state: null,
|
state: null,
|
||||||
meta: {
|
meta: {
|
||||||
songCount: 0,
|
songCount: 0,
|
||||||
totalLength: '00:00',
|
totalLength: '00:00'
|
||||||
},
|
},
|
||||||
selectedSongs: [],
|
selectedSongs: [],
|
||||||
showingControls: false,
|
showingControls: false,
|
||||||
songListControlConfig: {},
|
songListControlConfig: {},
|
||||||
isPhone: isMobile.phone,
|
isPhone: isMobile.phone
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
setSelectedSongs (songs) {
|
setSelectedSongs (songs) {
|
||||||
this.selectedSongs = songs;
|
this.selectedSongs = songs
|
||||||
},
|
},
|
||||||
|
|
||||||
updateMeta (meta) {
|
updateMeta (meta) {
|
||||||
this.meta = assign(this.meta, meta);
|
this.meta = assign(this.meta, meta)
|
||||||
},
|
},
|
||||||
|
|
||||||
shuffleAll () {
|
shuffleAll () {
|
||||||
playback.queueAndPlay(this.state.songs, true);
|
playback.queueAndPlay(this.state.songs, true)
|
||||||
},
|
},
|
||||||
|
|
||||||
shuffleSelected () {
|
shuffleSelected () {
|
||||||
playback.queueAndPlay(this.selectedSongs, true);
|
playback.queueAndPlay(this.selectedSongs, true)
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleControls () {
|
toggleControls () {
|
||||||
this.showingControls = !this.showingControls;
|
this.showingControls = !this.showingControls
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import $ from 'jquery';
|
import $ from 'jquery'
|
||||||
|
|
||||||
import { event } from '../utils';
|
import { event } from '../utils'
|
||||||
import toTopButton from '../components/shared/to-top-button.vue';
|
import toTopButton from '../components/shared/to-top-button.vue'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a "infinite scroll" functionality to any component using this mixin.
|
* Add a "infinite scroll" functionality to any component using this mixin.
|
||||||
|
@ -15,8 +15,8 @@ export default {
|
||||||
return {
|
return {
|
||||||
numOfItems: 30, // Number of currently loaded and displayed items
|
numOfItems: 30, // Number of currently loaded and displayed items
|
||||||
perPage: 30, // Number of items to be loaded per "page"
|
perPage: 30, // Number of items to be loaded per "page"
|
||||||
showBackToTop: false,
|
showBackToTop: false
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -24,32 +24,32 @@ export default {
|
||||||
// Here we check if the user has scrolled to the end of the wrapper (or 32px to the end).
|
// Here we check if the user has scrolled to the end of the wrapper (or 32px to the end).
|
||||||
// If that's true, load more items.
|
// If that's true, load more items.
|
||||||
if (e.target.scrollTop + e.target.clientHeight >= e.target.scrollHeight - 32) {
|
if (e.target.scrollTop + e.target.clientHeight >= e.target.scrollHeight - 32) {
|
||||||
this.displayMore();
|
this.displayMore()
|
||||||
}
|
}
|
||||||
|
|
||||||
this.showBackToTop = e.target.scrollTop > 64;
|
this.showBackToTop = e.target.scrollTop > 64
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load and display more items into the scrollable area.
|
* Load and display more items into the scrollable area.
|
||||||
*/
|
*/
|
||||||
displayMore () {
|
displayMore () {
|
||||||
this.numOfItems += this.perPage;
|
this.numOfItems += this.perPage
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scroll to top of the wrapper.
|
* Scroll to top of the wrapper.
|
||||||
*/
|
*/
|
||||||
scrollToTop () {
|
scrollToTop () {
|
||||||
$(this.$refs.wrapper).animate({ scrollTop: 0 }, 500);
|
$(this.$refs.wrapper).animate({ scrollTop: 0 }, 500)
|
||||||
this.showBackToTop = false;
|
this.showBackToTop = false
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
created () {
|
created () {
|
||||||
event.on('koel:teardown', () => {
|
event.on('koel:teardown', () => {
|
||||||
this.numOfItems = 30;
|
this.numOfItems = 30
|
||||||
this.showBackToTop = false;
|
this.showBackToTop = false
|
||||||
});
|
})
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import $ from 'jquery';
|
import $ from 'jquery'
|
||||||
|
|
||||||
import { queueStore, playlistStore, favoriteStore } from '../stores';
|
import { queueStore, playlistStore, favoriteStore } from '../stores'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Includes the methods triggerable on a song (context) menu.
|
* Includes the methods triggerable on a song (context) menu.
|
||||||
|
@ -13,8 +13,8 @@ export default {
|
||||||
return {
|
return {
|
||||||
shown: false,
|
shown: false,
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -24,41 +24,40 @@ export default {
|
||||||
* Close all submenus.
|
* Close all submenus.
|
||||||
*/
|
*/
|
||||||
close () {
|
close () {
|
||||||
$(this.$el).find('.submenu').hide();
|
$(this.$el).find('.submenu').hide()
|
||||||
|
this.shown = false
|
||||||
this.shown = false;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queue select songs after the current song.
|
* Queue select songs after the current song.
|
||||||
*/
|
*/
|
||||||
queueSongsAfterCurrent () {
|
queueSongsAfterCurrent () {
|
||||||
queueStore.queueAfterCurrent(this.songs);
|
queueStore.queueAfterCurrent(this.songs)
|
||||||
this.close();
|
this.close()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queue selected songs to bottom of queue.
|
* Queue selected songs to bottom of queue.
|
||||||
*/
|
*/
|
||||||
queueSongsToBottom () {
|
queueSongsToBottom () {
|
||||||
queueStore.queue(this.songs);
|
queueStore.queue(this.songs)
|
||||||
this.close();
|
this.close()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queue selected songs to top of queue.
|
* Queue selected songs to top of queue.
|
||||||
*/
|
*/
|
||||||
queueSongsToTop () {
|
queueSongsToTop () {
|
||||||
queueStore.queue(this.songs, false, true);
|
queueStore.queue(this.songs, false, true)
|
||||||
this.close();
|
this.close()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add the selected songs into Favorites.
|
* Add the selected songs into Favorites.
|
||||||
*/
|
*/
|
||||||
addSongsToFavorite () {
|
addSongsToFavorite () {
|
||||||
favoriteStore.like(this.songs);
|
favoriteStore.like(this.songs)
|
||||||
this.close();
|
this.close()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -67,8 +66,8 @@ export default {
|
||||||
* @param {Object} playlist The playlist.
|
* @param {Object} playlist The playlist.
|
||||||
*/
|
*/
|
||||||
addSongsToExistingPlaylist (playlist) {
|
addSongsToExistingPlaylist (playlist) {
|
||||||
playlistStore.addSongs(playlist, this.songs);
|
playlistStore.addSongs(playlist, this.songs)
|
||||||
this.close();
|
this.close()
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,66 +1,66 @@
|
||||||
import isMobile from 'ismobilejs';
|
import isMobile from 'ismobilejs'
|
||||||
|
|
||||||
import { loadMainView } from './utils';
|
import { loadMainView } from './utils'
|
||||||
import { artistStore, albumStore, songStore, queueStore, playlistStore, userStore } from './stores';
|
import { artistStore, albumStore, songStore, queueStore, playlistStore, userStore } from './stores'
|
||||||
import { playback } from './services';
|
import { playback } from './services'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
routes: {
|
routes: {
|
||||||
'/home' () {
|
'/home' () {
|
||||||
loadMainView('home');
|
loadMainView('home')
|
||||||
},
|
},
|
||||||
|
|
||||||
'/queue' () {
|
'/queue' () {
|
||||||
loadMainView('queue');
|
loadMainView('queue')
|
||||||
},
|
},
|
||||||
|
|
||||||
'/songs' () {
|
'/songs' () {
|
||||||
loadMainView('songs');
|
loadMainView('songs')
|
||||||
},
|
},
|
||||||
|
|
||||||
'/albums' () {
|
'/albums' () {
|
||||||
loadMainView('albums');
|
loadMainView('albums')
|
||||||
},
|
},
|
||||||
|
|
||||||
'/album/(\\d+)' (id) {
|
'/album/(\\d+)' (id) {
|
||||||
const album = albumStore.byId(~~id);
|
const album = albumStore.byId(~~id)
|
||||||
if (album) {
|
if (album) {
|
||||||
loadMainView('album', album);
|
loadMainView('album', album)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
'/artists' () {
|
'/artists' () {
|
||||||
loadMainView('artists');
|
loadMainView('artists')
|
||||||
},
|
},
|
||||||
|
|
||||||
'/artist/(\\d+)' (id) {
|
'/artist/(\\d+)' (id) {
|
||||||
const artist = artistStore.byId(~~id);
|
const artist = artistStore.byId(~~id)
|
||||||
if (artist) {
|
if (artist) {
|
||||||
loadMainView('artist', artist);
|
loadMainView('artist', artist)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
'/favorites' () {
|
'/favorites' () {
|
||||||
loadMainView('favorites');
|
loadMainView('favorites')
|
||||||
},
|
},
|
||||||
|
|
||||||
'/playlist/(\\d+)' (id) {
|
'/playlist/(\\d+)' (id) {
|
||||||
const playlist = playlistStore.byId(~~id);
|
const playlist = playlistStore.byId(~~id)
|
||||||
if (playlist) {
|
if (playlist) {
|
||||||
loadMainView('playlist', playlist);
|
loadMainView('playlist', playlist)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
'/settings' () {
|
'/settings' () {
|
||||||
userStore.current.is_admin && loadMainView('settings');
|
userStore.current.is_admin && loadMainView('settings')
|
||||||
},
|
},
|
||||||
|
|
||||||
'/users' () {
|
'/users' () {
|
||||||
userStore.current.is_admin && loadMainView('users');
|
userStore.current.is_admin && loadMainView('users')
|
||||||
},
|
},
|
||||||
|
|
||||||
'/profile' () {
|
'/profile' () {
|
||||||
loadMainView('profile');
|
loadMainView('profile')
|
||||||
},
|
},
|
||||||
|
|
||||||
'/login' () {
|
'/login' () {
|
||||||
|
@ -68,41 +68,41 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
'/song/([a-z0-9]{32})' (id) {
|
'/song/([a-z0-9]{32})' (id) {
|
||||||
const song = songStore.byId(id);
|
const song = songStore.byId(id)
|
||||||
if (!song) return;
|
if (!song) return
|
||||||
|
|
||||||
if (isMobile.apple.device) {
|
if (isMobile.apple.device) {
|
||||||
// Mobile Safari doesn't allow autoplay, so we just queue.
|
// Mobile Safari doesn't allow autoplay, so we just queue.
|
||||||
queueStore.queue(song);
|
queueStore.queue(song)
|
||||||
loadMainView('queue');
|
loadMainView('queue')
|
||||||
} else {
|
} else {
|
||||||
playback.queueAndPlay(song);
|
playback.queueAndPlay(song)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
'/youtube' () {
|
'/youtube' () {
|
||||||
loadMainView('youtubePlayer');
|
loadMainView('youtubePlayer')
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
init () {
|
init () {
|
||||||
this.loadState();
|
this.loadState()
|
||||||
window.addEventListener('popstate', () => this.loadState(), true);
|
window.addEventListener('popstate', () => this.loadState(), true)
|
||||||
},
|
},
|
||||||
|
|
||||||
loadState () {
|
loadState () {
|
||||||
if (!window.location.hash) {
|
if (!window.location.hash) {
|
||||||
return this.go('home');
|
return this.go('home')
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.keys(this.routes).forEach(route => {
|
Object.keys(this.routes).forEach(route => {
|
||||||
const matches = window.location.hash.match(new RegExp(`^#!${route}$`));
|
const matches = window.location.hash.match(new RegExp(`^#!${route}$`))
|
||||||
if (matches) {
|
if (matches) {
|
||||||
const [, ...params] = matches;
|
const [, ...params] = matches
|
||||||
this.routes[route](...params);
|
this.routes[route](...params)
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -112,13 +112,13 @@ export default {
|
||||||
*/
|
*/
|
||||||
go (path) {
|
go (path) {
|
||||||
if (path[0] !== '/') {
|
if (path[0] !== '/') {
|
||||||
path = `/${path}`;
|
path = `/${path}`
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path.indexOf('/#!') !== 0) {
|
if (path.indexOf('/#!') !== 0) {
|
||||||
path = `/#!${path}`;
|
path = `/#!${path}`
|
||||||
}
|
}
|
||||||
|
|
||||||
document.location.href = `${document.location.origin}${path}`;
|
document.location.href = `${document.location.origin}${path}`
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import $ from 'jquery';
|
import $ from 'jquery'
|
||||||
import { map } from 'lodash';
|
import { map } from 'lodash'
|
||||||
|
|
||||||
import { playlistStore, favoriteStore } from '../stores';
|
import { playlistStore, favoriteStore } from '../stores'
|
||||||
import { ls } from '.';
|
import { ls } from '.'
|
||||||
|
|
||||||
export const download = {
|
export const download = {
|
||||||
/**
|
/**
|
||||||
|
@ -11,11 +11,11 @@ export const download = {
|
||||||
* @param {Array.<Object>|Object} songs
|
* @param {Array.<Object>|Object} songs
|
||||||
*/
|
*/
|
||||||
fromSongs (songs) {
|
fromSongs (songs) {
|
||||||
songs = [].concat(songs);
|
songs = [].concat(songs)
|
||||||
const ids = map(songs, 'id');
|
const ids = map(songs, 'id')
|
||||||
const params = $.param({ songs: ids });
|
const params = $.param({ songs: ids })
|
||||||
|
|
||||||
return this.trigger(`songs?${params}`);
|
return this.trigger(`songs?${params}`)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,7 +24,7 @@ export const download = {
|
||||||
* @param {Object} album
|
* @param {Object} album
|
||||||
*/
|
*/
|
||||||
fromAlbum (album) {
|
fromAlbum (album) {
|
||||||
return this.trigger(`album/${album.id}`);
|
return this.trigger(`album/${album.id}`)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,7 +36,7 @@ export const download = {
|
||||||
// It's safe to assume an artist always has songs.
|
// It's safe to assume an artist always has songs.
|
||||||
// After all, what's an artist without her songs?
|
// After all, what's an artist without her songs?
|
||||||
// (See what I did there? Yes, I'm advocating for women's rights).
|
// (See what I did there? Yes, I'm advocating for women's rights).
|
||||||
return this.trigger(`artist/${artist.id}`);
|
return this.trigger(`artist/${artist.id}`)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -46,11 +46,11 @@ export const download = {
|
||||||
*/
|
*/
|
||||||
fromPlaylist (playlist) {
|
fromPlaylist (playlist) {
|
||||||
if (!playlistStore.getSongs(playlist).length) {
|
if (!playlistStore.getSongs(playlist).length) {
|
||||||
console.warn('Empty playlist.');
|
console.warn('Empty playlist.')
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.trigger(`playlist/${playlist.id}`);
|
return this.trigger(`playlist/${playlist.id}`)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -58,11 +58,11 @@ export const download = {
|
||||||
*/
|
*/
|
||||||
fromFavorites () {
|
fromFavorites () {
|
||||||
if (!favoriteStore.all.length) {
|
if (!favoriteStore.all.length) {
|
||||||
console.warn("You don't like any song? Come on, don't be that grumpy.");
|
console.warn("You don't like any song? Come on, don't be that grumpy.")
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.trigger('favorites');
|
return this.trigger('favorites')
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -72,9 +72,9 @@ export const download = {
|
||||||
* artist, playlist, or album.
|
* artist, playlist, or album.
|
||||||
*/
|
*/
|
||||||
trigger (uri) {
|
trigger (uri) {
|
||||||
const sep = uri.indexOf('?') === -1 ? '?' : '&';
|
const sep = uri.indexOf('?') === -1 ? '?' : '&'
|
||||||
const frameId = `downloader${Date.now()}`;
|
const frameId = `downloader${Date.now()}`
|
||||||
$(`<iframe id="${frameId}" style="display:none"></iframe`).appendTo('body');
|
$(`<iframe id="${frameId}" style="display:none"></iframe`).appendTo('body')
|
||||||
document.getElementById(frameId).src = `/api/download/${uri}${sep}jwt-token=${ls.get('jwt-token')}`;
|
document.getElementById(frameId).src = `/api/download/${uri}${sep}jwt-token=${ls.get('jwt-token')}`
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import $ from 'jquery';
|
import $ from 'jquery'
|
||||||
import NProgress from 'nprogress';
|
import NProgress from 'nprogress'
|
||||||
|
|
||||||
import { event } from '../utils';
|
import { event } from '../utils'
|
||||||
import { ls } from '../services';
|
import { ls } from '../services'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Responsible for all HTTP requests.
|
* Responsible for all HTTP requests.
|
||||||
|
@ -15,25 +15,25 @@ export const http = {
|
||||||
url: `/api/${url}`,
|
url: `/api/${url}`,
|
||||||
method: method.toUpperCase(),
|
method: method.toUpperCase(),
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${ls.get('jwt-token')}`,
|
Authorization: `Bearer ${ls.get('jwt-token')}`
|
||||||
}
|
}
|
||||||
}).done(successCb).fail(errorCb);
|
}).done(successCb).fail(errorCb)
|
||||||
},
|
},
|
||||||
|
|
||||||
get (url, successCb = null, errorCb = null) {
|
get (url, successCb = null, errorCb = null) {
|
||||||
return this.request('get', url, {}, successCb, errorCb);
|
return this.request('get', url, {}, successCb, errorCb)
|
||||||
},
|
},
|
||||||
|
|
||||||
post (url, data, successCb = null, errorCb = null) {
|
post (url, data, successCb = null, errorCb = null) {
|
||||||
return this.request('post', url, data, successCb, errorCb);
|
return this.request('post', url, data, successCb, errorCb)
|
||||||
},
|
},
|
||||||
|
|
||||||
put (url, data, successCb = null, errorCb = null) {
|
put (url, data, successCb = null, errorCb = null) {
|
||||||
return this.request('put', url, data, successCb, errorCb);
|
return this.request('put', url, data, successCb, errorCb)
|
||||||
},
|
},
|
||||||
|
|
||||||
delete (url, data = {}, successCb = null, errorCb = null) {
|
delete (url, data = {}, successCb = null, errorCb = null) {
|
||||||
return this.request('delete', url, data, successCb, errorCb);
|
return this.request('delete', url, data, successCb, errorCb)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,24 +41,24 @@ export const http = {
|
||||||
*/
|
*/
|
||||||
init () {
|
init () {
|
||||||
$(document).ajaxComplete((e, r, settings) => {
|
$(document).ajaxComplete((e, r, settings) => {
|
||||||
NProgress.done();
|
NProgress.done()
|
||||||
|
|
||||||
if (r.status === 400 || r.status === 401) {
|
if (r.status === 400 || r.status === 401) {
|
||||||
if (!(settings.method === 'POST' && /\/api\/me\/?$/.test(settings.url))) {
|
if (!(settings.method === 'POST' && /\/api\/me\/?$/.test(settings.url))) {
|
||||||
// This is not a failed login. Log out then.
|
// This is not a failed login. Log out then.
|
||||||
event.emit('logout');
|
event.emit('logout')
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = r.getResponseHeader('Authorization');
|
const token = r.getResponseHeader('Authorization')
|
||||||
if (token) {
|
if (token) {
|
||||||
ls.set('jwt-token', token);
|
ls.set('jwt-token', token)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r.responseJSON && r.responseJSON.token && r.responseJSON.token.length > 10) {
|
if (r.responseJSON && r.responseJSON.token && r.responseJSON.token.length > 10) {
|
||||||
ls.set('jwt-token', r.responseJSON.token);
|
ls.set('jwt-token', r.responseJSON.token)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
export * from './info';
|
export * from './info'
|
||||||
export * from './download';
|
export * from './download'
|
||||||
export * from './http';
|
export * from './http'
|
||||||
export * from './ls';
|
export * from './ls'
|
||||||
export * from './playback';
|
export * from './playback'
|
||||||
export * from './youtube';
|
export * from './youtube'
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { each } from 'lodash';
|
import { each } from 'lodash'
|
||||||
|
|
||||||
import { secondsToHis } from '../../utils';
|
import { secondsToHis } from '../../utils'
|
||||||
import { http } from '..';
|
import { http } from '..'
|
||||||
|
|
||||||
export const albumInfo = {
|
export const albumInfo = {
|
||||||
/**
|
/**
|
||||||
|
@ -12,15 +12,15 @@ export const albumInfo = {
|
||||||
fetch (album) {
|
fetch (album) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (album.info) {
|
if (album.info) {
|
||||||
resolve(album);
|
resolve(album)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
http.get(`album/${album.id}/info`, data => {
|
http.get(`album/${album.id}/info`, data => {
|
||||||
data && this.merge(album, data);
|
data && this.merge(album, data)
|
||||||
resolve(album);
|
resolve(album)
|
||||||
}, r => reject(r));
|
}, r => reject(r))
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,18 +31,20 @@ export const albumInfo = {
|
||||||
*/
|
*/
|
||||||
merge (album, info) {
|
merge (album, info) {
|
||||||
// Convert the duration into i:s
|
// Convert the duration into i:s
|
||||||
info.tracks && each(info.tracks, track => track.fmtLength = secondsToHis(track.length));
|
info.tracks && each(info.tracks, track => {
|
||||||
|
track.fmtLength = secondsToHis(track.length)
|
||||||
|
})
|
||||||
|
|
||||||
// If the album cover is not in a nice form, discard.
|
// If the album cover is not in a nice form, discard.
|
||||||
if (typeof info.image !== 'string') {
|
if (typeof info.image !== 'string') {
|
||||||
info.image = null;
|
info.image = null
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the album cover on the client side to the retrieved image from server.
|
// Set the album cover on the client side to the retrieved image from server.
|
||||||
if (info.cover) {
|
if (info.cover) {
|
||||||
album.cover = info.cover;
|
album.cover = info.cover
|
||||||
}
|
}
|
||||||
|
|
||||||
album.info = info;
|
album.info = info
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { http } from '..';
|
import { http } from '..'
|
||||||
|
|
||||||
export const artistInfo = {
|
export const artistInfo = {
|
||||||
/**
|
/**
|
||||||
|
@ -9,15 +9,15 @@ export const artistInfo = {
|
||||||
fetch (artist) {
|
fetch (artist) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (artist.info) {
|
if (artist.info) {
|
||||||
resolve(artist);
|
resolve(artist)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
http.get(`artist/${artist.id}/info`, data => {
|
http.get(`artist/${artist.id}/info`, data => {
|
||||||
data && this.merge(artist, data);
|
data && this.merge(artist, data)
|
||||||
resolve(artist);
|
resolve(artist)
|
||||||
}, r => reject(r));
|
}, r => reject(r))
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,14 +29,14 @@ export const artistInfo = {
|
||||||
merge (artist, info) {
|
merge (artist, info) {
|
||||||
// If the artist image is not in a nice form, discard.
|
// If the artist image is not in a nice form, discard.
|
||||||
if (typeof info.image !== 'string') {
|
if (typeof info.image !== 'string') {
|
||||||
info.image = null;
|
info.image = null
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the artist image on the client side to the retrieved image from server.
|
// Set the artist image on the client side to the retrieved image from server.
|
||||||
if (info.image) {
|
if (info.image) {
|
||||||
artist.image = info.image;
|
artist.image = info.image
|
||||||
}
|
}
|
||||||
|
|
||||||
artist.info = info;
|
artist.info = info
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
export * from './album';
|
export * from './album'
|
||||||
export * from './artist';
|
export * from './artist'
|
||||||
export * from './song';
|
export * from './song'
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { http, albumInfo, artistInfo } from '..';
|
import { http, albumInfo, artistInfo } from '..'
|
||||||
|
|
||||||
export const songInfo = {
|
export const songInfo = {
|
||||||
/**
|
/**
|
||||||
|
@ -10,18 +10,18 @@ export const songInfo = {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// Check if the song's info has been retrieved before.
|
// Check if the song's info has been retrieved before.
|
||||||
if (song.infoRetrieved) {
|
if (song.infoRetrieved) {
|
||||||
resolve(song);
|
resolve(song)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
http.get(`${song.id}/info`, data => {
|
http.get(`${song.id}/info`, data => {
|
||||||
song.lyrics = data.lyrics;
|
song.lyrics = data.lyrics
|
||||||
data.artist_info && artistInfo.merge(song.artist, data.artist_info);
|
data.artist_info && artistInfo.merge(song.artist, data.artist_info)
|
||||||
data.album_info && albumInfo.merge(song.album, data.album_info);
|
data.album_info && albumInfo.merge(song.album, data.album_info)
|
||||||
song.youtube = data.youtube;
|
song.youtube = data.youtube
|
||||||
song.infoRetrieved = true;
|
song.infoRetrieved = true
|
||||||
resolve(song);
|
resolve(song)
|
||||||
}, r => reject(r));
|
}, r => reject(r))
|
||||||
});
|
})
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import localStore from 'local-storage';
|
import localStore from 'local-storage'
|
||||||
|
|
||||||
export const ls = {
|
export const ls = {
|
||||||
get (key, defaultVal = null) {
|
get (key, defaultVal = null) {
|
||||||
const val = localStore(key);
|
const val = localStore(key)
|
||||||
|
|
||||||
return val ? val : defaultVal;
|
return val || defaultVal
|
||||||
},
|
},
|
||||||
|
|
||||||
set (key, val) {
|
set (key, val) {
|
||||||
return localStore(key, val);
|
return localStore(key, val)
|
||||||
},
|
},
|
||||||
|
|
||||||
remove (key) {
|
remove (key) {
|
||||||
return localStore.remove(key);
|
return localStore.remove(key)
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { shuffle, orderBy } from 'lodash';
|
import { shuffle, orderBy } from 'lodash'
|
||||||
import $ from 'jquery';
|
import $ from 'jquery'
|
||||||
import plyr from 'plyr';
|
import plyr from 'plyr'
|
||||||
import Vue from 'vue';
|
import Vue from 'vue'
|
||||||
|
|
||||||
import { event } from '../utils';
|
import { event } from '../utils'
|
||||||
import { queueStore, sharedStore, userStore, songStore, artistStore, preferenceStore as preferences } from '../stores';
|
import { queueStore, sharedStore, userStore, songStore, preferenceStore as preferences } from '../stores'
|
||||||
import config from '../config';
|
import config from '../config'
|
||||||
import router from '../router';
|
import router from '../router'
|
||||||
|
|
||||||
export const playback = {
|
export const playback = {
|
||||||
player: null,
|
player: null,
|
||||||
|
@ -20,60 +20,60 @@ export const playback = {
|
||||||
init () {
|
init () {
|
||||||
// We don't need to init this service twice, or the media events will be duplicated.
|
// We don't need to init this service twice, or the media events will be duplicated.
|
||||||
if (this.initialized) {
|
if (this.initialized) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.player = plyr.setup({
|
this.player = plyr.setup({
|
||||||
controls: [],
|
controls: []
|
||||||
})[0];
|
})[0]
|
||||||
|
|
||||||
this.audio = $('audio');
|
this.audio = $('audio')
|
||||||
|
|
||||||
this.$volumeInput = $('#volumeRange');
|
this.$volumeInput = $('#volumeRange')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listen to 'error' event on the audio player and play the next song if any.
|
* Listen to 'error' event on the audio player and play the next song if any.
|
||||||
*/
|
*/
|
||||||
document.querySelector('.plyr').addEventListener('error', e => {
|
document.querySelector('.plyr').addEventListener('error', e => {
|
||||||
this.playNext();
|
this.playNext()
|
||||||
}, true);
|
}, true)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listen to 'ended' event on the audio player and play the next song in the queue.
|
* Listen to 'ended' event on the audio player and play the next song in the queue.
|
||||||
*/
|
*/
|
||||||
document.querySelector('.plyr').addEventListener('ended', e => {
|
document.querySelector('.plyr').addEventListener('ended', e => {
|
||||||
if (sharedStore.state.useLastfm && userStore.current.preferences.lastfm_session_key) {
|
if (sharedStore.state.useLastfm && userStore.current.preferences.lastfm_session_key) {
|
||||||
songStore.scrobble(queueStore.current);
|
songStore.scrobble(queueStore.current)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (preferences.repeatMode === 'REPEAT_ONE') {
|
if (preferences.repeatMode === 'REPEAT_ONE') {
|
||||||
this.restart();
|
this.restart()
|
||||||
|
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.playNext();
|
this.playNext()
|
||||||
});
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt to preload the next song if the current song is about to end.
|
* Attempt to preload the next song if the current song is about to end.
|
||||||
*/
|
*/
|
||||||
document.querySelector('.plyr').addEventListener('timeupdate', e => {
|
document.querySelector('.plyr').addEventListener('timeupdate', e => {
|
||||||
if (!this.player.media.duration || this.player.media.currentTime + 10 < this.player.media.duration) {
|
if (!this.player.media.duration || this.player.media.currentTime + 10 < this.player.media.duration) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// The current song has only 10 seconds left to play.
|
// The current song has only 10 seconds left to play.
|
||||||
const nextSong = queueStore.next;
|
const nextSong = queueStore.next
|
||||||
if (!nextSong || nextSong.preloaded) {
|
if (!nextSong || nextSong.preloaded) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const $preloader = $('<audio>');
|
const $preloader = $('<audio>')
|
||||||
$preloader.attr('src', songStore.getSourceUrl(nextSong));
|
$preloader.attr('src', songStore.getSourceUrl(nextSong))
|
||||||
|
|
||||||
nextSong.preloaded = true;
|
nextSong.preloaded = true
|
||||||
});
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listen to 'input' event on the volume range control.
|
* Listen to 'input' event on the volume range control.
|
||||||
|
@ -81,16 +81,16 @@ export const playback = {
|
||||||
* update the volume on the plyr object.
|
* update the volume on the plyr object.
|
||||||
*/
|
*/
|
||||||
this.$volumeInput.on('input', e => {
|
this.$volumeInput.on('input', e => {
|
||||||
this.setVolume($(e.target).val());
|
this.setVolume($(e.target).val())
|
||||||
});
|
})
|
||||||
|
|
||||||
// On init, set the volume to the value found in the local storage.
|
// On init, set the volume to the value found in the local storage.
|
||||||
this.setVolume(preferences.volume);
|
this.setVolume(preferences.volume)
|
||||||
|
|
||||||
// Init the equalizer if supported.
|
// Init the equalizer if supported.
|
||||||
event.emit('equalizer:init', this.player.media);
|
event.emit('equalizer:init', this.player.media)
|
||||||
|
|
||||||
this.initialized = true;
|
this.initialized = true
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -105,64 +105,64 @@ export const playback = {
|
||||||
*/
|
*/
|
||||||
play (song) {
|
play (song) {
|
||||||
if (!song) {
|
if (!song) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (queueStore.current) {
|
if (queueStore.current) {
|
||||||
queueStore.current.playbackState = 'stopped';
|
queueStore.current.playbackState = 'stopped'
|
||||||
}
|
}
|
||||||
|
|
||||||
song.playbackState = 'playing';
|
song.playbackState = 'playing'
|
||||||
|
|
||||||
// Set the song as the current song
|
// Set the song as the current song
|
||||||
queueStore.current = song;
|
queueStore.current = song
|
||||||
|
|
||||||
// Add it into the "recent" list
|
// Add it into the "recent" list
|
||||||
songStore.addRecent(song);
|
songStore.addRecent(song)
|
||||||
|
|
||||||
// Manually set the `src` attribute of the audio to prevent plyr from resetting
|
// Manually set the `src` attribute of the audio to prevent plyr from resetting
|
||||||
// the audio media object and cause our equalizer to malfunction.
|
// the audio media object and cause our equalizer to malfunction.
|
||||||
this.player.media.src = songStore.getSourceUrl(song);
|
this.player.media.src = songStore.getSourceUrl(song)
|
||||||
|
|
||||||
$('title').text(`${song.title} ♫ ${config.appTitle}`);
|
$('title').text(`${song.title} ♫ ${config.appTitle}`)
|
||||||
$('.plyr audio').attr('title', `${song.artist.name} - ${song.title}`);
|
$('.plyr audio').attr('title', `${song.artist.name} - ${song.title}`)
|
||||||
|
|
||||||
// We'll just "restart" playing the song, which will handle notification, scrobbling etc.
|
// We'll just "restart" playing the song, which will handle notification, scrobbling etc.
|
||||||
this.restart();
|
this.restart()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restart playing a song.
|
* Restart playing a song.
|
||||||
*/
|
*/
|
||||||
restart () {
|
restart () {
|
||||||
const song = queueStore.current;
|
const song = queueStore.current
|
||||||
|
|
||||||
// Record the UNIX timestamp the song start playing, for scrobbling purpose
|
// Record the UNIX timestamp the song start playing, for scrobbling purpose
|
||||||
song.playStartTime = Math.floor(Date.now() / 1000);
|
song.playStartTime = Math.floor(Date.now() / 1000)
|
||||||
|
|
||||||
event.emit('song:played', song);
|
event.emit('song:played', song)
|
||||||
|
|
||||||
this.player.restart();
|
this.player.restart()
|
||||||
this.player.play();
|
this.player.play()
|
||||||
|
|
||||||
// Register the play to the server
|
// Register the play to the server
|
||||||
songStore.registerPlay(song);
|
songStore.registerPlay(song)
|
||||||
|
|
||||||
// Show the notification if we're allowed to
|
// Show the notification if we're allowed to
|
||||||
if (!window.Notification || !preferences.notify) {
|
if (!window.Notification || !preferences.notify) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const notification = new Notification(`♫ ${song.title}`, {
|
const notif = new window.Notification(`♫ ${song.title}`, {
|
||||||
icon: song.album.cover,
|
icon: song.album.cover,
|
||||||
body: `${song.album.name} – ${song.artist.name}`
|
body: `${song.album.name} – ${song.artist.name}`
|
||||||
});
|
})
|
||||||
|
|
||||||
notification.onclick = () => window.focus();
|
notif.onclick = () => window.focus()
|
||||||
|
|
||||||
// Close the notif after 5 secs.
|
// Close the notif after 5 secs.
|
||||||
window.setTimeout(() => notification.close(), 5000);
|
window.setTimeout(() => notif.close(), 5000)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Notification fails.
|
// Notification fails.
|
||||||
// @link https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration/showNotification
|
// @link https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration/showNotification
|
||||||
|
@ -176,14 +176,14 @@ export const playback = {
|
||||||
* @return {Object} The song
|
* @return {Object} The song
|
||||||
*/
|
*/
|
||||||
get next () {
|
get next () {
|
||||||
const next = queueStore.next;
|
const next = queueStore.next
|
||||||
|
|
||||||
if (next) {
|
if (next) {
|
||||||
return next;
|
return next
|
||||||
}
|
}
|
||||||
|
|
||||||
if (preferences.repeatMode === 'REPEAT_ALL') {
|
if (preferences.repeatMode === 'REPEAT_ALL') {
|
||||||
return queueStore.first;
|
return queueStore.first
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -194,14 +194,14 @@ export const playback = {
|
||||||
* @return {Object} The song
|
* @return {Object} The song
|
||||||
*/
|
*/
|
||||||
get previous () {
|
get previous () {
|
||||||
const prev = queueStore.previous;
|
const prev = queueStore.previous
|
||||||
|
|
||||||
if (prev) {
|
if (prev) {
|
||||||
return prev;
|
return prev
|
||||||
}
|
}
|
||||||
|
|
||||||
if (preferences.repeatMode === 'REPEAT_ALL') {
|
if (preferences.repeatMode === 'REPEAT_ALL') {
|
||||||
return queueStore.last;
|
return queueStore.last
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -210,13 +210,13 @@ export const playback = {
|
||||||
* The selected mode will be stored into local storage as well.
|
* The selected mode will be stored into local storage as well.
|
||||||
*/
|
*/
|
||||||
changeRepeatMode () {
|
changeRepeatMode () {
|
||||||
let idx = this.repeatModes.indexOf(preferences.repeatMode) + 1;
|
let idx = this.repeatModes.indexOf(preferences.repeatMode) + 1
|
||||||
|
|
||||||
if (idx >= this.repeatModes.length) {
|
if (idx >= this.repeatModes.length) {
|
||||||
idx = 0;
|
idx = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
preferences.repeatMode = this.repeatModes[idx];
|
preferences.repeatMode = this.repeatModes[idx]
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -227,20 +227,20 @@ export const playback = {
|
||||||
// If the song's duration is greater than 5 seconds and we've passed 5 seconds into it,
|
// If the song's duration is greater than 5 seconds and we've passed 5 seconds into it,
|
||||||
// restart playing instead.
|
// restart playing instead.
|
||||||
if (this.player.media.currentTime > 5 && queueStore.current.length > 5) {
|
if (this.player.media.currentTime > 5 && queueStore.current.length > 5) {
|
||||||
this.player.restart();
|
this.player.restart()
|
||||||
|
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const prev = this.previous;
|
const prev = this.previous
|
||||||
|
|
||||||
if (!prev && preferences.repeatMode === 'NO_REPEAT') {
|
if (!prev && preferences.repeatMode === 'NO_REPEAT') {
|
||||||
this.stop();
|
this.stop()
|
||||||
|
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.play(prev);
|
this.play(prev)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -248,16 +248,16 @@ export const playback = {
|
||||||
* If the next song is not found and the current mode is NO_REPEAT, we stop completely.
|
* If the next song is not found and the current mode is NO_REPEAT, we stop completely.
|
||||||
*/
|
*/
|
||||||
playNext () {
|
playNext () {
|
||||||
const next = this.next;
|
const next = this.next
|
||||||
|
|
||||||
if (!next && preferences.repeatMode === 'NO_REPEAT') {
|
if (!next && preferences.repeatMode === 'NO_REPEAT') {
|
||||||
// Nothing lasts forever, even cold November rain.
|
// Nothing lasts forever, even cold November rain.
|
||||||
this.stop();
|
this.stop()
|
||||||
|
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.play(next);
|
this.play(next)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -267,20 +267,20 @@ export const playback = {
|
||||||
* @param {Boolean=true} persist Whether the volume should be saved into local storage
|
* @param {Boolean=true} persist Whether the volume should be saved into local storage
|
||||||
*/
|
*/
|
||||||
setVolume (volume, persist = true) {
|
setVolume (volume, persist = true) {
|
||||||
this.player.setVolume(volume);
|
this.player.setVolume(volume)
|
||||||
|
|
||||||
if (persist) {
|
if (persist) {
|
||||||
preferences.volume = volume;
|
preferences.volume = volume
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$volumeInput.val(volume);
|
this.$volumeInput.val(volume)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mute playback.
|
* Mute playback.
|
||||||
*/
|
*/
|
||||||
mute () {
|
mute () {
|
||||||
this.setVolume(0, false);
|
this.setVolume(0, false)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -289,22 +289,22 @@ export const playback = {
|
||||||
unmute () {
|
unmute () {
|
||||||
// If the saved volume is 0, we unmute to the default level (7).
|
// If the saved volume is 0, we unmute to the default level (7).
|
||||||
if (preferences.volume === '0' || preferences.volume === 0) {
|
if (preferences.volume === '0' || preferences.volume === 0) {
|
||||||
preferences.volume = 7;
|
preferences.volume = 7
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setVolume(preferences.volume);
|
this.setVolume(preferences.volume)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Completely stop playback.
|
* Completely stop playback.
|
||||||
*/
|
*/
|
||||||
stop () {
|
stop () {
|
||||||
$('title').text(config.appTitle);
|
$('title').text(config.appTitle)
|
||||||
this.player.pause();
|
this.player.pause()
|
||||||
this.player.seek(0);
|
this.player.seek(0)
|
||||||
|
|
||||||
if (queueStore.current) {
|
if (queueStore.current) {
|
||||||
queueStore.current.playbackState = 'stopped';
|
queueStore.current.playbackState = 'stopped'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -312,17 +312,17 @@ export const playback = {
|
||||||
* Pause playback.
|
* Pause playback.
|
||||||
*/
|
*/
|
||||||
pause () {
|
pause () {
|
||||||
this.player.pause();
|
this.player.pause()
|
||||||
queueStore.current.playbackState = 'paused';
|
queueStore.current.playbackState = 'paused'
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resume playback.
|
* Resume playback.
|
||||||
*/
|
*/
|
||||||
resume () {
|
resume () {
|
||||||
this.player.play();
|
this.player.play()
|
||||||
queueStore.current.playbackState = 'playing';
|
queueStore.current.playbackState = 'playing'
|
||||||
event.emit('song:played', queueStore.current);
|
event.emit('song:played', queueStore.current)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -333,25 +333,25 @@ export const playback = {
|
||||||
*/
|
*/
|
||||||
queueAndPlay (songs = null, shuffled = false) {
|
queueAndPlay (songs = null, shuffled = false) {
|
||||||
if (!songs) {
|
if (!songs) {
|
||||||
songs = songStore.all;
|
songs = songStore.all
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!songs.length) {
|
if (!songs.length) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shuffled) {
|
if (shuffled) {
|
||||||
songs = shuffle(songs);
|
songs = shuffle(songs)
|
||||||
}
|
}
|
||||||
|
|
||||||
queueStore.queue(songs, true);
|
queueStore.queue(songs, true)
|
||||||
|
|
||||||
// Wrap this inside a nextTick() to wait for the DOM to complete updating
|
// Wrap this inside a nextTick() to wait for the DOM to complete updating
|
||||||
// and then play the first song in the queue.
|
// and then play the first song in the queue.
|
||||||
Vue.nextTick(() => {
|
Vue.nextTick(() => {
|
||||||
router.go('queue');
|
router.go('queue')
|
||||||
this.play(queueStore.first);
|
this.play(queueStore.first)
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -360,12 +360,12 @@ export const playback = {
|
||||||
*/
|
*/
|
||||||
playFirstInQueue () {
|
playFirstInQueue () {
|
||||||
if (!queueStore.all.length) {
|
if (!queueStore.all.length) {
|
||||||
this.queueAndPlay();
|
this.queueAndPlay()
|
||||||
|
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.play(queueStore.first);
|
this.play(queueStore.first)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -375,7 +375,7 @@ export const playback = {
|
||||||
* @param {Boolean=true} shuffled Whether to shuffle the songs
|
* @param {Boolean=true} shuffled Whether to shuffle the songs
|
||||||
*/
|
*/
|
||||||
playAllByArtist (artist, shuffled = true) {
|
playAllByArtist (artist, shuffled = true) {
|
||||||
this.queueAndPlay(artist.songs, shuffled);
|
this.queueAndPlay(artist.songs, shuffled)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -386,11 +386,10 @@ export const playback = {
|
||||||
*/
|
*/
|
||||||
playAllInAlbum (album, shuffled = true) {
|
playAllInAlbum (album, shuffled = true) {
|
||||||
if (!shuffled) {
|
if (!shuffled) {
|
||||||
this.queueAndPlay(orderBy(album.songs, 'track'));
|
this.queueAndPlay(orderBy(album.songs, 'track'))
|
||||||
|
return
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.queueAndPlay(album.songs, true);
|
this.queueAndPlay(album.songs, true)
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { http, playback } from '.';
|
import { http } from '.'
|
||||||
import { assign } from 'lodash';
|
import { event } from '../utils'
|
||||||
import { event, loadMainView } from '../utils';
|
import router from '../router'
|
||||||
import router from '../router';
|
|
||||||
|
|
||||||
export const youtube = {
|
export const youtube = {
|
||||||
/**
|
/**
|
||||||
|
@ -12,15 +11,15 @@ export const youtube = {
|
||||||
*/
|
*/
|
||||||
searchVideosRelatedToSong (song, cb = null) {
|
searchVideosRelatedToSong (song, cb = null) {
|
||||||
if (!song.youtube) {
|
if (!song.youtube) {
|
||||||
song.youtube = {};
|
song.youtube = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const pageToken = song.youtube.nextPageToken || '';
|
const pageToken = song.youtube.nextPageToken || ''
|
||||||
http.get(`youtube/search/song/${song.id}?pageToken=${pageToken}`).then(data => {
|
http.get(`youtube/search/song/${song.id}?pageToken=${pageToken}`).then(data => {
|
||||||
song.youtube.nextPageToken = data.nextPageToken;
|
song.youtube.nextPageToken = data.nextPageToken
|
||||||
song.youtube.items.push(...data.items);
|
song.youtube.items.push(...data.items)
|
||||||
cb && cb();
|
cb && cb()
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,7 +28,7 @@ export const youtube = {
|
||||||
* @param {string} id The video ID
|
* @param {string} id The video ID
|
||||||
*/
|
*/
|
||||||
play (id) {
|
play (id) {
|
||||||
event.emit('youtube:play', id);
|
event.emit('youtube:play', id)
|
||||||
router.go('youtube');
|
router.go('youtube')
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
import Vue from 'vue';
|
import Vue from 'vue'
|
||||||
import { reduce, each, find, union, difference, take, filter, orderBy } from 'lodash';
|
import { reduce, each, find, union, difference, take, filter, orderBy } from 'lodash'
|
||||||
|
|
||||||
import { secondsToHis } from '../utils';
|
import { secondsToHis } from '../utils'
|
||||||
import stub from '../stubs/album';
|
import stub from '../stubs/album'
|
||||||
import { songStore, artistStore } from '.';
|
import { songStore, artistStore } from '.'
|
||||||
|
|
||||||
export const albumStore = {
|
export const albumStore = {
|
||||||
stub,
|
stub,
|
||||||
|
|
||||||
state: {
|
state: {
|
||||||
albums: [stub],
|
albums: [stub]
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,23 +23,23 @@ export const albumStore = {
|
||||||
// While we're doing so, for each album, we get its length
|
// While we're doing so, for each album, we get its length
|
||||||
// and keep a back reference to the artist too.
|
// and keep a back reference to the artist too.
|
||||||
each(artist.albums, album => {
|
each(artist.albums, album => {
|
||||||
this.setupAlbum(album, artist);
|
this.setupAlbum(album, artist)
|
||||||
});
|
})
|
||||||
|
|
||||||
return albums.concat(artist.albums);
|
return albums.concat(artist.albums)
|
||||||
}, []);
|
}, [])
|
||||||
|
|
||||||
// Then we init the song store.
|
// Then we init the song store.
|
||||||
songStore.init(this.all);
|
songStore.init(this.all)
|
||||||
},
|
},
|
||||||
|
|
||||||
setupAlbum (album, artist) {
|
setupAlbum (album, artist) {
|
||||||
Vue.set(album, 'playCount', 0);
|
Vue.set(album, 'playCount', 0)
|
||||||
Vue.set(album, 'artist', artist);
|
Vue.set(album, 'artist', artist)
|
||||||
Vue.set(album, 'info', null);
|
Vue.set(album, 'info', null)
|
||||||
this.getLength(album);
|
this.getLength(album)
|
||||||
|
|
||||||
return album;
|
return album
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -48,7 +48,7 @@ export const albumStore = {
|
||||||
* @return {Array.<Object>}
|
* @return {Array.<Object>}
|
||||||
*/
|
*/
|
||||||
get all () {
|
get all () {
|
||||||
return this.state.albums;
|
return this.state.albums
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -57,11 +57,11 @@ export const albumStore = {
|
||||||
* @param {Array.<Object>} value
|
* @param {Array.<Object>} value
|
||||||
*/
|
*/
|
||||||
set all (value) {
|
set all (value) {
|
||||||
this.state.albums = value;
|
this.state.albums = value
|
||||||
},
|
},
|
||||||
|
|
||||||
byId (id) {
|
byId (id) {
|
||||||
return find(this.all, { id });
|
return find(this.all, { id })
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -73,10 +73,10 @@ export const albumStore = {
|
||||||
* @return {String} The H:i:s format of the album length.
|
* @return {String} The H:i:s format of the album length.
|
||||||
*/
|
*/
|
||||||
getLength (album) {
|
getLength (album) {
|
||||||
Vue.set(album, 'length', reduce(album.songs, (length, song) => length + song.length, 0));
|
Vue.set(album, 'length', reduce(album.songs, (length, song) => length + song.length, 0))
|
||||||
Vue.set(album, 'fmtLength', secondsToHis(album.length));
|
Vue.set(album, 'fmtLength', secondsToHis(album.length))
|
||||||
|
|
||||||
return album.fmtLength;
|
return album.fmtLength
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -85,13 +85,13 @@ export const albumStore = {
|
||||||
* @param {Array.<Object>|Object} albums
|
* @param {Array.<Object>|Object} albums
|
||||||
*/
|
*/
|
||||||
add (albums) {
|
add (albums) {
|
||||||
albums = [].concat(albums);
|
albums = [].concat(albums)
|
||||||
each(albums, a => {
|
each(albums, a => {
|
||||||
this.setupAlbum(a, a.artist)
|
this.setupAlbum(a, a.artist)
|
||||||
a.playCount = reduce(a.songs, (count, song) => count + song.playCount, 0);
|
a.playCount = reduce(a.songs, (count, song) => count + song.playCount, 0)
|
||||||
});
|
})
|
||||||
|
|
||||||
this.all = union(this.all, albums);
|
this.all = union(this.all, albums)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -101,17 +101,17 @@ export const albumStore = {
|
||||||
* @param {Array.<Object>|Object} song
|
* @param {Array.<Object>|Object} song
|
||||||
*/
|
*/
|
||||||
addSongsIntoAlbum (album, songs) {
|
addSongsIntoAlbum (album, songs) {
|
||||||
songs = [].concat(songs);
|
songs = [].concat(songs)
|
||||||
|
|
||||||
album.songs = union(album.songs ? album.songs : [], songs);
|
album.songs = union(album.songs ? album.songs : [], songs)
|
||||||
|
|
||||||
each(songs, song => {
|
each(songs, song => {
|
||||||
song.album_id = album.id;
|
song.album_id = album.id
|
||||||
song.album = album;
|
song.album = album
|
||||||
});
|
})
|
||||||
|
|
||||||
album.playCount = reduce(album.songs, (count, song) => count + song.playCount, 0);
|
album.playCount = reduce(album.songs, (count, song) => count + song.playCount, 0)
|
||||||
this.getLength(album);
|
this.getLength(album)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -121,9 +121,9 @@ export const albumStore = {
|
||||||
* @param {Array.<Object>|Object} songs
|
* @param {Array.<Object>|Object} songs
|
||||||
*/
|
*/
|
||||||
removeSongsFromAlbum (album, songs) {
|
removeSongsFromAlbum (album, songs) {
|
||||||
album.songs = difference(album.songs, [].concat(songs));
|
album.songs = difference(album.songs, [].concat(songs))
|
||||||
album.playCount = reduce(album.songs, (count, song) => count + song.playCount, 0);
|
album.playCount = reduce(album.songs, (count, song) => count + song.playCount, 0)
|
||||||
this.getLength(album);
|
this.getLength(album)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -134,7 +134,7 @@ export const albumStore = {
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
isAlbumEmpty (album) {
|
isAlbumEmpty (album) {
|
||||||
return !album.songs.length;
|
return !album.songs.length
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -143,13 +143,13 @@ export const albumStore = {
|
||||||
* @param {Array.<Object>|Object} albums
|
* @param {Array.<Object>|Object} albums
|
||||||
*/
|
*/
|
||||||
remove (albums) {
|
remove (albums) {
|
||||||
albums = [].concat(albums);
|
albums = [].concat(albums)
|
||||||
this.all = difference(this.all, albums);
|
this.all = difference(this.all, albums)
|
||||||
|
|
||||||
// Remove from the artist as well
|
// Remove from the artist as well
|
||||||
each(albums, album => {
|
each(albums, album => {
|
||||||
artistStore.removeAlbumsFromArtist(album.artist, album);
|
artistStore.removeAlbumsFromArtist(album.artist, album)
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -162,10 +162,10 @@ export const albumStore = {
|
||||||
getMostPlayed (n = 6) {
|
getMostPlayed (n = 6) {
|
||||||
// Only non-unknown albums with actually play count are applicable.
|
// Only non-unknown albums with actually play count are applicable.
|
||||||
const applicable = filter(this.all, album => {
|
const applicable = filter(this.all, album => {
|
||||||
return album.playCount && album.id !== 1;
|
return album.playCount && album.id !== 1
|
||||||
});
|
})
|
||||||
|
|
||||||
return take(orderBy(applicable, 'playCount', 'desc'), n);
|
return take(orderBy(applicable, 'playCount', 'desc'), n)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -176,8 +176,8 @@ export const albumStore = {
|
||||||
* @return {Array.<Object>}
|
* @return {Array.<Object>}
|
||||||
*/
|
*/
|
||||||
getRecentlyAdded (n = 6) {
|
getRecentlyAdded (n = 6) {
|
||||||
const applicable = filter(this.all, album => album.id !== 1);
|
const applicable = filter(this.all, album => album.id !== 1)
|
||||||
|
|
||||||
return take(orderBy(applicable, 'created_at', 'desc'), n);
|
return take(orderBy(applicable, 'created_at', 'desc'), n)
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
import Vue from 'vue';
|
import Vue from 'vue'
|
||||||
import { reduce, each, find, union, difference, take, filter, orderBy } from 'lodash';
|
import { reduce, each, find, union, difference, take, filter, orderBy } from 'lodash'
|
||||||
|
|
||||||
import config from '../config';
|
import config from '../config'
|
||||||
import stub from '../stubs/artist';
|
import stub from '../stubs/artist'
|
||||||
import { albumStore } from '.';
|
import { albumStore } from '.'
|
||||||
|
|
||||||
const UNKNOWN_ARTIST_ID = 1;
|
const UNKNOWN_ARTIST_ID = 1
|
||||||
const VARIOUS_ARTISTS_ID = 2;
|
const VARIOUS_ARTISTS_ID = 2
|
||||||
|
|
||||||
export const artistStore = {
|
export const artistStore = {
|
||||||
stub,
|
stub,
|
||||||
|
|
||||||
state: {
|
state: {
|
||||||
artists: [],
|
artists: []
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,14 +21,14 @@ export const artistStore = {
|
||||||
* @param {Array.<Object>} artists The array of artists we got from the server.
|
* @param {Array.<Object>} artists The array of artists we got from the server.
|
||||||
*/
|
*/
|
||||||
init (artists) {
|
init (artists) {
|
||||||
this.all = artists;
|
this.all = artists
|
||||||
|
|
||||||
albumStore.init(this.all);
|
albumStore.init(this.all)
|
||||||
|
|
||||||
// Traverse through artists array to get the cover and number of songs for each.
|
// Traverse through artists array to get the cover and number of songs for each.
|
||||||
each(this.all, artist => {
|
each(this.all, artist => {
|
||||||
this.setupArtist(artist);
|
this.setupArtist(artist)
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,8 +37,8 @@ export const artistStore = {
|
||||||
* @param {Object} artist
|
* @param {Object} artist
|
||||||
*/
|
*/
|
||||||
setupArtist (artist) {
|
setupArtist (artist) {
|
||||||
this.getImage(artist);
|
this.getImage(artist)
|
||||||
Vue.set(artist, 'playCount', 0);
|
Vue.set(artist, 'playCount', 0)
|
||||||
|
|
||||||
// Here we build a list of songs performed by the artist, so that we don't need to traverse
|
// Here we build a list of songs performed by the artist, so that we don't need to traverse
|
||||||
// down the "artist > albums > items" route later.
|
// down the "artist > albums > items" route later.
|
||||||
|
@ -46,18 +46,18 @@ export const artistStore = {
|
||||||
Vue.set(artist, 'songs', reduce(artist.albums, (songs, album) => {
|
Vue.set(artist, 'songs', reduce(artist.albums, (songs, album) => {
|
||||||
// If the album is compilation, we cater for the songs contributed by this artist only.
|
// If the album is compilation, we cater for the songs contributed by this artist only.
|
||||||
if (album.is_compilation) {
|
if (album.is_compilation) {
|
||||||
return songs.concat(filter(album.songs, { contributing_artist_id: artist.id }));
|
return songs.concat(filter(album.songs, { contributing_artist_id: artist.id }))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, just use all songs in the album.
|
// Otherwise, just use all songs in the album.
|
||||||
return songs.concat(album.songs);
|
return songs.concat(album.songs)
|
||||||
}, []));
|
}, []))
|
||||||
|
|
||||||
Vue.set(artist, 'songCount', artist.songs.length);
|
Vue.set(artist, 'songCount', artist.songs.length)
|
||||||
|
|
||||||
Vue.set(artist, 'info', null);
|
Vue.set(artist, 'info', null)
|
||||||
|
|
||||||
return artist;
|
return artist
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,7 +66,7 @@ export const artistStore = {
|
||||||
* @return {Array.<Object>}
|
* @return {Array.<Object>}
|
||||||
*/
|
*/
|
||||||
get all () {
|
get all () {
|
||||||
return this.state.artists;
|
return this.state.artists
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -75,7 +75,7 @@ export const artistStore = {
|
||||||
* @param {Array.<Object>} value
|
* @param {Array.<Object>} value
|
||||||
*/
|
*/
|
||||||
set all (value) {
|
set all (value) {
|
||||||
this.state.artists = value;
|
this.state.artists = value
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -84,7 +84,7 @@ export const artistStore = {
|
||||||
* @param {Number} id
|
* @param {Number} id
|
||||||
*/
|
*/
|
||||||
byId (id) {
|
byId (id) {
|
||||||
return find(this.all, { id });
|
return find(this.all, { id })
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -93,10 +93,10 @@ export const artistStore = {
|
||||||
* @param {Array.<Object>|Object} artists
|
* @param {Array.<Object>|Object} artists
|
||||||
*/
|
*/
|
||||||
add (artists) {
|
add (artists) {
|
||||||
artists = [].concat(artists);
|
artists = [].concat(artists)
|
||||||
each(artists, a => this.setupArtist(a));
|
each(artists, a => this.setupArtist(a))
|
||||||
|
|
||||||
this.all = union(this.all, artists);
|
this.all = union(this.all, artists)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -105,7 +105,7 @@ export const artistStore = {
|
||||||
* @param {Array.<Object>|Object} artists
|
* @param {Array.<Object>|Object} artists
|
||||||
*/
|
*/
|
||||||
remove (artists) {
|
remove (artists) {
|
||||||
this.all = difference(this.all, [].concat(artists));
|
this.all = difference(this.all, [].concat(artists))
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -116,15 +116,15 @@ export const artistStore = {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
addAlbumsIntoArtist (artist, albums) {
|
addAlbumsIntoArtist (artist, albums) {
|
||||||
albums = [].concat(albums);
|
albums = [].concat(albums)
|
||||||
|
|
||||||
artist.albums = union(artist.albums ? artist.albums : [], albums);
|
artist.albums = union(artist.albums ? artist.albums : [], albums)
|
||||||
|
|
||||||
each(albums, album => {
|
each(albums, album => {
|
||||||
album.artist_id = artist.id;
|
album.artist_id = artist.id
|
||||||
album.artist = artist;
|
album.artist = artist
|
||||||
artist.playCount += album.playCount;
|
artist.playCount += album.playCount
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -134,9 +134,11 @@ export const artistStore = {
|
||||||
* @param {Array.<Object>|Object} albums
|
* @param {Array.<Object>|Object} albums
|
||||||
*/
|
*/
|
||||||
removeAlbumsFromArtist (artist, albums) {
|
removeAlbumsFromArtist (artist, albums) {
|
||||||
albums = [].concat(albums);
|
albums = [].concat(albums)
|
||||||
artist.albums = difference(artist.albums, albums);
|
artist.albums = difference(artist.albums, albums)
|
||||||
each(albums, album => artist.playCount -= album.playCount);
|
each(albums, album => {
|
||||||
|
artist.playCount -= album.playCount
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -147,7 +149,7 @@ export const artistStore = {
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
isArtistEmpty (artist) {
|
isArtistEmpty (artist) {
|
||||||
return !artist.albums.length;
|
return !artist.albums.length
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -158,7 +160,7 @@ export const artistStore = {
|
||||||
* @return {Boolean}
|
* @return {Boolean}
|
||||||
*/
|
*/
|
||||||
isVariousArtists (artist) {
|
isVariousArtists (artist) {
|
||||||
return artist.id === VARIOUS_ARTISTS_ID;
|
return artist.id === VARIOUS_ARTISTS_ID
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -169,7 +171,7 @@ export const artistStore = {
|
||||||
* @return {Boolean}
|
* @return {Boolean}
|
||||||
*/
|
*/
|
||||||
isUnknownArtist (artist) {
|
isUnknownArtist (artist) {
|
||||||
return artist.id === UNKNOWN_ARTIST_ID;
|
return artist.id === UNKNOWN_ARTIST_ID
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -180,7 +182,7 @@ export const artistStore = {
|
||||||
* @return {Array.<Object>}
|
* @return {Array.<Object>}
|
||||||
*/
|
*/
|
||||||
getSongsByArtist (artist) {
|
getSongsByArtist (artist) {
|
||||||
return artist.songs;
|
return artist.songs
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -193,20 +195,20 @@ export const artistStore = {
|
||||||
getImage (artist) {
|
getImage (artist) {
|
||||||
if (!artist.image) {
|
if (!artist.image) {
|
||||||
// Try to get an image from one of the albums.
|
// Try to get an image from one of the albums.
|
||||||
artist.image = config.unknownCover;
|
artist.image = config.unknownCover
|
||||||
|
|
||||||
artist.albums.every(album => {
|
artist.albums.every(album => {
|
||||||
// If there's a "real" cover, use it.
|
// If there's a "real" cover, use it.
|
||||||
if (album.image !== config.unknownCover) {
|
if (album.image !== config.unknownCover) {
|
||||||
artist.image = album.cover;
|
artist.image = album.cover
|
||||||
|
|
||||||
// I want to break free.
|
// I want to break free.
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return artist.image;
|
return artist.image
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -220,11 +222,11 @@ export const artistStore = {
|
||||||
// Only non-unknown artists with actually play count are applicable.
|
// Only non-unknown artists with actually play count are applicable.
|
||||||
// Also, "Various Artists" doesn't count.
|
// Also, "Various Artists" doesn't count.
|
||||||
const applicable = filter(this.all, artist => {
|
const applicable = filter(this.all, artist => {
|
||||||
return artist.playCount
|
return artist.playCount &&
|
||||||
&& !this.isUnknownArtist(artist)
|
!this.isUnknownArtist(artist) &&
|
||||||
&& !this.isVariousArtists(artist);
|
!this.isVariousArtists(artist)
|
||||||
});
|
})
|
||||||
|
|
||||||
return take(orderBy(applicable, 'playCount', 'desc'), n);
|
return take(orderBy(applicable, 'playCount', 'desc'), n)
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { find } from 'lodash';
|
import { find } from 'lodash'
|
||||||
|
|
||||||
import { preferenceStore } from '.';
|
import { preferenceStore } from '.'
|
||||||
|
|
||||||
export const equalizerStore = {
|
export const equalizerStore = {
|
||||||
presets: [
|
presets: [
|
||||||
|
@ -8,25 +8,25 @@ export const equalizerStore = {
|
||||||
id: 0,
|
id: 0,
|
||||||
name: 'Default',
|
name: 'Default',
|
||||||
preamp: 0,
|
preamp: 0,
|
||||||
gains: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
gains: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'Classical',
|
name: 'Classical',
|
||||||
preamp: -1,
|
preamp: -1,
|
||||||
gains: [-1, -1, -1, -1, -1, -1, -7, -7, -7, -9],
|
gains: [-1, -1, -1, -1, -1, -1, -7, -7, -7, -9]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
name: 'Club',
|
name: 'Club',
|
||||||
preamp: -6.7,
|
preamp: -6.7,
|
||||||
gains: [-1, -1, 8, 5, 5, 5, 3, -1, -1, -1],
|
gains: [-1, -1, 8, 5, 5, 5, 3, -1, -1, -1]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
name: 'Dance',
|
name: 'Dance',
|
||||||
preamp: -4.3,
|
preamp: -4.3,
|
||||||
gains: [9, 7, 2, -1, -1, -5, -7, -7, -1, -1],
|
gains: [9, 7, 2, -1, -1, -5, -7, -7, -1, -1]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 4,
|
id: 4,
|
||||||
|
@ -50,48 +50,48 @@ export const equalizerStore = {
|
||||||
id: 7,
|
id: 7,
|
||||||
name: 'Large Hall',
|
name: 'Large Hall',
|
||||||
preamp: -7.2,
|
preamp: -7.2,
|
||||||
gains: [10, 10, 5, 5, -1, -4, -4, -4, -1, -1],
|
gains: [10, 10, 5, 5, -1, -4, -4, -4, -1, -1]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 8,
|
id: 8,
|
||||||
name: 'Live',
|
name: 'Live',
|
||||||
preamp: -5.3,
|
preamp: -5.3,
|
||||||
gains: [-4, -1, 4, 5, 5, 5, 4, 2, 2, 2],
|
gains: [-4, -1, 4, 5, 5, 5, 4, 2, 2, 2]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 9,
|
id: 9,
|
||||||
name: 'Pop',
|
name: 'Pop',
|
||||||
preamp: -6.2,
|
preamp: -6.2,
|
||||||
gains: [-1, 4, 7, 8, 5, -1, -2, -2, -1, -1],
|
gains: [-1, 4, 7, 8, 5, -1, -2, -2, -1, -1]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 10,
|
id: 10,
|
||||||
name: 'Reggae',
|
name: 'Reggae',
|
||||||
preamp: -8.2,
|
preamp: -8.2,
|
||||||
gains: [-1, -1, -1, -5, -1, 6, 6, -1, -1, -1],
|
gains: [-1, -1, -1, -5, -1, 6, 6, -1, -1, -1]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 11,
|
id: 11,
|
||||||
name: 'Rock',
|
name: 'Rock',
|
||||||
preamp: -10,
|
preamp: -10,
|
||||||
gains: [8, 4, -5, -8, -3, 4, 8, 11, 11, 11],
|
gains: [8, 4, -5, -8, -3, 4, 8, 11, 11, 11]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 12,
|
id: 12,
|
||||||
name: 'Soft Rock',
|
name: 'Soft Rock',
|
||||||
preamp: -5.3,
|
preamp: -5.3,
|
||||||
gains: [4, 4, 2, -1, -4, -5, -3, -1, 2, 8],
|
gains: [4, 4, 2, -1, -4, -5, -3, -1, 2, 8]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 13,
|
id: 13,
|
||||||
name: 'Techno',
|
name: 'Techno',
|
||||||
preamp: -7.7,
|
preamp: -7.7,
|
||||||
gains: [8, 5, -1, -5, -4, -1, 8, 9, 9, 8],
|
gains: [8, 5, -1, -5, -4, -1, 8, 9, 9, 8]
|
||||||
},
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
getPresetById (id) {
|
getPresetById (id) {
|
||||||
return find(this.presets, { id });
|
return find(this.presets, { id })
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -101,7 +101,7 @@ export const equalizerStore = {
|
||||||
*/
|
*/
|
||||||
get () {
|
get () {
|
||||||
if (!this.presets[preferenceStore.selectedPreset]) {
|
if (!this.presets[preferenceStore.selectedPreset]) {
|
||||||
return preferenceStore.equalizer;
|
return preferenceStore.equalizer
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the user chose a preset (instead of customizing one), just return it.
|
// If the user chose a preset (instead of customizing one), just return it.
|
||||||
|
@ -115,6 +115,6 @@ export const equalizerStore = {
|
||||||
* @param {Array.<Number>} gains The band's gain value (dB)
|
* @param {Array.<Number>} gains The band's gain value (dB)
|
||||||
*/
|
*/
|
||||||
set (preamp, gains) {
|
set (preamp, gains) {
|
||||||
preferenceStore.equalizer = { preamp, gains };
|
preferenceStore.equalizer = { preamp, gains }
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { each, map, difference, union } from 'lodash';
|
import { each, map, difference, union } from 'lodash'
|
||||||
|
|
||||||
import { http } from '../services';
|
import { http } from '../services'
|
||||||
|
|
||||||
export const favoriteStore = {
|
export const favoriteStore = {
|
||||||
state: {
|
state: {
|
||||||
songs: [],
|
songs: [],
|
||||||
length: 0,
|
length: 0,
|
||||||
fmtLength: '',
|
fmtLength: ''
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -15,7 +15,7 @@ export const favoriteStore = {
|
||||||
* @return {Array.<Object>}
|
* @return {Array.<Object>}
|
||||||
*/
|
*/
|
||||||
get all () {
|
get all () {
|
||||||
return this.state.songs;
|
return this.state.songs
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,7 +24,7 @@ export const favoriteStore = {
|
||||||
* @param {Array.<Object>} value
|
* @param {Array.<Object>} value
|
||||||
*/
|
*/
|
||||||
set all (value) {
|
set all (value) {
|
||||||
this.state.songs = value;
|
this.state.songs = value
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,12 +36,12 @@ export const favoriteStore = {
|
||||||
toggleOne (song) {
|
toggleOne (song) {
|
||||||
// Don't wait for the HTTP response to update the status, just toggle right away.
|
// Don't wait for the HTTP response to update the status, just toggle right away.
|
||||||
// This may cause a minor problem if the request fails somehow, but do we care?
|
// This may cause a minor problem if the request fails somehow, but do we care?
|
||||||
song.liked = !song.liked;
|
song.liked = !song.liked
|
||||||
song.liked ? this.add(song) : this.remove(song);
|
song.liked ? this.add(song) : this.remove(song)
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
http.post('interaction/like', { song: song.id }, data => resolve(data), r => reject(r));
|
http.post('interaction/like', { song: song.id }, data => resolve(data), r => reject(r))
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -50,7 +50,7 @@ export const favoriteStore = {
|
||||||
* @param {Array.<Object>|Object} songs
|
* @param {Array.<Object>|Object} songs
|
||||||
*/
|
*/
|
||||||
add (songs) {
|
add (songs) {
|
||||||
this.all = union(this.all, [].concat(songs));
|
this.all = union(this.all, [].concat(songs))
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -59,14 +59,14 @@ export const favoriteStore = {
|
||||||
* @param {Array.<Object>|Object} songs
|
* @param {Array.<Object>|Object} songs
|
||||||
*/
|
*/
|
||||||
remove (songs) {
|
remove (songs) {
|
||||||
this.all = difference(this.all, [].concat(songs));
|
this.all = difference(this.all, [].concat(songs))
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove all favorites.
|
* Remove all favorites.
|
||||||
*/
|
*/
|
||||||
clear () {
|
clear () {
|
||||||
this.all = [];
|
this.all = []
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -77,12 +77,14 @@ export const favoriteStore = {
|
||||||
like (songs) {
|
like (songs) {
|
||||||
// Don't wait for the HTTP response to update the status, just set them to Liked right away.
|
// Don't wait for the HTTP response to update the status, just set them to Liked right away.
|
||||||
// This may cause a minor problem if the request fails somehow, but do we care?
|
// This may cause a minor problem if the request fails somehow, but do we care?
|
||||||
each(songs, song => song.liked = true);
|
each(songs, song => {
|
||||||
this.add(songs);
|
song.liked = true
|
||||||
|
})
|
||||||
|
this.add(songs)
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
http.post('interaction/batch/like', { songs: map(songs, 'id') }, data => resolve(data), r => reject(r));
|
http.post('interaction/batch/like', { songs: map(songs, 'id') }, data => resolve(data), r => reject(r))
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -91,11 +93,13 @@ export const favoriteStore = {
|
||||||
* @param {Array.<Object>} songs
|
* @param {Array.<Object>} songs
|
||||||
*/
|
*/
|
||||||
unlike (songs) {
|
unlike (songs) {
|
||||||
each(songs, song => song.liked = false);
|
each(songs, song => {
|
||||||
this.remove(songs);
|
song.liked = false
|
||||||
|
})
|
||||||
|
this.remove(songs)
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
http.post('interaction/batch/unlike', { songs: map(songs, 'id') }, data => resolve(data), r => reject(r));
|
http.post('interaction/batch/unlike', { songs: map(songs, 'id') }, data => resolve(data), r => reject(r))
|
||||||
});
|
})
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
export * from './album';
|
export * from './album'
|
||||||
export * from './artist';
|
export * from './artist'
|
||||||
export * from './equalizer';
|
export * from './equalizer'
|
||||||
export * from './favorite';
|
export * from './favorite'
|
||||||
export * from './playlist';
|
export * from './playlist'
|
||||||
export * from './preference';
|
export * from './preference'
|
||||||
export * from './queue';
|
export * from './queue'
|
||||||
export * from './setting';
|
export * from './setting'
|
||||||
export * from './shared';
|
export * from './shared'
|
||||||
export * from './song';
|
export * from './song'
|
||||||
export * from './user';
|
export * from './user'
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
import { each, find, map, difference, union, without } from 'lodash';
|
import { each, find, map, difference, union } from 'lodash'
|
||||||
import NProgress from 'nprogress';
|
import NProgress from 'nprogress'
|
||||||
|
|
||||||
import stub from '../stubs/playlist';
|
import stub from '../stubs/playlist'
|
||||||
import { http } from '../services';
|
import { http } from '../services'
|
||||||
import { songStore } from '.';
|
import { songStore } from '.'
|
||||||
|
|
||||||
export const playlistStore = {
|
export const playlistStore = {
|
||||||
stub,
|
stub,
|
||||||
|
|
||||||
state: {
|
state: {
|
||||||
playlists: [],
|
playlists: []
|
||||||
},
|
},
|
||||||
|
|
||||||
init (playlists) {
|
init (playlists) {
|
||||||
this.all = playlists;
|
this.all = playlists
|
||||||
each(this.all, this.objectifySongs);
|
each(this.all, this.objectifySongs)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,7 +23,7 @@ export const playlistStore = {
|
||||||
* @return {Array.<Object>}
|
* @return {Array.<Object>}
|
||||||
*/
|
*/
|
||||||
get all () {
|
get all () {
|
||||||
return this.state.playlists;
|
return this.state.playlists
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -32,7 +32,7 @@ export const playlistStore = {
|
||||||
* @param {Array.<Object>} value
|
* @param {Array.<Object>} value
|
||||||
*/
|
*/
|
||||||
set all (value) {
|
set all (value) {
|
||||||
this.state.playlists = value;
|
this.state.playlists = value
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -43,7 +43,7 @@ export const playlistStore = {
|
||||||
* @return {Object}
|
* @return {Object}
|
||||||
*/
|
*/
|
||||||
byId (id) {
|
byId (id) {
|
||||||
return find(this.all, { id });
|
return find(this.all, { id })
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -53,7 +53,7 @@ export const playlistStore = {
|
||||||
* @param {Object} playlist
|
* @param {Object} playlist
|
||||||
*/
|
*/
|
||||||
objectifySongs (playlist) {
|
objectifySongs (playlist) {
|
||||||
playlist.songs = songStore.byIds(playlist.songs);
|
playlist.songs = songStore.byIds(playlist.songs)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -64,7 +64,7 @@ export const playlistStore = {
|
||||||
* return {Array.<Object>}
|
* return {Array.<Object>}
|
||||||
*/
|
*/
|
||||||
getSongs (playlist) {
|
getSongs (playlist) {
|
||||||
return playlist.songs;
|
return playlist.songs
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -73,7 +73,7 @@ export const playlistStore = {
|
||||||
* @param {Array.<Object>|Object} playlists
|
* @param {Array.<Object>|Object} playlists
|
||||||
*/
|
*/
|
||||||
add (playlists) {
|
add (playlists) {
|
||||||
this.all = union(this.all, [].concat(playlists));
|
this.all = union(this.all, [].concat(playlists))
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -82,7 +82,7 @@ export const playlistStore = {
|
||||||
* @param {Array.<Object>|Object} playlist
|
* @param {Array.<Object>|Object} playlist
|
||||||
*/
|
*/
|
||||||
remove (playlists) {
|
remove (playlists) {
|
||||||
this.all = difference(this.all, [].concat(playlists));
|
this.all = difference(this.all, [].concat(playlists))
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -94,19 +94,19 @@ export const playlistStore = {
|
||||||
store (name, songs = []) {
|
store (name, songs = []) {
|
||||||
if (songs.length) {
|
if (songs.length) {
|
||||||
// Extract the IDs from the song objects.
|
// Extract the IDs from the song objects.
|
||||||
songs = map(songs, 'id');
|
songs = map(songs, 'id')
|
||||||
}
|
}
|
||||||
|
|
||||||
NProgress.start();
|
NProgress.start()
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
http.post('playlist', { name, songs }, playlist => {
|
http.post('playlist', { name, songs }, playlist => {
|
||||||
playlist.songs = songs;
|
playlist.songs = songs
|
||||||
this.objectifySongs(playlist);
|
this.objectifySongs(playlist)
|
||||||
this.add(playlist);
|
this.add(playlist)
|
||||||
resolve(playlist);
|
resolve(playlist)
|
||||||
}, r => reject(r));
|
}, r => reject(r))
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -115,14 +115,14 @@ export const playlistStore = {
|
||||||
* @param {Object} playlist
|
* @param {Object} playlist
|
||||||
*/
|
*/
|
||||||
delete (playlist) {
|
delete (playlist) {
|
||||||
NProgress.start();
|
NProgress.start()
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
http.delete(`playlist/${playlist.id}`, {}, data => {
|
http.delete(`playlist/${playlist.id}`, {}, data => {
|
||||||
this.remove(playlist);
|
this.remove(playlist)
|
||||||
resolve(data);
|
resolve(data)
|
||||||
}, r => reject(r));
|
}, r => reject(r))
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -133,18 +133,18 @@ export const playlistStore = {
|
||||||
*/
|
*/
|
||||||
addSongs (playlist, songs) {
|
addSongs (playlist, songs) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const count = playlist.songs.length;
|
const count = playlist.songs.length
|
||||||
playlist.songs = union(playlist.songs, songs);
|
playlist.songs = union(playlist.songs, songs)
|
||||||
|
|
||||||
if (count === playlist.songs.length) {
|
if (count === playlist.songs.length) {
|
||||||
resolve(playlist);
|
resolve(playlist)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
http.put(`playlist/${playlist.id}/sync`, { songs: map(playlist.songs, 'id') },
|
http.put(`playlist/${playlist.id}/sync`, { songs: map(playlist.songs, 'id') },
|
||||||
data => resolve(playlist),
|
data => resolve(playlist),
|
||||||
r => reject(r)
|
r => reject(r)
|
||||||
);
|
)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -155,13 +155,13 @@ export const playlistStore = {
|
||||||
* @param {Array.<Object>} songs
|
* @param {Array.<Object>} songs
|
||||||
*/
|
*/
|
||||||
removeSongs (playlist, songs) {
|
removeSongs (playlist, songs) {
|
||||||
playlist.songs = difference(playlist.songs, songs);
|
playlist.songs = difference(playlist.songs, songs)
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
http.put(`playlist/${playlist.id}/sync`, { songs: map(playlist.songs, 'id') },
|
http.put(`playlist/${playlist.id}/sync`, { songs: map(playlist.songs, 'id') },
|
||||||
data => resolve(playlist),
|
data => resolve(playlist),
|
||||||
r => reject(r)
|
r => reject(r)
|
||||||
);
|
)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -171,10 +171,10 @@ export const playlistStore = {
|
||||||
* @param {Object} playlist
|
* @param {Object} playlist
|
||||||
*/
|
*/
|
||||||
update (playlist) {
|
update (playlist) {
|
||||||
NProgress.start();
|
NProgress.start()
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
http.put(`playlist/${playlist.id}`, { name: playlist.name }, data => resolve(playlist), r => reject(r));
|
http.put(`playlist/${playlist.id}`, { name: playlist.name }, data => resolve(playlist), r => reject(r))
|
||||||
});
|
})
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { extend, has, each } from 'lodash';
|
import { extend, has, each } from 'lodash'
|
||||||
|
|
||||||
import { userStore } from '.';
|
import { userStore } from '.'
|
||||||
import { ls } from '../services';
|
import { ls } from '../services'
|
||||||
|
|
||||||
export const preferenceStore = {
|
export const preferenceStore = {
|
||||||
storeKey: '',
|
storeKey: '',
|
||||||
|
@ -14,11 +14,11 @@ export const preferenceStore = {
|
||||||
confirmClosing: false,
|
confirmClosing: false,
|
||||||
equalizer: {
|
equalizer: {
|
||||||
preamp: 0,
|
preamp: 0,
|
||||||
gains: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
gains: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
},
|
},
|
||||||
artistsViewMode: null,
|
artistsViewMode: null,
|
||||||
albumsViewMode: null,
|
albumsViewMode: null,
|
||||||
selectedPreset: -1,
|
selectedPreset: -1
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,12 +28,12 @@ export const preferenceStore = {
|
||||||
*/
|
*/
|
||||||
init (user = null) {
|
init (user = null) {
|
||||||
if (!user) {
|
if (!user) {
|
||||||
user = userStore.current;
|
user = userStore.current
|
||||||
}
|
}
|
||||||
|
|
||||||
this.storeKey = `preferences_${user.id}`;
|
this.storeKey = `preferences_${user.id}`
|
||||||
extend(this.state, ls.get(this.storeKey, this.state));
|
extend(this.state, ls.get(this.storeKey, this.state))
|
||||||
this.setupProxy();
|
this.setupProxy()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -44,24 +44,24 @@ export const preferenceStore = {
|
||||||
Object.defineProperty(this, key, {
|
Object.defineProperty(this, key, {
|
||||||
get: () => this.state[key],
|
get: () => this.state[key],
|
||||||
set: (value) => {
|
set: (value) => {
|
||||||
this.state[key] = value;
|
this.state[key] = value
|
||||||
this.save();
|
this.save()
|
||||||
},
|
},
|
||||||
configurable: true,
|
configurable: true
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
set (key, val) {
|
set (key, val) {
|
||||||
this.state[key] = val;
|
this.state[key] = val
|
||||||
this.save();
|
this.save()
|
||||||
},
|
},
|
||||||
|
|
||||||
get (key) {
|
get (key) {
|
||||||
return has(this.state, key) ? this.state[key] : null;
|
return has(this.state, key) ? this.state[key] : null
|
||||||
},
|
},
|
||||||
|
|
||||||
save () {
|
save () {
|
||||||
ls.set(this.storeKey, this.state);
|
ls.set(this.storeKey, this.state)
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { head, last, each, includes, union, difference, map, shuffle as _shuffle, first } from 'lodash';
|
import { head, last, each, includes, union, difference, map, shuffle as _shuffle, first } from 'lodash'
|
||||||
|
|
||||||
export const queueStore = {
|
export const queueStore = {
|
||||||
state: {
|
state: {
|
||||||
songs: [],
|
songs: [],
|
||||||
current: null,
|
current: null
|
||||||
},
|
},
|
||||||
|
|
||||||
init () {
|
init () {
|
||||||
|
@ -37,7 +37,7 @@ export const queueStore = {
|
||||||
* @return {Array.<Object>}
|
* @return {Array.<Object>}
|
||||||
*/
|
*/
|
||||||
get all () {
|
get all () {
|
||||||
return this.state.songs;
|
return this.state.songs
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -46,7 +46,7 @@ export const queueStore = {
|
||||||
* @param {Array.<Object>}
|
* @param {Array.<Object>}
|
||||||
*/
|
*/
|
||||||
set all (songs) {
|
set all (songs) {
|
||||||
this.state.songs = songs;
|
this.state.songs = songs
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,7 +55,7 @@ export const queueStore = {
|
||||||
* @return {?Object}
|
* @return {?Object}
|
||||||
*/
|
*/
|
||||||
get first () {
|
get first () {
|
||||||
return head(this.all);
|
return head(this.all)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -64,7 +64,7 @@ export const queueStore = {
|
||||||
* @return {?Object}
|
* @return {?Object}
|
||||||
*/
|
*/
|
||||||
get last () {
|
get last () {
|
||||||
return last(this.all);
|
return last(this.all)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -75,7 +75,7 @@ export const queueStore = {
|
||||||
* @return {Boolean}
|
* @return {Boolean}
|
||||||
*/
|
*/
|
||||||
contains (song) {
|
contains (song) {
|
||||||
return includes(this.all, song);
|
return includes(this.all, song)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -87,12 +87,12 @@ export const queueStore = {
|
||||||
* @param {Boolean} toTop Whether to prepend or append to the queue
|
* @param {Boolean} toTop Whether to prepend or append to the queue
|
||||||
*/
|
*/
|
||||||
queue (songs, replace = false, toTop = false) {
|
queue (songs, replace = false, toTop = false) {
|
||||||
songs = [].concat(songs);
|
songs = [].concat(songs)
|
||||||
|
|
||||||
if (replace) {
|
if (replace) {
|
||||||
this.all = songs;
|
this.all = songs
|
||||||
} else {
|
} else {
|
||||||
this.all = toTop ? union(songs, this.all) : union(this.all, songs);
|
this.all = toTop ? union(songs, this.all) : union(this.all, songs)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -102,17 +102,17 @@ export const queueStore = {
|
||||||
* @param {Array.<Object>|Object} songs
|
* @param {Array.<Object>|Object} songs
|
||||||
*/
|
*/
|
||||||
queueAfterCurrent (songs) {
|
queueAfterCurrent (songs) {
|
||||||
songs = [].concat(songs);
|
songs = [].concat(songs)
|
||||||
|
|
||||||
if (!this.current || !this.all.length) {
|
if (!this.current || !this.all.length) {
|
||||||
return this.queue(songs);
|
return this.queue(songs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// First we unqueue the songs to make sure there are no duplicates.
|
// First we unqueue the songs to make sure there are no duplicates.
|
||||||
this.unqueue(songs);
|
this.unqueue(songs)
|
||||||
|
|
||||||
const head = this.all.splice(0, this.indexOf(this.current) + 1);
|
const head = this.all.splice(0, this.indexOf(this.current) + 1)
|
||||||
this.all = head.concat(songs, this.all);
|
this.all = head.concat(songs, this.all)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -121,7 +121,7 @@ export const queueStore = {
|
||||||
* @param {Object|String|Array.<Object>} songs The song(s) to unqueue
|
* @param {Object|String|Array.<Object>} songs The song(s) to unqueue
|
||||||
*/
|
*/
|
||||||
unqueue (songs) {
|
unqueue (songs) {
|
||||||
this.all = difference(this.all, [].concat(songs));
|
this.all = difference(this.all, [].concat(songs))
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -131,20 +131,20 @@ export const queueStore = {
|
||||||
* @param {Object} target The target song object
|
* @param {Object} target The target song object
|
||||||
*/
|
*/
|
||||||
move (songs, target) {
|
move (songs, target) {
|
||||||
const $targetIndex = this.indexOf(target);
|
const $targetIndex = this.indexOf(target)
|
||||||
|
|
||||||
each(songs, song => {
|
each(songs, song => {
|
||||||
this.all.splice(this.indexOf(song), 1);
|
this.all.splice(this.indexOf(song), 1)
|
||||||
this.all.splice($targetIndex, 0, song);
|
this.all.splice($targetIndex, 0, song)
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear the current queue.
|
* Clear the current queue.
|
||||||
*/
|
*/
|
||||||
clear () {
|
clear () {
|
||||||
this.all = [];
|
this.all = []
|
||||||
this.current = null;
|
this.current = null
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -155,7 +155,7 @@ export const queueStore = {
|
||||||
* @return {?Integer}
|
* @return {?Integer}
|
||||||
*/
|
*/
|
||||||
indexOf (song) {
|
indexOf (song) {
|
||||||
return this.all.indexOf(song);
|
return this.all.indexOf(song)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -165,12 +165,12 @@ export const queueStore = {
|
||||||
*/
|
*/
|
||||||
get next () {
|
get next () {
|
||||||
if (!this.current) {
|
if (!this.current) {
|
||||||
return first(this.all);
|
return first(this.all)
|
||||||
}
|
}
|
||||||
|
|
||||||
const idx = map(this.all, 'id').indexOf(this.current.id) + 1;
|
const idx = map(this.all, 'id').indexOf(this.current.id) + 1
|
||||||
|
|
||||||
return idx >= this.all.length ? null : this.all[idx];
|
return idx >= this.all.length ? null : this.all[idx]
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -180,12 +180,12 @@ export const queueStore = {
|
||||||
*/
|
*/
|
||||||
get previous () {
|
get previous () {
|
||||||
if (!this.current) {
|
if (!this.current) {
|
||||||
return last(this.all);
|
return last(this.all)
|
||||||
}
|
}
|
||||||
|
|
||||||
const idx = map(this.all, 'id').indexOf(this.current.id) - 1;
|
const idx = map(this.all, 'id').indexOf(this.current.id) - 1
|
||||||
|
|
||||||
return idx < 0 ? null : this.all[idx];
|
return idx < 0 ? null : this.all[idx]
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -194,7 +194,7 @@ export const queueStore = {
|
||||||
* @return {Object}
|
* @return {Object}
|
||||||
*/
|
*/
|
||||||
get current () {
|
get current () {
|
||||||
return this.state.current;
|
return this.state.current
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -205,7 +205,8 @@ export const queueStore = {
|
||||||
* @return {Object} The queued song.
|
* @return {Object} The queued song.
|
||||||
*/
|
*/
|
||||||
set current (song) {
|
set current (song) {
|
||||||
return this.state.current = song;
|
this.state.current = song
|
||||||
|
return this.state.current
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -214,6 +215,7 @@ export const queueStore = {
|
||||||
* @return {Array.<Object>} The shuffled array of song objects
|
* @return {Array.<Object>} The shuffled array of song objects
|
||||||
*/
|
*/
|
||||||
shuffle () {
|
shuffle () {
|
||||||
return this.all = _shuffle(this.all);
|
this.all = _shuffle(this.all)
|
||||||
},
|
return this.all
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
import { http } from '../services';
|
import { http } from '../services'
|
||||||
import stub from '../stubs/settings';
|
import stub from '../stubs/settings'
|
||||||
|
|
||||||
export const settingStore = {
|
export const settingStore = {
|
||||||
stub,
|
stub,
|
||||||
|
|
||||||
state: {
|
state: {
|
||||||
settings: [],
|
settings: []
|
||||||
},
|
},
|
||||||
|
|
||||||
init (settings) {
|
init (settings) {
|
||||||
this.state.settings = settings;
|
this.state.settings = settings
|
||||||
},
|
},
|
||||||
|
|
||||||
get all () {
|
get all () {
|
||||||
return this.state.settings;
|
return this.state.settings
|
||||||
},
|
},
|
||||||
|
|
||||||
update () {
|
update () {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
http.post('settings', this.all, data => resolve(data), r => reject(r));
|
http.post('settings', this.all, data => resolve(data), r => reject(r))
|
||||||
});
|
})
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { assign } from 'lodash';
|
import { assign } from 'lodash'
|
||||||
import isMobile from 'ismobilejs';
|
import isMobile from 'ismobilejs'
|
||||||
|
|
||||||
import { http } from '../services';
|
import { http } from '../services'
|
||||||
import { userStore, preferenceStore, artistStore, songStore, playlistStore, queueStore, settingStore } from '.';
|
import { userStore, preferenceStore, artistStore, songStore, playlistStore, queueStore, settingStore } from '.'
|
||||||
|
|
||||||
export const sharedStore = {
|
export const sharedStore = {
|
||||||
state: {
|
state: {
|
||||||
|
@ -22,58 +22,58 @@ export const sharedStore = {
|
||||||
currentVersion: '',
|
currentVersion: '',
|
||||||
latestVersion: '',
|
latestVersion: '',
|
||||||
cdnUrl: '',
|
cdnUrl: '',
|
||||||
originalMediaPath: '',
|
originalMediaPath: ''
|
||||||
},
|
},
|
||||||
|
|
||||||
init () {
|
init () {
|
||||||
this.reset();
|
this.reset()
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
http.get('data', data => {
|
http.get('data', data => {
|
||||||
// Don't allow downloading on mobile devices
|
// Don't allow downloading on mobile devices
|
||||||
data.allowDownload = data.allowDownload && !isMobile.any;
|
data.allowDownload = data.allowDownload && !isMobile.any
|
||||||
|
|
||||||
assign(this.state, data);
|
assign(this.state, data)
|
||||||
|
|
||||||
// Always disable YouTube integration on mobile.
|
// Always disable YouTube integration on mobile.
|
||||||
this.state.useYouTube = this.state.useYouTube && !isMobile.phone;
|
this.state.useYouTube = this.state.useYouTube && !isMobile.phone
|
||||||
|
|
||||||
// If this is a new user, initialize his preferences to be an empty object.
|
// If this is a new user, initialize his preferences to be an empty object.
|
||||||
if (!this.state.currentUser.preferences) {
|
if (!this.state.currentUser.preferences) {
|
||||||
this.state.currentUser.preferences = {};
|
this.state.currentUser.preferences = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
userStore.init(this.state.users, this.state.currentUser);
|
userStore.init(this.state.users, this.state.currentUser)
|
||||||
preferenceStore.init(this.state.preferences);
|
preferenceStore.init(this.state.preferences)
|
||||||
artistStore.init(this.state.artists); // This will init album and song stores as well.
|
artistStore.init(this.state.artists) // This will init album and song stores as well.
|
||||||
songStore.initInteractions(this.state.interactions);
|
songStore.initInteractions(this.state.interactions)
|
||||||
playlistStore.init(this.state.playlists);
|
playlistStore.init(this.state.playlists)
|
||||||
queueStore.init();
|
queueStore.init()
|
||||||
settingStore.init(this.state.settings);
|
settingStore.init(this.state.settings)
|
||||||
|
|
||||||
// Keep a copy of the media path. We'll need this to properly warn the user later.
|
// Keep a copy of the media path. We'll need this to properly warn the user later.
|
||||||
this.state.originalMediaPath = this.state.settings.media_path;
|
this.state.originalMediaPath = this.state.settings.media_path
|
||||||
|
|
||||||
resolve(data)
|
resolve(data)
|
||||||
}, r => reject(r));
|
}, r => reject(r))
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
reset () {
|
reset () {
|
||||||
this.state.songs = [];
|
this.state.songs = []
|
||||||
this.state.albums = [];
|
this.state.albums = []
|
||||||
this.state.artists = [];
|
this.state.artists = []
|
||||||
this.state.favorites = [];
|
this.state.favorites = []
|
||||||
this.state.queued = [];
|
this.state.queued = []
|
||||||
this.state.interactions = [];
|
this.state.interactions = []
|
||||||
this.state.users = [];
|
this.state.users = []
|
||||||
this.state.settings = [];
|
this.state.settings = []
|
||||||
this.state.currentUser = null;
|
this.state.currentUser = null
|
||||||
this.state.playlists = [];
|
this.state.playlists = []
|
||||||
this.state.useLastfm = false;
|
this.state.useLastfm = false
|
||||||
this.state.allowDownload = false;
|
this.state.allowDownload = false
|
||||||
this.state.currentVersion = '';
|
this.state.currentVersion = ''
|
||||||
this.state.latestVersion = '';
|
this.state.latestVersion = ''
|
||||||
this.state.cdnUrl = '';
|
this.state.cdnUrl = ''
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import Vue from 'vue';
|
import Vue from 'vue'
|
||||||
import { without, map, take, remove, orderBy, each, union } from 'lodash';
|
import { without, map, take, remove, orderBy, each, union } from 'lodash'
|
||||||
|
|
||||||
import { secondsToHis } from '../utils';
|
import { secondsToHis } from '../utils'
|
||||||
import { http, ls } from '../services';
|
import { http, ls } from '../services'
|
||||||
import { sharedStore, favoriteStore, userStore, albumStore, artistStore } from '.';
|
import { sharedStore, favoriteStore, albumStore, artistStore } from '.'
|
||||||
import stub from '../stubs/song';
|
import stub from '../stubs/song'
|
||||||
|
|
||||||
export const songStore = {
|
export const songStore = {
|
||||||
stub,
|
stub,
|
||||||
|
@ -24,7 +24,7 @@ export const songStore = {
|
||||||
*
|
*
|
||||||
* @type {Array}
|
* @type {Array}
|
||||||
*/
|
*/
|
||||||
recent: [],
|
recent: []
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,34 +37,34 @@ export const songStore = {
|
||||||
// While doing so, we populate some other information into the songs as well.
|
// While doing so, we populate some other information into the songs as well.
|
||||||
this.all = albums.reduce((songs, album) => {
|
this.all = albums.reduce((songs, album) => {
|
||||||
each(album.songs, song => {
|
each(album.songs, song => {
|
||||||
this.setupSong(song, album);
|
this.setupSong(song, album)
|
||||||
});
|
})
|
||||||
|
|
||||||
return songs.concat(album.songs);
|
return songs.concat(album.songs)
|
||||||
}, []);
|
}, [])
|
||||||
},
|
},
|
||||||
|
|
||||||
setupSong (song, album) {
|
setupSong (song, album) {
|
||||||
song.fmtLength = secondsToHis(song.length);
|
song.fmtLength = secondsToHis(song.length)
|
||||||
|
|
||||||
// Manually set these additional properties to be reactive
|
// Manually set these additional properties to be reactive
|
||||||
Vue.set(song, 'playCount', 0);
|
Vue.set(song, 'playCount', 0)
|
||||||
Vue.set(song, 'album', album);
|
Vue.set(song, 'album', album)
|
||||||
Vue.set(song, 'liked', false);
|
Vue.set(song, 'liked', false)
|
||||||
Vue.set(song, 'lyrics', null);
|
Vue.set(song, 'lyrics', null)
|
||||||
Vue.set(song, 'playbackState', 'stopped');
|
Vue.set(song, 'playbackState', 'stopped')
|
||||||
|
|
||||||
if (song.contributing_artist_id) {
|
if (song.contributing_artist_id) {
|
||||||
const artist = artistStore.byId(song.contributing_artist_id);
|
const artist = artistStore.byId(song.contributing_artist_id)
|
||||||
artist.albums = union(artist.albums, [album]);
|
artist.albums = union(artist.albums, [album])
|
||||||
artistStore.setupArtist(artist);
|
artistStore.setupArtist(artist)
|
||||||
Vue.set(song, 'artist', artist);
|
Vue.set(song, 'artist', artist)
|
||||||
} else {
|
} else {
|
||||||
Vue.set(song, 'artist', artistStore.byId(song.album.artist.id));
|
Vue.set(song, 'artist', artistStore.byId(song.album.artist.id))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache the song, so that byId() is faster
|
// Cache the song, so that byId() is faster
|
||||||
this.cache[song.id] = song;
|
this.cache[song.id] = song
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -73,24 +73,24 @@ export const songStore = {
|
||||||
* @param {Array.<Object>} interactions The array of interactions of the current user
|
* @param {Array.<Object>} interactions The array of interactions of the current user
|
||||||
*/
|
*/
|
||||||
initInteractions (interactions) {
|
initInteractions (interactions) {
|
||||||
favoriteStore.clear();
|
favoriteStore.clear()
|
||||||
|
|
||||||
each(interactions, interaction => {
|
each(interactions, interaction => {
|
||||||
const song = this.byId(interaction.song_id);
|
const song = this.byId(interaction.song_id)
|
||||||
|
|
||||||
if (!song) {
|
if (!song) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
song.liked = interaction.liked;
|
song.liked = interaction.liked
|
||||||
song.playCount = interaction.play_count;
|
song.playCount = interaction.play_count
|
||||||
song.album.playCount += song.playCount;
|
song.album.playCount += song.playCount
|
||||||
song.artist.playCount += song.playCount;
|
song.artist.playCount += song.playCount
|
||||||
|
|
||||||
if (song.liked) {
|
if (song.liked) {
|
||||||
favoriteStore.add(song);
|
favoriteStore.add(song)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -102,9 +102,9 @@ export const songStore = {
|
||||||
* @return {Float|String}
|
* @return {Float|String}
|
||||||
*/
|
*/
|
||||||
getLength (songs, toHis) {
|
getLength (songs, toHis) {
|
||||||
const duration = songs.reduce((length, song) => length + song.length, 0);
|
const duration = songs.reduce((length, song) => length + song.length, 0)
|
||||||
|
|
||||||
return toHis ? secondsToHis(duration) : duration;
|
return toHis ? secondsToHis(duration) : duration
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -113,7 +113,7 @@ export const songStore = {
|
||||||
* @return {Array.<Object>}
|
* @return {Array.<Object>}
|
||||||
*/
|
*/
|
||||||
get all () {
|
get all () {
|
||||||
return this.state.songs;
|
return this.state.songs
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -122,7 +122,7 @@ export const songStore = {
|
||||||
* @param {Array.<Object>} value
|
* @param {Array.<Object>} value
|
||||||
*/
|
*/
|
||||||
set all (value) {
|
set all (value) {
|
||||||
this.state.songs = value;
|
this.state.songs = value
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -133,7 +133,7 @@ export const songStore = {
|
||||||
* @return {Object}
|
* @return {Object}
|
||||||
*/
|
*/
|
||||||
byId (id) {
|
byId (id) {
|
||||||
return this.cache[id];
|
return this.cache[id]
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -144,7 +144,7 @@ export const songStore = {
|
||||||
* @return {Array.<Object>}
|
* @return {Array.<Object>}
|
||||||
*/
|
*/
|
||||||
byIds (ids) {
|
byIds (ids) {
|
||||||
return ids.map(id => this.byId(id));
|
return ids.map(id => this.byId(id))
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -154,17 +154,17 @@ export const songStore = {
|
||||||
*/
|
*/
|
||||||
registerPlay (song) {
|
registerPlay (song) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const oldCount = song.playCount;
|
const oldCount = song.playCount
|
||||||
|
|
||||||
http.post('interaction/play', { song: song.id }, data => {
|
http.post('interaction/play', { song: song.id }, data => {
|
||||||
// Use the data from the server to make sure we don't miss a play from another device.
|
// Use the data from the server to make sure we don't miss a play from another device.
|
||||||
song.playCount = data.play_count;
|
song.playCount = data.play_count
|
||||||
song.album.playCount += song.playCount - oldCount;
|
song.album.playCount += song.playCount - oldCount
|
||||||
song.artist.playCount += song.playCount - oldCount;
|
song.artist.playCount += song.playCount - oldCount
|
||||||
|
|
||||||
resolve(data);
|
resolve(data)
|
||||||
}, r => reject(r));
|
}, r => reject(r))
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -174,10 +174,10 @@ export const songStore = {
|
||||||
*/
|
*/
|
||||||
addRecent (song) {
|
addRecent (song) {
|
||||||
// First we make sure that there's no duplicate.
|
// First we make sure that there's no duplicate.
|
||||||
this.state.recent = without(this.state.recent, song);
|
this.state.recent = without(this.state.recent, song)
|
||||||
|
|
||||||
// Then we prepend the song into the list.
|
// Then we prepend the song into the list.
|
||||||
this.state.recent.unshift(song);
|
this.state.recent.unshift(song)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -187,7 +187,7 @@ export const songStore = {
|
||||||
*/
|
*/
|
||||||
scrobble (song) {
|
scrobble (song) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
http.post(`${song.id}/scrobble/${song.playStartTime}`, {}, data => resolve(data), r => reject(r));
|
http.post(`${song.id}/scrobble/${song.playStartTime}`, {}, data => resolve(data), r => reject(r))
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -201,12 +201,12 @@ export const songStore = {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
http.put('songs', {
|
http.put('songs', {
|
||||||
data,
|
data,
|
||||||
songs: map(songs, 'id'),
|
songs: map(songs, 'id')
|
||||||
}, songs => {
|
}, songs => {
|
||||||
each(songs, song => this.syncUpdatedSong(song));
|
each(songs, song => this.syncUpdatedSong(song))
|
||||||
resolve(songs);
|
resolve(songs)
|
||||||
}, r => reject(r));
|
}, r => reject(r))
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -229,77 +229,77 @@ export const songStore = {
|
||||||
// 2.b. Artist changes as well. Note that an artist might have been created.
|
// 2.b. Artist changes as well. Note that an artist might have been created.
|
||||||
|
|
||||||
// Find the original song,
|
// Find the original song,
|
||||||
const originalSong = this.byId(updatedSong.id);
|
const originalSong = this.byId(updatedSong.id)
|
||||||
|
|
||||||
if (!originalSong) {
|
if (!originalSong) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// and keep track of original album/artist.
|
// and keep track of original album/artist.
|
||||||
const originalAlbumId = originalSong.album.id;
|
const originalAlbumId = originalSong.album.id
|
||||||
const originalArtistId = originalSong.artist.id;
|
const originalArtistId = originalSong.artist.id
|
||||||
|
|
||||||
// First, we update the title, lyrics, and track #
|
// First, we update the title, lyrics, and track #
|
||||||
originalSong.title = updatedSong.title;
|
originalSong.title = updatedSong.title
|
||||||
originalSong.lyrics = updatedSong.lyrics;
|
originalSong.lyrics = updatedSong.lyrics
|
||||||
originalSong.track = updatedSong.track;
|
originalSong.track = updatedSong.track
|
||||||
|
|
||||||
if (updatedSong.album.id === originalAlbumId) { // case 1
|
if (updatedSong.album.id === originalAlbumId) { // case 1
|
||||||
// Nothing to do
|
// Nothing to do
|
||||||
} else { // case 2
|
} else { // case 2
|
||||||
// First, remove it from its old album
|
// First, remove it from its old album
|
||||||
albumStore.removeSongsFromAlbum(originalSong.album, originalSong);
|
albumStore.removeSongsFromAlbum(originalSong.album, originalSong)
|
||||||
|
|
||||||
const existingAlbum = albumStore.byId(updatedSong.album.id);
|
const existingAlbum = albumStore.byId(updatedSong.album.id)
|
||||||
const newAlbumCreated = !existingAlbum;
|
const newAlbumCreated = !existingAlbum
|
||||||
|
|
||||||
if (!newAlbumCreated) {
|
if (!newAlbumCreated) {
|
||||||
// The song changed to an existing album. We now add it to such album.
|
// The song changed to an existing album. We now add it to such album.
|
||||||
albumStore.addSongsIntoAlbum(existingAlbum, originalSong);
|
albumStore.addSongsIntoAlbum(existingAlbum, originalSong)
|
||||||
} else {
|
} else {
|
||||||
// A new album was created. We:
|
// A new album was created. We:
|
||||||
// - Add the new album into our collection
|
// - Add the new album into our collection
|
||||||
// - Add the song into it
|
// - Add the song into it
|
||||||
albumStore.addSongsIntoAlbum(updatedSong.album, originalSong);
|
albumStore.addSongsIntoAlbum(updatedSong.album, originalSong)
|
||||||
albumStore.add(updatedSong.album);
|
albumStore.add(updatedSong.album)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updatedSong.album.artist.id === originalArtistId) { // case 2.a
|
if (updatedSong.album.artist.id === originalArtistId) { // case 2.a
|
||||||
// Same artist, but what if the album is new?
|
// Same artist, but what if the album is new?
|
||||||
if (newAlbumCreated) {
|
if (newAlbumCreated) {
|
||||||
artistStore.addAlbumsIntoArtist(artistStore.byId(originalArtistId), updatedSong.album);
|
artistStore.addAlbumsIntoArtist(artistStore.byId(originalArtistId), updatedSong.album)
|
||||||
}
|
}
|
||||||
} else { // case 2.b
|
} else { // case 2.b
|
||||||
// The artist changes.
|
// The artist changes.
|
||||||
const existingArtist = artistStore.byId(updatedSong.album.artist.id);
|
const existingArtist = artistStore.byId(updatedSong.album.artist.id)
|
||||||
|
|
||||||
if (existingArtist) {
|
if (existingArtist) {
|
||||||
originalSong.artist = existingArtist;
|
originalSong.artist = existingArtist
|
||||||
} else {
|
} else {
|
||||||
// New artist created. We:
|
// New artist created. We:
|
||||||
// - Add the album into it, because now it MUST BE a new album
|
// - Add the album into it, because now it MUST BE a new album
|
||||||
// (there's no "new artist with existing album" in our system).
|
// (there's no "new artist with existing album" in our system).
|
||||||
// - Add the new artist into our collection
|
// - Add the new artist into our collection
|
||||||
artistStore.addAlbumsIntoArtist(updatedSong.album.artist, updatedSong.album);
|
artistStore.addAlbumsIntoArtist(updatedSong.album.artist, updatedSong.album)
|
||||||
artistStore.add(updatedSong.album.artist);
|
artistStore.add(updatedSong.album.artist)
|
||||||
originalSong.artist = updatedSong.album.artist;
|
originalSong.artist = updatedSong.album.artist
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// As a last step, we purify our library of empty albums/artists.
|
// As a last step, we purify our library of empty albums/artists.
|
||||||
if (albumStore.isAlbumEmpty(albumStore.byId(originalAlbumId))) {
|
if (albumStore.isAlbumEmpty(albumStore.byId(originalAlbumId))) {
|
||||||
albumStore.remove(albumStore.byId(originalAlbumId));
|
albumStore.remove(albumStore.byId(originalAlbumId))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (artistStore.isArtistEmpty(artistStore.byId(originalArtistId))) {
|
if (artistStore.isArtistEmpty(artistStore.byId(originalArtistId))) {
|
||||||
artistStore.remove(artistStore.byId(originalArtistId));
|
artistStore.remove(artistStore.byId(originalArtistId))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we make sure the next call to info() get the refreshed, correct info.
|
// Now we make sure the next call to info() get the refreshed, correct info.
|
||||||
originalSong.infoRetrieved = false;
|
originalSong.infoRetrieved = false
|
||||||
}
|
}
|
||||||
|
|
||||||
return originalSong;
|
return originalSong
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -310,7 +310,7 @@ export const songStore = {
|
||||||
* @return {string} The source URL, with JWT token appended.
|
* @return {string} The source URL, with JWT token appended.
|
||||||
*/
|
*/
|
||||||
getSourceUrl (song) {
|
getSourceUrl (song) {
|
||||||
return `${sharedStore.state.cdnUrl}api/${song.id}/play?jwt-token=${ls.get('jwt-token')}`;
|
return `${sharedStore.state.cdnUrl}api/${song.id}/play?jwt-token=${ls.get('jwt-token')}`
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -322,7 +322,7 @@ export const songStore = {
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
getShareableUrl (song) {
|
getShareableUrl (song) {
|
||||||
return `${window.location.origin}/#!/song/${song.id}`;
|
return `${window.location.origin}/#!/song/${song.id}`
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -333,7 +333,7 @@ export const songStore = {
|
||||||
* @return {Array.<Object>}
|
* @return {Array.<Object>}
|
||||||
*/
|
*/
|
||||||
getRecent (n = 10) {
|
getRecent (n = 10) {
|
||||||
return take(this.state.recent, n);
|
return take(this.state.recent, n)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -344,12 +344,12 @@ export const songStore = {
|
||||||
* @return {Array.<Object>}
|
* @return {Array.<Object>}
|
||||||
*/
|
*/
|
||||||
getMostPlayed (n = 10) {
|
getMostPlayed (n = 10) {
|
||||||
const songs = take(orderBy(this.all, 'playCount', 'desc'), n);
|
const songs = take(orderBy(this.all, 'playCount', 'desc'), n)
|
||||||
|
|
||||||
// Remove those with playCount=0
|
// Remove those with playCount=0
|
||||||
remove(songs, song => !song.playCount);
|
remove(songs, song => !song.playCount)
|
||||||
|
|
||||||
return songs;
|
return songs
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -358,7 +358,7 @@ export const songStore = {
|
||||||
* @return {Array.<Object>}
|
* @return {Array.<Object>}
|
||||||
*/
|
*/
|
||||||
getRecentlyAdded (n = 10) {
|
getRecentlyAdded (n = 10) {
|
||||||
return take(orderBy(this.all, 'created_at', 'desc'), n);
|
return take(orderBy(this.all, 'created_at', 'desc'), n)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -366,6 +366,6 @@ export const songStore = {
|
||||||
* Reset stuff.
|
* Reset stuff.
|
||||||
*/
|
*/
|
||||||
teardown () {
|
teardown () {
|
||||||
this.state.recent = [];
|
this.state.recent = []
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import { each, find, without } from 'lodash';
|
import { each, find, without } from 'lodash'
|
||||||
import md5 from 'blueimp-md5';
|
import md5 from 'blueimp-md5'
|
||||||
import Vue from 'vue';
|
import Vue from 'vue'
|
||||||
import NProgress from 'nprogress';
|
import NProgress from 'nprogress'
|
||||||
|
|
||||||
import { http } from '../services';
|
import { http } from '../services'
|
||||||
import stub from '../stubs/user';
|
import stub from '../stubs/user'
|
||||||
|
|
||||||
export const userStore = {
|
export const userStore = {
|
||||||
stub,
|
stub,
|
||||||
|
|
||||||
state: {
|
state: {
|
||||||
users: [],
|
users: [],
|
||||||
current: stub,
|
current: stub
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,14 +21,14 @@ export const userStore = {
|
||||||
* @param {Object} currentUser The current user.
|
* @param {Object} currentUser The current user.
|
||||||
*/
|
*/
|
||||||
init (users, currentUser) {
|
init (users, currentUser) {
|
||||||
this.all = users;
|
this.all = users
|
||||||
this.current = currentUser;
|
this.current = currentUser
|
||||||
|
|
||||||
// Set the avatar for each of the users…
|
// Set the avatar for each of the users…
|
||||||
each(this.all, this.setAvatar);
|
each(this.all, this.setAvatar)
|
||||||
|
|
||||||
// …and the current user as well.
|
// …and the current user as well.
|
||||||
this.setAvatar();
|
this.setAvatar()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,7 +37,7 @@ export const userStore = {
|
||||||
* @return {Array.<Object>}
|
* @return {Array.<Object>}
|
||||||
*/
|
*/
|
||||||
get all () {
|
get all () {
|
||||||
return this.state.users;
|
return this.state.users
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -46,7 +46,7 @@ export const userStore = {
|
||||||
* @param {Array.<Object>} value
|
* @param {Array.<Object>} value
|
||||||
*/
|
*/
|
||||||
set all (value) {
|
set all (value) {
|
||||||
this.state.users = value;
|
this.state.users = value
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -57,7 +57,7 @@ export const userStore = {
|
||||||
* @return {Object}
|
* @return {Object}
|
||||||
*/
|
*/
|
||||||
byId (id) {
|
byId (id) {
|
||||||
return find(this.all, { id });
|
return find(this.all, { id })
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,7 +66,7 @@ export const userStore = {
|
||||||
* @return {Object}
|
* @return {Object}
|
||||||
*/
|
*/
|
||||||
get current () {
|
get current () {
|
||||||
return this.state.current;
|
return this.state.current
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -77,7 +77,8 @@ export const userStore = {
|
||||||
* @return {Object}
|
* @return {Object}
|
||||||
*/
|
*/
|
||||||
set current (user) {
|
set current (user) {
|
||||||
return this.state.current = user;
|
this.state.current = user
|
||||||
|
return this.state.current
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -87,10 +88,10 @@ export const userStore = {
|
||||||
*/
|
*/
|
||||||
setAvatar (user = null) {
|
setAvatar (user = null) {
|
||||||
if (!user) {
|
if (!user) {
|
||||||
user = this.current;
|
user = this.current
|
||||||
}
|
}
|
||||||
|
|
||||||
Vue.set(user, 'avatar', `https://www.gravatar.com/avatar/${md5(user.email)}?s=256`);
|
Vue.set(user, 'avatar', `https://www.gravatar.com/avatar/${md5(user.email)}?s=256`)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -100,11 +101,11 @@ export const userStore = {
|
||||||
* @param {String} password
|
* @param {String} password
|
||||||
*/
|
*/
|
||||||
login (email, password) {
|
login (email, password) {
|
||||||
NProgress.start();
|
NProgress.start()
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
http.post('me', { email, password }, data => resolve(data), r => reject(r));
|
http.post('me', { email, password }, data => resolve(data), r => reject(r))
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -112,8 +113,8 @@ export const userStore = {
|
||||||
*/
|
*/
|
||||||
logout () {
|
logout () {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
http.delete('me', {}, data => resolve(data), r => reject(r));
|
http.delete('me', {}, data => resolve(data), r => reject(r))
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -122,7 +123,7 @@ export const userStore = {
|
||||||
* @param {string} password Can be an empty string if the user is not changing his password.
|
* @param {string} password Can be an empty string if the user is not changing his password.
|
||||||
*/
|
*/
|
||||||
updateProfile (password) {
|
updateProfile (password) {
|
||||||
NProgress.start();
|
NProgress.start()
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
http.put('me', {
|
http.put('me', {
|
||||||
|
@ -130,11 +131,11 @@ export const userStore = {
|
||||||
name: this.current.name,
|
name: this.current.name,
|
||||||
email: this.current.email
|
email: this.current.email
|
||||||
}, () => {
|
}, () => {
|
||||||
this.setAvatar();
|
this.setAvatar()
|
||||||
resolve(this.current)
|
resolve(this.current)
|
||||||
}, r => reject(r)
|
},
|
||||||
);
|
r => reject(r))
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -145,15 +146,15 @@ export const userStore = {
|
||||||
* @param {string} password
|
* @param {string} password
|
||||||
*/
|
*/
|
||||||
store (name, email, password) {
|
store (name, email, password) {
|
||||||
NProgress.start();
|
NProgress.start()
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
http.post('user', { name, email, password }, user => {
|
http.post('user', { name, email, password }, user => {
|
||||||
this.setAvatar(user);
|
this.setAvatar(user)
|
||||||
this.all.unshift(user);
|
this.all.unshift(user)
|
||||||
resolve(user);
|
resolve(user)
|
||||||
}, r => reject(r));
|
}, r => reject(r))
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -165,15 +166,15 @@ export const userStore = {
|
||||||
* @param {String} password
|
* @param {String} password
|
||||||
*/
|
*/
|
||||||
update (user, name, email, password) {
|
update (user, name, email, password) {
|
||||||
NProgress.start();
|
NProgress.start()
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
http.put(`user/${user.id}`, { name, email, password }, () => {
|
http.put(`user/${user.id}`, { name, email, password }, () => {
|
||||||
this.setAvatar(user);
|
this.setAvatar(user)
|
||||||
user.password = '';
|
user.password = ''
|
||||||
resolve(user);
|
resolve(user)
|
||||||
}, r => reject(r));
|
}, r => reject(r))
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -182,11 +183,11 @@ export const userStore = {
|
||||||
* @param {Object} user
|
* @param {Object} user
|
||||||
*/
|
*/
|
||||||
destroy (user) {
|
destroy (user) {
|
||||||
NProgress.start();
|
NProgress.start()
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
http.delete(`user/${user.id}`, {}, data => {
|
http.delete(`user/${user.id}`, {}, data => {
|
||||||
this.all = without(this.all, user);
|
this.all = without(this.all, user)
|
||||||
|
|
||||||
// Mama, just killed a man
|
// Mama, just killed a man
|
||||||
// Put a gun against his head
|
// Put a gun against his head
|
||||||
|
@ -210,8 +211,8 @@ export const userStore = {
|
||||||
/**
|
/**
|
||||||
* Brian May enters the stage.
|
* Brian May enters the stage.
|
||||||
*/
|
*/
|
||||||
resolve(data);
|
resolve(data)
|
||||||
}, r => reject(r));
|
}, r => reject(r))
|
||||||
});
|
})
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import config from '../config';
|
import config from '../config'
|
||||||
import artist from './artist';
|
import artist from './artist'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
artist,
|
artist,
|
||||||
|
@ -10,5 +10,5 @@ export default {
|
||||||
playCount: 0,
|
playCount: 0,
|
||||||
length: 0,
|
length: 0,
|
||||||
fmtLength: '00:00',
|
fmtLength: '00:00',
|
||||||
songs: [],
|
songs: []
|
||||||
};
|
}
|
||||||
|
|
|
@ -4,5 +4,5 @@ export default {
|
||||||
image: null,
|
image: null,
|
||||||
playCount: 0,
|
playCount: 0,
|
||||||
albums: [],
|
albums: [],
|
||||||
songs: [],
|
songs: []
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export default {
|
export default {
|
||||||
name: '',
|
name: '',
|
||||||
songs: [],
|
songs: []
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
export default {
|
export default {
|
||||||
media_path: '',
|
media_path: ''
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import album from './album';
|
import album from './album'
|
||||||
import artist from './artist';
|
import artist from './artist'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
album,
|
album,
|
||||||
|
@ -12,5 +12,5 @@ export default {
|
||||||
lyrics: '',
|
lyrics: '',
|
||||||
liked: false,
|
liked: false,
|
||||||
playCount: 0,
|
playCount: 0,
|
||||||
playbackState: 'stopped',
|
playbackState: 'stopped'
|
||||||
};
|
}
|
||||||
|
|
|
@ -3,5 +3,5 @@ export default {
|
||||||
name: '',
|
name: '',
|
||||||
email: '',
|
email: '',
|
||||||
avatar: '',
|
avatar: '',
|
||||||
is_admin: false,
|
is_admin: false
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,32 +1,32 @@
|
||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
song_id: "7900ab518f51775fe6cf06092c074ee5",
|
song_id: '7900ab518f51775fe6cf06092c074ee5',
|
||||||
liked: false,
|
liked: false,
|
||||||
play_count: 1
|
play_count: 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
song_id: "95c0ffc33c08c8c14ea5de0a44d5df3c",
|
song_id: '95c0ffc33c08c8c14ea5de0a44d5df3c',
|
||||||
liked: false,
|
liked: false,
|
||||||
play_count: 2
|
play_count: 2
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
song_id: "c83b201502eb36f1084f207761fa195c",
|
song_id: 'c83b201502eb36f1084f207761fa195c',
|
||||||
liked: false,
|
liked: false,
|
||||||
play_count: 1
|
play_count: 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 4,
|
id: 4,
|
||||||
song_id: "cb7edeac1f097143e65b1b2cde102482",
|
song_id: 'cb7edeac1f097143e65b1b2cde102482',
|
||||||
liked: true,
|
liked: true,
|
||||||
play_count: 3
|
play_count: 3
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 5,
|
id: 5,
|
||||||
song_id: "ccc38cc14bb95aefdf6da4b34adcf548",
|
song_id: 'ccc38cc14bb95aefdf6da4b34adcf548',
|
||||||
liked: false,
|
liked: false,
|
||||||
play_count: 4
|
play_count: 4
|
||||||
}
|
}
|
||||||
];
|
]
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
name: "All-4-One",
|
name: 'All-4-One',
|
||||||
albums: [
|
albums: [
|
||||||
{
|
{
|
||||||
id: 1193,
|
id: 1193,
|
||||||
artist_id: 1,
|
artist_id: 1,
|
||||||
name: "All-4-One",
|
name: 'All-4-One',
|
||||||
cover: "/public/img/covers/565c0f7067425.jpeg",
|
cover: '/public/img/covers/565c0f7067425.jpeg',
|
||||||
songs: [
|
songs: [
|
||||||
{
|
{
|
||||||
id: "39189f4545f9d5671fb3dc964f0080a0",
|
id: '39189f4545f9d5671fb3dc964f0080a0',
|
||||||
album_id: 1193,
|
album_id: 1193,
|
||||||
title: "I Swear",
|
title: 'I Swear',
|
||||||
length: 259.92,
|
length: 259.92,
|
||||||
playCount: 4
|
playCount: 4
|
||||||
}
|
}
|
||||||
|
@ -21,13 +21,13 @@ export default [
|
||||||
{
|
{
|
||||||
id: 1194,
|
id: 1194,
|
||||||
artist_id: 1,
|
artist_id: 1,
|
||||||
name: "And The Music Speaks",
|
name: 'And The Music Speaks',
|
||||||
cover: "/public/img/covers/unknown-album.png",
|
cover: '/public/img/covers/unknown-album.png',
|
||||||
songs: [
|
songs: [
|
||||||
{
|
{
|
||||||
id: "a6a550f7d950d2a2520f9bf1a60f025a",
|
id: 'a6a550f7d950d2a2520f9bf1a60f025a',
|
||||||
album_id: 1194,
|
album_id: 1194,
|
||||||
title: "I can love you like that",
|
title: 'I can love you like that',
|
||||||
length: 262.61,
|
length: 262.61,
|
||||||
playCount: 2
|
playCount: 2
|
||||||
}
|
}
|
||||||
|
@ -36,13 +36,13 @@ export default [
|
||||||
{
|
{
|
||||||
id: 1195,
|
id: 1195,
|
||||||
artist_id: 1,
|
artist_id: 1,
|
||||||
name: "Space Jam",
|
name: 'Space Jam',
|
||||||
cover: "/public/img/covers/565c0f7115e0f.png",
|
cover: '/public/img/covers/565c0f7115e0f.png',
|
||||||
songs: [
|
songs: [
|
||||||
{
|
{
|
||||||
id: "d86c30fd34f13c1aff8db59b7fc9c610",
|
id: 'd86c30fd34f13c1aff8db59b7fc9c610',
|
||||||
album_id: 1195,
|
album_id: 1195,
|
||||||
title: "I turn to you",
|
title: 'I turn to you',
|
||||||
length: 293.04
|
length: 293.04
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -51,18 +51,18 @@ export default [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
name: "Bob Dylan",
|
name: 'Bob Dylan',
|
||||||
albums: [
|
albums: [
|
||||||
{
|
{
|
||||||
id: 1217,
|
id: 1217,
|
||||||
artist_id: 2,
|
artist_id: 2,
|
||||||
name: "Highway 61 Revisited",
|
name: 'Highway 61 Revisited',
|
||||||
cover: "/public/img/covers/565c0f76dc6e8.jpeg",
|
cover: '/public/img/covers/565c0f76dc6e8.jpeg',
|
||||||
songs: [
|
songs: [
|
||||||
{
|
{
|
||||||
id: "e6d3977f3ffa147801ca5d1fdf6fa55e",
|
id: 'e6d3977f3ffa147801ca5d1fdf6fa55e',
|
||||||
album_id: 1217,
|
album_id: 1217,
|
||||||
title: "Like a rolling stone",
|
title: 'Like a rolling stone',
|
||||||
length: 373.63
|
length: 373.63
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -70,13 +70,13 @@ export default [
|
||||||
{
|
{
|
||||||
id: 1218,
|
id: 1218,
|
||||||
artist_id: 2,
|
artist_id: 2,
|
||||||
name: "Pat Garrett & Billy the Kid",
|
name: 'Pat Garrett & Billy the Kid',
|
||||||
cover: "/public/img/covers/unknown-album.png",
|
cover: '/public/img/covers/unknown-album.png',
|
||||||
songs: [
|
songs: [
|
||||||
{
|
{
|
||||||
id: "aa16bbef6a9710eb9a0f41ecc534fad5",
|
id: 'aa16bbef6a9710eb9a0f41ecc534fad5',
|
||||||
album_id: 1218,
|
album_id: 1218,
|
||||||
title: "Knockin' on heaven's door",
|
title: 'Knockin\' on heaven\'s door',
|
||||||
length: 151.9
|
length: 151.9
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -84,13 +84,13 @@ export default [
|
||||||
{
|
{
|
||||||
id: 1219,
|
id: 1219,
|
||||||
artist_id: 2,
|
artist_id: 2,
|
||||||
name: "The Times They Are A-Changin'",
|
name: 'The Times They Are A-Changin\'',
|
||||||
cover: "/public/img/covers/unknown-album.png",
|
cover: '/public/img/covers/unknown-album.png',
|
||||||
songs: [
|
songs: [
|
||||||
{
|
{
|
||||||
id: "cb7edeac1f097143e65b1b2cde102482",
|
id: 'cb7edeac1f097143e65b1b2cde102482',
|
||||||
album_id: 1219,
|
album_id: 1219,
|
||||||
title: "The times they are a-changin'",
|
title: 'The times they are a-changin\'',
|
||||||
length: 196
|
length: 196
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -99,116 +99,116 @@ export default [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
name: "James Blunt",
|
name: 'James Blunt',
|
||||||
albums: [
|
albums: [
|
||||||
{
|
{
|
||||||
id: 1268,
|
id: 1268,
|
||||||
artist_id: 3,
|
artist_id: 3,
|
||||||
name: "Back To Bedlam",
|
name: 'Back To Bedlam',
|
||||||
cover: "/public/img/covers/unknown-album.png",
|
cover: '/public/img/covers/unknown-album.png',
|
||||||
songs: [
|
songs: [
|
||||||
{
|
{
|
||||||
id: "0ba9fb128427b32683b9eb9140912a70",
|
id: '0ba9fb128427b32683b9eb9140912a70',
|
||||||
album_id: 1268,
|
album_id: 1268,
|
||||||
title: "No bravery",
|
title: 'No bravery',
|
||||||
length: 243.12
|
length: 243.12
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "123fd1ad32240ecab28a4e86ed5173",
|
id: '123fd1ad32240ecab28a4e86ed5173',
|
||||||
album_id: 1268,
|
album_id: 1268,
|
||||||
title: "So long, Jimmy",
|
title: 'So long, Jimmy',
|
||||||
length: 265.04
|
length: 265.04
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "6a54c674d8b16732f26df73f59c63e21",
|
id: '6a54c674d8b16732f26df73f59c63e21',
|
||||||
album_id: 1268,
|
album_id: 1268,
|
||||||
title: "Wisemen",
|
title: 'Wisemen',
|
||||||
length: 223.14
|
length: 223.14
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "6df7d82a9a8701e40d1c291cf14a16bc",
|
id: '6df7d82a9a8701e40d1c291cf14a16bc',
|
||||||
album_id: 1268,
|
album_id: 1268,
|
||||||
title: "Goodbye my lover",
|
title: 'Goodbye my lover',
|
||||||
length: 258.61
|
length: 258.61
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "74a2000d343e4587273d3ad14e2fd741",
|
id: '74a2000d343e4587273d3ad14e2fd741',
|
||||||
album_id: 1268,
|
album_id: 1268,
|
||||||
title: "High",
|
title: 'High',
|
||||||
length: 245.86
|
length: 245.86
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "7900ab518f51775fe6cf06092c074ee5",
|
id: '7900ab518f51775fe6cf06092c074ee5',
|
||||||
album_id: 1268,
|
album_id: 1268,
|
||||||
title: "You're beautiful",
|
title: 'You\'re beautiful',
|
||||||
length: 213.29
|
length: 213.29
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "803910a51f9893347e087af851e38777",
|
id: '803910a51f9893347e087af851e38777',
|
||||||
album_id: 1268,
|
album_id: 1268,
|
||||||
title: "Cry",
|
title: 'Cry',
|
||||||
length: 246.91
|
length: 246.91
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "d82b0d4d4803ebbcb61000a5b6a868f5",
|
id: 'd82b0d4d4803ebbcb61000a5b6a868f5',
|
||||||
album_id: 1268,
|
album_id: 1268,
|
||||||
title: "Tears and rain",
|
title: 'Tears and rain',
|
||||||
length: 244.45
|
length: 244.45
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
];
|
]
|
||||||
|
|
||||||
export const singleAlbum = {
|
export const singleAlbum = {
|
||||||
id: 9999,
|
id: 9999,
|
||||||
artist_id: 99,
|
artist_id: 99,
|
||||||
name: "Foo bar",
|
name: 'Foo bar',
|
||||||
cover: "/foo.jpg",
|
cover: '/foo.jpg',
|
||||||
songs: [
|
songs: [
|
||||||
{
|
{
|
||||||
id: "39189f4545f0d5671fc3dc964f0080a0",
|
id: '39189f4545f0d5671fc3dc964f0080a0',
|
||||||
album_id: 9999,
|
album_id: 9999,
|
||||||
title: "A Foo Song",
|
title: 'A Foo Song',
|
||||||
length: 100,
|
length: 100,
|
||||||
playCount: 4
|
playCount: 4
|
||||||
}, {
|
}, {
|
||||||
id: "39189f4545f9d5671fc3dc96cf1080a0",
|
id: '39189f4545f9d5671fc3dc96cf1080a0',
|
||||||
album_id: 9999,
|
album_id: 9999,
|
||||||
title: "A Bar Song",
|
title: 'A Bar Song',
|
||||||
length: 200,
|
length: 200,
|
||||||
playCount: 7
|
playCount: 7
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
}
|
||||||
|
|
||||||
export const singleArtist = {
|
export const singleArtist = {
|
||||||
id: 999,
|
id: 999,
|
||||||
name: "John Cena",
|
name: 'John Cena',
|
||||||
albums: [
|
albums: [
|
||||||
{
|
{
|
||||||
id: 9991,
|
id: 9991,
|
||||||
artist_id: 999,
|
artist_id: 999,
|
||||||
name: "It's John Cena!!!!",
|
name: 'It\'s John Cena!!!!',
|
||||||
cover: "/tmp/john.jpg",
|
cover: '/tmp/john.jpg',
|
||||||
songs: [
|
songs: [
|
||||||
{
|
{
|
||||||
id: "e6d3977f3ffa147801ca5d1fdf6fa55f",
|
id: 'e6d3977f3ffa147801ca5d1fdf6fa55f',
|
||||||
album_id: 9991,
|
album_id: 9991,
|
||||||
title: "John Cena to the Rescue",
|
title: 'John Cena to the Rescue',
|
||||||
length: 300
|
length: 300
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
}
|
||||||
|
|
||||||
export const singleSong = {
|
export const singleSong = {
|
||||||
id: "dccb0d4d4803ebbcb61000a5b6a868f5",
|
id: 'dccb0d4d4803ebbcb61000a5b6a868f5',
|
||||||
album_id: 1193,
|
album_id: 1193,
|
||||||
title: "Foo and Bar",
|
title: 'Foo and Bar',
|
||||||
length: 100,
|
length: 100,
|
||||||
playCount: 4,
|
playCount: 4,
|
||||||
lyrics: ''
|
lyrics: ''
|
||||||
};
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ export default {
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'Phan An',
|
name: 'Phan An',
|
||||||
email: 'me@phanan.net',
|
email: 'me@phanan.net',
|
||||||
is_admin: true,
|
is_admin: true
|
||||||
},
|
},
|
||||||
|
|
||||||
users: [
|
users: [
|
||||||
|
@ -11,13 +11,13 @@ export default {
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'Phan An',
|
name: 'Phan An',
|
||||||
email: 'me@phanan.net',
|
email: 'me@phanan.net',
|
||||||
is_admin: true,
|
is_admin: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
name: 'John Doe',
|
name: 'John Doe',
|
||||||
email: 'john@doe.tld',
|
email: 'john@doe.tld',
|
||||||
is_admin: false,
|
is_admin: false
|
||||||
},
|
}
|
||||||
]
|
]
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import { jsdom } from 'jsdom';
|
import { jsdom } from 'jsdom'
|
||||||
|
|
||||||
const doc = jsdom('<!doctype html><html><body></body></html>');
|
const doc = jsdom('<!doctype html><html><body></body></html>')
|
||||||
const win = doc.defaultView;
|
const win = doc.defaultView
|
||||||
|
|
||||||
global.document = doc;
|
global.document = doc
|
||||||
global.window = win;
|
global.window = win
|
||||||
|
|
||||||
Object.keys(window).forEach((key) => {
|
Object.keys(window).forEach((key) => {
|
||||||
if (!(key in global)) {
|
if (!(key in global)) {
|
||||||
global[key] = window[key];
|
global[key] = window[key]
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
|
@ -1,34 +1,35 @@
|
||||||
require('chai').should();
|
require('chai').should()
|
||||||
|
|
||||||
import localStorage from 'local-storage';
|
import localStorage from 'local-storage'
|
||||||
import { ls } from '../../services';
|
import { ls } from '../../services'
|
||||||
|
|
||||||
describe('services/ls', () => {
|
describe('services/ls', () => {
|
||||||
beforeEach(() => localStorage.remove('foo'));
|
beforeEach(() => localStorage.remove('foo'))
|
||||||
|
|
||||||
describe('#get', () => {
|
describe('#get', () => {
|
||||||
it('correctly gets an existing item from local storage', () => {
|
it('correctly gets an existing item from local storage', () => {
|
||||||
localStorage('foo', 'bar');
|
localStorage('foo', 'bar')
|
||||||
ls.get('foo').should.equal('bar');
|
ls.get('foo').should.equal('bar')
|
||||||
});
|
})
|
||||||
|
|
||||||
it('correctly returns the default value for a non exising item', () => {
|
it('correctly returns the default value for a non exising item', () => {
|
||||||
ls.get('baz', 'qux').should.equal('qux');
|
ls.get('baz', 'qux').should.equal('qux')
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#set', () => {
|
describe('#set', () => {
|
||||||
it('correctly sets an item into local storage', () => {
|
it('correctly sets an item into local storage', () => {
|
||||||
ls.set('foo', 'bar');
|
ls.set('foo', 'bar')
|
||||||
localStorage('foo').should.equal('bar');
|
localStorage('foo').should.equal('bar')
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#remove', () => {
|
describe('#remove', () => {
|
||||||
it('correctly removes an item from local storage', () => {
|
it('correctly removes an item from local storage', () => {
|
||||||
localStorage('foo', 'bar');
|
localStorage('foo', 'bar')
|
||||||
ls.remove('foo');
|
ls.remove('foo')
|
||||||
(localStorage('foo') === null).should.be.true;
|
var result = localStorage('foo') === null
|
||||||
});
|
result.should.be.true
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
})
|
||||||
|
|
|
@ -1,102 +1,102 @@
|
||||||
require('chai').should();
|
require('chai').should()
|
||||||
import { cloneDeep, last } from 'lodash';
|
import { cloneDeep, last } from 'lodash'
|
||||||
|
|
||||||
import { albumStore, artistStore } from '../../stores';
|
import { albumStore, artistStore } from '../../stores'
|
||||||
import { default as artists, singleAlbum, singleSong } from '../blobs/media';
|
import { default as artists, singleAlbum, singleSong } from '../blobs/media'
|
||||||
|
|
||||||
describe('stores/album', () => {
|
describe('stores/album', () => {
|
||||||
beforeEach(() => albumStore.init(cloneDeep(artists)));
|
beforeEach(() => albumStore.init(cloneDeep(artists)))
|
||||||
|
|
||||||
afterEach(() => albumStore.state.albums = []);
|
afterEach(() => albumStore.state.albums = [])
|
||||||
|
|
||||||
describe('#init', () => {
|
describe('#init', () => {
|
||||||
it('correctly gathers albums', () => {
|
it('correctly gathers albums', () => {
|
||||||
albumStore.state.albums.length.should.equal(7);
|
albumStore.state.albums.length.should.equal(7)
|
||||||
});
|
})
|
||||||
|
|
||||||
it('correctly sets albums length', () => {
|
it('correctly sets albums length', () => {
|
||||||
albumStore.state.albums[0].length.should.equal(259.92);
|
albumStore.state.albums[0].length.should.equal(259.92)
|
||||||
});
|
})
|
||||||
|
|
||||||
it('correctly sets album artists', () => {
|
it('correctly sets album artists', () => {
|
||||||
albumStore.state.albums[0].artist.id.should.equal(1);
|
albumStore.state.albums[0].artist.id.should.equal(1)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#all', () => {
|
describe('#all', () => {
|
||||||
it('correctly returns all songs', () => {
|
it('correctly returns all songs', () => {
|
||||||
albumStore.all.length.should.equal(7);
|
albumStore.all.length.should.equal(7)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#getLength', () => {
|
describe('#getLength', () => {
|
||||||
it('correctly calculates an album’s length', () => {
|
it('correctly calculates an album’s length', () => {
|
||||||
albumStore.getLength(albumStore.state.albums[6]);
|
albumStore.getLength(albumStore.state.albums[6])
|
||||||
albumStore.state.albums[6].length.should.equal(1940.42); // I'm sorry…
|
albumStore.state.albums[6].length.should.equal(1940.42); // I'm sorry…
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#add', () => {
|
describe('#add', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
albumStore.add(cloneDeep(singleAlbum));
|
albumStore.add(cloneDeep(singleAlbum))
|
||||||
});
|
})
|
||||||
|
|
||||||
it('correctly adds a new album into the state', () => {
|
it('correctly adds a new album into the state', () => {
|
||||||
last(albumStore.state.albums).id.should.equal(9999);
|
last(albumStore.state.albums).id.should.equal(9999)
|
||||||
});
|
})
|
||||||
|
|
||||||
it('correctly recalculates the length', () => {
|
it('correctly recalculates the length', () => {
|
||||||
last(albumStore.state.albums).length.should.equal(300);
|
last(albumStore.state.albums).length.should.equal(300)
|
||||||
});
|
})
|
||||||
|
|
||||||
it('correctly recalculates the play count', () => {
|
it('correctly recalculates the play count', () => {
|
||||||
last(albumStore.state.albums).playCount.should.equal(11);
|
last(albumStore.state.albums).playCount.should.equal(11)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#remove', () => {
|
describe('#remove', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
albumStore.remove(albumStore.state.albums[0]); // ID 1193
|
albumStore.remove(albumStore.state.albums[0]); // ID 1193
|
||||||
});
|
})
|
||||||
|
|
||||||
it('correctly removes an album', () => {
|
it('correctly removes an album', () => {
|
||||||
albumStore.state.albums.length.should.equal(6);
|
albumStore.state.albums.length.should.equal(6)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#addSongsIntoAlbum', () => {
|
describe('#addSongsIntoAlbum', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
albumStore.addSongsIntoAlbum(albumStore.state.albums[0], cloneDeep(singleSong));
|
albumStore.addSongsIntoAlbum(albumStore.state.albums[0], cloneDeep(singleSong))
|
||||||
});
|
})
|
||||||
|
|
||||||
it('correctly adds a song into an album', () => {
|
it('correctly adds a song into an album', () => {
|
||||||
albumStore.state.albums[0].songs.length.should.equal(2);
|
albumStore.state.albums[0].songs.length.should.equal(2)
|
||||||
});
|
})
|
||||||
|
|
||||||
it('correctly recalculates the play count', () => {
|
it('correctly recalculates the play count', () => {
|
||||||
albumStore.state.albums[0].playCount.should.equal(4);
|
albumStore.state.albums[0].playCount.should.equal(4)
|
||||||
});
|
})
|
||||||
|
|
||||||
it ('correctly recalculates album length', () => {
|
it ('correctly recalculates album length', () => {
|
||||||
albumStore.state.albums[0].length.should.equal(359.92);
|
albumStore.state.albums[0].length.should.equal(359.92)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#removeSongsFromAlbum', () => {
|
describe('#removeSongsFromAlbum', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
albumStore.removeSongsFromAlbum(albumStore.state.albums[0], albumStore.state.albums[0].songs[0]);
|
albumStore.removeSongsFromAlbum(albumStore.state.albums[0], albumStore.state.albums[0].songs[0])
|
||||||
});
|
})
|
||||||
|
|
||||||
it('correctly removes a song from an album', () => {
|
it('correctly removes a song from an album', () => {
|
||||||
albumStore.state.albums[0].songs.length.should.equal(0);
|
albumStore.state.albums[0].songs.length.should.equal(0)
|
||||||
});
|
})
|
||||||
|
|
||||||
it('correctly recalculates the play count', () => {
|
it('correctly recalculates the play count', () => {
|
||||||
albumStore.state.albums[0].playCount.should.equal(0);
|
albumStore.state.albums[0].playCount.should.equal(0)
|
||||||
});
|
})
|
||||||
|
|
||||||
it('correctly recalculates the length', () => {
|
it('correctly recalculates the length', () => {
|
||||||
albumStore.state.albums[0].length.should.equal(0);
|
albumStore.state.albums[0].length.should.equal(0)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
|
@ -1,73 +1,73 @@
|
||||||
require('chai').should();
|
require('chai').should()
|
||||||
import { cloneDeep, last } from 'lodash';
|
import { cloneDeep, last } from 'lodash'
|
||||||
|
|
||||||
import { artistStore } from '../../stores';
|
import { artistStore } from '../../stores'
|
||||||
import { default as artists, singleAlbum, singleArtist } from '../blobs/media';
|
import { default as artists, singleAlbum, singleArtist } from '../blobs/media'
|
||||||
|
|
||||||
describe('stores/artist', () => {
|
describe('stores/artist', () => {
|
||||||
beforeEach(() => artistStore.init(cloneDeep(artists)));
|
beforeEach(() => artistStore.init(cloneDeep(artists)))
|
||||||
afterEach(() => artistStore.state.artists = []);
|
afterEach(() => artistStore.state.artists = [])
|
||||||
|
|
||||||
describe('#init', () => {
|
describe('#init', () => {
|
||||||
it('correctly gathers artists', () => {
|
it('correctly gathers artists', () => {
|
||||||
artistStore.state.artists.length.should.equal(3);
|
artistStore.state.artists.length.should.equal(3)
|
||||||
});
|
})
|
||||||
|
|
||||||
it('correctly gets artist images', () => {
|
it('correctly gets artist images', () => {
|
||||||
artistStore.state.artists[0].image.should.equal('/public/img/covers/565c0f7067425.jpeg');
|
artistStore.state.artists[0].image.should.equal('/public/img/covers/565c0f7067425.jpeg')
|
||||||
});
|
})
|
||||||
|
|
||||||
it('correctly counts songs by artists', () => {
|
it('correctly counts songs by artists', () => {
|
||||||
artistStore.state.artists[0].songCount = 3;
|
artistStore.state.artists[0].songCount = 3
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#getImage', () => {
|
describe('#getImage', () => {
|
||||||
it('correctly gets an artist’s image', () => {
|
it('correctly gets an artist’s image', () => {
|
||||||
artistStore.getImage(artistStore.state.artists[0]).should.equal('/public/img/covers/565c0f7067425.jpeg');
|
artistStore.getImage(artistStore.state.artists[0]).should.equal('/public/img/covers/565c0f7067425.jpeg')
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#add', () => {
|
describe('#add', () => {
|
||||||
beforeEach(() => artistStore.add(cloneDeep(singleArtist)));
|
beforeEach(() => artistStore.add(cloneDeep(singleArtist)))
|
||||||
|
|
||||||
it('correctly adds an artist', () => {
|
it('correctly adds an artist', () => {
|
||||||
last(artistStore.state.artists).name.should.equal('John Cena');
|
last(artistStore.state.artists).name.should.equal('John Cena')
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#remove', () => {
|
describe('#remove', () => {
|
||||||
beforeEach(() => artistStore.remove(artistStore.state.artists[0]));
|
beforeEach(() => artistStore.remove(artistStore.state.artists[0]))
|
||||||
|
|
||||||
it('correctly removes an artist', () => {
|
it('correctly removes an artist', () => {
|
||||||
artistStore.state.artists.length.should.equal(2);
|
artistStore.state.artists.length.should.equal(2)
|
||||||
artistStore.state.artists[0].name.should.equal('Bob Dylan');
|
artistStore.state.artists[0].name.should.equal('Bob Dylan')
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#addAlbumsIntoArtist', () => {
|
describe('#addAlbumsIntoArtist', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
artistStore.addAlbumsIntoArtist(artistStore.state.artists[0], cloneDeep(singleAlbum));
|
artistStore.addAlbumsIntoArtist(artistStore.state.artists[0], cloneDeep(singleAlbum))
|
||||||
});
|
})
|
||||||
|
|
||||||
it('correctly adds albums into an artist', () => {
|
it('correctly adds albums into an artist', () => {
|
||||||
artistStore.state.artists[0].albums.length.should.equal(4);
|
artistStore.state.artists[0].albums.length.should.equal(4)
|
||||||
});
|
})
|
||||||
|
|
||||||
it('correctly sets the album artist', () => {
|
it('correctly sets the album artist', () => {
|
||||||
const addedAlbum = last(artistStore.state.artists[0].albums);
|
const addedAlbum = last(artistStore.state.artists[0].albums)
|
||||||
addedAlbum.artist.should.equal(artistStore.state.artists[0]);
|
addedAlbum.artist.should.equal(artistStore.state.artists[0])
|
||||||
addedAlbum.artist_id.should.equal(artistStore.state.artists[0].id);
|
addedAlbum.artist_id.should.equal(artistStore.state.artists[0].id)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#removeAlbumsFromArtist', () => {
|
describe('#removeAlbumsFromArtist', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
artistStore.removeAlbumsFromArtist(artistStore.state.artists[0], artistStore.state.artists[0].albums[0]);
|
artistStore.removeAlbumsFromArtist(artistStore.state.artists[0], artistStore.state.artists[0].albums[0])
|
||||||
});
|
})
|
||||||
|
|
||||||
it('correctly removes an album from an artist', () => {
|
it('correctly removes an album from an artist', () => {
|
||||||
artistStore.state.artists[0].albums.length.should.equal(2);
|
artistStore.state.artists[0].albums.length.should.equal(2)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
|
@ -1,37 +1,37 @@
|
||||||
require('chai').should();
|
require('chai').should()
|
||||||
|
|
||||||
import localStorage from 'local-storage';
|
import localStorage from 'local-storage'
|
||||||
import { preferenceStore } from '../../stores';
|
import { preferenceStore } from '../../stores'
|
||||||
|
|
||||||
const user = { id: 0 };
|
const user = { id: 0 }
|
||||||
const preferences = {
|
const preferences = {
|
||||||
volume: 8,
|
volume: 8,
|
||||||
notify: false,
|
notify: false
|
||||||
};
|
}
|
||||||
|
|
||||||
describe('stores/preference', () => {
|
describe('stores/preference', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
localStorage.set(`preferences_${user.id}`, preferences);
|
localStorage.set(`preferences_${user.id}`, preferences)
|
||||||
preferenceStore.init(user);
|
preferenceStore.init(user)
|
||||||
});
|
})
|
||||||
|
|
||||||
describe("#set", () => {
|
describe("#set", () => {
|
||||||
it('correctly sets preferences', () => {
|
it('correctly sets preferences', () => {
|
||||||
preferenceStore.set('volume', 5);
|
preferenceStore.set('volume', 5)
|
||||||
localStorage.get(`preferences_${user.id}`).volume.should.equal(5);
|
localStorage.get(`preferences_${user.id}`).volume.should.equal(5)
|
||||||
|
|
||||||
// Test the proxy
|
// Test the proxy
|
||||||
preferenceStore.volume = 6;
|
preferenceStore.volume = 6
|
||||||
localStorage.get(`preferences_${user.id}`).volume.should.equal(6);
|
localStorage.get(`preferences_${user.id}`).volume.should.equal(6)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe("#get", () => {
|
describe("#get", () => {
|
||||||
it('returns correct preference values', () => {
|
it('returns correct preference values', () => {
|
||||||
preferenceStore.get('volume').should.equal(8);
|
preferenceStore.get('volume').should.equal(8)
|
||||||
|
|
||||||
// Test the proxy
|
// Test the proxy
|
||||||
preferenceStore.volume.should.equal(8);
|
preferenceStore.volume.should.equal(8)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
|
@ -1,107 +1,109 @@
|
||||||
require('chai').should();
|
require('chai').should()
|
||||||
|
|
||||||
import { queueStore } from '../../stores';
|
import { queueStore } from '../../stores'
|
||||||
import artists from '../blobs/media';
|
import artists from '../blobs/media'
|
||||||
|
|
||||||
const songs = artists[2].albums[0].songs;
|
const songs = artists[2].albums[0].songs
|
||||||
|
|
||||||
describe('stores/queue', () => {
|
describe('stores/queue', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
queueStore.state.songs = songs;
|
queueStore.state.songs = songs
|
||||||
queueStore.state.current = songs[1];
|
queueStore.state.current = songs[1]
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#all', () => {
|
describe('#all', () => {
|
||||||
it('correctly returns all queued songs', () => {
|
it('correctly returns all queued songs', () => {
|
||||||
queueStore.all.should.equal(songs);
|
queueStore.all.should.equal(songs)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#first', () => {
|
describe('#first', () => {
|
||||||
it('correctly returns the first queued song', () => {
|
it('correctly returns the first queued song', () => {
|
||||||
queueStore.first.title.should.equal('No bravery');
|
queueStore.first.title.should.equal('No bravery')
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#last', () => {
|
describe('#last', () => {
|
||||||
it('correctly returns the last queued song', () => {
|
it('correctly returns the last queued song', () => {
|
||||||
queueStore.last.title.should.equal('Tears and rain');
|
queueStore.last.title.should.equal('Tears and rain')
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#queue', () => {
|
describe('#queue', () => {
|
||||||
beforeEach(() => queueStore.state.songs = songs);
|
beforeEach(() => queueStore.state.songs = songs)
|
||||||
|
|
||||||
const song = artists[0].albums[0].songs[0];
|
const song = artists[0].albums[0].songs[0]
|
||||||
|
|
||||||
it('correctly appends a song to end of the queue', () => {
|
it('correctly appends a song to end of the queue', () => {
|
||||||
queueStore.queue(song);
|
queueStore.queue(song)
|
||||||
queueStore.last.title.should.equal('I Swear');
|
queueStore.last.title.should.equal('I Swear')
|
||||||
});
|
})
|
||||||
|
|
||||||
it('correctly prepends a song to top of the queue', () => {
|
it('correctly prepends a song to top of the queue', () => {
|
||||||
queueStore.queue(song, false, true);
|
queueStore.queue(song, false, true)
|
||||||
queueStore.first.title.should.equal('I Swear');
|
queueStore.first.title.should.equal('I Swear')
|
||||||
});
|
})
|
||||||
|
|
||||||
it('correctly replaces the whole queue', () => {
|
it('correctly replaces the whole queue', () => {
|
||||||
queueStore.queue(song, true);
|
queueStore.queue(song, true)
|
||||||
queueStore.all.length.should.equal(1);
|
queueStore.all.length.should.equal(1)
|
||||||
queueStore.first.title.should.equal('I Swear');
|
queueStore.first.title.should.equal('I Swear')
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#unqueue', () => {
|
describe('#unqueue', () => {
|
||||||
beforeEach(() => queueStore.state.songs = songs);
|
beforeEach(() => queueStore.state.songs = songs)
|
||||||
|
|
||||||
it('correctly removes a song from queue', () => {
|
it('correctly removes a song from queue', () => {
|
||||||
queueStore.unqueue(queueStore.state.songs[0]);
|
queueStore.unqueue(queueStore.state.songs[0])
|
||||||
queueStore.first.title.should.equal('So long, Jimmy'); // Oh the irony.
|
queueStore.first.title.should.equal('So long, Jimmy'); // Oh the irony.
|
||||||
});
|
})
|
||||||
|
|
||||||
it('correctly removes mutiple songs from queue', () => {
|
it('correctly removes mutiple songs from queue', () => {
|
||||||
queueStore.unqueue([queueStore.state.songs[0], queueStore.state.songs[1]]);
|
queueStore.unqueue([queueStore.state.songs[0], queueStore.state.songs[1]])
|
||||||
queueStore.first.title.should.equal('Wisemen');
|
queueStore.first.title.should.equal('Wisemen')
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#clear', () => {
|
describe('#clear', () => {
|
||||||
it('correctly clears all songs from queue', () => {
|
it('correctly clears all songs from queue', () => {
|
||||||
queueStore.clear();
|
queueStore.clear()
|
||||||
queueStore.state.songs.length.should.equal(0);
|
queueStore.state.songs.length.should.equal(0)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#current', () => {
|
describe('#current', () => {
|
||||||
it('returns the correct current song', () => {
|
it('returns the correct current song', () => {
|
||||||
queueStore.current.title.should.equal('So long, Jimmy');
|
queueStore.current.title.should.equal('So long, Jimmy')
|
||||||
});
|
})
|
||||||
|
|
||||||
it('successfully sets the current song', () => {
|
it('successfully sets the current song', () => {
|
||||||
queueStore.current = queueStore.state.songs[0];
|
queueStore.current = queueStore.state.songs[0]
|
||||||
queueStore.current.title.should.equal('No bravery');
|
queueStore.current.title.should.equal('No bravery')
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#getNextSong', () => {
|
describe('#getNextSong', () => {
|
||||||
it('correctly gets the next song in queue', () => {
|
it('correctly gets the next song in queue', () => {
|
||||||
queueStore.next.title.should.equal('Wisemen');
|
queueStore.next.title.should.equal('Wisemen')
|
||||||
});
|
})
|
||||||
|
|
||||||
it('correctly returns null if at end of queue', () => {
|
it('correctly returns null if at end of queue', () => {
|
||||||
queueStore.current = queueStore.state.songs[queueStore.state.songs.length - 1];
|
queueStore.current = queueStore.state.songs[queueStore.state.songs.length - 1]
|
||||||
(queueStore.next === null).should.be.true;
|
var result = queueStore.next === null
|
||||||
});
|
result.should.be.true
|
||||||
});
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('#getPrevSong', () => {
|
describe('#getPrevSong', () => {
|
||||||
it('correctly gets the previous song in queue', () => {
|
it('correctly gets the previous song in queue', () => {
|
||||||
queueStore.previous.title.should.equal('No bravery');
|
queueStore.previous.title.should.equal('No bravery')
|
||||||
});
|
})
|
||||||
|
|
||||||
it('correctly returns null if at end of queue', () => {
|
it('correctly returns null if at end of queue', () => {
|
||||||
queueStore.current = queueStore.state.songs[0];
|
queueStore.current = queueStore.state.songs[0]
|
||||||
(queueStore.previous === null).should.be.true;
|
var result = queueStore.previous === null
|
||||||
});
|
result.should.be.true
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
})
|
||||||
|
|
|
@ -1,61 +1,61 @@
|
||||||
require('chai').should();
|
require('chai').should()
|
||||||
import { cloneDeep, last } from 'lodash';
|
import { cloneDeep, last } from 'lodash'
|
||||||
|
|
||||||
import { songStore, albumStore, artistStore } from '../../stores';
|
import { songStore, albumStore, artistStore } from '../../stores'
|
||||||
import artists from '../blobs/media';
|
import artists from '../blobs/media'
|
||||||
import interactions from '../blobs/interactions';
|
import interactions from '../blobs/interactions'
|
||||||
|
|
||||||
describe('stores/song', () => {
|
describe('stores/song', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
artistStore.init(artists);
|
artistStore.init(artists)
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#init', () => {
|
describe('#init', () => {
|
||||||
it('correctly gathers all songs', () => {
|
it('correctly gathers all songs', () => {
|
||||||
songStore.state.songs.length.should.equal(14);
|
songStore.state.songs.length.should.equal(14)
|
||||||
});
|
})
|
||||||
|
|
||||||
it ('coverts lengths to formatted lengths', () => {
|
it ('coverts lengths to formatted lengths', () => {
|
||||||
songStore.state.songs[0].fmtLength.should.be.a.string;
|
songStore.state.songs[0].fmtLength.should.be.a.string
|
||||||
});
|
})
|
||||||
|
|
||||||
it('correctly sets albums', () => {
|
it('correctly sets albums', () => {
|
||||||
songStore.state.songs[0].album.id.should.equal(1193);
|
songStore.state.songs[0].album.id.should.equal(1193)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#all', () => {
|
describe('#all', () => {
|
||||||
it('correctly returns all songs', () => {
|
it('correctly returns all songs', () => {
|
||||||
songStore.all.length.should.equal(14);
|
songStore.all.length.should.equal(14)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#byId', () => {
|
describe('#byId', () => {
|
||||||
it('correctly gets a song by ID', () => {
|
it('correctly gets a song by ID', () => {
|
||||||
songStore.byId('e6d3977f3ffa147801ca5d1fdf6fa55e').title.should.equal('Like a rolling stone');
|
songStore.byId('e6d3977f3ffa147801ca5d1fdf6fa55e').title.should.equal('Like a rolling stone')
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#byIds', () => {
|
describe('#byIds', () => {
|
||||||
it('correctly gets multiple songs by IDs', () => {
|
it('correctly gets multiple songs by IDs', () => {
|
||||||
const songs = songStore.byIds(['e6d3977f3ffa147801ca5d1fdf6fa55e', 'aa16bbef6a9710eb9a0f41ecc534fad5']);
|
const songs = songStore.byIds(['e6d3977f3ffa147801ca5d1fdf6fa55e', 'aa16bbef6a9710eb9a0f41ecc534fad5'])
|
||||||
songs[0].title.should.equal('Like a rolling stone');
|
songs[0].title.should.equal('Like a rolling stone')
|
||||||
songs[1].title.should.equal("Knockin' on heaven's door");
|
songs[1].title.should.equal("Knockin' on heaven's door")
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#initInteractions', () => {
|
describe('#initInteractions', () => {
|
||||||
beforeEach(() => songStore.initInteractions(interactions));
|
beforeEach(() => songStore.initInteractions(interactions))
|
||||||
|
|
||||||
it('correctly sets interaction status', () => {
|
it('correctly sets interaction status', () => {
|
||||||
const song = songStore.byId('cb7edeac1f097143e65b1b2cde102482');
|
const song = songStore.byId('cb7edeac1f097143e65b1b2cde102482')
|
||||||
song.liked.should.be.true;
|
song.liked.should.be.true
|
||||||
song.playCount.should.equal(3);
|
song.playCount.should.equal(3)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#syncUpdatedSong', () => {
|
describe('#syncUpdatedSong', () => {
|
||||||
beforeEach(() => artistStore.init(artists));
|
beforeEach(() => artistStore.init(artists))
|
||||||
|
|
||||||
const updatedSong = {
|
const updatedSong = {
|
||||||
id: "39189f4545f9d5671fb3dc964f0080a0",
|
id: "39189f4545f9d5671fb3dc964f0080a0",
|
||||||
|
@ -66,19 +66,19 @@ describe('stores/song', () => {
|
||||||
arist_id: 1,
|
arist_id: 1,
|
||||||
artist: {
|
artist: {
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'All-4-One',
|
name: 'All-4-One'
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
it ('correctly syncs an updated song with no album changes', () => {
|
it ('correctly syncs an updated song with no album changes', () => {
|
||||||
songStore.syncUpdatedSong(cloneDeep(updatedSong));
|
songStore.syncUpdatedSong(cloneDeep(updatedSong))
|
||||||
songStore.byId(updatedSong.id).title.should.equal('I Swear A Lot');
|
songStore.byId(updatedSong.id).title.should.equal('I Swear A Lot')
|
||||||
});
|
})
|
||||||
|
|
||||||
it ('correctly syncs an updated song into an existing album of same artist', () => {
|
it ('correctly syncs an updated song into an existing album of same artist', () => {
|
||||||
const song = cloneDeep(updatedSong);
|
const song = cloneDeep(updatedSong)
|
||||||
song.album_id = 1194;
|
song.album_id = 1194
|
||||||
song.album = {
|
song.album = {
|
||||||
id: 1194,
|
id: 1194,
|
||||||
artist_id: 1,
|
artist_id: 1,
|
||||||
|
@ -86,62 +86,62 @@ describe('stores/song', () => {
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'All-4-One',
|
name: 'All-4-One',
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
|
|
||||||
songStore.syncUpdatedSong(song);
|
songStore.syncUpdatedSong(song)
|
||||||
songStore.byId(song.id).album.name.should.equal('And The Music Speaks');
|
songStore.byId(song.id).album.name.should.equal('And The Music Speaks')
|
||||||
});
|
})
|
||||||
|
|
||||||
it ('correctly syncs an updated song into a new album of same artist', () => {
|
it ('correctly syncs an updated song into a new album of same artist', () => {
|
||||||
const song = cloneDeep(updatedSong);
|
const song = cloneDeep(updatedSong)
|
||||||
song.album_id = 9999;
|
song.album_id = 9999
|
||||||
song.album = {
|
song.album = {
|
||||||
id: 9999,
|
id: 9999,
|
||||||
artist_id: 1,
|
artist_id: 1,
|
||||||
name: 'Brand New Album from All-4-One',
|
name: 'Brand New Album from All-4-One',
|
||||||
artist: {
|
artist: {
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'All-4-One',
|
name: 'All-4-One'
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
songStore.syncUpdatedSong(song);
|
songStore.syncUpdatedSong(song)
|
||||||
|
|
||||||
// A new album should be created...
|
// A new album should be created...
|
||||||
last(albumStore.all).name.should.equal('Brand New Album from All-4-One');
|
last(albumStore.all).name.should.equal('Brand New Album from All-4-One')
|
||||||
|
|
||||||
// ...and assigned with the song.
|
// ...and assigned with the song.
|
||||||
songStore.byId(song.id).album.name.should.equal('Brand New Album from All-4-One');
|
songStore.byId(song.id).album.name.should.equal('Brand New Album from All-4-One')
|
||||||
});
|
})
|
||||||
|
|
||||||
it ('correctly syncs an updated song into a new album of a new artist', () => {
|
it ('correctly syncs an updated song into a new album of a new artist', () => {
|
||||||
const song = cloneDeep(updatedSong);
|
const song = cloneDeep(updatedSong)
|
||||||
song.album_id = 10000;
|
song.album_id = 10000
|
||||||
song.album = {
|
song.album = {
|
||||||
id: 10000,
|
id: 10000,
|
||||||
name: "It's... John Cena!!!",
|
name: "It's... John Cena!!!",
|
||||||
artist_id: 10000,
|
artist_id: 10000,
|
||||||
artist: {
|
artist: {
|
||||||
id: 10000,
|
id: 10000,
|
||||||
name: 'John Cena',
|
name: 'John Cena'
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
songStore.syncUpdatedSong(song);
|
songStore.syncUpdatedSong(song)
|
||||||
|
|
||||||
// A new artist should be created...
|
// A new artist should be created...
|
||||||
const lastArtist = last(artistStore.all);
|
const lastArtist = last(artistStore.all)
|
||||||
lastArtist.name.should.equal('John Cena');
|
lastArtist.name.should.equal('John Cena')
|
||||||
|
|
||||||
// A new album should be created
|
// A new album should be created
|
||||||
const lastAlbum = last(albumStore.all);
|
const lastAlbum = last(albumStore.all)
|
||||||
lastAlbum.name.should.equal("It's... John Cena!!!");
|
lastAlbum.name.should.equal("It's... John Cena!!!")
|
||||||
|
|
||||||
// The album must belong to John Cena of course!
|
// The album must belong to John Cena of course!
|
||||||
last(lastArtist.albums).should.equal(lastAlbum);
|
last(lastArtist.albums).should.equal(lastAlbum)
|
||||||
|
|
||||||
// And the song belongs to the album.
|
// And the song belongs to the album.
|
||||||
songStore.byId(song.id).album.should.equal(lastAlbum);
|
songStore.byId(song.id).album.should.equal(lastAlbum)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
|
@ -1,66 +1,50 @@
|
||||||
require('chai').should();
|
require('chai').should()
|
||||||
|
|
||||||
import { userStore } from '../../stores';
|
import { userStore } from '../../stores'
|
||||||
import data from '../blobs/users';
|
import data from '../blobs/users'
|
||||||
|
|
||||||
describe('stores/user', () => {
|
describe('stores/user', () => {
|
||||||
beforeEach(() => userStore.init(data.users, data.currentUser));
|
beforeEach(() => userStore.init(data.users, data.currentUser))
|
||||||
|
|
||||||
describe('#init', () => {
|
describe('#init', () => {
|
||||||
it('correctly sets data state', () => {
|
it('correctly sets data state', () => {
|
||||||
userStore.state.users.should.equal(data.users);
|
userStore.state.users.should.equal(data.users)
|
||||||
userStore.state.current.should.equal(data.currentUser);
|
userStore.state.current.should.equal(data.currentUser)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#all', () => {
|
describe('#all', () => {
|
||||||
it('correctly returns all users', () => {
|
it('correctly returns all users', () => {
|
||||||
userStore.all.should.equal(data.users);
|
userStore.all.should.equal(data.users)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#byId', () => {
|
describe('#byId', () => {
|
||||||
it('correctly gets a user by ID', () => {
|
it('correctly gets a user by ID', () => {
|
||||||
userStore.byId(1).should.equal(data.users[0]);
|
userStore.byId(1).should.equal(data.users[0])
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#current', () => {
|
describe('#current', () => {
|
||||||
it('correctly gets the current user', () => {
|
it('correctly gets the current user', () => {
|
||||||
userStore.current.id.should.equal(1);
|
userStore.current.id.should.equal(1)
|
||||||
});
|
})
|
||||||
|
|
||||||
it('correctly sets the current user', () => {
|
it('correctly sets the current user', () => {
|
||||||
userStore.current = data.users[1];
|
userStore.current = data.users[1]
|
||||||
userStore.current.id.should.equal(2);
|
userStore.current.id.should.equal(2)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#setAvatar', () => {
|
describe('#setAvatar', () => {
|
||||||
it('correctly sets the current user’s avatar', () => {
|
it('correctly sets the current user’s avatar', () => {
|
||||||
userStore.setAvatar();
|
userStore.setAvatar()
|
||||||
userStore.current.avatar.should.equal('https://www.gravatar.com/avatar/b9611f1bba1aacbe6f5de5856695a202?s=256');
|
userStore.current.avatar.should.equal('https://www.gravatar.com/avatar/b9611f1bba1aacbe6f5de5856695a202?s=256')
|
||||||
});
|
})
|
||||||
|
|
||||||
it('correctly sets a user’s avatar', () => {
|
it('correctly sets a user’s avatar', () => {
|
||||||
userStore.setAvatar(data.users[1]);
|
userStore.setAvatar(data.users[1])
|
||||||
data.users[1].avatar.should.equal('https://www.gravatar.com/avatar/5024672cfe53f113b746e1923e373058?s=256');
|
data.users[1].avatar.should.equal('https://www.gravatar.com/avatar/5024672cfe53f113b746e1923e373058?s=256')
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
})
|
||||||
describe('#updateProfile', () => {
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#store', () => {
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#update', () => {
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#destroy', () => {
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
|
@ -1,34 +1,34 @@
|
||||||
require('chai').should();
|
require('chai').should()
|
||||||
|
|
||||||
import { secondsToHis, parseValidationError } from '../../utils';
|
import { secondsToHis, parseValidationError } from '../../utils'
|
||||||
|
|
||||||
describe('services/utils', () => {
|
describe('services/utils', () => {
|
||||||
describe('#secondsToHis', () => {
|
describe('#secondsToHis', () => {
|
||||||
it('correctly formats a duration to H:i:s', () => {
|
it('correctly formats a duration to H:i:s', () => {
|
||||||
secondsToHis(7547).should.equal('02:05:47');
|
secondsToHis(7547).should.equal('02:05:47')
|
||||||
});
|
})
|
||||||
|
|
||||||
it('ommits hours from short duration when formats to H:i:s', () => {
|
it('ommits hours from short duration when formats to H:i:s', () => {
|
||||||
secondsToHis(314).should.equal('05:14');
|
secondsToHis(314).should.equal('05:14')
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe('#parseValidationError', () => {
|
describe('#parseValidationError', () => {
|
||||||
it('correctly parses single-level validation error', () => {
|
it('correctly parses single-level validation error', () => {
|
||||||
const error = {
|
const error = {
|
||||||
err_1: ['Foo'],
|
err_1: ['Foo']
|
||||||
};
|
}
|
||||||
|
|
||||||
parseValidationError(error).should.eql(['Foo']);
|
parseValidationError(error).should.eql(['Foo'])
|
||||||
});
|
})
|
||||||
|
|
||||||
it('correctly parses multi-level validation error', () => {
|
it('correctly parses multi-level validation error', () => {
|
||||||
const error = {
|
const error = {
|
||||||
err_1: ['Foo', 'Bar'],
|
err_1: ['Foo', 'Bar'],
|
||||||
err_2: ['Baz', 'Qux'],
|
err_2: ['Baz', 'Qux']
|
||||||
};
|
}
|
||||||
|
|
||||||
parseValidationError(error).should.eql(['Foo', 'Bar', 'Baz', 'Qux']);
|
parseValidationError(error).should.eql(['Foo', 'Bar', 'Baz', 'Qux'])
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* Other common methods.
|
* Other common methods.
|
||||||
*/
|
*/
|
||||||
import select from 'select';
|
import select from 'select'
|
||||||
import { event } from '../utils'
|
import { event } from '../utils'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -11,17 +11,17 @@ import { event } from '../utils'
|
||||||
* @param {...*} Extra data to attach to the view.
|
* @param {...*} Extra data to attach to the view.
|
||||||
*/
|
*/
|
||||||
export function loadMainView (view, ...args) {
|
export function loadMainView (view, ...args) {
|
||||||
event.emit('main-content-view:load', view, ...args);
|
event.emit('main-content-view:load', view, ...args)
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Force reloading window regardless of "Confirm before reload" setting.
|
* Force reloading window regardless of "Confirm before reload" setting.
|
||||||
* This is handy for certain cases, for example Last.fm connect/disconnect.
|
* This is handy for certain cases, for example Last.fm connect/disconnect.
|
||||||
*/
|
*/
|
||||||
export function forceReloadWindow () {
|
export function forceReloadWindow () {
|
||||||
window.onbeforeunload = function() {};
|
window.onbeforeunload = function () {}
|
||||||
window.location.reload();
|
window.location.reload()
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show the overlay.
|
* Show the overlay.
|
||||||
|
@ -31,15 +31,15 @@ export function forceReloadWindow() {
|
||||||
* @param {Boolean} dismissable
|
* @param {Boolean} dismissable
|
||||||
*/
|
*/
|
||||||
export function showOverlay (message = 'Just a little patience…', type = 'loading', dismissable = false) {
|
export function showOverlay (message = 'Just a little patience…', type = 'loading', dismissable = false) {
|
||||||
event.emit('overlay:show', { message, type, dismissable });
|
event.emit('overlay:show', { message, type, dismissable })
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hide the overlay.
|
* Hide the overlay.
|
||||||
*/
|
*/
|
||||||
export function hideOverlay () {
|
export function hideOverlay () {
|
||||||
event.emit('overlay:hide');
|
event.emit('overlay:hide')
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy a text into clipboard.
|
* Copy a text into clipboard.
|
||||||
|
@ -47,9 +47,9 @@ export function hideOverlay() {
|
||||||
* @param {string} txt
|
* @param {string} txt
|
||||||
*/
|
*/
|
||||||
export function copyText (txt) {
|
export function copyText (txt) {
|
||||||
const copyArea = document.querySelector('#copyArea');
|
const copyArea = document.querySelector('#copyArea')
|
||||||
copyArea.style.top = (window.pageYOffset || document.documentElement.scrollTop) + 'px';
|
copyArea.style.top = (window.pageYOffset || document.documentElement.scrollTop) + 'px'
|
||||||
copyArea.value = txt;
|
copyArea.value = txt
|
||||||
select(copyArea);
|
select(copyArea)
|
||||||
document.execCommand('copy');
|
document.execCommand('copy')
|
||||||
};
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue