Merge branch 'develop' of dev.funkwhale.audio:funkwhale/funkwhale into develop

This commit is contained in:
Agate 2020-07-09 11:58:28 +02:00
commit 6ceb88264d
13 changed files with 72 additions and 116 deletions

View File

@ -0,0 +1 @@
More consistent search UX on /albums, /artists, /radios and /playlists (#1131)

View File

@ -0,0 +1 @@
Confirm email without requiring the user to validate the form manually (#407)

View File

@ -89,7 +89,6 @@
<tr <tr
:id="'queue-item-' + index" :id="'queue-item-' + index"
role="button" role="button"
tabindex="0"
v-if="track.sources.length > 0" v-if="track.sources.length > 0"
:key="index" :key="index"
:class="[{active: index === currentIndex}]" :class="[{active: index === currentIndex}]"

View File

@ -107,10 +107,12 @@ export default {
return this.track.uploads && this.track.uploads.length > 0 return this.track.uploads && this.track.uploads.length > 0
} else if (this.artist && this.artist.tracks_count) { } else if (this.artist && this.artist.tracks_count) {
return this.artist.tracks_count > 0 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 this.artist.albums.filter((a) => {
return a.is_playable === true return a.is_playable === true
}).length > 0 }).length > 0
} else if (this.album) {
return this.album.is_playable
} else if (this.tracks) { } else if (this.tracks) {
return this.tracks.filter((t) => { return this.tracks.filter((t) => {
return t.uploads && t.uploads.length > 0 return t.uploads && t.uploads.length > 0
@ -229,6 +231,7 @@ export default {
jQuery(self.$el).find('.ui.dropdown').dropdown('hide') jQuery(self.$el).find('.ui.dropdown').dropdown('hide')
}, },
addNext (next) { addNext (next) {
console.log('CLICKED')
let self = this let self = this
let wasEmpty = this.$store.state.queue.tracks.length === 0 let wasEmpty = this.$store.state.queue.tracks.length === 0
this.getPlayableTracks().then((tracks) => { this.getPlayableTracks().then((tracks) => {
@ -253,7 +256,6 @@ export default {
}, },
watch: { watch: {
clicked () { clicked () {
let self = this let self = this
this.$nextTick(() => { this.$nextTick(() => {
jQuery(this.$el).find('.ui.dropdown').dropdown({ jQuery(this.$el).find('.ui.dropdown').dropdown({

View File

@ -40,7 +40,7 @@
<translate translate-context="*/Login/*/Verb">Reset your password</translate> <translate translate-context="*/Login/*/Verb">Reset your password</translate>
</router-link> </router-link>
</label> </label>
<password-input :index="2" required v-model="credentials.password" /> <password-input required v-model="credentials.password" />
</div> </div>
</template> </template>

View File

@ -3,7 +3,6 @@
<input <input
required required
name="password" name="password"
:tabindex="index"
:type="passwordInputType" :type="passwordInputType"
@input="$emit('input', $event.target.value)" @input="$emit('input', $event.target.value)"
:value="value"> :value="value">
@ -30,7 +29,7 @@ function copyStringToClipboard (str) {
} }
export default { export default {
props: ['value', 'index', 'defaultShow', 'copyButton'], props: ['value', 'defaultShow', 'copyButton'],
data () { data () {
return { return {
showPassword: this.defaultShow || false, showPassword: this.defaultShow || false,

View File

@ -169,13 +169,16 @@ export default {
methods: { methods: {
async fetchData() { async fetchData() {
this.isLoading = true 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 albumResponse = await axios.get(`albums/${this.id}/`, {params: {refresh: 'true'}})
let artistResponse = await axios.get(`artists/${albumResponse.data.artist.id}/`) let artistResponse = await axios.get(`artists/${albumResponse.data.artist.id}/`)
this.artist = artistResponse.data this.artist = artistResponse.data
if (this.artist.channel) { if (this.artist.channel) {
this.artist.channel.artist = this.artist 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.discs = this.object.tracks.reduce(groupByDisc, [])
this.isLoading = false this.isLoading = false

View File

@ -4,13 +4,18 @@
<h2 class="ui header"> <h2 class="ui header">
<translate translate-context="Content/Album/Title">Browsing albums</translate> <translate translate-context="Content/Album/Title">Browsing albums</translate>
</h2> </h2>
<div :class="['ui', {'loading': isLoading}, 'form']"> <form :class="['ui', {'loading': isLoading}, 'form']" @submit.prevent="updateQueryString();fetchData()">
<div class="fields"> <div class="fields">
<div class="field"> <div class="field">
<label> <label>
<translate translate-context="Content/Search/Input.Label/Noun">Search</translate> <translate translate-context="Content/Search/Input.Label/Noun">Search</translate>
</label> </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>
<div class="field"> <div class="field">
<label><translate translate-context="*/*/*/Noun">Tags</translate></label> <label><translate translate-context="*/*/*/Noun">Tags</translate></label>
@ -40,7 +45,7 @@
</select> </select>
</div> </div>
</div> </div>
</div> </form>
<div class="ui hidden divider"></div> <div class="ui hidden divider"></div>
<div <div
v-if="result" v-if="result"
@ -144,7 +149,7 @@ export default {
} }
}, },
methods: { methods: {
updateQueryString: _.debounce(function() { updateQueryString: function() {
history.pushState( history.pushState(
{}, {},
null, null,
@ -157,8 +162,8 @@ export default {
ordering: this.getOrderingAsString() ordering: this.getOrderingAsString()
}).toString() }).toString()
) )
}, 500), },
fetchData: _.debounce(function() { fetchData: function() {
var self = this var self = this
this.isLoading = true this.isLoading = true
let url = FETCH_URL let url = FETCH_URL
@ -187,7 +192,7 @@ export default {
self.result = null self.result = null
self.isLoading = false self.isLoading = false
}) })
}, 500), },
selectPage: function(page) { selectPage: function(page) {
this.page = page this.page = page
} }
@ -197,26 +202,6 @@ export default {
this.updateQueryString() this.updateQueryString()
this.fetchData() 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 () { "$store.state.moderation.lastUpdate": function () {
this.fetchData() this.fetchData()
} }

View File

@ -195,9 +195,7 @@ export default {
self.nextAlbumsUrl = response.data.next self.nextAlbumsUrl = response.data.next
self.totalAlbums = response.data.count self.totalAlbums = response.data.count
let parsed = JSON.parse(JSON.stringify(response.data.results)) let parsed = JSON.parse(JSON.stringify(response.data.results))
self.albums = parsed.map(album => { self.albums = parsed
return backend.Album.clean(album)
})
}) })
await trackPromise await trackPromise

View File

@ -4,13 +4,18 @@
<h2 class="ui header"> <h2 class="ui header">
<translate translate-context="Content/Artist/Title">Browsing artists</translate> <translate translate-context="Content/Artist/Title">Browsing artists</translate>
</h2> </h2>
<div :class="['ui', {'loading': isLoading}, 'form']"> <form :class="['ui', {'loading': isLoading}, 'form']" @submit.prevent="updateQueryString();fetchData()">
<div class="fields"> <div class="fields">
<div class="field"> <div class="field">
<label> <label>
<translate translate-context="Content/Search/Input.Label/Noun">Search</translate> <translate translate-context="Content/Search/Input.Label/Noun">Search</translate>
</label> </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>
<div class="field"> <div class="field">
<label><translate translate-context="*/*/*/Noun">Tags</translate></label> <label><translate translate-context="*/*/*/Noun">Tags</translate></label>
@ -40,7 +45,7 @@
</select> </select>
</div> </div>
</div> </div>
</div> </form>
<div class="ui hidden divider"></div> <div class="ui hidden divider"></div>
<div v-if="result && result.results.length > 0" class="ui five app-cards cards"> <div v-if="result && result.results.length > 0" class="ui five app-cards cards">
<div v-if="isLoading" class="ui inverted active dimmer"> <div v-if="isLoading" class="ui inverted active dimmer">
@ -134,7 +139,7 @@ export default {
} }
}, },
methods: { methods: {
updateQueryString: _.debounce(function() { updateQueryString: function() {
history.pushState( history.pushState(
{}, {},
null, null,
@ -147,8 +152,8 @@ export default {
ordering: this.getOrderingAsString() ordering: this.getOrderingAsString()
}).toString() }).toString()
) )
}, 500), },
fetchData: _.debounce(function() { fetchData: function() {
var self = this var self = this
this.isLoading = true this.isLoading = true
let url = FETCH_URL let url = FETCH_URL
@ -178,7 +183,7 @@ export default {
self.result = null self.result = null
self.isLoading = false self.isLoading = false
}) })
}, 500), },
selectPage: function(page) { selectPage: function(page) {
this.page = page this.page = page
} }
@ -188,26 +193,6 @@ export default {
this.updateQueryString() this.updateQueryString()
this.fetchData() 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 () { "$store.state.moderation.lastUpdate": function () {
this.fetchData() this.fetchData()
} }

View File

@ -25,11 +25,16 @@
<translate translate-context="Content/Radio/Button.Label/Verb">Create your own radio</translate> <translate translate-context="Content/Radio/Button.Label/Verb">Create your own radio</translate>
</router-link> </router-link>
<div class="ui hidden divider"></div> <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="fields">
<div class="field"> <div class="field">
<label><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> <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>
<div class="field"> <div class="field">
<label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label>
@ -59,7 +64,7 @@
</select> </select>
</div> </div>
</div> </div>
</div> </form>
<div class="ui hidden divider"></div> <div class="ui hidden divider"></div>
<div v-if="result && !result.results.length > 0" class="ui placeholder segment"> <div v-if="result && !result.results.length > 0" class="ui placeholder segment">
<div class="ui icon header"> <div class="ui icon header">
@ -157,7 +162,7 @@ export default {
}, },
}, },
methods: { methods: {
updateQueryString: _.debounce(function() { updateQueryString: function() {
history.pushState( history.pushState(
{}, {},
null, null,
@ -169,8 +174,8 @@ export default {
ordering: this.getOrderingAsString() ordering: this.getOrderingAsString()
}).toString() }).toString()
) )
}, 500), },
fetchData: _.debounce(function() { fetchData: function() {
var self = this var self = this
this.isLoading = true this.isLoading = true
let url = FETCH_URL let url = FETCH_URL
@ -186,7 +191,7 @@ export default {
self.result = response.data self.result = response.data
self.isLoading = false self.isLoading = false
}) })
}, 500), },
selectPage: function(page) { selectPage: function(page) {
this.page = page this.page = page
} }
@ -196,22 +201,6 @@ export default {
this.updateQueryString() this.updateQueryString()
this.fetchData() this.fetchData()
}, },
paginateBy() {
this.updateQueryString()
this.fetchData()
},
ordering() {
this.updateQueryString()
this.fetchData()
},
orderingDirection() {
this.updateQueryString()
this.fetchData()
},
query() {
this.updateQueryString()
this.fetchData()
}
} }
} }
</script> </script>

View File

@ -52,6 +52,11 @@ export default {
} }
} }
}, },
mounted () {
if (this.key) {
this.submit()
}
},
methods: { methods: {
submit() { submit() {
let self = this let self = this

View File

@ -2,17 +2,22 @@
<main v-title="labels.playlists"> <main v-title="labels.playlists">
<section class="ui vertical stripe segment"> <section class="ui vertical stripe segment">
<h2 class="ui header"><translate translate-context="Content/Playlist/Title">Browsing playlists</translate></h2> <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">
<template v-if="$store.state.auth.authenticated"> <button
<button @click="$store.commit('playlists/chooseTrack', null)"
@click="$store.commit('playlists/chooseTrack', null)" class="ui basic success button"><translate translate-context="Content/Playlist/Button.Label/Verb">Manage your playlists</translate></button>
class="ui basic success button"><translate translate-context="Content/Playlist/Button.Label/Verb">Manage your playlists</translate></button> <div class="ui hidden divider"></div>
<div class="ui hidden divider"></div> </template>
</template> <form :class="['ui', {'loading': isLoading}, 'form']" @submit.prevent="updateQueryString();fetchData()">
<div class="fields"> <div class="fields">
<div class="field"> <div class="field">
<label><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> <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>
<div class="field"> <div class="field">
<label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label>
@ -38,7 +43,7 @@
</select> </select>
</div> </div>
</div> </div>
</div> </form>
<div class="ui hidden divider"></div> <div class="ui hidden divider"></div>
<playlist-card-list v-if="result && result.results.length > 0" :playlists="result.results"></playlist-card-list> <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"> <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: { methods: {
updateQueryString: _.debounce(function() { updateQueryString: function() {
history.pushState( history.pushState(
{}, {},
null, null,
@ -136,8 +141,8 @@ export default {
ordering: this.getOrderingAsString() ordering: this.getOrderingAsString()
}).toString() }).toString()
) )
}, 250), },
fetchData: _.debounce(function() { fetchData: function() {
var self = this var self = this
this.isLoading = true this.isLoading = true
let url = FETCH_URL let url = FETCH_URL
@ -153,7 +158,7 @@ export default {
self.result = response.data self.result = response.data
self.isLoading = false self.isLoading = false
}) })
}, 500), },
selectPage: function(page) { selectPage: function(page) {
this.page = page this.page = page
} }
@ -163,22 +168,6 @@ export default {
this.updateQueryString() this.updateQueryString()
this.fetchData() this.fetchData()
}, },
paginateBy() {
this.updateQueryString()
this.fetchData()
},
ordering() {
this.updateQueryString()
this.fetchData()
},
orderingDirection() {
this.updateQueryString()
this.fetchData()
},
query() {
this.updateQueryString()
this.fetchData()
}
} }
} }
</script> </script>