Add field to filter existing playlists in “add to playlist…” dialog
Also fix the duplicate track add confirmation margins Fix #974
This commit is contained in:
parent
2d9c235890
commit
27538ccd34
|
@ -0,0 +1 @@
|
||||||
|
Support filtering playlist by name and several additional UX improvements in playlists modal (#974)
|
|
@ -1,29 +1,30 @@
|
||||||
<template>
|
<template>
|
||||||
<modal @update:show="update" :show="$store.state.playlists.showModal">
|
<modal @update:show="update" :show="$store.state.playlists.showModal">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<translate translate-context="Popup/Playlist/Title/Verb">Manage playlists</translate>
|
<template v-if="track">
|
||||||
</div>
|
<h2 class="ui header">
|
||||||
<div class="scrolling content">
|
<translate translate-context="Popup/Playlist/Title/Verb">Add to playlist</translate>
|
||||||
<div class="description">
|
<div
|
||||||
<template v-if="track">
|
class="ui sub header"
|
||||||
<h4 class="ui header"><translate translate-context="Popup/Playlist/Title">Current track</translate></h4>
|
|
||||||
<span
|
|
||||||
translate-context="Popup/Playlist/Paragraph"
|
translate-context="Popup/Playlist/Paragraph"
|
||||||
v-translate="{artist: track.artist.name, title: track.title}"
|
v-translate="{artist: track.artist.name, title: track.title}"
|
||||||
:translate-params="{artist: track.artist.name, title: track.title}">
|
:translate-params="{artist: track.artist.name, title: track.title}">
|
||||||
"%{ title }", by %{ artist }
|
"%{ title }", by %{ artist }
|
||||||
</span>
|
</div>
|
||||||
<div class="ui divider"></div>
|
</h2>
|
||||||
</template>
|
</template>
|
||||||
|
<translate v-else translate-context="Popup/Playlist/Title/Verb">Manage playlists</translate>
|
||||||
<playlist-form :key="formKey"></playlist-form>
|
</div>
|
||||||
<div class="ui divider"></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">
|
<div v-if="showDuplicateTrackAddConfirmation" class="ui warning message">
|
||||||
<p translate-context="Popup/Playlist/Paragraph"
|
<p translate-context="Popup/Playlist/Paragraph"
|
||||||
v-translate="{track: track.title, playlist: duplicateTrackAddInfo.playlist_name}"
|
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>
|
:translate-params="{track: track.title, playlist: duplicateTrackAddInfo.playlist_name}"><strong>%{ track }</strong> is already in <strong>%{ playlist }</strong>.</p>
|
||||||
<button
|
<button
|
||||||
@click="update(false)"
|
@click="duplicateTrackAddConfirm(false)"
|
||||||
class="ui small cancel button"><translate translate-context="*/*/Button.Label/Verb">Cancel</translate>
|
class="ui small cancel button"><translate translate-context="*/*/Button.Label/Verb">Cancel</translate>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
@ -37,10 +38,16 @@
|
||||||
<li v-for="error in errors">{{ error }}</li>
|
<li v-for="error in errors">{{ error }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div v-if="playlists.length > 0">
|
|
||||||
<h4 class="ui header"><translate translate-context="Popup/Playlist/Title">Available playlists</translate></h4>
|
<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>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th></th>
|
<th></th>
|
||||||
|
@ -73,6 +80,13 @@
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</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>
|
</div>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="ui placeholder segment">
|
<div class="ui placeholder segment">
|
||||||
|
@ -93,7 +107,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<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 axios from 'axios'
|
||||||
import {mapState} from 'vuex'
|
import {mapState} from 'vuex'
|
||||||
|
|
||||||
|
@ -110,6 +127,7 @@ export default {
|
||||||
return {
|
return {
|
||||||
formKey: String(new Date()),
|
formKey: String(new Date()),
|
||||||
errors: [],
|
errors: [],
|
||||||
|
playlistNameFilter: '',
|
||||||
duplicateTrackAddInfo: {},
|
duplicateTrackAddInfo: {},
|
||||||
showDuplicateTrackAddConfirmation: false,
|
showDuplicateTrackAddConfirmation: false,
|
||||||
lastSelectedPlaylist: -1,
|
lastSelectedPlaylist: -1,
|
||||||
|
@ -142,6 +160,9 @@ export default {
|
||||||
self.showDuplicateTrackAddConfirmation = false
|
self.showDuplicateTrackAddConfirmation = false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
duplicateTrackAddConfirm (v) {
|
||||||
|
this.showDuplicateTrackAddConfirmation = v
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -151,11 +172,16 @@ export default {
|
||||||
}),
|
}),
|
||||||
labels () {
|
labels () {
|
||||||
return {
|
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 () {
|
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()
|
p.reverse()
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
@ -175,4 +201,7 @@ export default {
|
||||||
|
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.ui.small.placeholder.segment {
|
||||||
|
min-height: auto;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue