From 5e79a2737ed59742c8a45935d480ada43b6b65b5 Mon Sep 17 00:00:00 2001 From: An Phan Date: Wed, 23 Dec 2015 01:46:54 +0800 Subject: [PATCH] Refactor and docs --- resources/assets/js/app.vue | 21 ------ resources/assets/js/stores/album.js | 9 +-- resources/assets/js/stores/artist.js | 11 ++- resources/assets/js/stores/favorite.js | 12 ++-- resources/assets/js/stores/playlist.js | 2 +- resources/assets/js/stores/preference.js | 5 +- resources/assets/js/stores/queue.js | 12 ++-- resources/assets/js/stores/shared.js | 22 +++++- resources/assets/js/stores/song.js | 74 +++++++++----------- resources/assets/js/stores/user.js | 36 +++++----- resources/assets/js/tests/stores/songTest.js | 6 +- resources/assets/js/tests/stores/userTest.js | 2 +- 12 files changed, 96 insertions(+), 116 deletions(-) diff --git a/resources/assets/js/app.vue b/resources/assets/js/app.vue index dbfe2600..725d67c0 100644 --- a/resources/assets/js/app.vue +++ b/resources/assets/js/app.vue @@ -24,11 +24,6 @@ import overlay from './components/shared/overlay.vue'; import sharedStore from './stores/shared'; - import artistStore from './stores/artist'; - import playlistStore from './stores/playlist'; - import queueStore from './stores/queue'; - import userStore from './stores/user'; - import settingStore from './stores/setting'; import preferenceStore from './stores/preference'; import playback from './services/playback'; @@ -56,7 +51,6 @@ // Make the most important HTTP request to get all necessary data from the server. // Afterwards, init all mandatory stores and services. sharedStore.init(() => { - this.initStores(); playback.init(this); this.hideOverlay(); @@ -70,21 +64,6 @@ }, methods: { - /** - * Initialize all stores to be used throughout the application. - */ - initStores() { - userStore.init(); - preferenceStore.init(); - - // This will init album and song stores as well. - artistStore.init(); - - playlistStore.init(); - queueStore.init(); - settingStore.init(); - }, - /** * Toggle playback when user presses Space key. * diff --git a/resources/assets/js/stores/album.js b/resources/assets/js/stores/album.js index a96b07fc..2c30e5ec 100644 --- a/resources/assets/js/stores/album.js +++ b/resources/assets/js/stores/album.js @@ -6,7 +6,6 @@ import songStore from './song'; export default { stub, - artists: [], state: { albums: [stub], @@ -15,11 +14,9 @@ export default { /** * Init the store. * - * @param array artists The array of artists to extract album data from. + * @param {Array} artists The array of artists to extract album data from. */ init(artists) { - this.artists = artists; - // Traverse through the artists array and add their albums into our master album list. this.state.albums = _.reduce(artists, (albums, artist) => { // While we're doing so, for each album, we get its length @@ -44,9 +41,9 @@ export default { * Get the total length of an album by summing up its songs' duration. * The length will also be converted into a H:i:s format and stored as fmtLength. * - * @param object album + * @param {Array} album * - * @return string The H:i:s format of the album. + * @return {String} The H:i:s format of the album length. */ getLength(album) { album.length = _.reduce(album.songs, (length, song) => length + song.length, 0); diff --git a/resources/assets/js/stores/artist.js b/resources/assets/js/stores/artist.js index 4dd37575..c75d5d75 100644 --- a/resources/assets/js/stores/artist.js +++ b/resources/assets/js/stores/artist.js @@ -2,7 +2,6 @@ import _ from 'lodash'; import config from '../config'; import albumStore from './album'; -import sharedStore from './shared'; export default { state: { @@ -14,12 +13,8 @@ export default { * * @param {Array} artists The array of artists we got from the server. */ - init(artists = null) { - this.state.artists = artists ? artists: sharedStore.state.artists; - - // Init the album store. This must be called prior to the next logic, - // because we're using some data from the album store later. - albumStore.init(this.state.artists); + init(artists) { + this.state.artists = artists; // Traverse through artists array to get the cover and number of songs for each. _.each(this.state.artists, artist => { @@ -27,6 +22,8 @@ export default { artist.songCount = _.reduce(artist.albums, (count, album) => count + album.songs.length, 0); }); + + albumStore.init(this.state.artists); }, all() { diff --git a/resources/assets/js/stores/favorite.js b/resources/assets/js/stores/favorite.js index 31ea6ff1..66f4fab1 100644 --- a/resources/assets/js/stores/favorite.js +++ b/resources/assets/js/stores/favorite.js @@ -16,8 +16,8 @@ export default { * Toggle like/unlike a song. * A request to the server will be made. * - * @param object The song object - * @param closure|null The function to execute afterwards + * @param {Object} The song object + * @param {Function} The function to execute afterwards */ toggleOne(song, cb = null) { http.post('interaction/like', { id: song.id }, data => { @@ -38,7 +38,7 @@ export default { /** * Add a song into the store. * - * @param object The song object + * @param {Object} The song object */ add(song) { this.state.songs.push(song); @@ -47,7 +47,7 @@ export default { /** * Remove a song from the store. * - * @param object The song object + * @param {Object} The song object */ remove(song) { this.state.songs = _.difference(this.state.songs, [song]); @@ -56,7 +56,7 @@ export default { /** * Like a bunch of songs. * - * @param array An array of songs to like + * @param {Array} An array of songs to like */ like(songs, cb = null) { this.state.songs = _.union(this.state.songs, songs); @@ -73,7 +73,7 @@ export default { /** * Unlike a bunch of songs. * - * @param array An array of songs to unlike + * @param {Array} An array of songs to unlike */ unlike(songs, cb = null) { this.state.songs = _.difference(this.state.songs, songs); diff --git a/resources/assets/js/stores/playlist.js b/resources/assets/js/stores/playlist.js index f378c9f7..f6a7a069 100644 --- a/resources/assets/js/stores/playlist.js +++ b/resources/assets/js/stores/playlist.js @@ -25,7 +25,7 @@ export default { /** * Get all songs in a playlist. * - * return array + * return {Array} */ getSongs(playlist) { return (playlist.songs = songStore.byIds(playlist.songs)); diff --git a/resources/assets/js/stores/preference.js b/resources/assets/js/stores/preference.js index c94dc632..3a2c51ac 100644 --- a/resources/assets/js/stores/preference.js +++ b/resources/assets/js/stores/preference.js @@ -14,8 +14,9 @@ export default { }, /** - * Init the store - * @param object user The user whose preferences we are managing. + * Init the store. + * + * @param {Object} user The user whose preferences we are managing. */ init(user = null) { if (!user) { diff --git a/resources/assets/js/stores/queue.js b/resources/assets/js/stores/queue.js index eb32c509..201d3f93 100644 --- a/resources/assets/js/stores/queue.js +++ b/resources/assets/js/stores/queue.js @@ -51,9 +51,9 @@ export default { * Add a list of songs to the end of the current queue, * or replace the current queue as a whole if `replace` is true. * - * @param object|array songs The song, or an array of songs - * @param bool replace Whether to replace the current queue - * @param bool toTop Whether to prepend of append to the queue + * @param {Object|Array} songs The song, or an array of songs + * @param {Boolean} replace Whether to replace the current queue + * @param {Boolean} toTop Whether to prepend of append to the queue */ queue(songs, replace = false, toTop = false) { if (!Array.isArray(songs)) { @@ -74,7 +74,7 @@ export default { /** * Unqueue a song, or several songs at once. * - * @param object|string|array songs The song(s) to unqueue. + * @param {Object|String|Array} songs The song(s) to unqueue. */ unqueue(songs) { if (!Array.isArray(songs)) { @@ -98,7 +98,7 @@ export default { /** * Get the next song in queue. * - * @return object|null + * @return {Object|Null} */ getNextSong() { var i = _.pluck(this.state.songs, 'id').indexOf(this.current().id) + 1; @@ -109,7 +109,7 @@ export default { /** * Get the previous song in queue. * - * @return object|null + * @return {Object|Null} */ getPrevSong() { var i = _.pluck(this.state.songs, 'id').indexOf(this.current().id) - 1; diff --git a/resources/assets/js/stores/shared.js b/resources/assets/js/stores/shared.js index a50dee2b..1c35fd48 100644 --- a/resources/assets/js/stores/shared.js +++ b/resources/assets/js/stores/shared.js @@ -1,6 +1,14 @@ -import http from '../services/http'; import { assign } from 'lodash'; +import http from '../services/http'; +import userStore from './user'; +import preferenceStore from './preference'; +import artistStore from './artist'; +import songStore from './song'; +import playlistStore from './playlist'; +import queueStore from './queue'; +import settingStore from './setting'; + export default { state: { songs: [], @@ -17,7 +25,7 @@ export default { }, init(cb = null) { - http.get('data', {}, data => { + http.get('data', data => { assign(this.state, data); // If this is a new user, initialize his preferences to be an empty object. @@ -25,6 +33,16 @@ export default { this.state.currentUser.preferences = {}; } + userStore.init(this.state.users, this.state.currentUser); + preferenceStore.init(this.state.preferences); + artistStore.init(this.state.artists); // This will init album and song stores as well. + songStore.initInteractions(this.state.interactions); + playlistStore.init(this.state.playlists); + queueStore.init(); + settingStore.init(this.state.settings); + + window.useLastfm = this.state.useLastfm = data.useLastfm; + if (cb) { cb(); } diff --git a/resources/assets/js/stores/song.js b/resources/assets/js/stores/song.js index a3f098f8..aef0265e 100644 --- a/resources/assets/js/stores/song.js +++ b/resources/assets/js/stores/song.js @@ -3,32 +3,23 @@ import _ from 'lodash'; import http from '../services/http'; import utils from '../services/utils'; import stub from '../stubs/song'; -import albumStore from './album'; import favoriteStore from './favorite'; -import sharedStore from './shared'; import userStore from './user'; export default { stub, - sharedStore: null, albums: [], state: { songs: [stub], - interactions: [], }, /** * Init the store. * - * @param array albums The array of albums to extract our songs from - * @param array interactions The array of interactions (like/play count) of the current user + * @param {Array} albums The array of albums to extract our songs from */ - init(albums, interactions = null) { - this.albums = albums; - - this.state.interactions = interactions ? interactions : sharedStore.state.interactions; - + init(albums) { // Iterate through the albums. With each, add its songs into our master song list. this.state.songs = _.reduce(albums, (songs, album) => { // While doing so, we populate some other information into the songs as well. @@ -37,20 +28,38 @@ export default { // Keep a back reference to the album song.album = album; - - this.setInteractionStats(song); - - if (song.liked) { - favoriteStore.add(song); - } }); return songs.concat(album.songs); }, []); }, + /** + * Initializes the interaction (like/play count) information. + * + * @param {Array} interactions The array of interactions of the current user + */ + initInteractions(interactions) { + _.each(interactions, interaction => { + var song = this.byId(interaction.song_id); + + if (!song) { + return; + } + + song.liked = interaction.liked; + song.playCount = interaction.play_count; + + if (song.liked) { + favoriteStore.add(song); + } + }); + }, + /** * Get all songs. + * + * @return {Array} */ all() { return this.state.songs; @@ -59,9 +68,9 @@ export default { /** * Get a song by its ID * - * @param string id + * @param {String} id * - * @return object + * @return {Object} */ byId(id) { return _.find(this.state.songs, {id}); @@ -70,37 +79,18 @@ export default { /** * Get songs by their ID's * - * @param array ids + * @param {Array} ids * - * @return array + * @return {Array} */ byIds(ids) { return _.filter(this.state.songs, song => _.contains(ids, song.id)); }, - /** - * Set the interaction stats (like status and playcount) for a song. - * - * @param object song - */ - setInteractionStats(song) { - var interaction = _.find(this.state.interactions, { song_id: song.id }); - - if (!interaction) { - song.liked = false; - song.playCount = 0; - - return; - } - - song.liked = interaction.liked; - song.playCount = interaction.play_count; - }, - /** * Increase a play count for a song. * - * @param object song + * @param {Object} song */ registerPlay(song) { // Increase playcount @@ -159,7 +149,7 @@ export default { * @param {Function} cb */ scrobble(song, cb = null) { - if (!sharedStore.state.useLastfm || !userStore.current().preferences.lastfm_session_key) { + if (!window.useLastfm || !userStore.current().preferences.lastfm_session_key) { return; } diff --git a/resources/assets/js/stores/user.js b/resources/assets/js/stores/user.js index 779560f2..22ab6849 100644 --- a/resources/assets/js/stores/user.js +++ b/resources/assets/js/stores/user.js @@ -15,19 +15,10 @@ export default { /** * Init the store. - * - * @param object data The data object that contain the users array. - * Mostly for DI and testing purpose. - * For production, this data is retrieved from the shared store. - * */ - init(data = null) { - if (!data) { - data = sharedStore.state; - } - - this.state.users = data.users; - this.state.current = data.currentUser; + init(users, currentUser) { + this.state.users = users; + this.state.current = currentUser; // Set the avatar for each of the users… _.each(this.state.users, this.setAvatar); @@ -36,6 +27,11 @@ export default { this.setAvatar(); }, + /** + * Get all users. + * + * @return {Array} + */ all() { return this.state.users; }, @@ -43,9 +39,9 @@ export default { /** * Get a user by his ID * - * @param integer id + * @param {Integer} id * - * @return object + * @return {Object} */ byId(id) { return _.find(this.state.users, {id}); @@ -65,7 +61,7 @@ export default { /** * Set a user's avatar using Gravatar's service. * - * @param object user The user. If null, the current user. + * @param {Object} user The user. If null, the current user. */ setAvatar(user = null) { if (!user) { @@ -78,7 +74,7 @@ export default { /** * Update the current user's profile. * - * @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 = null, cb = null) { http.put('me', { @@ -98,10 +94,10 @@ export default { /** * Stores a new user into the database. * - * @param string name - * @param string email - * @param string password - * @param function cb + * @param {String} name + * @param {String} email + * @param {String} password + * @param {Function} cb */ store(name, email, password, cb = null) { http.post('user', { name, email, password }, user => { diff --git a/resources/assets/js/tests/stores/songTest.js b/resources/assets/js/tests/stores/songTest.js index 1b243589..1149f2f2 100644 --- a/resources/assets/js/tests/stores/songTest.js +++ b/resources/assets/js/tests/stores/songTest.js @@ -9,7 +9,7 @@ describe('stores/song', () => { beforeEach(() => { // This is ugly and not very "unit," but anyway. albumStore.init(artists); - songStore.init(albumStore.all(), interactions); + songStore.init(albumStore.all()); }); describe('#init', () => { @@ -46,7 +46,9 @@ describe('stores/song', () => { }); }); - describe('#setInteractionStats', () => { + describe('#initInteractions', () => { + beforeEach(() => songStore.initInteractions(interactions)); + it('correctly sets interaction status', () => { let song = songStore.byId('cb7edeac1f097143e65b1b2cde102482'); song.liked.should.be.true; diff --git a/resources/assets/js/tests/stores/userTest.js b/resources/assets/js/tests/stores/userTest.js index 8e42d600..e2ee3498 100644 --- a/resources/assets/js/tests/stores/userTest.js +++ b/resources/assets/js/tests/stores/userTest.js @@ -4,7 +4,7 @@ import userStore from '../../stores/user'; import data from '../blobs/users'; describe('stores/user', () => { - beforeEach(() => userStore.init(data)); + beforeEach(() => userStore.init(data.users, data.currentUser)); describe('#init', () => { it('correctly sets data state', () => {