From 6ad8b212cff39072c8890796b31dbb19c306a41c Mon Sep 17 00:00:00 2001
From: Eliot Berriot <contact@eliotberriot.com>
Date: Sun, 17 Dec 2017 20:07:18 +0100
Subject: [PATCH] Fixed #50: ordering for favorites

---
 api/funkwhale_api/music/views.py           |  7 +-
 front/src/components/favorites/List.vue    | 76 ++++++++++++++++++++--
 front/src/components/library/Artists.vue   | 31 +++------
 front/src/components/mixins/Ordering.vue   | 26 ++++++++
 front/src/components/mixins/Pagination.vue |  8 +++
 front/src/router/index.js                  |  6 +-
 6 files changed, 124 insertions(+), 30 deletions(-)
 create mode 100644 front/src/components/mixins/Ordering.vue
 create mode 100644 front/src/components/mixins/Pagination.vue

diff --git a/api/funkwhale_api/music/views.py b/api/funkwhale_api/music/views.py
index 5bfefc29..532942e2 100644
--- a/api/funkwhale_api/music/views.py
+++ b/api/funkwhale_api/music/views.py
@@ -95,7 +95,12 @@ class TrackViewSet(TagViewSetMixin, SearchMixin, viewsets.ReadOnlyModelViewSet):
     serializer_class = serializers.TrackSerializerNested
     permission_classes = [ConditionalAuthentication]
     search_fields = ['title', 'artist__name']
-    ordering_fields = ('creation_date',)
+    ordering_fields = (
+        'creation_date',
+        'title',
+        'album__title',
+        'artist__name',
+    )
 
     def get_queryset(self):
         queryset = super().get_queryset()
diff --git a/front/src/components/favorites/List.vue b/front/src/components/favorites/List.vue
index 63c3ba79..91efd729 100644
--- a/front/src/components/favorites/List.vue
+++ b/front/src/components/favorites/List.vue
@@ -9,9 +9,36 @@
         {{ favoriteTracks.count }} favorites
       </h2>
       <radio-button type="favorites"></radio-button>
-
     </div>
     <div class="ui vertical stripe segment">
+      <div :class="['ui', {'loading': isLoading}, 'form']">
+        <div class="fields">
+          <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>
+
       <track-table v-if="results" :tracks="results.results"></track-table>
       <div class="ui center aligned basic segment">
         <pagination
@@ -27,6 +54,7 @@
 </template>
 
 <script>
+import $ from 'jquery'
 import Vue from 'vue'
 import logger from '@/logging'
 import config from '@/config'
@@ -34,37 +62,60 @@ import favoriteTracks from '@/favorites/tracks'
 import TrackTable from '@/components/audio/track/Table'
 import RadioButton from '@/components/radios/Button'
 import Pagination from '@/components/Pagination'
