From ccca292405d1536778bd0caa466430d4e6694324 Mon Sep 17 00:00:00 2001
From: Eliot Berriot <contact@eliotberriot.com>
Date: Tue, 16 Jul 2019 14:50:39 +0200
Subject: [PATCH] See #432: added tag filter on artist/album browse page

---
 front/package.json                            |  1 +
 front/src/components/library/Albums.vue       | 31 ++++++++++++++++---
 front/src/components/library/Artists.vue      | 31 ++++++++++++++++---
 front/src/components/library/TagsSelector.vue |  2 +-
 front/src/router/index.js                     |  2 ++
 front/yarn.lock                               |  2 +-
 6 files changed, 59 insertions(+), 10 deletions(-)

diff --git a/front/package.json b/front/package.json
index 54cf3c8e..2c8f041f 100644
--- a/front/package.json
+++ b/front/package.json
@@ -22,6 +22,7 @@
     "masonry-layout": "^4.2.2",
     "moment": "^2.22.2",
     "fomantic-ui-css": "^2.7",
+    "qs": "^6.7.0",
     "showdown": "^1.8.6",
     "vue": "^2.5.17",
     "vue-gettext": "^2.1.0",
diff --git a/front/src/components/library/Albums.vue b/front/src/components/library/Albums.vue
index 9817af83..ed97f7a4 100644
--- a/front/src/components/library/Albums.vue
+++ b/front/src/components/library/Albums.vue
@@ -20,6 +20,10 @@
               </option>
             </select>
           </div>
+          <div class="field">
+            <label><translate translate-context="*/*/*/Noun">Tags</translate></label>
+            <tags-selector v-model="tags"></tags-selector>
+          </div>
           <div class="field">
             <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label>
             <select class="ui dropdown" v-model="orderingDirection">
@@ -70,6 +74,7 @@
 </template>
 
 <script>
+import qs from 'qs'
 import axios from "axios"
 import _ from "@/lodash"
 import $ from "jquery"
@@ -81,17 +86,20 @@ import PaginationMixin from "@/components/mixins/Pagination"
 import TranslationsMixin from "@/components/mixins/Translations"
 import AlbumCard from "@/components/audio/album/Card"
 import Pagination from "@/components/Pagination"
