220 lines
6.1 KiB
Vue
220 lines
6.1 KiB
Vue
<template>
|
|
<div class="component-track-widget">
|
|
<h3 v-if="!!$slots.title">
|
|
<slot name="title" />
|
|
<span
|
|
v-if="showCount"
|
|
class="ui tiny circular label"
|
|
>{{ count }}</span>
|
|
</h3>
|
|
<div
|
|
v-if="count > 0"
|
|
class="ui divided unstackable items"
|
|
>
|
|
<div
|
|
v-for="object in objects"
|
|
:key="object.id"
|
|
:class="['item', itemClasses]"
|
|
>
|
|
<div class="ui tiny image">
|
|
<img
|
|
v-if="object.track.album && object.track.album.cover"
|
|
v-lazy="$store.getters['instance/absoluteUrl'](object.track.album.cover.urls.medium_square_crop)"
|
|
alt=""
|
|
>
|
|
<img
|
|
v-else-if="object.track.cover"
|
|
v-lazy="$store.getters['instance/absoluteUrl'](object.track.cover.urls.medium_square_crop)"
|
|
alt=""
|
|
>
|
|
<img
|
|
v-else-if="object.track.artist.cover"
|
|
v-lazy="$store.getters['instance/absoluteUrl'](object.track.artist.cover.urls.medium_square_crop)"
|
|
alt=""
|
|
>
|
|
<img
|
|
v-else
|
|
alt=""
|
|
src="../../../assets/audio/default-cover.png"
|
|
>
|
|
<play-button
|
|
class="play-overlay"
|
|
:icon-only="true"
|
|
:button-classes="['ui', 'circular', 'tiny', 'vibrant', 'icon', 'button']"
|
|
:track="object.track"
|
|
/>
|
|
</div>
|
|
<div class="middle aligned content">
|
|
<div class="ui unstackable grid">
|
|
<div class="thirteen wide stretched column">
|
|
<div class="ellipsis">
|
|
<router-link :to="{name: 'library.tracks.detail', params: {id: object.track.id}}">
|
|
{{ object.track.title }}
|
|
</router-link>
|
|
</div>
|
|
<div class="meta ellipsis">
|
|
<span>
|
|
<router-link
|
|
class="discrete link"
|
|
:to="{name: 'library.artists.detail', params: {id: object.track.artist.id}}"
|
|
>
|
|
{{ object.track.artist.name }}
|
|
</router-link>
|
|
</span>
|
|
</div>
|
|
<tags-list
|
|
label-classes="tiny"
|
|
:truncate-size="20"
|
|
:limit="2"
|
|
:show-more="false"
|
|
:tags="object.track.tags"
|
|
/>
|
|
|
|
<div
|
|
v-if="isActivity"
|
|
class="extra"
|
|
>
|
|
<router-link
|
|
class="left floated"
|
|
:to="{name: 'profile.overview', params: {username: object.user.username}}"
|
|
>
|
|
@{{ object.user.username }}
|
|
</router-link>
|
|
<span class="right floated"><human-date :date="object.creation_date" /></span>
|
|
</div>
|
|
</div>
|
|
<div class="one wide stretched column">
|
|
<play-button
|
|
class="basic icon"
|
|
:account="object.actor"
|
|
:dropdown-only="true"
|
|
:dropdown-icon-classes="['ellipsis', 'vertical', 'large really discrete']"
|
|
:track="object.track"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div
|
|
v-if="isLoading"
|
|
class="ui inverted active dimmer"
|
|
>
|
|
<div class="ui loader" />
|
|
</div>
|
|
</div>
|
|
<div
|
|
v-else
|
|
class="ui placeholder segment"
|
|
>
|
|
<div class="ui icon header">
|
|
<i class="music icon" />
|
|
<translate translate-context="Content/Home/Placeholder">
|
|
Nothing found
|
|
</translate>
|
|
</div>
|
|
<div
|
|
v-if="isLoading"
|
|
class="ui inverted active dimmer"
|
|
>
|
|
<div class="ui loader" />
|
|
</div>
|
|
</div>
|
|
<template v-if="nextPage">
|
|
<div class="ui hidden divider" />
|
|
<button
|
|
v-if="nextPage"
|
|
:class="['ui', 'basic', 'button']"
|
|
@click="fetchData(nextPage)"
|
|
>
|
|
<translate translate-context="*/*/Button,Label">
|
|
Show more
|
|
</translate>
|
|
</button>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import _ from 'lodash'
|
|
import axios from 'axios'
|
|
import PlayButton from '@/components/audio/PlayButton.vue'
|
|
import TagsList from '@/components/tags/List.vue'
|
|
|
|
export default {
|
|
components: {
|
|
PlayButton,
|
|
TagsList
|
|
},
|
|
props: {
|
|
filters: { type: Object, required: true },
|
|
url: { type: String, required: true },
|
|
isActivity: { type: Boolean, default: true },
|
|
showCount: { type: Boolean, default: false },
|
|
limit: { type: Number, default: 5 },
|
|
itemClasses: { type: String, default: '' }
|
|
},
|
|
data () {
|
|
return {
|
|
objects: [],
|
|
count: 0,
|
|
isLoading: false,
|
|
errors: null,
|
|
previousPage: null,
|
|
nextPage: null
|
|
}
|
|
},
|
|
watch: {
|
|
offset () {
|
|
this.fetchData()
|
|
},
|
|
'$store.state.moderation.lastUpdate': function () {
|
|
this.fetchData(this.url)
|
|
},
|
|
count (v) {
|
|
this.$emit('count', v)
|
|
}
|
|
},
|
|
created () {
|
|
this.fetchData(this.url)
|
|
},
|
|
methods: {
|
|
fetchData (url) {
|
|
if (!url) {
|
|
return
|
|
}
|
|
this.isLoading = true
|
|
const self = this
|
|
const params = _.clone(this.filters)
|
|
params.page_size = this.limit
|
|
params.offset = this.offset
|
|
axios.get(url, { params: params }).then((response) => {
|
|
self.previousPage = response.data.previous
|
|
self.nextPage = response.data.next
|
|
self.isLoading = false
|
|
self.count = response.data.count
|
|
let newObjects
|
|
if (self.isActivity) {
|
|
// we have listening/favorites objects, not directly tracks
|
|
newObjects = response.data.results
|
|
} else {
|
|
newObjects = response.data.results.map((r) => {
|
|
return { track: r }
|
|
})
|
|
}
|
|
self.objects = [...self.objects, ...newObjects]
|
|
}, error => {
|
|
self.isLoading = false
|
|
self.errors = error.backendErrors
|
|
})
|
|
},
|
|
updateOffset (increment) {
|
|
if (increment) {
|
|
this.offset += this.limit
|
|
} else {
|
|
this.offset = Math.max(this.offset - this.limit, 0)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script>
|