Refactor Edit songs form

This commit is contained in:
An Phan 2016-11-28 17:27:59 +07:00
parent c3b0cbe361
commit 30237d27ad
No known key found for this signature in database
GPG key ID: 05536BB4BCDC02A2

View file

@ -76,335 +76,339 @@
</template>
<script>
import { every, filter } from 'lodash'
import { every, filter } from 'lodash'
import { br2nl, forceReloadWindow } from '../../utils'
import { songInfo } from '../../services/info'
import { artistStore, albumStore, songStore } from '../../stores'
import { br2nl, forceReloadWindow } from '../../utils'
import { songInfo } from '../../services/info'
import { artistStore, albumStore, songStore } from '../../stores'
import soundBar from '../shared/sound-bar.vue'
import typeahead from '../shared/typeahead.vue'
import soundBar from '../shared/sound-bar.vue'
import typeahead from '../shared/typeahead.vue'
const COMPILATION_STATES = {
NONE: 0, // No songs belong to a compilation album
ALL: 1, // All songs belong to compilation album(s)
SOME: 2 // Some of the songs belong to compilation album(s)
}
const COMPILATION_STATES = {
NONE: 0, // No songs belong to a compilation album
ALL: 1, // All songs belong to compilation album(s)
SOME: 2 // Some of the songs belong to compilation album(s)
}
export default {
components: { soundBar, typeahead },
export default {
components: { soundBar, typeahead },
data () {
return {
shown: false,
songs: [],
currentView: '',
loading: false,
needsReload: false,
data () {
return {
shown: false,
songs: [],
currentView: '',
loading: false,
needsReload: false,
artistState: artistStore.state,
artistTypeaheadOptions: {
displayKey: 'name',
filterKey: 'name'
},
artistState: artistStore.state,
artistTypeaheadOptions: {
displayKey: 'name',
filterKey: 'name'
},
albumState: albumStore.state,
albumTypeaheadOptions: {
displayKey: 'name',
filterKey: 'name'
},
albumState: albumStore.state,
albumTypeaheadOptions: {
displayKey: 'name',
filterKey: 'name'
},
/**
* In order not to mess up the original songs, we manually assign and manipulate
* their attributes.
*
* @type {Object}
*/
formData: {
title: '',
albumName: '',
artistName: '',
lyrics: '',
track: '',
compilationState: null
}
/**
* In order not to mess up the original songs, we manually assign and manipulate
* their attributes.
*
* @type {Object}
*/
formData: {
title: '',
albumName: '',
artistName: '',
lyrics: '',
track: '',
compilationState: null
}
}
},
computed: {
/**
* Determine if we're editing but one song.
*
* @return {boolean}
*/
editSingle () {
return this.songs.length === 1
},
/**
* Determine if all songs we're editing are by the same artist.
*
* @return {boolean}
*/
bySameArtist () {
return every(this.songs, song => {
song.artist.id === this.songs[0].artist.id
})
},
/**
* Determine if all songs we're editing are from the same album.
*
* @return {boolean}
*/
inSameAlbum () {
return every(this.songs, song => {
song.album.id === this.songs[0].album.id
})
},
/**
* URL of the cover to display.
*
* @return {string}
*/
coverUrl () {
return this.inSameAlbum ? this.songs[0].album.cover : '/public/img/covers/unknown-album.png'
},
/**
* Determine the compilation state of the songs.
*
* @return {Number}
*/
compilationState () {
const contributedSongs = filter(this.songs, song => song.contributing_artist_id)
if (!contributedSongs.length) {
this.formData.compilationState = COMPILATION_STATES.NONE
} else if (contributedSongs.length === this.songs.length) {
this.formData.compilationState = COMPILATION_STATES.ALL
} else {
this.formData.compilationState = COMPILATION_STATES.SOME
}
return this.formData.compilationState
},
/**
* The song title to be displayed.
*
* @return {string}
*/
displayedTitle () {
return this.editSingle ? this.formData.title : `${this.songs.length} songs selected`
},
/**
* The album name to be displayed.
*
* @return {string}
*/
displayedAlbum () {
if (this.editSingle) {
return this.formData.albumName
} else {
return this.formData.albumName ? this.formData.albumName : 'Mixed Albums'
}
},
computed: {
/**
* Determine if we're editing but one song.
*
* @return {boolean}
*/
editSingle () {
return this.songs.length === 1
},
/**
* Determine if all songs we're editing are by the same artist.
*
* @return {boolean}
*/
bySameArtist () {
return every(this.songs, song => song.artist.id === this.songs[0].artist.id)
},
/**
* Determine if all songs we're editing are from the same album.
*
* @return {boolean}
*/
inSameAlbum () {
return every(this.songs, song => song.album.id === this.songs[0].album.id)
},
/**
* URL of the cover to display.
*
* @return {string}
*/
coverUrl () {
return this.inSameAlbum ? this.songs[0].album.cover : '/public/img/covers/unknown-album.png'
},
/**
* Determine the compilation state of the songs.
*
* @return {Number}
*/
compilationState () {
const contributedSongs = filter(this.songs, song => song.contributing_artist_id)
if (!contributedSongs.length) {
this.formData.compilationState = COMPILATION_STATES.NONE
} else if (contributedSongs.length === this.songs.length) {
this.formData.compilationState = COMPILATION_STATES.ALL
} else {
this.formData.compilationState = COMPILATION_STATES.SOME
}
return this.formData.compilationState
},
/**
* The song title to be displayed.
*
* @return {string}
*/
displayedTitle () {
return this.editSingle ? this.formData.title : `${this.songs.length} songs selected`
},
/**
* The album name to be displayed.
*
* @return {string}
*/
displayedAlbum () {
if (this.editSingle) {
return this.formData.albumName
} else {
return this.formData.albumName ? this.formData.albumName : 'Mixed Albums'
}
},
/**
* The artist name to be displayed.
*
* @return {string}
*/
displayedArtist () {
if (this.editSingle) {
return this.formData.artistName
} else {
return this.formData.artistName ? this.formData.artistName : 'Mixed Artists'
}
/**
* The artist name to be displayed.
*
* @return {string}
*/
displayedArtist () {
if (this.editSingle) {
return this.formData.artistName
} else {
return this.formData.artistName ? this.formData.artistName : 'Mixed Artists'
}
},
}
},
methods: {
open (songs) {
this.shown = true
this.songs = songs
this.currentView = 'details'
this.needsReload = false
methods: {
open (songs) {
this.shown = true
this.songs = songs
this.currentView = 'details'
this.needsReload = false
if (this.editSingle) {
this.formData.title = this.songs[0].title
this.formData.albumName = this.songs[0].album.name
this.formData.artistName = this.songs[0].artist.name
if (this.editSingle) {
this.formData.title = this.songs[0].title
this.formData.albumName = this.songs[0].album.name
this.formData.artistName = this.songs[0].artist.name
// If we're editing only one song and the song's info (including lyrics)
// hasn't been loaded, load it now.
if (!this.songs[0].infoRetrieved) {
this.loading = true
// If we're editing only one song and the song's info (including lyrics)
// hasn't been loaded, load it now.
if (!this.songs[0].infoRetrieved) {
this.loading = true
songInfo.fetch(this.songs[0]).then(r => {
this.loading = false
this.formData.lyrics = br2nl(this.songs[0].lyrics)
this.formData.track = this.songs[0].track
this.initCompilationStateCheckbox()
})
} else {
songInfo.fetch(this.songs[0]).then(r => {
this.loading = false
this.formData.lyrics = br2nl(this.songs[0].lyrics)
this.formData.track = this.songs[0].track
this.initCompilationStateCheckbox()
}
})
} else {
this.formData.albumName = this.inSameAlbum ? this.songs[0].album.name : ''
this.formData.artistName = this.bySameArtist ? this.songs[0].artist.name : ''
this.loading = false
this.formData.lyrics = br2nl(this.songs[0].lyrics)
this.formData.track = this.songs[0].track
this.initCompilationStateCheckbox()
}
},
/**
* Initialize the compilation state's checkbox of the editing songs' album(s).
*/
initCompilationStateCheckbox () {
// This must be wrapped in a $nextTick callback, because the form is dynamically
// attached into DOM in conjunction with `this.loading` data binding.
this.$nextTick(() => {
const chk = this.$refs.compilationStateChk
switch (this.compilationState) {
case COMPILATION_STATES.ALL:
chk.checked = true
chk.indeterminate = false
break
case COMPILATION_STATES.NONE:
chk.checked = false
chk.indeterminate = false
break
default:
chk.checked = false
chk.indeterminate = true
break
}
})
},
/**
* Manually set the compilation state.
* We can't use v-model here due to the tri-state nature of the property.
* Also, following iTunes style, we don't support circular switching of the states -
* once the user clicks the checkbox, there's no going back to indeterminate state.
*/
changeCompilationState (e) {
this.formData.compilationState = e.target.checked ? COMPILATION_STATES.ALL : COMPILATION_STATES.NONE
this.needsReload = true
},
/**
* Close the modal.
*/
close () {
this.shown = false
},
/**
* Submit the form.
*/
submit () {
this.loading = true
songStore.update(this.songs, this.formData).then(r => {
this.loading = false
this.close()
this.needsReload && forceReloadWindow()
}).catch(r => {
this.loading = false
})
} else {
this.formData.albumName = this.inSameAlbum ? this.songs[0].album.name : ''
this.formData.artistName = this.bySameArtist ? this.songs[0].artist.name : ''
this.loading = false
this.initCompilationStateCheckbox()
}
},
/**
* Initialize the compilation state's checkbox of the editing songs' album(s).
*/
initCompilationStateCheckbox () {
// This must be wrapped in a $nextTick callback, because the form is dynamically
// attached into DOM in conjunction with `this.loading` data binding.
this.$nextTick(() => {
const chk = this.$refs.compilationStateChk
switch (this.compilationState) {
case COMPILATION_STATES.ALL:
chk.checked = true
chk.indeterminate = false
break
case COMPILATION_STATES.NONE:
chk.checked = false
chk.indeterminate = false
break
default:
chk.checked = false
chk.indeterminate = true
break
}
})
},
/**
* Manually set the compilation state.
* We can't use v-model here due to the tri-state nature of the property.
* Also, following iTunes style, we don't support circular switching of the states -
* once the user clicks the checkbox, there's no going back to indeterminate state.
*/
changeCompilationState (e) {
this.formData.compilationState = e.target.checked ? COMPILATION_STATES.ALL : COMPILATION_STATES.NONE
this.needsReload = true
},
/**
* Close the modal.
*/
close () {
this.shown = false
},
/**
* Submit the form.
*/
submit () {
this.loading = true
songStore.update(this.songs, this.formData).then(r => {
this.loading = false
this.close()
this.needsReload && forceReloadWindow()
}).catch(r => {
this.loading = false
})
}
}
}
</script>
<style lang="sass">
@import "../../../sass/partials/_vars.scss";
@import "../../../sass/partials/_mixins.scss";
@import "../../../sass/partials/_vars.scss";
@import "../../../sass/partials/_mixins.scss";
#editSongsOverlay {
z-index: 9999;
position: fixed;
top: 0;
left: 0;
#editSongsOverlay {
z-index: 9999;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, .7);
overflow: auto;
@include vertical-center();
$borderRadius: 5px;
form {
position: relative;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, .7);
overflow: auto;
max-width: 460px;
background: #fff;
border-radius: $borderRadius;
color: #333;
@include vertical-center();
input[type="checkbox"] {
border: 1px solid #ccc;
}
$borderRadius: 5px;
.form-row:first-child {
margin-top: 0;
}
form {
position: relative;
> header, > div, > footer {
padding: 16px;
}
> div {
padding-bottom: 0;
}
input[type="text"], input[type="number"], textarea {
border: 1px solid #ccc;
width: 100%;
max-width: 460px;
background: #fff;
border-radius: $borderRadius;
color: #333;
max-width: 100%;
input[type="checkbox"] {
border: 1px solid #ccc;
&:focus {
border-color: $colorOrange;
}
}
.warning {
color: #f00;
}
textarea {
min-height: 192px;
}
> header {
display: flex;
background: #eee;
border-radius: $borderRadius $borderRadius 0 0;
img {
flex: 0 0 96px;
}
.form-row:first-child {
margin-top: 0;
}
.meta {
flex: 1;
padding-left: 8px;
> header, > div, > footer {
padding: 16px;
}
> div {
padding-bottom: 0;
}
input[type="text"], input[type="number"], textarea {
border: 1px solid #ccc;
width: 100%;
max-width: 100%;
&:focus {
border-color: $colorOrange;
}
}
.warning {
color: #f00;
}
textarea {
min-height: 192px;
}
> header {
display: flex;
background: #eee;
border-radius: $borderRadius $borderRadius 0 0;
img {
flex: 0 0 96px;
h1 {
font-size: 1.8rem;
line-height: 2.2rem;
margin-bottom: .3rem;
}
.meta {
flex: 1;
padding-left: 8px;
h1 {
font-size: 1.8rem;
line-height: 2.2rem;
margin-bottom: .3rem;
}
.mixed {
opacity: .5;
}
.mixed {
opacity: .5;
}
}
}
}
}
</style>