From 27538ccd3443ea519ff8b48ba74e2e2dcbfb405b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?M=C3=A9lanie=20Chauvel?= <perso@hack-libre.org>
Date: Sat, 7 Dec 2019 17:06:24 +0100
Subject: [PATCH] =?UTF-8?q?Add=20field=20to=20filter=20existing=20playlist?=
 =?UTF-8?q?s=20in=20=E2=80=9Cadd=20to=20playlist=E2=80=A6=E2=80=9D=20dialo?=
 =?UTF-8?q?g?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Also fix the duplicate track add confirmation margins
Fix #974
---
 changes/changelog.d/974.enhancement           |  1 +
 .../components/playlists/PlaylistModal.vue    | 69 +++++++++++++------
 2 files changed, 50 insertions(+), 20 deletions(-)
 create mode 100644 changes/changelog.d/974.enhancement

diff --git a/changes/changelog.d/974.enhancement b/changes/changelog.d/974.enhancement
new file mode 100644
index 0000000000..0d59fbd0a2
--- /dev/null
+++ b/changes/changelog.d/974.enhancement
@@ -0,0 +1 @@
+Support filtering playlist by name and several additional UX improvements in playlists modal (#974)
diff --git a/front/src/components/playlists/PlaylistModal.vue b/front/src/components/playlists/PlaylistModal.vue
index 714d9feba5..7ada395b60 100644
--- a/front/src/components/playlists/PlaylistModal.vue
+++ b/front/src/components/playlists/PlaylistModal.vue
@@ -1,29 +1,30 @@
 <template>
   <modal @update:show="update" :show="$store.state.playlists.showModal">
     <div class="header">
-      <translate translate-context="Popup/Playlist/Title/Verb">Manage playlists</translate>
-    </div>
-    <div class="scrolling content">
-      <div class="description">
-        <template v-if="track">
-          <h4 class="ui header"><translate translate-context="Popup/Playlist/Title">Current track</translate></h4>
-          <span
+      <template v-if="track">
+        <h2 class="ui header">
+          <translate translate-context="Popup/Playlist/Title/Verb">Add to playlist</translate>
+          <div
+            class="ui sub header"
             translate-context="Popup/Playlist/Paragraph"
             v-translate="{artist: track.artist.name, title: track.title}"
             :translate-params="{artist: track.artist.name, title: track.title}">
             "%{ title }", by %{ artist }
-          </span>
-          <div class="ui divider"></div>
-        </template>
-
-        <playlist-form :key="formKey"></playlist-form>
-        <div class="ui divider"></div>
+          </div>
+        </h2>
+      </template>
+      <translate v-else translate-context="Popup/Playlist/Title/Verb">Manage playlists</translate>
+    </div>
+    <div class="scrolling content">
+      <playlist-form :key="formKey"></playlist-form>
+      <div class="ui divider"></div>
+      <div v-if="playlists.length > 0">
         <div v-if="showDuplicateTrackAddConfirmation" class="ui warning message">
           <p translate-context="Popup/Playlist/Paragraph"
             v-translate="{track: track.title, playlist: duplicateTrackAddInfo.playlist_name}"
             :translate-params="{track: track.title, playlist: duplicateTrackAddInfo.playlist_name}"><strong>%{ track }</strong> is already in <strong>%{ playlist }</strong>.</p>
           <button
-            @click="update(false)"
+            @click="duplicateTrackAddConfirm(false)"
             class="ui small cancel button"><translate translate-context="*/*/Button.Label/Verb">Cancel</translate>
           </button>
           <button
@@ -37,10 +38,16 @@
             <li v-for="error in errors">{{ error }}</li>
           </ul>
         </div>
-        </div>
-        <div v-if="playlists.length > 0">
         <h4 class="ui header"><translate translate-context="Popup/Playlist/Title">Available playlists</translate></h4>
-        <table class="ui unstackable very basic table">
+        <div class="ui form">
+          <div class="fields">
+            <div class="field">
+              <label for="playlist-name-filter"><translate translate-context="Popup/Playlist/Label">Filter</translate></label>
+              <input name="playlist-name-filter" v-model="playlistNameFilter" type="text" class="inline" :placeholder="labels.filterPlaylistField" />
+            </div>
+          </div>
+        </div>
+        <table v-if="sortedPlaylists.length > 0" class="ui unstackable very basic table">
           <thead>
             <tr>
               <th></th>
@@ -73,6 +80,13 @@
             </tr>
           </tbody>
         </table>
+        <template v-else>
+          <div class="ui small placeholder segment">
+            <div class="ui header">
+               <translate translate-context="Popup/Playlist/EmptyState">No results matching your filter</translate>
+            </div>
+          </div>
+        </template>
         </div>
         <template v-else>
           <div class="ui placeholder segment">
@@ -93,7 +107,10 @@
 </template>
 
 <script>
-import _ from '@/lodash'
+import filter from "lodash/fp/filter";
+import sortBy from "lodash/fp/sortBy";
+import flow from "lodash/fp/flow";
+
 import axios from 'axios'
 import {mapState} from 'vuex'
 
@@ -110,6 +127,7 @@ export default {
     return {
       formKey: String(new Date()),
       errors: [],
+      playlistNameFilter: '',
       duplicateTrackAddInfo: {},
       showDuplicateTrackAddConfirmation: false,
       lastSelectedPlaylist: -1,
@@ -142,6 +160,9 @@ export default {
           self.showDuplicateTrackAddConfirmation = false
         }
       })
+    },
+    duplicateTrackAddConfirm (v) {
+      this.showDuplicateTrackAddConfirmation = v
     }
   },
   computed: {
@@ -151,11 +172,16 @@ export default {
     }),
     labels () {
       return {
-        addToPlaylist: this.$pgettext('Popup/Playlist/Table.Button.Tooltip/Verb', 'Add to this playlist')
+        addToPlaylist: this.$pgettext('Popup/Playlist/Table.Button.Tooltip/Verb', 'Add to this playlist'),
+        filterPlaylistField: this.$pgettext('Popup/Playlist/Form/Placeholder', 'Enter playlist name')
       }
     },
     sortedPlaylists () {
-      let p = _.sortBy(this.playlists, [(e) => { return e.modification_date }])
+      let regexp = new RegExp(this.playlistNameFilter, 'i');
+      let p = flow(
+        filter((e) => e.name.match(regexp) !== null),
+        sortBy((e) => { return e.modification_date }),
+      )(this.playlists)
       p.reverse()
       return p
     }
@@ -175,4 +201,7 @@ export default {
 
 <!-- Add "scoped" attribute to limit CSS to this component only -->
 <style scoped>
+.ui.small.placeholder.segment {
+  min-height: auto;
+}
 </style>
-- 
GitLab