-
+import OrderingMixin from '@/components/mixins/Ordering'
+import PaginationMixin from '@/components/mixins/Pagination'
 const FAVORITES_URL = config.API_URL + 'tracks/'
 
 export default {
+  mixins: [OrderingMixin, PaginationMixin],
   components: {
     TrackTable,
     RadioButton,
     Pagination
   },
   data () {
+    let defaultOrdering = this.getOrderingFromString(this.defaultOrdering || 'artist__name')
     return {
       results: null,
       isLoading: false,
       nextLink: null,
       previousLink: null,
-      page: 1,
-      paginateBy: 25,
-      favoriteTracks
+      favoriteTracks,
+      page: parseInt(this.defaultPage),
+      paginateBy: parseInt(this.defaultPaginateBy || 25),
+      orderingDirection: defaultOrdering.direction,
+      ordering: defaultOrdering.field,
+      orderingOptions: [
+        ['title', 'Track name'],
+        ['album__title', 'Album name'],
+        ['artist__name', 'Artist name']
+      ]
     }
   },
   created () {
     this.fetchFavorites(FAVORITES_URL)
   },
+  mounted () {
+    $('.ui.dropdown').dropdown()
+  },
   methods: {
+    updateQueryString: function () {
+      this.$router.replace({
+        query: {
+          page: this.page,
+          paginateBy: this.paginateBy,
+          ordering: this.getOrderingAsString()
+        }
+      })
+    },
     fetchFavorites (url) {
       var self = this
       this.isLoading = true
       let params = {
         favorites: 'true',
         page: this.page,
-        page_size: this.paginateBy
+        page_size: this.paginateBy,
+        ordering: this.getOrderingAsString()
       }
       logger.default.time('Loading user favorites')
       this.$http.get(url, {params: params}).then((response) => {
@@ -86,6 +137,19 @@ export default {
   },
   watch: {
     page: function () {
+      this.updateQueryString()
+      this.fetchFavorites(FAVORITES_URL)
+    },
+    paginateBy: function () {
+      this.updateQueryString()
+      this.fetchFavorites(FAVORITES_URL)
+    },
+    orderingDirection: function () {
+      this.updateQueryString()
+      this.fetchFavorites(FAVORITES_URL)
+    },
+    ordering: function () {
+      this.updateQueryString()
       this.fetchFavorites(FAVORITES_URL)
     }
   }
diff --git a/front/src/components/library/Artists.vue b/front/src/components/library/Artists.vue
index c3e9f1d1..8d0a4f55 100644
--- a/front/src/components/library/Artists.vue
+++ b/front/src/components/library/Artists.vue
@@ -63,30 +63,31 @@ import $ from 'jquery'
 import config from '@/config'
 import backend from '@/audio/backend'
 import logger from '@/logging'
+
+import OrderingMixin from '@/components/mixins/Ordering'
+import PaginationMixin from '@/components/mixins/Pagination'
 import ArtistCard from '@/components/audio/artist/Card'
 import Pagination from '@/components/Pagination'
 
 const FETCH_URL = config.API_URL + 'artists/'
 
 export default {
+  mixins: [OrderingMixin, PaginationMixin],
   props: {
-    defaultOrdering: {type: String, required: false, default: '-creation_date'},
-    defaultQuery: {type: String, required: false, default: ''},
-    defaultPage: {required: false, default: 1},
-    defaultPaginateBy: {required: false, default: 12}
+    defaultQuery: {type: String, required: false, default: ''}
   },
   components: {
     ArtistCard,
     Pagination
   },
   data () {
-    let defaultOrdering = this.getOrderingFromString(this.defaultOrdering)
+    let defaultOrdering = this.getOrderingFromString(this.defaultOrdering || '-creation_date')
     return {
       isLoading: true,
       result: null,
       page: parseInt(this.defaultPage),
       query: this.defaultQuery,
-      paginateBy: parseInt(this.defaultPaginateBy),
+      paginateBy: parseInt(this.defaultPaginateBy || 12),
       orderingDirection: defaultOrdering.direction,
       ordering: defaultOrdering.field,
       orderingOptions: [
@@ -102,27 +103,13 @@ export default {
     $('.ui.dropdown').dropdown()
   },
   methods: {
-    getOrderingFromString (s) {
-      let parts = s.split('-')
-      if (parts.length > 1) {
-        return {
-          direction: '-',
-          field: parts.slice(1).join('-')
-        }
-      } else {
-        return {
-          direction: '',
-          field: s
-        }
-      }
-    },
     updateQueryString: function () {
       this.$router.replace({
         query: {
           query: this.query,
           page: this.page,
           paginateBy: this.paginateBy,
-          ordering: [this.orderingDirection, this.ordering].join('')
+          ordering: this.getOrderingAsString()
         }
       })
     },
@@ -134,7 +121,7 @@ export default {
         page: this.page,
         page_size: this.paginateBy,
         name__icontains: this.query,
-        ordering: [this.orderingDirection, this.ordering].join('')
+        ordering: this.getOrderingAsString()
       }
       logger.default.debug('Fetching artists')
       this.$http.get(url, {params: params}).then((response) => {
diff --git a/front/src/components/mixins/Ordering.vue b/front/src/components/mixins/Ordering.vue
new file mode 100644
index 00000000..494dddce
--- /dev/null
+++ b/front/src/components/mixins/Ordering.vue
@@ -0,0 +1,26 @@
+<script>
+export default {
+  props: {
+    defaultOrdering: {type: String, required: false}
+  },
+  methods: {
+    getOrderingFromString (s) {
+      let parts = s.split('-')
+      if (parts.length > 1) {
+        return {
+          direction: '-',
+          field: parts.slice(1).join('-')
+        }
+      } else {
+        return {
+          direction: '',
+          field: s
+        }
+      }
+    },
+    getOrderingAsString () {
+      return [this.orderingDirection, this.ordering].join('')
+    }
+  }
+}
+</script>
diff --git a/front/src/components/mixins/Pagination.vue b/front/src/components/mixins/Pagination.vue
new file mode 100644
index 00000000..532faaaa
--- /dev/null
+++ b/front/src/components/mixins/Pagination.vue
@@ -0,0 +1,8 @@
+<script>
+export default {
+  props: {
+    defaultPage: {required: false, default: 1},
+    defaultPaginateBy: {required: false}
+  }
+}
+</script>
diff --git a/front/src/router/index.js b/front/src/router/index.js
index f6653e73..7db5da6b 100644
--- a/front/src/router/index.js
+++ b/front/src/router/index.js
@@ -47,7 +47,11 @@ export default new Router({
     },
     {
       path: '/favorites',
-      component: Favorites
+      component: Favorites,
+      props: (route) => ({
+        defaultOrdering: route.query.ordering,
+        defaultPage: route.query.page
+      })
     },
     {
       path: '/library',
-- 
GitLab