From f66dff3504fbb43eb6c628e92d50644870772501 Mon Sep 17 00:00:00 2001 From: Eliot Berriot <contact@eliotberriot.com> Date: Wed, 21 Mar 2018 11:58:53 +0100 Subject: [PATCH] Added playlist list in library --- front/src/components/library/Library.vue | 1 + front/src/components/playlists/Card.vue | 40 +++++ front/src/components/playlists/CardList.vue | 34 +++++ front/src/components/playlists/Form.vue | 5 +- front/src/router/index.js | 12 ++ front/src/views/playlists/Detail.vue | 2 +- front/src/views/playlists/List.vue | 158 ++++++++++++++++++++ 7 files changed, 250 insertions(+), 2 deletions(-) create mode 100644 front/src/components/playlists/Card.vue create mode 100644 front/src/components/playlists/CardList.vue create mode 100644 front/src/views/playlists/List.vue diff --git a/front/src/components/library/Library.vue b/front/src/components/library/Library.vue index c209221274..161d4519b2 100644 --- a/front/src/components/library/Library.vue +++ b/front/src/components/library/Library.vue @@ -4,6 +4,7 @@ <router-link class="ui item" to="/library" exact>Browse</router-link> <router-link class="ui item" to="/library/artists" exact>Artists</router-link> <router-link class="ui item" to="/library/radios" exact>Radios</router-link> + <router-link class="ui item" to="/library/playlists" exact>Playlists</router-link> <div class="ui secondary right menu"> <router-link v-if="$store.state.auth.authenticated" class="ui item" to="/library/requests/" exact> Requests diff --git a/front/src/components/playlists/Card.vue b/front/src/components/playlists/Card.vue new file mode 100644 index 0000000000..6dd1b0a0ce --- /dev/null +++ b/front/src/components/playlists/Card.vue @@ -0,0 +1,40 @@ +<template> + <div class="ui card"> + <div class="content"> + <div class="header"> + <router-link class="discrete link" :to="{name: 'library.playlists.detail', params: {id: playlist.id }}"> + {{ playlist.name }} + </router-link> + </div> + <div class="meta"> + <i class="user icon"></i> {{ playlist.user.username }} + </div> + <div class="meta"> + <i class="clock icon"></i> Updated <human-date :date="playlist.modification_date"></human-date> + </div> + </div> + <div class="extra content"> + <span> + <i class="sound icon"></i> + {{ playlist.tracks_count }} tracks + </span> + <play-button class="mini basic orange right floated" :playlist="playlist">Play all</play-button> + </div> + </div> +</template> + +<script> +import PlayButton from '@/components/audio/PlayButton' + +export default { + props: ['playlist'], + components: { + PlayButton + } +} +</script> + +<!-- Add "scoped" attribute to limit CSS to this component only --> +<style scoped> + +</style> diff --git a/front/src/components/playlists/CardList.vue b/front/src/components/playlists/CardList.vue new file mode 100644 index 0000000000..4d4746090f --- /dev/null +++ b/front/src/components/playlists/CardList.vue @@ -0,0 +1,34 @@ +<template> + <div + v-if="playlists.length > 0" + v-masonry + transition-duration="0" + item-selector=".column" + percent-position="true" + stagger="0" + class="ui stackable three column doubling grid"> + <div + v-masonry-tile + v-for="playlist in playlists" + :key="playlist.id" + class="column"> + <playlist-card class="fluid" :playlist="playlist"></playlist-card> + </div> + </div> +</template> + +<script> + +import PlaylistCard from '@/components/playlists/Card' + +export default { + props: ['playlists'], + components: { + PlaylistCard + } +} +</script> + +<!-- Add "scoped" attribute to limit CSS to this component only --> +<style scoped> +</style> diff --git a/front/src/components/playlists/Form.vue b/front/src/components/playlists/Form.vue index 267e2c10e5..21ddfeaec7 100644 --- a/front/src/components/playlists/Form.vue +++ b/front/src/components/playlists/Form.vue @@ -21,8 +21,11 @@ <option :value="c.value" v-for="c in privacyLevelChoices">{{ c.label }}</option> </select> </div> + <div class="field"> + <label> </label> + <button :class="['ui', {'loading': isLoading}, 'button']" type="submit">Create playlist</button> + </div> </div> - <button :class="['ui', {'loading': isLoading}, 'button']" type="submit">Create playlist</button> </form> </template> diff --git a/front/src/router/index.js b/front/src/router/index.js index 7cffb6f997..8028444613 100644 --- a/front/src/router/index.js +++ b/front/src/router/index.js @@ -22,6 +22,7 @@ import BatchList from '@/components/library/import/BatchList' import BatchDetail from '@/components/library/import/BatchDetail' import RequestsList from '@/components/requests/RequestsList' import PlaylistDetail from '@/views/playlists/Detail' +import PlaylistList from '@/views/playlists/List' import Favorites from '@/components/favorites/List' Vue.use(Router) @@ -110,6 +111,17 @@ export default new Router({ }, { path: 'radios/build', name: 'library.radios.build', component: RadioBuilder, props: true }, { path: 'radios/build/:id', name: 'library.radios.edit', component: RadioBuilder, props: true }, + { + path: 'playlists/', + name: 'library.playlists.browse', + component: PlaylistList, + props: (route) => ({ + defaultOrdering: route.query.ordering, + defaultQuery: route.query.query, + defaultPaginateBy: route.query.paginateBy, + defaultPage: route.query.page + }) + }, { path: 'playlists/:id', name: 'library.playlists.detail', diff --git a/front/src/views/playlists/Detail.vue b/front/src/views/playlists/Detail.vue index 1f3caa4942..52c38b6181 100644 --- a/front/src/views/playlists/Detail.vue +++ b/front/src/views/playlists/Detail.vue @@ -34,7 +34,7 @@ </dangerous-button> </div> </div> - <div v-if="tracks.length > 0" class="ui vertical stripe segment"> + <div class="ui vertical stripe segment"> <template v-if="edit"> <playlist-editor @tracks-updated="updatePlts" :playlist="playlist" :playlist-tracks="playlistTracks"></playlist-editor> </template> diff --git a/front/src/views/playlists/List.vue b/front/src/views/playlists/List.vue new file mode 100644 index 0000000000..fc5dcbe54b --- /dev/null +++ b/front/src/views/playlists/List.vue @@ -0,0 +1,158 @@ +<template> + <div> + <div class="ui vertical stripe segment"> + <h2 class="ui header">Browsing playlists</h2> + <div :class="['ui', {'loading': isLoading}, 'form']"> + <template v-if="$store.state.auth.authenticated"> + <button + @click="$store.commit('playlists/chooseTrack', null)" + class="ui basic green button">Manage your playlists</button> + <div class="ui hidden divider"></div> + </template> + <div class="fields"> + <div class="field"> + <label>Search</label> + <input type="text" v-model="query" placeholder="Enter an playlist name..."/> + </div> + <div class="field"> + <label>Ordering</label> + <select class="ui dropdown" v-model="ordering"> + <option v-for="option in orderingOptions" :value="option[0]"> + {{ option[1] }} + </option> + </select> + </div> + <div class="field"> + <label>Ordering direction</label> + <select class="ui dropdown" v-model="orderingDirection"> + <option value="">Ascending</option> + <option value="-">Descending</option> + </select> + </div> + <div class="field"> + <label>Results per page</label> + <select class="ui dropdown" v-model="paginateBy"> + <option :value="parseInt(12)">12</option> + <option :value="parseInt(25)">25</option> + <option :value="parseInt(50)">50</option> + </select> + </div> + </div> + </div> + <div class="ui hidden divider"></div> + <playlist-card-list v-if="result" :playlists="result.results"></playlist-card-list> + <div class="ui center aligned basic segment"> + <pagination + v-if="result && result.results.length > 0" + @page-changed="selectPage" + :current="page" + :paginate-by="paginateBy" + :total="result.count" + ></pagination> + </div> + </div> + </div> +</template> + +<script> +import axios from 'axios' +import _ from 'lodash' +import $ from 'jquery' + +import OrderingMixin from '@/components/mixins/Ordering' +import PaginationMixin from '@/components/mixins/Pagination' +import PlaylistCardList from '@/components/playlists/CardList' +import Pagination from '@/components/Pagination' + +const FETCH_URL = 'playlists/' + +export default { + mixins: [OrderingMixin, PaginationMixin], + props: { + defaultQuery: {type: String, required: false, default: ''} + }, + components: { + PlaylistCardList, + Pagination + }, + data () { + let defaultOrdering = this.getOrderingFromString(this.defaultOrdering || '-creation_date') + return { + isLoading: true, + result: null, + page: parseInt(this.defaultPage), + query: this.defaultQuery, + paginateBy: parseInt(this.defaultPaginateBy || 12), + orderingDirection: defaultOrdering.direction, + ordering: defaultOrdering.field, + orderingOptions: [ + ['creation_date', 'Creation date'], + ['modification_date', 'Last modification date'], + ['name', 'Name'] + ] + } + }, + created () { + this.fetchData() + }, + mounted () { + $('.ui.dropdown').dropdown() + }, + methods: { + updateQueryString: _.debounce(function () { + this.$router.replace({ + query: { + query: this.query, + page: this.page, + paginateBy: this.paginateBy, + ordering: this.getOrderingAsString() + } + }) + }, 500), + fetchData: _.debounce(function () { + var self = this + this.isLoading = true + let url = FETCH_URL + let params = { + page: this.page, + page_size: this.paginateBy, + q: this.query, + ordering: this.getOrderingAsString() + } + axios.get(url, {params: params}).then((response) => { + self.result = response.data + self.isLoading = false + }) + }, 500), + selectPage: function (page) { + this.page = page + } + }, + watch: { + page () { + this.updateQueryString() + this.fetchData() + }, + paginateBy () { + this.updateQueryString() + this.fetchData() + }, + ordering () { + this.updateQueryString() + this.fetchData() + }, + orderingDirection () { + this.updateQueryString() + this.fetchData() + }, + query () { + this.updateQueryString() + this.fetchData() + } + } +} +</script> + +<!-- Add "scoped" attribute to limit CSS to this component only --> +<style scoped> +</style> -- GitLab