diff --git a/changes/changelog.d/1131.enhancement b/changes/changelog.d/1131.enhancement new file mode 100644 index 0000000000000000000000000000000000000000..eb2f25b0b8525290442f474190fab2c507ed4499 --- /dev/null +++ b/changes/changelog.d/1131.enhancement @@ -0,0 +1 @@ +More consistent search UX on /albums, /artists, /radios and /playlists (#1131) \ No newline at end of file diff --git a/changes/changelog.d/407.enhancement b/changes/changelog.d/407.enhancement new file mode 100644 index 0000000000000000000000000000000000000000..c21a34063e1b6b1930c7af15a5cfd5d72f218bb4 --- /dev/null +++ b/changes/changelog.d/407.enhancement @@ -0,0 +1 @@ +Confirm email without requiring the user to validate the form manually (#407) \ No newline at end of file diff --git a/front/src/EmbedFrame.vue b/front/src/EmbedFrame.vue index a8dcce4fa01d9511831cf4cbf4936d97c42b8e58..afee7c82f927d8d79cd7fe84afe4c81cfc31eba8 100644 --- a/front/src/EmbedFrame.vue +++ b/front/src/EmbedFrame.vue @@ -89,7 +89,6 @@ <tr :id="'queue-item-' + index" role="button" - tabindex="0" v-if="track.sources.length > 0" :key="index" :class="[{active: index === currentIndex}]" diff --git a/front/src/components/audio/PlayButton.vue b/front/src/components/audio/PlayButton.vue index de299a383f8e537ce18e8ca1e3d8f888cff370a0..1a46b5774d69a6f45bfd0cf74cf1b936c18ce98e 100644 --- a/front/src/components/audio/PlayButton.vue +++ b/front/src/components/audio/PlayButton.vue @@ -107,10 +107,12 @@ export default { return this.track.uploads && this.track.uploads.length > 0 } else if (this.artist && this.artist.tracks_count) { return this.artist.tracks_count > 0 - } else if (this.artist && this.artist.albums) { + } else if (this.artist && this.artist.albums) { return this.artist.albums.filter((a) => { return a.is_playable === true }).length > 0 + } else if (this.album) { + return this.album.is_playable } else if (this.tracks) { return this.tracks.filter((t) => { return t.uploads && t.uploads.length > 0 @@ -229,6 +231,7 @@ export default { jQuery(self.$el).find('.ui.dropdown').dropdown('hide') }, addNext (next) { + console.log('CLICKED') let self = this let wasEmpty = this.$store.state.queue.tracks.length === 0 this.getPlayableTracks().then((tracks) => { @@ -253,7 +256,6 @@ export default { }, watch: { clicked () { - let self = this this.$nextTick(() => { jQuery(this.$el).find('.ui.dropdown').dropdown({ diff --git a/front/src/components/auth/LoginForm.vue b/front/src/components/auth/LoginForm.vue index f0591a5385d36c59dd64fc556c5e5fe6ccb03abe..3f6eb0edb1a6720f4eed19b6a38a87d988f5391c 100644 --- a/front/src/components/auth/LoginForm.vue +++ b/front/src/components/auth/LoginForm.vue @@ -40,7 +40,7 @@ <translate translate-context="*/Login/*/Verb">Reset your password</translate> </router-link> </label> - <password-input :index="2" required v-model="credentials.password" /> + <password-input required v-model="credentials.password" /> </div> </template> diff --git a/front/src/components/forms/PasswordInput.vue b/front/src/components/forms/PasswordInput.vue index fe1a01a9ff688a599f6ca96af9061ecc12e675b5..2e4f227a59f879006d97846ae2d7a99002e85ec8 100644 --- a/front/src/components/forms/PasswordInput.vue +++ b/front/src/components/forms/PasswordInput.vue @@ -3,7 +3,6 @@ <input required name="password" - :tabindex="index" :type="passwordInputType" @input="$emit('input', $event.target.value)" :value="value"> @@ -30,7 +29,7 @@ function copyStringToClipboard (str) { } export default { - props: ['value', 'index', 'defaultShow', 'copyButton'], + props: ['value', 'defaultShow', 'copyButton'], data () { return { showPassword: this.defaultShow || false, diff --git a/front/src/components/library/AlbumBase.vue b/front/src/components/library/AlbumBase.vue index 071b133574ef69a0db0b26a1afdc85addb4b5f42..7b572e145616ba0435b96db10a9dc3b22c1f7b68 100644 --- a/front/src/components/library/AlbumBase.vue +++ b/front/src/components/library/AlbumBase.vue @@ -169,13 +169,16 @@ export default { methods: { async fetchData() { this.isLoading = true + let tracksResponse = axios.get(`tracks/`, {params: {ordering: 'disc_number,position', album: this.id, page_size: 100}}) let albumResponse = await axios.get(`albums/${this.id}/`, {params: {refresh: 'true'}}) let artistResponse = await axios.get(`artists/${albumResponse.data.artist.id}/`) this.artist = artistResponse.data if (this.artist.channel) { this.artist.channel.artist = this.artist } - this.object = backend.Album.clean(albumResponse.data) + tracksResponse = await tracksResponse + this.object = albumResponse.data + this.object.tracks = tracksResponse.data.results this.discs = this.object.tracks.reduce(groupByDisc, []) this.isLoading = false diff --git a/front/src/components/library/Albums.vue b/front/src/components/library/Albums.vue index 60bafe245141b9b44370a97c54ba8ec4caadd744..647e37b475315a49c8dbaccea20cf22777807620 100644 --- a/front/src/components/library/Albums.vue +++ b/front/src/components/library/Albums.vue @@ -4,13 +4,18 @@ <h2 class="ui header"> <translate translate-context="Content/Album/Title">Browsing albums</translate> </h2> - <div :class="['ui', {'loading': isLoading}, 'form']"> + <form :class="['ui', {'loading': isLoading}, 'form']" @submit.prevent="updateQueryString();fetchData()"> <div class="fields"> <div class="field"> <label> <translate translate-context="Content/Search/Input.Label/Noun">Search</translate> </label> - <input type="text" name="search" v-model="query" :placeholder="labels.searchPlaceholder"/> + <div class="ui action input"> + <input type="text" name="search" v-model="query" :placeholder="labels.searchPlaceholder"/> + <button class="ui icon button" type="submit" :aria-label="$pgettext('Content/Search/Input.Label/Noun', 'Search')"> + <i class="search icon"></i> + </button> + </div> </div> <div class="field"> <label><translate translate-context="*/*/*/Noun">Tags</translate></label> @@ -40,7 +45,7 @@ </select> </div> </div> - </div> + </form> <div class="ui hidden divider"></div> <div v-if="result" @@ -144,7 +149,7 @@ export default { } }, methods: { - updateQueryString: _.debounce(function() { + updateQueryString: function() { history.pushState( {}, null, @@ -157,8 +162,8 @@ export default { ordering: this.getOrderingAsString() }).toString() ) - }, 500), - fetchData: _.debounce(function() { + }, + fetchData: function() { var self = this this.isLoading = true let url = FETCH_URL @@ -187,7 +192,7 @@ export default { self.result = null self.isLoading = false }) - }, 500), + }, selectPage: function(page) { this.page = page } @@ -197,26 +202,6 @@ export default { this.updateQueryString() this.fetchData() }, - paginateBy() { - this.updateQueryString() - this.fetchData() - }, - ordering() { - this.updateQueryString() - this.fetchData() - }, - orderingDirection() { - this.updateQueryString() - this.fetchData() - }, - query() { - this.updateQueryString() - this.fetchData() - }, - tags() { - this.updateQueryString() - this.fetchData() - }, "$store.state.moderation.lastUpdate": function () { this.fetchData() } diff --git a/front/src/components/library/ArtistBase.vue b/front/src/components/library/ArtistBase.vue index fefadd60b806b78e2bf4da6ef703b7951e2250f9..725d91bbec607a392aee7de1132143c74b3edc54 100644 --- a/front/src/components/library/ArtistBase.vue +++ b/front/src/components/library/ArtistBase.vue @@ -195,9 +195,7 @@ export default { self.nextAlbumsUrl = response.data.next self.totalAlbums = response.data.count let parsed = JSON.parse(JSON.stringify(response.data.results)) - self.albums = parsed.map(album => { - return backend.Album.clean(album) - }) + self.albums = parsed }) await trackPromise diff --git a/front/src/components/library/Artists.vue b/front/src/components/library/Artists.vue index 19e22089bf8f798456504c08e9ba11a6d8ce7da7..98d06e77630eb1998cd17fe818d780d73fff0d29 100644 --- a/front/src/components/library/Artists.vue +++ b/front/src/components/library/Artists.vue @@ -4,13 +4,18 @@ <h2 class="ui header"> <translate translate-context="Content/Artist/Title">Browsing artists</translate> </h2> - <div :class="['ui', {'loading': isLoading}, 'form']"> + <form :class="['ui', {'loading': isLoading}, 'form']" @submit.prevent="updateQueryString();fetchData()"> <div class="fields"> <div class="field"> <label> <translate translate-context="Content/Search/Input.Label/Noun">Search</translate> </label> - <input type="text" name="search" v-model="query" :placeholder="labels.searchPlaceholder"/> + <div class="ui action input"> + <input type="text" name="search" v-model="query" :placeholder="labels.searchPlaceholder"/> + <button class="ui icon button" type="submit" :aria-label="$pgettext('Content/Search/Input.Label/Noun', 'Search')"> + <i class="search icon"></i> + </button> + </div> </div> <div class="field"> <label><translate translate-context="*/*/*/Noun">Tags</translate></label> @@ -40,7 +45,7 @@ </select> </div> </div> - </div> + </form> <div class="ui hidden divider"></div> <div v-if="result && result.results.length > 0" class="ui five app-cards cards"> <div v-if="isLoading" class="ui inverted active dimmer"> @@ -134,7 +139,7 @@ export default { } }, methods: { - updateQueryString: _.debounce(function() { + updateQueryString: function() { history.pushState( {}, null, @@ -147,8 +152,8 @@ export default { ordering: this.getOrderingAsString() }).toString() ) - }, 500), - fetchData: _.debounce(function() { + }, + fetchData: function() { var self = this this.isLoading = true let url = FETCH_URL @@ -178,7 +183,7 @@ export default { self.result = null self.isLoading = false }) - }, 500), + }, selectPage: function(page) { this.page = page } @@ -188,26 +193,6 @@ export default { this.updateQueryString() this.fetchData() }, - paginateBy() { - this.updateQueryString() - this.fetchData() - }, - ordering() { - this.updateQueryString() - this.fetchData() - }, - orderingDirection() { - this.updateQueryString() - this.fetchData() - }, - query() { - this.updateQueryString() - this.fetchData() - }, - tags() { - this.updateQueryString() - this.fetchData() - }, "$store.state.moderation.lastUpdate": function () { this.fetchData() } diff --git a/front/src/components/library/Radios.vue b/front/src/components/library/Radios.vue index 1f5b121dcdbebf6b41052849b82b72e5c608b0c1..f96c25272366ee26c39a853b384d1cc4089f9108 100644 --- a/front/src/components/library/Radios.vue +++ b/front/src/components/library/Radios.vue @@ -25,11 +25,16 @@ <translate translate-context="Content/Radio/Button.Label/Verb">Create your own radio</translate> </router-link> <div class="ui hidden divider"></div> - <div :class="['ui', {'loading': isLoading}, 'form']"> + <form :class="['ui', {'loading': isLoading}, 'form']" @submit.prevent="updateQueryString();fetchData()"> <div class="fields"> <div class="field"> <label><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> - <input name="search" type="text" v-model="query" :placeholder="labels.searchPlaceholder"/> + <div class="ui action input"> + <input type="text" name="search" v-model="query" :placeholder="labels.searchPlaceholder"/> + <button class="ui icon button" type="submit" :aria-label="$pgettext('Content/Search/Input.Label/Noun', 'Search')"> + <i class="search icon"></i> + </button> + </div> </div> <div class="field"> <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> @@ -59,7 +64,7 @@ </select> </div> </div> - </div> + </form> <div class="ui hidden divider"></div> <div v-if="result && !result.results.length > 0" class="ui placeholder segment"> <div class="ui icon header"> @@ -157,7 +162,7 @@ export default { }, }, methods: { - updateQueryString: _.debounce(function() { + updateQueryString: function() { history.pushState( {}, null, @@ -169,8 +174,8 @@ export default { ordering: this.getOrderingAsString() }).toString() ) - }, 500), - fetchData: _.debounce(function() { + }, + fetchData: function() { var self = this this.isLoading = true let url = FETCH_URL @@ -186,7 +191,7 @@ export default { self.result = response.data self.isLoading = false }) - }, 500), + }, selectPage: function(page) { this.page = page } @@ -196,22 +201,6 @@ export default { this.updateQueryString() this.fetchData() }, - paginateBy() { - this.updateQueryString() - this.fetchData() - }, - ordering() { - this.updateQueryString() - this.fetchData() - }, - orderingDirection() { - this.updateQueryString() - this.fetchData() - }, - query() { - this.updateQueryString() - this.fetchData() - } } } </script> diff --git a/front/src/views/auth/EmailConfirm.vue b/front/src/views/auth/EmailConfirm.vue index 26ccc5d70ced38e42a2c23b26bd4094c239bad3d..4e255ffb9c454f0eb2f7fdb55af4343e93c926d0 100644 --- a/front/src/views/auth/EmailConfirm.vue +++ b/front/src/views/auth/EmailConfirm.vue @@ -52,6 +52,11 @@ export default { } } }, + mounted () { + if (this.key) { + this.submit() + } + }, methods: { submit() { let self = this diff --git a/front/src/views/playlists/List.vue b/front/src/views/playlists/List.vue index 237c2becc45e30e9d1073b837f0532a074de224a..83b29897f67b2d0b3c40b90033f585c96eb1a69e 100644 --- a/front/src/views/playlists/List.vue +++ b/front/src/views/playlists/List.vue @@ -2,17 +2,22 @@ <main v-title="labels.playlists"> <section class="ui vertical stripe segment"> <h2 class="ui header"><translate translate-context="Content/Playlist/Title">Browsing playlists</translate></h2> - <div :class="['ui', {'loading': isLoading}, 'form']"> - <template v-if="$store.state.auth.authenticated"> - <button - @click="$store.commit('playlists/chooseTrack', null)" - class="ui basic success button"><translate translate-context="Content/Playlist/Button.Label/Verb">Manage your playlists</translate></button> - <div class="ui hidden divider"></div> - </template> + <template v-if="$store.state.auth.authenticated"> + <button + @click="$store.commit('playlists/chooseTrack', null)" + class="ui basic success button"><translate translate-context="Content/Playlist/Button.Label/Verb">Manage your playlists</translate></button> + <div class="ui hidden divider"></div> + </template> + <form :class="['ui', {'loading': isLoading}, 'form']" @submit.prevent="updateQueryString();fetchData()"> <div class="fields"> <div class="field"> <label><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> - <input type="text" name="search" v-model="query" :placeholder="labels.searchPlaceholder"/> + <div class="ui action input"> + <input type="text" name="search" v-model="query" :placeholder="labels.searchPlaceholder"/> + <button class="ui icon button" type="submit" :aria-label="$pgettext('Content/Search/Input.Label/Noun', 'Search')"> + <i class="search icon"></i> + </button> + </div> </div> <div class="field"> <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> @@ -38,7 +43,7 @@ </select> </div> </div> - </div> + </form> <div class="ui hidden divider"></div> <playlist-card-list v-if="result && result.results.length > 0" :playlists="result.results"></playlist-card-list> <div v-else-if="result && !result.results.length > 0" class="ui placeholder segment sixteen wide column" style="text-align: center; display: flex; align-items: center"> @@ -124,7 +129,7 @@ export default { } }, methods: { - updateQueryString: _.debounce(function() { + updateQueryString: function() { history.pushState( {}, null, @@ -136,8 +141,8 @@ export default { ordering: this.getOrderingAsString() }).toString() ) - }, 250), - fetchData: _.debounce(function() { + }, + fetchData: function() { var self = this this.isLoading = true let url = FETCH_URL @@ -153,7 +158,7 @@ export default { self.result = response.data self.isLoading = false }) - }, 500), + }, selectPage: function(page) { this.page = page } @@ -163,22 +168,6 @@ export default { this.updateQueryString() this.fetchData() }, - paginateBy() { - this.updateQueryString() - this.fetchData() - }, - ordering() { - this.updateQueryString() - this.fetchData() - }, - orderingDirection() { - this.updateQueryString() - this.fetchData() - }, - query() { - this.updateQueryString() - this.fetchData() - } } } </script>