Detail library view with settings update
This commit is contained in:
parent
f4f75dcb4f
commit
472cc7e26a
|
@ -112,6 +112,8 @@ class APIActorSerializer(serializers.ModelSerializer):
|
|||
'manually_approves_followers',
|
||||
|
||||
]
|
||||
|
||||
|
||||
class LibraryActorSerializer(ActorSerializer):
|
||||
url = serializers.ListField(
|
||||
child=serializers.JSONField())
|
||||
|
@ -137,33 +139,52 @@ class LibraryActorSerializer(ActorSerializer):
|
|||
return validated_data
|
||||
|
||||
|
||||
class APIFollowSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = models.Follow
|
||||
fields = [
|
||||
'uuid',
|
||||
'actor',
|
||||
'target',
|
||||
'approved',
|
||||
'creation_date',
|
||||
'modification_date',
|
||||
]
|
||||
|
||||
|
||||
class APILibrarySerializer(serializers.ModelSerializer):
|
||||
actor = APIActorSerializer
|
||||
actor = APIActorSerializer()
|
||||
follow = APIFollowSerializer()
|
||||
|
||||
class Meta:
|
||||
model = models.Library
|
||||
fields = [
|
||||
|
||||
read_only_fields = [
|
||||
'actor',
|
||||
'autoimport',
|
||||
'federation_enabled',
|
||||
'download_files',
|
||||
'tracks_count',
|
||||
'url',
|
||||
'uuid',
|
||||
'creation_date',
|
||||
'url',
|
||||
'tracks_count',
|
||||
'follow',
|
||||
'fetched_date',
|
||||
'modification_date',
|
||||
'creation_date',
|
||||
]
|
||||
fields = [
|
||||
'autoimport',
|
||||
'federation_enabled',
|
||||
'download_files',
|
||||
] + read_only_fields
|
||||
|
||||
|
||||
class APILibraryCreateSerializer(serializers.ModelSerializer):
|
||||
actor = serializers.URLField()
|
||||
federation_enabled = serializers.BooleanField()
|
||||
uuid = serializers.UUIDField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = models.Library
|
||||
fields = [
|
||||
'uuid',
|
||||
'actor',
|
||||
'autoimport',
|
||||
'federation_enabled',
|
||||
|
|
|
@ -166,6 +166,8 @@ class MusicFilesViewSet(FederationMixin, viewsets.GenericViewSet):
|
|||
|
||||
|
||||
class LibraryViewSet(
|
||||
mixins.RetrieveModelMixin,
|
||||
mixins.UpdateModelMixin,
|
||||
mixins.ListModelMixin,
|
||||
viewsets.GenericViewSet):
|
||||
permission_classes = [rest_permissions.DjangoModelPermissions]
|
||||
|
@ -173,6 +175,7 @@ class LibraryViewSet(
|
|||
'actor',
|
||||
'follow',
|
||||
)
|
||||
lookup_field = 'uuid'
|
||||
filter_class = filters.LibraryFilter
|
||||
serializer_class = serializers.APILibrarySerializer
|
||||
ordering_fields = (
|
||||
|
|
|
@ -275,3 +275,34 @@ def test_can_list_libraries(factories, superuser_api_client):
|
|||
serializers.APILibrarySerializer(library1).data,
|
||||
serializers.APILibrarySerializer(library2).data,
|
||||
]
|
||||
|
||||
|
||||
def test_can_detail_library(factories, superuser_api_client):
|
||||
library = factories['federation.Library']()
|
||||
|
||||
url = reverse(
|
||||
'api:v1:federation:libraries-detail',
|
||||
kwargs={'uuid': str(library.uuid)})
|
||||
response = superuser_api_client.get(url)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.data == serializers.APILibrarySerializer(library).data
|
||||
|
||||
|
||||
def test_can_patch_library(factories, superuser_api_client):
|
||||
library = factories['federation.Library']()
|
||||
data = {
|
||||
'federation_enabled': not library.federation_enabled,
|
||||
'download_files': not library.download_files,
|
||||
'autoimport': not library.autoimport,
|
||||
}
|
||||
url = reverse(
|
||||
'api:v1:federation:libraries-detail',
|
||||
kwargs={'uuid': str(library.uuid)})
|
||||
response = superuser_api_client.patch(url, data)
|
||||
|
||||
assert response.status_code == 200
|
||||
library.refresh_from_db()
|
||||
|
||||
for k, v in data.items():
|
||||
assert getattr(library, k) == v
|
||||
|
|
|
@ -25,7 +25,9 @@ import RequestsList from '@/components/requests/RequestsList'
|
|||
import PlaylistDetail from '@/views/playlists/Detail'
|
||||
import PlaylistList from '@/views/playlists/List'
|
||||
import Favorites from '@/components/favorites/List'
|
||||
import Federation from '@/views/federation/Home'
|
||||
import FederationBase from '@/views/federation/Base'
|
||||
import FederationHome from '@/views/federation/Home'
|
||||
import FederationLibraryDetail from '@/views/federation/LibraryDetail'
|
||||
|
||||
Vue.use(Router)
|
||||
|
||||
|
@ -86,7 +88,11 @@ export default new Router({
|
|||
},
|
||||
{
|
||||
path: '/manage/federation',
|
||||
component: Federation
|
||||
component: FederationBase,
|
||||
children: [
|
||||
{ path: '', component: FederationHome },
|
||||
{ path: 'library/:id', name: 'federation.libraries.detail', component: FederationLibraryDetail, props: true }
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/library',
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<template>
|
||||
<div class="main pusher" v-title="'Federation'">
|
||||
<div class="ui secondary pointing menu">
|
||||
</div>
|
||||
<router-view :key="$route.fullPath"></router-view>
|
||||
</div>
|
||||
</template>
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="main pusher" v-title="'Federation'">
|
||||
<div>
|
||||
<div class="ui vertical stripe segment">
|
||||
<h1 class="ui header">Manage federation</h1>
|
||||
<library-form @scanned="updateLibraryData"></library-form>
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
<template>
|
||||
<div>
|
||||
<div v-if="isLoading" class="ui vertical segment" v-title="'Library'">
|
||||
<div :class="['ui', 'centered', 'active', 'inline', 'loader']"></div>
|
||||
</div>
|
||||
<template v-if="object">
|
||||
<div :class="['ui', 'head', 'vertical', 'center', 'aligned', 'stripe', 'segment']" v-title="libraryUsername">
|
||||
<div class="segment-content">
|
||||
<h2 class="ui center aligned icon header">
|
||||
<i class="circular inverted cloud olive icon"></i>
|
||||
<div class="content">
|
||||
{{ libraryUsername }}
|
||||
</div>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="ui hidden divider"></div>
|
||||
<div class="ui one column centered grid">
|
||||
<table class="ui collapsing very basic table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Follow status</td>
|
||||
<td>
|
||||
<template v-if="object.follow.approved === null">
|
||||
<i class="loading icon"></i> Pending approval
|
||||
</template>
|
||||
<template v-else-if="object.follow.approved === true">
|
||||
<i class="check icon"></i> Following
|
||||
</template>
|
||||
<template v-else-if="object.follow.approved === false">
|
||||
<i class="x icon"></i> Not following
|
||||
</template>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Federation</td>
|
||||
<td>
|
||||
<div class="ui toggle checkbox">
|
||||
<input
|
||||
@change="update('federation_enabled')"
|
||||
v-model="object.federation_enabled" type="checkbox">
|
||||
<label></label>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Auto importing</td>
|
||||
<td>
|
||||
<div class="ui toggle checkbox">
|
||||
<input
|
||||
@change="update('autoimport')"
|
||||
v-model="object.autoimport" type="checkbox">
|
||||
<label></label>
|
||||
</div>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>File mirroring</td>
|
||||
<td>
|
||||
<div class="ui toggle checkbox">
|
||||
<input
|
||||
@change="update('download_files')"
|
||||
v-model="object.download_files" type="checkbox">
|
||||
<label></label>
|
||||
</div>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Library size</td>
|
||||
<td>
|
||||
{{ object.tracks_count }} tracks
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui vertical stripe segment">
|
||||
<h2>Tracks available in this library</h2>
|
||||
<div class="ui stackable doubling three column grid">
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
import logger from '@/logging'
|
||||
|
||||
export default {
|
||||
props: ['id'],
|
||||
components: {},
|
||||
data () {
|
||||
return {
|
||||
isLoading: true,
|
||||
object: null
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
fetchData () {
|
||||
var self = this
|
||||
this.isLoading = true
|
||||
let url = 'federation/libraries/' + this.id + '/'
|
||||
logger.default.debug('Fetching library "' + this.id + '"')
|
||||
axios.get(url).then((response) => {
|
||||
self.object = response.data
|
||||
self.isLoading = false
|
||||
})
|
||||
},
|
||||
update (attr) {
|
||||
let newValue = this.object[attr]
|
||||
let params = {}
|
||||
let self = this
|
||||
params[attr] = newValue
|
||||
axios.patch('federation/libraries/' + this.id + '/', params).then((response) => {
|
||||
console.log(`${attr} was updated succcessfully to ${newValue}`)
|
||||
}, (error) => {
|
||||
console.log(`Error while setting ${attr} to ${newValue}`, error)
|
||||
self.object[attr] = !newValue
|
||||
})
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
libraryUsername () {
|
||||
let actor = this.object.actor
|
||||
return `${actor.preferred_username}@${actor.domain}`
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
id () {
|
||||
this.fetchData()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
</style>
|
Loading…
Reference in New Issue