mirror of
https://github.com/koel/koel
synced 2024-11-10 06:34:14 +00:00
Allow Navigate from "Most Played Songs" (fixes #303)
This commit is contained in:
parent
07c1ae0f59
commit
d25e899593
6 changed files with 206 additions and 190 deletions
|
@ -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 {
|
||||
|
|
|
@ -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).
|
||||
*/
|
||||
|
|
|
@ -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).
|
||||
*/
|
||||
|
|
161
resources/assets/js/components/shared/home-song-item.vue
Normal file
161
resources/assets/js/components/shared/home-song-item.vue
Normal 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>
|
|
@ -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);
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
21
resources/assets/js/mixins/artist-album-details.js
Normal file
21
resources/assets/js/mixins/artist-album-details.js
Normal 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);
|
||||
},
|
||||
},
|
||||
};
|
Loading…
Reference in a new issue