See #432: added tag detail page
This commit is contained in:
parent
3e9bda24e1
commit
e678b03ad8
|
@ -2,10 +2,11 @@
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<h3 class="ui header">
|
<h3 class="ui header">
|
||||||
<slot name="title"></slot>
|
<slot name="title"></slot>
|
||||||
|
<span v-if="showCount" class="ui tiny circular label">{{ count }}</span>
|
||||||
</h3>
|
</h3>
|
||||||
<button :disabled="!previousPage" @click="fetchData(previousPage)" :class="['ui', {disabled: !previousPage}, 'circular', 'icon', 'basic', 'button']"><i :class="['ui', 'angle left', 'icon']"></i></button>
|
<button v-if="controls" :disabled="!previousPage" @click="fetchData(previousPage)" :class="['ui', {disabled: !previousPage}, 'circular', 'icon', 'basic', 'button']"><i :class="['ui', 'angle left', 'icon']"></i></button>
|
||||||
<button :disabled="!nextPage" @click="fetchData(nextPage)" :class="['ui', {disabled: !nextPage}, 'circular', 'icon', 'basic', 'button']"><i :class="['ui', 'angle right', 'icon']"></i></button>
|
<button v-if="controls" :disabled="!nextPage" @click="fetchData(nextPage)" :class="['ui', {disabled: !nextPage}, 'circular', 'icon', 'basic', 'button']"><i :class="['ui', 'angle right', 'icon']"></i></button>
|
||||||
<button @click="fetchData('albums/')" :class="['ui', 'circular', 'icon', 'basic', 'button']"><i :class="['ui', 'refresh', 'icon']"></i></button>
|
<button v-if="controls" @click="fetchData('albums/')" :class="['ui', 'circular', 'icon', 'basic', 'button']"><i :class="['ui', 'refresh', 'icon']"></i></button>
|
||||||
<div class="ui hidden divider"></div>
|
<div class="ui hidden divider"></div>
|
||||||
<div class="ui five cards">
|
<div class="ui five cards">
|
||||||
<div v-if="isLoading" class="ui inverted active dimmer">
|
<div v-if="isLoading" class="ui inverted active dimmer">
|
||||||
|
@ -33,6 +34,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="!isLoading && albums.length === 0">No results matching your query.</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -43,7 +45,9 @@ import PlayButton from '@/components/audio/PlayButton'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
filters: {type: Object, required: true}
|
filters: {type: Object, required: true},
|
||||||
|
controls: {type: Boolean, default: true},
|
||||||
|
showCount: {type: Boolean, default: false},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
PlayButton
|
PlayButton
|
||||||
|
@ -52,6 +56,7 @@ export default {
|
||||||
return {
|
return {
|
||||||
albums: [],
|
albums: [],
|
||||||
limit: 12,
|
limit: 12,
|
||||||
|
count: 0,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
errors: null,
|
errors: null,
|
||||||
previousPage: null,
|
previousPage: null,
|
||||||
|
@ -76,6 +81,7 @@ export default {
|
||||||
self.nextPage = response.data.next
|
self.nextPage = response.data.next
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
self.albums = response.data.results
|
self.albums = response.data.results
|
||||||
|
self.count = response.data.count
|
||||||
}, error => {
|
}, error => {
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
self.errors = error.backendErrors
|
self.errors = error.backendErrors
|
||||||
|
|
|
@ -142,8 +142,14 @@ export default {
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui.three.cards .card {
|
.ui.three.cards .card {
|
||||||
width: 25em;
|
width: 100%;
|
||||||
|
}
|
||||||
|
@include media(">tablet") {
|
||||||
|
.ui.three.cards .card {
|
||||||
|
width: 25em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.with-overlay {
|
.with-overlay {
|
||||||
background-size: cover !important;
|
background-size: cover !important;
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
<div>
|
<div>
|
||||||
<h3 class="ui header">
|
<h3 class="ui header">
|
||||||
<slot name="title"></slot>
|
<slot name="title"></slot>
|
||||||
|
<span v-if="showCount" class="ui tiny circular label">{{ count }}</span>
|
||||||
</h3>
|
</h3>
|
||||||
<button :disabled="!previousPage" @click="fetchData(previousPage)" :class="['ui', {disabled: !previousPage}, 'circular', 'icon', 'basic', 'button']"><i :class="['ui', 'angle up', 'icon']"></i></button>
|
<button :disabled="!previousPage" @click="fetchData(previousPage)" :class="['ui', {disabled: !previousPage}, 'circular', 'icon', 'basic', 'button']"><i :class="['ui', 'angle up', 'icon']"></i></button>
|
||||||
<button :disabled="!nextPage" @click="fetchData(nextPage)" :class="['ui', {disabled: !nextPage}, 'circular', 'icon', 'basic', 'button']"><i :class="['ui', 'angle down', 'icon']"></i></button>
|
<button :disabled="!nextPage" @click="fetchData(nextPage)" :class="['ui', {disabled: !nextPage}, 'circular', 'icon', 'basic', 'button']"><i :class="['ui', 'angle down', 'icon']"></i></button>
|
||||||
<button @click="fetchData(url)" :class="['ui', 'circular', 'icon', 'basic', 'button']"><i :class="['ui', 'refresh', 'icon']"></i></button>
|
<button @click="fetchData(url)" :class="['ui', 'circular', 'icon', 'basic', 'button']"><i :class="['ui', 'refresh', 'icon']"></i></button>
|
||||||
<div class="ui divided unstackable items">
|
<div class="ui divided unstackable items">
|
||||||
<div class="item" v-for="object in objects" :key="object.id">
|
<div :class="['item', itemClasses]" v-for="object in objects" :key="object.id">
|
||||||
<div class="ui tiny image">
|
<div class="ui tiny image">
|
||||||
<img v-if="object.track.album.cover.original" v-lazy="$store.getters['instance/absoluteUrl'](object.track.album.cover.medium_square_crop)">
|
<img v-if="object.track.album.cover.original" v-lazy="$store.getters['instance/absoluteUrl'](object.track.album.cover.medium_square_crop)">
|
||||||
<img v-else src="../../../assets/audio/default-cover.png">
|
<img v-else src="../../../assets/audio/default-cover.png">
|
||||||
|
@ -28,7 +29,9 @@
|
||||||
</router-link>
|
</router-link>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="extra">
|
<tags-list label-classes="tiny" :truncate-size="20" :limit="2" :show-more="false" :tags="object.track.tags"></tags-list>
|
||||||
|
|
||||||
|
<div class="extra" v-if="isActivity">
|
||||||
<span class="left floated">@{{ object.user.username }}</span>
|
<span class="left floated">@{{ object.user.username }}</span>
|
||||||
<span class="right floated"><human-date :date="object.creation_date" /></span>
|
<span class="right floated"><human-date :date="object.creation_date" /></span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -50,19 +53,25 @@
|
||||||
import _ from '@/lodash'
|
import _ from '@/lodash'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import PlayButton from '@/components/audio/PlayButton'
|
import PlayButton from '@/components/audio/PlayButton'
|
||||||
|
import TagsList from "@/components/tags/List"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
filters: {type: Object, required: true},
|
filters: {type: Object, required: true},
|
||||||
url: {type: String, 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: ''},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
PlayButton
|
PlayButton,
|
||||||
|
TagsList
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
objects: [],
|
objects: [],
|
||||||
limit: 5,
|
count: 0,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
errors: null,
|
errors: null,
|
||||||
previousPage: null,
|
previousPage: null,
|
||||||
|
@ -86,7 +95,15 @@ export default {
|
||||||
self.previousPage = response.data.previous
|
self.previousPage = response.data.previous
|
||||||
self.nextPage = response.data.next
|
self.nextPage = response.data.next
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
self.objects = response.data.results
|
self.count = response.data.count
|
||||||
|
if (self.isActivity) {
|
||||||
|
// we have listening/favorites objects, not directly tracks
|
||||||
|
self.objects = response.data.results
|
||||||
|
} else {
|
||||||
|
self.objects = response.data.results.map((r) => {
|
||||||
|
return {track: r}
|
||||||
|
})
|
||||||
|
}
|
||||||
}, error => {
|
}, error => {
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
self.errors = error.backendErrors
|
self.errors = error.backendErrors
|
||||||
|
@ -129,4 +146,18 @@ export default {
|
||||||
.ui.divided.items > .item:last-child {
|
.ui.divided.items > .item:last-child {
|
||||||
padding-bottom: 1em !important;
|
padding-bottom: 1em !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@include media(">tablet") {
|
||||||
|
.divided.items > .track-item.inline {
|
||||||
|
width: 25em;
|
||||||
|
float: left;
|
||||||
|
border-top: none;
|
||||||
|
&,
|
||||||
|
&:first-child {
|
||||||
|
margin-top: 0.5em !important;
|
||||||
|
margin-right: 0.5em !important;
|
||||||
|
padding: 1em 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
<template>
|
||||||
|
<main v-title="labels.title">
|
||||||
|
<section class="ui vertical stripe segment">
|
||||||
|
<h2 class="ui header">
|
||||||
|
<span class="ui circular huge hashtag label">
|
||||||
|
{{ labels.title }}
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
<div class="ui hidden divider"></div>
|
||||||
|
<div class="ui row">
|
||||||
|
<artist-widget :controls="false" :filters="{playable: true, ordering: '-creation_date', tag: id}">
|
||||||
|
<template slot="title">
|
||||||
|
<router-link :to="{name: 'library.artists.browse', query: {tag: id}}">
|
||||||
|
<translate translate-context="*/*/*">Artists</translate>
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
|
</artist-widget>
|
||||||
|
<div class="ui hidden divider"></div>
|
||||||
|
<div class="ui hidden divider"></div>
|
||||||
|
<album-widget :show-count="true" :controls="false" :filters="{playable: true, ordering: '-creation_date', tag: id}">
|
||||||
|
<template slot="title">
|
||||||
|
<router-link :to="{name: 'library.albums.browse', query: {tag: id}}">
|
||||||
|
<translate translate-context="*/*/*">Albums</translate>
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
|
</album-widget>
|
||||||
|
<div class="ui hidden divider"></div>
|
||||||
|
<div class="ui hidden divider"></div>
|
||||||
|
<track-widget :show-count="true" :limit="12" item-classes="track-item inline" :url="'/tracks/'" :is-activity="false" :filters="{playable: true, ordering: '-creation_date', tag: id}">
|
||||||
|
<template slot="title">
|
||||||
|
<translate translate-context="*/*/*">Tracks</translate>
|
||||||
|
</template>
|
||||||
|
</track-widget>
|
||||||
|
<div class="ui clearing hidden divider"></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
|
||||||
|
import TrackWidget from "@/components/audio/track/Widget"
|
||||||
|
import AlbumWidget from "@/components/audio/album/Widget"
|
||||||
|
import ArtistWidget from "@/components/audio/artist/Widget"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
id: { type: String, required: true }
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
ArtistWidget,
|
||||||
|
AlbumWidget,
|
||||||
|
TrackWidget,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
labels() {
|
||||||
|
let title = `#${this.id}`
|
||||||
|
return {
|
||||||
|
title
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isAuthenticated () {
|
||||||
|
return this.$store.state.auth.authenticated
|
||||||
|
},
|
||||||
|
hasFavorites () {
|
||||||
|
return this.$store.state.favorites.count > 0
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||||
|
<style scoped>
|
||||||
|
.ui.circular.label {
|
||||||
|
padding-left: 1em !important;
|
||||||
|
padding-right: 1em !important;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -473,6 +473,13 @@ export default new Router({
|
||||||
id: route.params.id,
|
id: route.params.id,
|
||||||
defaultEdit: route.query.mode === 'edit' })
|
defaultEdit: route.query.mode === 'edit' })
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'tags/:id',
|
||||||
|
name: 'library.tags.detail',
|
||||||
|
component: () =>
|
||||||
|
import(/* webpackChunkName: "core" */ "@/components/library/TagDetail"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'artists/:id',
|
path: 'artists/:id',
|
||||||
component: () =>
|
component: () =>
|
||||||
|
|
|
@ -370,7 +370,7 @@ input + .help {
|
||||||
|
|
||||||
|
|
||||||
.tag-list {
|
.tag-list {
|
||||||
margin-top: 1em;
|
margin-top: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@import "./themes/_light.scss";
|
@import "./themes/_light.scss";
|
||||||
|
|
Loading…
Reference in New Issue