diff --git a/platypush/backend/http/static/css/source/webpanel/plugins/music.mpd/index.scss b/platypush/backend/http/static/css/source/webpanel/plugins/music.mpd/index.scss index b7e5e25da5..7216a9f1c8 100644 --- a/platypush/backend/http/static/css/source/webpanel/plugins/music.mpd/index.scss +++ b/platypush/backend/http/static/css/source/webpanel/plugins/music.mpd/index.scss @@ -21,13 +21,13 @@ &.selected { background: $selected-bg !important; } .artist { - font-size: $artist-font-size; + font-size: .9em; } } * > .duration { color: $duration-color; - font-size: $duration-font-size; + font-size: .7em; } * > button { @@ -65,7 +65,7 @@ background: $browser-panel-bg; border-right: $default-border-2; padding: .3rem 1rem 6rem 1rem; - font-size: $browser-font-size; + font-size: .9em; .item { background: none; @@ -163,6 +163,15 @@ .artist { font-weight: bold; } + + a { + color: initial; + text-decoration: none; + + &:hover { + color: $track-info-hover-color; + } + } } } @@ -226,8 +235,8 @@ * > .elapsed-time, * > .total-time { - font-size: $control-time-font-size; - color: $control-time-color; + font-size: .7em; + color: .7em; } * > .elapsed-time { @@ -267,10 +276,6 @@ } } - .dropdown { - z-index: 503; - } - .results-controls { position: fixed; padding: 0; @@ -322,6 +327,12 @@ } } +#music-mpd-search-modal { + .dropdown { + z-index: 503; + } +} + @media #{map-get($widths, 's')} { #music-mpd-info { .modal { diff --git a/platypush/backend/http/static/css/source/webpanel/plugins/music.mpd/vars.scss b/platypush/backend/http/static/css/source/webpanel/plugins/music.mpd/vars.scss index ea7803d9ee..4636494502 100644 --- a/platypush/backend/http/static/css/source/webpanel/plugins/music.mpd/vars.scss +++ b/platypush/backend/http/static/css/source/webpanel/plugins/music.mpd/vars.scss @@ -2,18 +2,13 @@ $button-enabled-color: #59df3e; $button-hover-color: $button-enabled-color; $play-button-hover-color: #64ef4a; -$artist-font-size: $font-size * 0.9333; - $duration-color: #666; -$duration-font-size: $font-size * 0.86666; $control-panel-bg: rgba(245,245,245,0.95); $control-panel-shadow: 0 -2.5px 4px 0 #c0c0c0; $control-time-color: #666; -$control-time-font-size: $font-size * 0.666666; $browser-panel-bg: rgba(248,250,250,0.95); -$browser-font-size: $font-size * 0.8666; $empty-playlist-color: rgba(200,200,200,0.7); $empty-playlist-shadow: 2px 1px rgb(235,235,235); @@ -30,4 +25,5 @@ $search-modal-footer-border: 1px solid #ccc; $info-modal-row-border: 1px solid #ddd; $info-modal-attr-color: #777; +$track-info-hover-color: rgb(46,190,110); diff --git a/platypush/backend/http/static/js/elements/modal.js b/platypush/backend/http/static/js/elements/modal.js index 6d60078256..3a403d9b97 100644 --- a/platypush/backend/http/static/js/elements/modal.js +++ b/platypush/backend/http/static/js/elements/modal.js @@ -69,6 +69,28 @@ Vue.component('modal', { this.prevValue = this.value; } + if (this.value) { + // Make sure that a newly opened or visible+updated modal always comes to the front + const myZIndex = parseInt(getComputedStyle(this.$el).zIndex); + var maxZIndex = myZIndex; + var outermostModals = []; + + for (const modal of document.querySelectorAll('.modal-container:not(.hidden)')) { + const zIndex = parseInt(getComputedStyle(modal).zIndex); + + if (zIndex > maxZIndex) { + maxZIndex = zIndex; + outermostModals = [modal]; + } else if (zIndex == maxZIndex) { + outermostModals.push(modal); + } + } + + if (outermostModals.indexOf(this.$el) < 0 || outermostModals.length > 1) { + this.$el.style.zIndex = maxZIndex+1; + } + } + if (this.value && this.timeout && !this.timeoutId) { var handler = (self) => { return () => { diff --git a/platypush/backend/http/static/js/plugins/music.mpd/index.js b/platypush/backend/http/static/js/plugins/music.mpd/index.js index c306872868..9d037794b7 100644 --- a/platypush/backend/http/static/js/plugins/music.mpd/index.js +++ b/platypush/backend/http/static/js/plugins/music.mpd/index.js @@ -46,14 +46,39 @@ Vue.component('music-mpd', { var items = []; if (Object.keys(this.selectedPlaylistItems).length === 1) { + const track = Object.values(this.selectedPlaylistItems)[0]; + items.push({ - text: 'Play', - icon: 'play', - click: async function() { - await self.playpos(); - self.selectedPlaylistItems = {}; - }, - }); + text: 'Play', + icon: 'play', + click: async function() { + await self.playpos(); + self.selectedPlaylistItems = {}; + }, + } + ); + + if (track.artist && track.artist.length) { + items.push({ + text: 'View artist', + icon: 'user', + click: async function() { + await self.searchArtist(track); + self.selectedPlaylistItems = {}; + } + }); + } + + if (track.album && track.album.length) { + items.push({ + text: 'View album', + icon: 'compact-disc', + click: async function() { + await self.searchAlbum(track); + self.selectedPlaylistItems = {}; + }, + }); + } } items.push({ @@ -85,8 +110,7 @@ Vue.component('music-mpd', { text: 'View track info', icon: 'info', click: async function() { - self.infoItem = Object.values(self.selectedPlaylistItems)[0]; - self.modalVisible.info = true; + await self.info(Object.values(self.selectedPlaylistItems)[0]); }, }); } @@ -111,6 +135,8 @@ Vue.component('music-mpd', { } if (Object.keys(this.selectedBrowserItems).length === 1) { + const item = Object.values(this.selectedBrowserItems)[0]; + items.push( { text: 'Play', @@ -169,6 +195,28 @@ Vue.component('music-mpd', { }, } ); + + if (item.artist && item.artist.length) { + items.push({ + text: 'View artist', + icon: 'user', + click: async function() { + await self.searchArtist(item); + self.selectedPlaylistItems = {}; + } + }); + } + + if (item.album && item.album.length) { + items.push({ + text: 'View album', + icon: 'compact-disc', + click: async function() { + await self.searchAlbum(item); + self.selectedPlaylistItems = {}; + }, + }); + } } items.push( @@ -547,6 +595,41 @@ Vue.component('music-mpd', { this._parseBrowserItems(items); }, + info: async function(item) { + var info = item; + + if (typeof(item) === 'string') { + item = await request('music.mpd.search', {filter: {file: info}}); + } + + this.infoItem = item; + this.modalVisible.info = true; + }, + + searchArtist: async function(item) { + await this.search({artist: item.artist}); + }, + + searchAlbum: async function(item) { + var query = {}; + + if (item['x-albumuri']) { + query.file = item['x-albumuri']; + } else { + query.artist = item.albumartist || item.artist; + query.album = item.album; + } + + await this.search(query); + }, + + search: async function(query) { + this.$refs.search.resetQuery(); + this.$refs.search.query = query; + this.$refs.search.visible = true; + await this.$refs.search.search(); + }, + onNewPlayingTrack: async function(event) { var previousTrack = { file: this.track.file, @@ -791,7 +874,21 @@ Vue.component('music-mpd', { scrollToActiveTrack: function() { if (this.$refs.activePlaylistTrack && this.$refs.activePlaylistTrack.length) { this.$refs.activePlaylistTrack[0].$el.scrollIntoView({behavior: 'smooth'}); + } else { + return; } + + const self = this; + setTimeout(() => { + var parent = self.$refs.activePlaylistTrack[0].$el.parentElement; + if (parent.clientHeight + parent.scrollTop < parent.scrollHeight) { + if (parent.scrollTop-50 > 0) { + parent.scrollTop -= 50; + } else { + parent.scrollTop = 0; + } + } + }, 750); }, addToPlaylistPrompt: async function() { diff --git a/platypush/backend/http/static/js/plugins/music.mpd/search.js b/platypush/backend/http/static/js/plugins/music.mpd/search.js index c9abb07dce..a81ba63ef7 100644 --- a/platypush/backend/http/static/js/plugins/music.mpd/search.js +++ b/platypush/backend/http/static/js/plugins/music.mpd/search.js @@ -5,13 +5,14 @@ Vue.component('music-mpd-search', { return { visible: false, showResults: false, - results: false, + results: [], filter: '', selectionMode: false, selectedItems: {}, query: { any: '', + file: '', artist: '', title: '', album: '', @@ -66,9 +67,36 @@ Vue.component('music-mpd-search', { ); if (Object.keys(this.selectedItems).length === 1) { + const item = Object.values(this.selectedItems)[0]; + + if (item.artist && item.artist.length) { + items.push({ + text: 'View artist', + icon: 'user', + click: async function() { + await self.mpd.searchArtist(item); + self.selectedItems = {}; + } + }); + } + + if (item.album && item.album.length) { + items.push({ + text: 'View album', + icon: 'compact-disc', + click: async function() { + await self.mpd.searchAlbum(item); + self.selectedItems = {}; + }, + }); + } + items.push({ text: 'View info', icon: 'info', + click: function() { + self.$emit('info', item); + }, }); } @@ -86,7 +114,9 @@ Vue.component('music-mpd-search', { return items; }, []); + this.results = []; var results = await request('music.mpd.search', {filter: filter}); + this.results = results.sort((a,b) => { const tokenize = (t) => { return ''.concat(t.artist || '', '-', t.album || '', '-', t.disc || '', '-', t.track || '', t.title || '').toLocaleLowerCase(); @@ -135,11 +165,18 @@ Vue.component('music-mpd-search', { this.selectionMode = true; for (var item of this.results) { - this.selectedItems[item.id] = item; + this.selectedItems[item.file] = item; } openDropdown(this.$refs.dropdown.$el); }, + + resetQuery: function() { + this.filter = ''; + for (const attr of Object.keys(this.query)) { + this.query[attr] = ''; + } + }, }, }); diff --git a/platypush/backend/http/templates/plugins/music.mpd/index.html b/platypush/backend/http/templates/plugins/music.mpd/index.html index fd735ebf8d..5675c85cd7 100644 --- a/platypush/backend/http/templates/plugins/music.mpd/index.html +++ b/platypush/backend/http/templates/plugins/music.mpd/index.html @@ -6,7 +6,10 @@