Skip to content
Snippets Groups Projects
Verified Commit 9b4d7165 authored by Eliot Berriot's avatar Eliot Berriot
Browse files

Added federation libraries list, moved scan to a dedicate view

parent 321b9444
No related branches found
No related tags found
No related merge requests found
......@@ -7,6 +7,9 @@ from . import models
class LibraryFilter(django_filters.FilterSet):
approved = django_filters.BooleanFilter('following__approved')
q = fields.SearchFilter(search_fields=[
'actor__domain',
])
class Meta:
model = models.Library
......
......@@ -3,7 +3,7 @@
<div class="ui vertical center aligned stripe segment">
<div class="ui text container">
<h1 class="ui huge header">
Welcome on Funkwhale
Welcome on Funkwhale
</h1>
<p>We think listening music should be simple.</p>
<router-link class="ui icon button" to="/about">
......
......@@ -47,7 +47,7 @@
class="item" :to="{path: '/activity'}"><i class="bell icon"></i> Activity</router-link>
<router-link
class="item" v-if="$store.state.auth.availablePermissions['federation.manage']"
:to="{path: '/manage/federation'}"><i class="sitemap icon"></i> Federation</router-link>
:to="{path: '/manage/federation/libraries'}"><i class="sitemap icon"></i> Federation</router-link>
</div>
<player></player>
......
......@@ -2,33 +2,39 @@
<div class="ui card">
<div class="content">
<div class="header">
{{ libraryData.display_name }}
{{ displayName }}
</div>
</div>
<div class="content">
<span class="right floated" v-if="libraryData.actor.manuallyApprovesFollowers">
<span class="right floated" v-if="following">
<i class="check icon"></i> Following
</span>
<span class="right floated" v-else-if="manuallyApprovesFollowers">
<i class="lock icon"></i> Followers only
</span>
<span class="right floated" v-else>
<i class="open lock icon"></i> Open
</span>
<span>
<i class="music icon"></i>
{{ libraryData.library.totalItems }} tracks
{{ totalItems }} tracks
</span>
</div>
<div class="extra content">
<template v-if="libraryData.local.awaiting_approval">
<template v-if="awaitingApproval">
<i class="clock icon"></i>
Follow request pending approval
</template>
<template v-else-if="libraryData.local.following">Pending follow request
<template v-else-if="following">
<i class="check icon"></i>
Already following this library
</template>
<div
v-else-if="!library"
v-if="!library"
@click="follow"
:disabled="isLoading"
:class="['ui', 'basic', {loading: isLoading}, 'green', 'button']">
<template v-if="libraryData.actor.manuallyApprovesFollowers">
<template v-if="manuallyApprovesFollowers">
Send a follow request
</template>
<template v-else>
......@@ -49,13 +55,13 @@
import axios from 'axios'
export default {
props: ['libraryData'],
props: ['libraryData', 'libraryInstance'],
data () {
return {
library: this.libraryInstance,
isLoading: false,
data: null,
errors: [],
library: null
errors: []
}
},
methods: {
......@@ -77,6 +83,43 @@ export default {
self.errors = error.backendErrors
})
}
},
computed: {
displayName () {
if (this.libraryData) {
return this.libraryData.display_name
} else {
return `${this.library.actor.preferred_username}@${this.library.actor.domain}`
}
},
manuallyApprovesFollowers () {
if (this.libraryData) {
return this.libraryData.actor.manuallyApprovesFollowers
} else {
return this.library.actor.manually_approves_followers
}
},
totalItems () {
if (this.libraryData) {
return this.libraryData.library.totalItems
} else {
return this.library.tracks_count
}
},
awaitingApproval () {
if (this.libraryData) {
return this.libraryData.local.awaiting_approval
} else {
return this.library.follow.approved === null
}
},
following () {
if (this.libraryData) {
return this.libraryData.local.following
} else {
return this.library.follow.approved
}
}
}
}
</script>
......@@ -26,8 +26,9 @@ import PlaylistDetail from '@/views/playlists/Detail'
import PlaylistList from '@/views/playlists/List'
import Favorites from '@/components/favorites/List'
import FederationBase from '@/views/federation/Base'
import FederationHome from '@/views/federation/Home'
import FederationScan from '@/views/federation/Scan'
import FederationLibraryDetail from '@/views/federation/LibraryDetail'
import FederationLibraryList from '@/views/federation/LibraryList'
Vue.use(Router)
......@@ -90,15 +91,29 @@ export default new Router({
path: '/manage/federation',
component: FederationBase,
children: [
{ path: '', component: FederationHome },
{ path: 'library/:id', name: 'federation.libraries.detail', component: FederationLibraryDetail, props: true }
{
path: 'scan',
name: 'federation.libraries.scan',
component: FederationScan },
{
path: 'libraries',
name: 'federation.libraries.list',
component: FederationLibraryList,
props: (route) => ({
defaultOrdering: route.query.ordering,
defaultQuery: route.query.query,
defaultPaginateBy: route.query.paginateBy,
defaultPage: route.query.page
})
},
{ path: 'libraries/:id', name: 'federation.libraries.detail', component: FederationLibraryDetail, props: true }
]
},
{
path: '/library',
component: Library,
children: [
{ path: '', component: LibraryHome },
{ path: 'scan', component: LibraryHome },
{
path: 'artists/',
name: 'library.artists.browse',
......
<template>
<div class="main pusher" v-title="'Federation'">
<div class="ui secondary pointing menu">
<router-link
class="ui item"
:to="{name: 'federation.libraries.list'}">Libraries</router-link>
</div>
<router-view :key="$route.fullPath"></router-view>
</div>
</template>
<style lang="scss">
@import '../../style/vendor/media';
.main.pusher > .ui.secondary.menu {
@include media(">tablet") {
margin: 0 2.5rem;
}
.item {
padding-top: 1.5em;
padding-bottom: 1.5em;
}
}
</style>
<template>
<div v-title="'Artists'">
<div class="ui vertical stripe segment">
<h2 class="ui header">Browsing libraries</h2>
<router-link
class="ui basic green button"
:to="{name: 'federation.libraries.scan'}">
<i class="plus icon"></i>
Add a new library
</router-link>
<div class="ui hidden divider"></div>
<div :class="['ui', {'loading': isLoading}, 'form']">
<div class="fields">
<div class="field">
<label>Search</label>
<input type="text" v-model="query" placeholder="Enter an library domain name..."/>
</div>
<div class="field">
<label>Ordering</label>
<select class="ui dropdown" v-model="ordering">
<option v-for="option in orderingOptions" :value="option[0]">
{{ option[1] }}
</option>
</select>
</div>
<div class="field">
<label>Ordering direction</label>
<select class="ui dropdown" v-model="orderingDirection">
<option value="">Ascending</option>
<option value="-">Descending</option>
</select>
</div>
<div class="field">
<label>Results per page</label>
<select class="ui dropdown" v-model="paginateBy">
<option :value="parseInt(12)">12</option>
<option :value="parseInt(25)">25</option>
<option :value="parseInt(50)">50</option>
</select>
</div>
</div>
</div>
<div class="ui hidden divider"></div>
<div
v-if="result"
v-masonry
transition-duration="0"
item-selector=".column"
percent-position="true"
stagger="0"
class="ui stackable three column doubling grid">
<div
v-masonry-tile
v-if="result.results.length > 0"
v-for="library in result.results"
:key="library.id"
class="column">
<library-card class="fluid" :library-instance="library"></library-card>
</div>
</div>
<div class="ui center aligned basic segment">
<pagination
v-if="result && result.results.length > 0"
@page-changed="selectPage"
:current="page"
:paginate-by="paginateBy"
:total="result.count"
></pagination>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
import _ from 'lodash'
import $ from 'jquery'
import logger from '@/logging'
import OrderingMixin from '@/components/mixins/Ordering'
import PaginationMixin from '@/components/mixins/Pagination'
import LibraryCard from '@/components/federation/LibraryCard'
import Pagination from '@/components/Pagination'
const FETCH_URL = 'federation/libraries/'
export default {
mixins: [OrderingMixin, PaginationMixin],
props: {
defaultQuery: {type: String, required: false, default: ''}
},
components: {
LibraryCard,
Pagination
},
data () {
let defaultOrdering = this.getOrderingFromString(this.defaultOrdering || '-creation_date')
return {
isLoading: true,
result: null,
page: parseInt(this.defaultPage),
query: this.defaultQuery,
paginateBy: parseInt(this.defaultPaginateBy || 50),
orderingDirection: defaultOrdering.direction,
ordering: defaultOrdering.field,
orderingOptions: [
['creation_date', 'Creation date'],
['tracks_count', 'Available tracks']
]
}
},
created () {
this.fetchData()
},
mounted () {
$('.ui.dropdown').dropdown()
},
methods: {
updateQueryString: _.debounce(function () {
this.$router.replace({
query: {
query: this.query,
page: this.page,
paginateBy: this.paginateBy,
ordering: this.getOrderingAsString()
}
})
}, 500),
fetchData: _.debounce(function () {
var self = this
this.isLoading = true
let url = FETCH_URL
let params = {
page: this.page,
q: this.query,
ordering: this.getOrderingAsString()
}
logger.default.debug('Fetching libraries')
axios.get(url, {params: params}).then((response) => {
self.result = response.data
self.isLoading = false
})
}, 500),
selectPage: function (page) {
this.page = page
}
},
watch: {
page () {
this.updateQueryString()
this.fetchData()
},
ordering () {
this.updateQueryString()
this.fetchData()
},
orderingDirection () {
this.updateQueryString()
this.fetchData()
},
query () {
this.updateQueryString()
this.fetchData()
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
<template>
<div>
<div class="ui vertical stripe segment">
<h1 class="ui header">Manage federation</h1>
<library-form @scanned="updateLibraryData"></library-form>
<library-card v-if="libraryData" :library-data="libraryData"></library-card>
</div>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment