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',
|
'manually_approves_followers',
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class LibraryActorSerializer(ActorSerializer):
|
class LibraryActorSerializer(ActorSerializer):
|
||||||
url = serializers.ListField(
|
url = serializers.ListField(
|
||||||
child=serializers.JSONField())
|
child=serializers.JSONField())
|
||||||
|
@ -137,33 +139,52 @@ class LibraryActorSerializer(ActorSerializer):
|
||||||
return validated_data
|
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):
|
class APILibrarySerializer(serializers.ModelSerializer):
|
||||||
actor = APIActorSerializer
|
actor = APIActorSerializer()
|
||||||
|
follow = APIFollowSerializer()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Library
|
model = models.Library
|
||||||
fields = [
|
|
||||||
|
read_only_fields = [
|
||||||
'actor',
|
'actor',
|
||||||
'autoimport',
|
|
||||||
'federation_enabled',
|
|
||||||
'download_files',
|
|
||||||
'tracks_count',
|
|
||||||
'url',
|
|
||||||
'uuid',
|
'uuid',
|
||||||
'creation_date',
|
'url',
|
||||||
|
'tracks_count',
|
||||||
'follow',
|
'follow',
|
||||||
'fetched_date',
|
'fetched_date',
|
||||||
'modification_date',
|
'modification_date',
|
||||||
|
'creation_date',
|
||||||
]
|
]
|
||||||
|
fields = [
|
||||||
|
'autoimport',
|
||||||
|
'federation_enabled',
|
||||||
|
'download_files',
|
||||||
|
] + read_only_fields
|
||||||
|
|
||||||
|
|
||||||
class APILibraryCreateSerializer(serializers.ModelSerializer):
|
class APILibraryCreateSerializer(serializers.ModelSerializer):
|
||||||
actor = serializers.URLField()
|
actor = serializers.URLField()
|
||||||
federation_enabled = serializers.BooleanField()
|
federation_enabled = serializers.BooleanField()
|
||||||
|
uuid = serializers.UUIDField(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Library
|
model = models.Library
|
||||||
fields = [
|
fields = [
|
||||||
|
'uuid',
|
||||||
'actor',
|
'actor',
|
||||||
'autoimport',
|
'autoimport',
|
||||||
'federation_enabled',
|
'federation_enabled',
|
||||||
|
|
|
@ -166,6 +166,8 @@ class MusicFilesViewSet(FederationMixin, viewsets.GenericViewSet):
|
||||||
|
|
||||||
|
|
||||||
class LibraryViewSet(
|
class LibraryViewSet(
|
||||||
|
mixins.RetrieveModelMixin,
|
||||||
|
mixins.UpdateModelMixin,
|
||||||
mixins.ListModelMixin,
|
mixins.ListModelMixin,
|
||||||
viewsets.GenericViewSet):
|
viewsets.GenericViewSet):
|
||||||
permission_classes = [rest_permissions.DjangoModelPermissions]
|
permission_classes = [rest_permissions.DjangoModelPermissions]
|
||||||
|
@ -173,6 +175,7 @@ class LibraryViewSet(
|
||||||
'actor',
|
'actor',
|
||||||
'follow',
|
'follow',
|
||||||
)
|
)
|
||||||
|
lookup_field = 'uuid'
|
||||||
filter_class = filters.LibraryFilter
|
filter_class = filters.LibraryFilter
|
||||||
serializer_class = serializers.APILibrarySerializer
|
serializer_class = serializers.APILibrarySerializer
|
||||||
ordering_fields = (
|
ordering_fields = (
|
||||||
|
|
|
@ -275,3 +275,34 @@ def test_can_list_libraries(factories, superuser_api_client):
|
||||||
serializers.APILibrarySerializer(library1).data,
|
serializers.APILibrarySerializer(library1).data,
|
||||||
serializers.APILibrarySerializer(library2).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 PlaylistDetail from '@/views/playlists/Detail'
|
||||||
import PlaylistList from '@/views/playlists/List'
|
import PlaylistList from '@/views/playlists/List'
|
||||||
import Favorites from '@/components/favorites/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)
|
Vue.use(Router)
|
||||||
|
|
||||||
|
@ -86,7 +88,11 @@ export default new Router({
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/manage/federation',
|
path: '/manage/federation',
|
||||||
component: Federation
|
component: FederationBase,
|
||||||
|
children: [
|
||||||
|
{ path: '', component: FederationHome },
|
||||||
|
{ path: 'library/:id', name: 'federation.libraries.detail', component: FederationLibraryDetail, props: true }
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/library',
|
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>
|
<template>
|
||||||
<div class="main pusher" v-title="'Federation'">
|
<div>
|
||||||
<div class="ui vertical stripe segment">
|
<div class="ui vertical stripe segment">
|
||||||
<h1 class="ui header">Manage federation</h1>
|
<h1 class="ui header">Manage federation</h1>
|
||||||
<library-form @scanned="updateLibraryData"></library-form>
|
<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