Allow Navigate from "Most Played Songs" (fixes #303)

This commit is contained in:
An Phan 2016-05-30 16:04:22 +08:00
parent 07c1ae0f59
commit d25e899593
6 changed files with 206 additions and 190 deletions

View file

@ -11,23 +11,9 @@
<ol class="top-song-list">
<li v-for="song in topSongs"
:class="{ playing: song.playbackState === 'playing' || song.playbackState === 'paused' }"
@dblclick.prevent="play(song)"
>
<span class="cover" :style="{ backgroundImage: 'url(' + song.album.cover + ')' }">
<a class="control" @click.prevent="triggerPlay(song)">
<i class="fa fa-play" v-show="song.playbackState !== 'playing'"></i>
<i class="fa fa-pause" v-else></i>
</a>
</span>
<span class="details">
<span :style="{ width: song.playCount * 100 / topSongs[0].playCount + '%' }"
class="play-count"></span>
{{ song.title }}
<span class="by">{{ song.artist.name }}
{{ song.playCount }} {{ song.playCount | pluralize 'play' }}</span>
</span>
</li>
:top-play-count="topSongs[0].playCount"
:song="song"
is="song-item"></li>
</ol>
</section>
@ -36,20 +22,9 @@
<ol class="recent-song-list" v-show="recentSongs.length">
<li v-for="song in recentSongs"
:class="{ playing: song.playbackState === 'playing' || song.playbackState === 'paused' }"
@dblclick.prevent="play(song)"
>
<span class="cover" :style="{ backgroundImage: 'url(' + song.album.cover + ')' }">
<a class="control" @click.prevent="triggerPlay(song)">
<i class="fa fa-play" v-show="song.playbackState !== 'playing'"></i>
<i class="fa fa-pause" v-else></i>
</a>
</span>
<span class="details">
{{ song.title }}
<span class="by">{{ song.artist.name }}</span>
</span>
</li>
:top-play-count="topSongs[0].playCount"
:song="song"
is="song-item"></li>
</ol>
<p class="none" v-show="!recentSongs.length">
@ -85,23 +60,22 @@
<script>
import { sample } from 'lodash';
import playback from '../../../services/playback';
import songStore from '../../../stores/song';
import albumStore from '../../../stores/album';
import artistStore from '../../../stores/artist';
import userStore from '../../../stores/user';
import queueStore from '../../../stores/queue';
import preferenceStore from '../../../stores/preference';
import infiniteScroll from '../../../mixins/infinite-scroll';
import albumItem from '../../shared/album-item.vue';
import artistItem from '../../shared/artist-item.vue';
import songItem from '../../shared/home-song-item.vue';
export default {
components: { albumItem, artistItem },
components: { albumItem, artistItem, songItem },
/**
* We're not really using infinite scrolling here, but only the handy "Back to Top" button.
* @type {Array}
* Note: We're not really using infinite scrolling here,
* but only the handy "Back to Top" button.
*/
mixins: [infiniteScroll],
@ -134,27 +108,6 @@
},
methods: {
play(song) {
if (!queueStore.contains(song)) {
queueStore.queueAfterCurrent(song);
}
playback.play(song);
},
/**
* Trigger playing a song.
*/
triggerPlay(song) {
if (song.playbackState === 'stopped') {
this.play(song);
} else if (song.playbackState === 'paused') {
playback.resume();
} else {
playback.pause();
}
},
/**
* Refresh the dashboard with latest data.
*/
@ -194,101 +147,6 @@
margin-right: 8px;
}
}
ol li {
display: flex;
&.playing {
color: $colorHighlight;
}
&:hover .cover {
.control {
display: block;
}
&::before {
opacity: .7;
}
}
.cover {
flex: 0 0 48px;
height: 48px;
background-size: cover;
position: relative;
@include vertical-center();
&::before {
content: " ";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
background: #000;
opacity: 0;
html.touchevents & {
opacity: .7;
}
}
.control {
border-radius: 50%;
width: 28px;
height: 28px;
background: rgba(0, 0, 0, .7);
border: 1px solid transparent;
line-height: 26px;
font-size: 13px;
text-align: center;
z-index: 1;
display: none;
color: #fff;
transition: .3s;
&:hover {
transform: scale(1.2);
border-color: #fff;
}
html.touchevents & {
display: block;
}
}
}
.details {
flex: 1;
padding: 4px 8px;
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
.play-count {
background: rgba(255, 255, 255, 0.08);
position: absolute;
height: 100%;
top: 0;
left: 0;
pointer-events: none;
}
.by {
display: block;
font-size: 90%;
opacity: .6;
margin-top: 2px;
}
}
//border-bottom: 1px solid $color2ndBgr;
margin-bottom: 8px;
}
}
.none {

View file

@ -6,9 +6,12 @@
</a>
</span>
<footer>
<a class="name" @click.prevent="viewDetails">{{ album.name }}</a>
<a class="name" @click.prevent="viewAlbumDetails(album)">{{ album.name }}</a>
<span class="sep">by</span>
<a class="artist" v-if="isNormalArtist" @click.prevent="viewArtistDetails">{{ album.artist.name }}</a>
<a class="artist" v-if="isNormalArtist"
@click.prevent="viewArtistDetails(album.artist)">
{{ album.artist.name }}
</a>
<span class="artist nope" v-else>{{ album.artist.name }}</span>
<p class="meta">
{{ album.songs.length }} {{ album.songs.length | pluralize 'song' }}
@ -28,9 +31,11 @@
import playback from '../../services/playback';
import queueStore from '../../stores/queue';
import artistStore from '../../stores/artist';
import artistAlbumDetails from '../../mixins/artist-album-details';
export default {
props: ['album'],
mixins: [artistAlbumDetails],
computed: {
isNormalArtist() {
@ -51,20 +56,6 @@
}
},
/**
* Load the album details screen.
*/
viewDetails() {
this.$root.loadAlbum(this.album);
},
/**
* Load the artist details screen.
*/
viewArtistDetails() {
this.$root.loadArtist(this.album.artist);
},
/**
* Allow dragging the album (actually, its songs).
*/

View file

@ -6,7 +6,7 @@
</a>
</span>
<footer>
<a class="name" @click.prevent="viewDetails">{{ artist.name }}</a>
<a class="name" @click.prevent="viewArtistDetails(artist)">{{ artist.name }}</a>
<p class="meta">
{{ artist.albums.length }} {{ artist.albums.length | pluralize 'album' }}
@ -25,9 +25,11 @@
import playback from '../../services/playback';
import artistStore from '../../stores/artist';
import queueStore from '../../stores/queue';
import artistAlbumDetails from '../../mixins/artist-album-details';
export default {
props: ['artist'],
mixins: [artistAlbumDetails],
computed: {
/**
@ -53,10 +55,6 @@
}
},
viewDetails() {
this.$root.loadArtist(this.artist);
},
/**
* Allow dragging the artist (actually, their songs).
*/

View file

@ -0,0 +1,161 @@
<template>
<li class="song-item-home"
:class="{ playing: song.playbackState === 'playing' || song.playbackState === 'paused' }"
@dblclick.prevent="play"
>
<span class="cover" :style="{ backgroundImage: 'url(' + song.album.cover + ')' }">
<a class="control" @click.prevent="changeSongState">
<i class="fa fa-play" v-show="song.playbackState !== 'playing'"></i>
<i class="fa fa-pause" v-else></i>
</a>
</span>
<span class="details">
<span :style="{ width: song.playCount * 100 / topPlayCount + '%' }"
class="play-count"></span>
{{ song.title }}
<span class="by">
<a href="#" @click.prevent="viewArtistDetails(song.artist)">{{ song.artist.name }}</a> -
{{ song.playCount }} {{ song.playCount | pluralize 'play' }}
</span>
</span>
</li>
</template>
<script>
import playback from '../../services/playback';
import queueStore from '../../stores/queue';
import artistAlbumDetails from '../../mixins/artist-album-details';
export default {
props: ['song', 'topPlayCount'],
mixins: [artistAlbumDetails],
methods: {
play() {
if (!queueStore.contains(this.song)) {
queueStore.queueAfterCurrent(this.song);
}
playback.play(this.song);
},
changeSongState() {
if (this.song.playbackState === 'stopped') {
this.play(this.song);
} else if (this.song.playbackState === 'paused') {
this.playback.resume();
} else {
this.playback.pause();
}
},
},
};
</script>
<style lang="sass" scoped>
@import "../../../sass/partials/_vars.scss";
@import "../../../sass/partials/_mixins.scss";
.song-item-home {
display: flex;
&.playing {
color: $colorHighlight;
}
&:hover .cover {
.control {
display: block;
}
&::before {
opacity: .7;
}
}
.cover {
flex: 0 0 48px;
height: 48px;
background-size: cover;
position: relative;
@include vertical-center();
&::before {
content: " ";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
background: #000;
opacity: 0;
html.touchevents & {
opacity: .7;
}
}
.control {
border-radius: 50%;
width: 28px;
height: 28px;
background: rgba(0, 0, 0, .7);
border: 1px solid transparent;
line-height: 26px;
font-size: 13px;
text-align: center;
z-index: 1;
display: none;
color: #fff;
transition: .3s;
&:hover {
transform: scale(1.2);
border-color: #fff;
}
html.touchevents & {
display: block;
}
}
}
.details {
flex: 1;
padding: 4px 8px;
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
.play-count {
background: rgba(255, 255, 255, 0.08);
position: absolute;
height: 100%;
top: 0;
left: 0;
pointer-events: none;
}
.by {
display: block;
font-size: 90%;
margin-top: 2px;
color: $color2ndText;
opacity: .8;
a {
color: #fff;
&:hover {
color: $colorHighlight;
}
}
}
}
margin-bottom: 8px;
}
</style>

View file

@ -7,10 +7,10 @@
<span v-if="songs[0].playbackState !== 'playing'">Play</span>
<span v-else>Pause</span>
</li>
<li v-if="onlyOneSongSelected" @click="goToAlbum">
<li v-if="onlyOneSongSelected" @click="viewAlbumDetails(songs[0].album)">
<span>Go to Album</span>
</li>
<li v-if="onlyOneSongSelected" @click="goToArtist">
<li v-if="onlyOneSongSelected" @click="viewArtistDetails(songs[0].artist)">
<span>Go to Artist</span>
</li>
<li class="has-sub">Add To
@ -34,6 +34,7 @@
import $ from 'jquery';
import songMenuMethods from '../../mixins/song-menu-methods';
import artistAlbumDetails from '../../mixins/artist-album-details';
import queueStore from '../../stores/queue';
import userStore from '../../stores/user';
@ -42,7 +43,7 @@
export default {
props: ['songs'],
mixins: [songMenuMethods],
mixins: [songMenuMethods, artistAlbumDetails],
data() {
return {
@ -121,20 +122,6 @@
this.close();
},
/**
* Navigate to the song's album view
*/
goToAlbum() {
this.$root.loadAlbum(this.songs[0].album);
},
/**
* Navigate to the song's artist view
*/
goToArtist() {
this.$root.loadArtist(this.songs[0].artist);
},
},
/**

View file

@ -0,0 +1,21 @@
/**
* Add necessary functionalities into a view that triggers artist or album details views.
*/
export default {
methods: {
/**
* Load the album details screen.
*/
viewAlbumDetails(album) {
this.$root.loadAlbum(album);
},
/**
* Load the artist details screen.
*/
viewArtistDetails(artist) {
this.$root.loadArtist(artist);
},
},
};