+import TagsSelector from '@/components/library/TagsSelector'
 
 const FETCH_URL = "albums/"
 
 export default {
   mixins: [OrderingMixin, PaginationMixin, TranslationsMixin],
   props: {
-    defaultQuery: { type: String, required: false, default: "" }
+    defaultQuery: { type: String, required: false, default: "" },
+    defaultTags: { type: Array, required: false, default: () => { return [] } },
   },
   components: {
     AlbumCard,
-    Pagination
+    Pagination,
+    TagsSelector,
   },
   data() {
     let defaultOrdering = this.getOrderingFromString(
@@ -102,6 +110,7 @@ export default {
       result: null,
       page: parseInt(this.defaultPage),
       query: this.defaultQuery,
+      tags: this.defaultTags.filter((t) => { return t.length > 0 }) || [],
       paginateBy: parseInt(this.defaultPaginateBy || 25),
       orderingDirection: defaultOrdering.direction || "+",
       ordering: defaultOrdering.field,
@@ -130,6 +139,7 @@ export default {
         query: {
           query: this.query,
           page: this.page,
+          tag: this.tags,
           paginateBy: this.paginateBy,
           ordering: this.getOrderingAsString()
         }
@@ -144,10 +154,19 @@ export default {
         page_size: this.paginateBy,
         q: this.query,
         ordering: this.getOrderingAsString(),
-        playable: "true"
+        playable: "true",
+        tag: this.tags,
       }
       logger.default.debug("Fetching albums")
-      axios.get(url, { params: params }).then(response => {
+      axios.get(
+        url,
+        {
+          params: params,
+          paramsSerializer: function(params) {
+            return qs.stringify(params, { indices: false })
+          }
+        }
+      ).then(response => {
         self.result = response.data
         self.isLoading = false
       })
@@ -177,6 +196,10 @@ export default {
       this.updateQueryString()
       this.fetchData()
     },
+    tags() {
+      this.updateQueryString()
+      this.fetchData()
+    },
     "$store.state.moderation.lastUpdate": function () {
       this.fetchData()
     }
diff --git a/front/src/components/library/Artists.vue b/front/src/components/library/Artists.vue
index 5f4102ab..f16a6740 100644
--- a/front/src/components/library/Artists.vue
+++ b/front/src/components/library/Artists.vue
@@ -12,6 +12,10 @@
             </label>
             <input type="text" name="search" v-model="query" :placeholder="labels.searchPlaceholder"/>
           </div>
+          <div class="field">
+            <label><translate translate-context="*/*/*/Noun">Tags</translate></label>
+            <tags-selector v-model="tags"></tags-selector>
+          </div>
           <div class="field">
             <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label>
             <select class="ui dropdown" v-model="ordering">
@@ -67,6 +71,7 @@
 </template>
 
 <script>
+import qs from 'qs'
 import axios from "axios"
 import _ from "@/lodash"
 import $ from "jquery"
@@ -78,17 +83,20 @@ import PaginationMixin from "@/components/mixins/Pagination"
 import TranslationsMixin from "@/components/mixins/Translations"
 import ArtistCard from "@/components/audio/artist/Card"
 import Pagination from "@/components/Pagination"
+import TagsSelector from '@/components/library/TagsSelector'
 
 const FETCH_URL = "artists/"
 
 export default {
   mixins: [OrderingMixin, PaginationMixin, TranslationsMixin],
   props: {
-    defaultQuery: { type: String, required: false, default: "" }
+    defaultQuery: { type: String, required: false, default: "" },
+    defaultTags: { type: Array, required: false, default: () => { return [] } },
   },
   components: {
     ArtistCard,
-    Pagination
+    Pagination,
+    TagsSelector,
   },
   data() {
     let defaultOrdering = this.getOrderingFromString(
@@ -99,6 +107,7 @@ export default {
       result: null,
       page: parseInt(this.defaultPage),
       query: this.defaultQuery,
+      tags: this.defaultTags.filter((t) => { return t.length > 0 }) || [],
       paginateBy: parseInt(this.defaultPaginateBy || 12),
       orderingDirection: defaultOrdering.direction || "+",
       ordering: defaultOrdering.field,
@@ -127,6 +136,7 @@ export default {
         query: {
           query: this.query,
           page: this.page,
+          tag: this.tags,
           paginateBy: this.paginateBy,
           ordering: this.getOrderingAsString()
         }
@@ -141,10 +151,19 @@ export default {
         page_size: this.paginateBy,
         name__icontains: this.query,
         ordering: this.getOrderingAsString(),
-        playable: "true"
+        playable: "true",
+        tag: this.tags,
       }
       logger.default.debug("Fetching artists")
-      axios.get(url, { params: params }).then(response => {
+      axios.get(
+        url,
+        {
+          params: params,
+          paramsSerializer: function(params) {
+            return qs.stringify(params, { indices: false })
+          }
+        }
+      ).then(response => {
         self.result = response.data
         self.isLoading = false
       })
@@ -174,6 +193,10 @@ export default {
       this.updateQueryString()
       this.fetchData()
     },
+    tags() {
+      this.updateQueryString()
+      this.fetchData()
+    },
     "$store.state.moderation.lastUpdate": function () {
       this.fetchData()
     }
diff --git a/front/src/components/library/TagsSelector.vue b/front/src/components/library/TagsSelector.vue
index 5494fe9f..47cb83fa 100644
--- a/front/src/components/library/TagsSelector.vue
+++ b/front/src/components/library/TagsSelector.vue
@@ -4,7 +4,7 @@
     <i class="dropdown icon"></i>
     <input type="text" class="search">
     <div class="default text">
-      <translate translate-context="*/Dropdown/Placeholder/Verb">Search for existing tags…</translate>
+      <translate translate-context="*/Dropdown/Placeholder/Verb">Search for tags…</translate>
     </div>
   </div>
 </template>
diff --git a/front/src/router/index.js b/front/src/router/index.js
index 6a9ba611..630d4890 100644
--- a/front/src/router/index.js
+++ b/front/src/router/index.js
@@ -401,6 +401,7 @@ export default new Router({
           props: (route) => ({
             defaultOrdering: route.query.ordering,
             defaultQuery: route.query.query,
+            defaultTags: Array.isArray(route.query.tag || []) ? route.query.tag : [route.query.tag],
             defaultPaginateBy: route.query.paginateBy,
             defaultPage: route.query.page
           })
@@ -413,6 +414,7 @@ export default new Router({
           props: (route) => ({
             defaultOrdering: route.query.ordering,
             defaultQuery: route.query.query,
+            defaultTags: Array.isArray(route.query.tag || []) ? route.query.tag : [route.query.tag],
             defaultPaginateBy: route.query.paginateBy,
             defaultPage: route.query.page
           })
diff --git a/front/yarn.lock b/front/yarn.lock
index 1959e9e1..68e12838 100644
--- a/front/yarn.lock
+++ b/front/yarn.lock
@@ -7324,7 +7324,7 @@ q@^1.1.2:
   resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
   integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
 
-qs@6.7.0:
+qs@6.7.0, qs@^6.7.0:
   version "6.7.0"
   resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
   integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
-- 
GitLab