funkwhale/front/src/views/channels/DetailBase.vue

567 wiersze
21 KiB
Vue
Czysty Zwykły widok Historia

2020-02-05 14:06:07 +00:00
<template>
2021-12-06 10:35:20 +00:00
<main
v-title="labels.title"
class="main pusher"
>
<div
v-if="isLoading"
class="ui vertical segment"
>
<div :class="['ui', 'centered', 'active', 'inline', 'loader']" />
2020-02-05 14:06:07 +00:00
</div>
<template v-if="object && !isLoading">
2021-12-06 10:35:20 +00:00
<section
v-title="object.artist.name"
class="ui head vertical stripe segment container"
>
<div class="ui stackable grid">
<div class="seven wide column">
2020-02-05 14:06:07 +00:00
<div class="ui two column grid">
<div class="column">
2021-12-06 10:35:20 +00:00
<img
v-if="object.artist.cover"
alt=""
class="huge channel-image"
:src="$store.getters['instance/absoluteUrl'](object.artist.cover.urls.medium_square_crop)"
>
<i
v-else
class="huge circular inverted users violet icon"
/>
2020-02-05 14:06:07 +00:00
</div>
<div class="ui column right aligned">
2021-12-06 10:35:20 +00:00
<tags-list
v-if="object.artist.tags && object.artist.tags.length > 0"
:tags="object.artist.tags"
/>
<actor-link
v-if="object.actor"
:avatar="false"
:actor="object.attributed_to"
:display-name="true"
/>
2020-02-05 14:06:07 +00:00
<template v-if="totalTracks > 0">
2021-12-06 10:35:20 +00:00
<div class="ui hidden very small divider" />
<translate
v-if="object.artist.content_category === 'podcast'"
2021-12-06 10:35:20 +00:00
key="1"
translate-context="Content/Channel/Paragraph"
2020-02-05 14:06:07 +00:00
translate-plural="%{ count } episodes"
:translate-n="totalTracks"
2021-12-06 10:35:20 +00:00
:translate-params="{count: totalTracks}"
>
2020-02-05 14:06:07 +00:00
%{ count } episode
</translate>
2021-12-06 10:35:20 +00:00
<translate
v-else
key="2"
translate-context="*/*/*"
:translate-params="{count: totalTracks}"
:translate-n="totalTracks"
translate-plural="%{ count } tracks"
>
%{ count } track
</translate>
2020-03-25 14:32:10 +00:00
</template>
<template v-if="object.attributed_to.full_username === $store.state.auth.fullUsername || $store.getters['channels/isSubscribed'](object.uuid)">
2021-12-06 10:35:20 +00:00
<br><translate
translate-context="Content/Channel/Paragraph"
translate-plural="%{ count } subscribers"
:translate-n="object.subscriptions_count"
:translate-params="{count: object.subscriptions_count}"
>
%{ count } subscriber
</translate>
<br><translate
translate-context="Content/Channel/Paragraph"
translate-plural="%{ count } listenings"
:translate-n="object.downloads_count"
:translate-params="{count: object.downloads_count}"
>
%{ count } listening
</translate>
2020-02-05 14:06:07 +00:00
</template>
2021-12-06 10:35:20 +00:00
<div class="ui hidden small divider" />
<a
class="ui icon small basic button"
@click.stop.prevent="showSubscribeModal = true"
>
<i class="feed icon" />
2020-02-05 14:06:07 +00:00
</a>
2021-12-06 10:35:20 +00:00
<modal
2022-05-01 11:45:07 +00:00
v-model:show="showSubscribeModal"
2021-12-06 10:35:20 +00:00
class="tiny"
>
<h4 class="header">
2021-12-06 10:35:20 +00:00
<translate translate-context="Popup/Channel/Title/Verb">
Subscribe to this channel
</translate>
</h4>
<div class="scrollable content">
<div class="description">
<template v-if="$store.state.auth.authenticated">
<h3>
2021-12-06 10:35:20 +00:00
<i class="user icon" />
<translate translate-context="Content/Channels/Header">
Subscribe on Funkwhale
</translate>
</h3>
2021-12-06 10:35:20 +00:00
<subscribe-button
:channel="object"
@subscribed="object.subscriptions_count += 1"
@unsubscribed="object.subscriptions_count -= 1"
/>
</template>
<template v-if="object.rss_url">
<h3>
2021-12-06 10:35:20 +00:00
<i class="feed icon" />
<translate translate-context="Content/Channels/Header">
Subscribe via RSS
</translate>
</h3>
2021-12-06 10:35:20 +00:00
<p>
<translate translate-context="Content/Channels/Label">
Copy-paste the following URL in your favorite podcatcher:
</translate>
</p>
<copy-input :value="object.rss_url" />
</template>
<template v-if="object.actor">
<h3>
2021-12-06 10:35:20 +00:00
<i class="bell icon" />
<translate translate-context="Content/Channels/Header">
Subscribe on the Fediverse
</translate>
</h3>
2021-12-06 10:35:20 +00:00
<p>
<translate translate-context="Content/Channels/Label">
If you're using Mastodon or other fediverse applications, you can subscribe to this account:
</translate>
</p>
<copy-input :value="`@${object.actor.full_username}`" />
</template>
</div>
</div>
<div class="actions">
<button class="ui basic deny button">
2021-12-06 10:35:20 +00:00
<translate translate-context="*/*/Button.Label/Verb">
Cancel
</translate>
</button>
</div>
</modal>
2021-12-06 10:35:20 +00:00
<button
ref="dropdown"
v-dropdown="{direction: 'downward'}"
class="ui right floated pointing dropdown icon small basic button"
>
<i class="ellipsis vertical icon" />
2020-02-05 14:06:07 +00:00
<div class="menu">
2020-08-11 12:07:06 +00:00
<a
2020-02-05 14:06:07 +00:00
v-if="totalTracks > 0"
2021-12-06 10:35:20 +00:00
href=""
class="basic item"
2020-08-11 12:07:06 +00:00
@click.prevent="showEmbedModal = !showEmbedModal"
2021-12-06 10:35:20 +00:00
>
<i class="code icon" />
2020-02-05 14:06:07 +00:00
<translate translate-context="Content/*/Button.Label/Verb">Embed</translate>
2020-08-11 12:07:06 +00:00
</a>
<a
2020-09-01 09:31:08 +00:00
v-if="object.actor && object.actor.domain != $store.getters['instance/domain']"
2021-12-06 10:35:20 +00:00
:href="object.url"
target="_blank"
2021-12-06 10:35:20 +00:00
class="basic item"
>
<i class="external icon" />
<translate
:translate-params="{domain: object.actor.domain}"
translate-context="Content/*/Button.Label/Verb"
>View on %{ domain }</translate>
</a>
2021-12-06 10:35:20 +00:00
<div class="divider" />
2020-08-11 12:07:06 +00:00
<a
v-for="obj in getReportableObjs({account: object.attributed_to, channel: object})"
2020-02-05 14:06:07 +00:00
:key="obj.target.type + obj.target.id"
2021-12-06 10:35:20 +00:00
href=""
class="basic item"
@click.stop.prevent="$store.dispatch('moderation/report', obj.target)"
>
2020-02-05 14:06:07 +00:00
<i class="share icon" /> {{ obj.label }}
2020-08-11 12:07:06 +00:00
</a>
2020-02-05 14:06:07 +00:00
<template v-if="isOwner">
2021-12-06 10:35:20 +00:00
<div class="divider" />
2020-08-11 12:07:06 +00:00
<a
class="item"
2020-08-11 12:07:06 +00:00
href=""
2021-12-06 10:35:20 +00:00
@click.stop.prevent="showEditModal = true"
>
2022-03-22 15:13:19 +00:00
<i class="edit icon" />
<translate translate-context="*/*/*/Verb">Edit</translate>
2020-08-11 12:07:06 +00:00
</a>
<dangerous-button
v-if="object"
2021-12-06 10:35:20 +00:00
:class="['ui', {loading: isLoading}, 'item']"
@confirm="remove()"
>
2022-03-22 15:13:19 +00:00
<i class="ui trash icon" />
2021-12-06 10:35:20 +00:00
<translate translate-context="*/*/*/Verb">
Delete
</translate>
2022-05-01 14:15:57 +00:00
<template #modal-header>
2021-12-06 10:35:20 +00:00
<p>
2022-05-01 14:15:57 +00:00
<translate translate-context="Popup/Channel/Title">
Delete this Channel?
2021-12-06 10:35:20 +00:00
</translate>
</p>
2022-05-01 14:15:57 +00:00
</template>
<template #modal-content>
<div>
<p>
<translate translate-context="Content/Moderation/Paragraph">
The channel will be deleted, as well as any related files and data. This action is irreversible.
</translate>
</p>
</div>
</template>
<template #modal-confirm>
<p>
<translate translate-context="*/*/*/Verb">
Delete
</translate>
</p>
</template>
</dangerous-button>
</template>
2021-12-06 10:35:20 +00:00
<template v-if="$store.state.auth.availablePermissions['library']">
<div class="divider" />
<router-link
class="basic item"
:to="{name: 'manage.channels.detail', params: {id: object.uuid}}"
>
<i class="wrench icon" />
<translate translate-context="Content/Moderation/Link">
Open in moderation interface
</translate>
</router-link>
</template>
2020-02-05 14:06:07 +00:00
</div>
</button>
2020-02-05 14:06:07 +00:00
</div>
</div>
<h1 class="ui header">
2021-12-06 10:35:20 +00:00
<div
class="left aligned"
:title="object.artist.name"
>
2020-02-05 14:06:07 +00:00
{{ object.artist.name }}
2021-12-06 10:35:20 +00:00
<div class="ui hidden very small divider" />
<div
v-if="object.actor"
class="sub header ellipsis"
:title="object.actor.full_username"
>
2020-02-05 14:06:07 +00:00
{{ object.actor.full_username }}
</div>
2021-12-06 10:35:20 +00:00
<div
v-else
class="sub header ellipsis"
>
<a
:href="object.url || object.rss_url"
rel="noopener noreferrer"
target="_blank"
>
<i class="external link icon" />
<translate
:translate-params="{domain: externalDomain}"
translate-context="Content/Channel/Paragraph"
>Mirrored from %{ domain }</translate>
</a>
</div>
2020-02-05 14:06:07 +00:00
</div>
</h1>
<div class="header-buttons">
2021-12-06 10:35:20 +00:00
<div
v-if="isOwner"
class="ui buttons"
>
<button
class="ui basic labeled icon button"
@click.prevent.stop="$store.commit('channels/showUploadModal', {show: true, config: {channel: object}})"
>
<i class="upload icon" />
<translate translate-context="Content/Channels/Button.Label/Verb">
Upload
</translate>
</button>
</div>
2020-02-05 14:06:07 +00:00
<div class="ui buttons">
2021-12-06 10:35:20 +00:00
<play-button
:is-playable="isPlayable"
class="vibrant"
:artist="object.artist"
>
<translate translate-context="Content/Channels/Button.Label/Verb">
Play
</translate>
2020-02-05 14:06:07 +00:00
</play-button>
</div>
<div class="ui buttons">
2021-12-06 10:35:20 +00:00
<subscribe-button
:channel="object"
@subscribed="object.subscriptions_count += 1"
@unsubscribed="object.subscriptions_count -= 1"
/>
2020-02-05 14:06:07 +00:00
</div>
2021-12-06 10:35:20 +00:00
<modal
v-if="totalTracks > 0"
2022-05-01 11:45:07 +00:00
v-model:show="showEmbedModal"
2021-12-06 10:35:20 +00:00
>
<h4 class="header">
2021-12-06 10:35:20 +00:00
<translate translate-context="Popup/Artist/Title/Verb">
Embed this artist work on your website
</translate>
</h4>
<div class="scrolling content">
2020-02-05 14:06:07 +00:00
<div class="description">
2021-12-06 10:35:20 +00:00
<embed-wizard
:id="object.artist.id"
type="artist"
/>
2020-02-05 14:06:07 +00:00
</div>
</div>
<div class="actions">
<button class="ui basic deny button">
2021-12-06 10:35:20 +00:00
<translate translate-context="*/*/Button.Label/Verb">
Cancel
</translate>
</button>
2020-02-05 14:06:07 +00:00
</div>
</modal>
2021-12-06 10:35:20 +00:00
<modal
v-if="isOwner"
2022-05-01 11:45:07 +00:00
v-model:show="showEditModal"
2021-12-06 10:35:20 +00:00
>
<h4 class="header">
2021-12-06 10:35:20 +00:00
<translate
v-if="object.artist.content_category === 'podcast'"
key="1"
translate-context="Content/Channel/*"
>
Podcast channel
</translate>
<translate
v-else
key="2"
translate-context="Content/Channel/*"
>
Artist channel
</translate>
</h4>
<div class="scrolling content">
<channel-form
ref="editForm"
:object="object"
@loading="edit.isLoading = $event"
@submittable="edit.submittable = $event"
2021-12-06 10:35:20 +00:00
@updated="fetchData"
/>
<div class="ui hidden divider" />
</div>
<div class="actions">
<button class="ui left floated basic deny button">
2021-12-06 10:35:20 +00:00
<translate translate-context="*/*/Button.Label/Verb">
Cancel
</translate>
</button>
2021-12-06 10:35:20 +00:00
<button
:class="['ui', 'primary', 'confirm', {loading: edit.isLoading}, 'button']"
:disabled="!edit.submittable || null"
2021-12-06 10:35:20 +00:00
@click.stop="$refs.editForm.submit"
>
<translate translate-context="*/Channels/Button.Label">
Update channel
</translate>
</button>
</div>
</modal>
2020-02-05 14:06:07 +00:00
</div>
<div v-if="$store.getters['ui/layoutVersion'] === 'large'">
2020-02-05 14:06:07 +00:00
<rendered-description
:content="object.artist.description"
:update-url="`channels/${object.uuid}/`"
2021-12-06 10:35:20 +00:00
:can-update="false"
@updated="object = $event"
/>
2020-02-05 14:06:07 +00:00
</div>
</div>
<div class="nine wide column">
2020-02-05 14:06:07 +00:00
<div class="ui secondary pointing center aligned menu">
2021-12-06 10:35:20 +00:00
<router-link
class="item"
2022-05-01 12:57:04 +00:00
2021-12-06 10:35:20 +00:00
:to="{name: 'channels.detail', params: {id: id}}"
>
<translate translate-context="Content/Channels/Link">
Overview
</translate>
2020-02-05 14:06:07 +00:00
</router-link>
2021-12-06 10:35:20 +00:00
<router-link
class="item"
2022-05-01 12:57:04 +00:00
2021-12-06 10:35:20 +00:00
:to="{name: 'channels.detail.episodes', params: {id: id}}"
>
<translate
v-if="isPodcast"
key="1"
translate-context="Content/Channels/*"
>
All Episodes
</translate>
<translate
v-else
key="2"
translate-context="*/*/*"
>
Tracks
</translate>
2020-02-05 14:06:07 +00:00
</router-link>
</div>
2021-12-06 10:35:20 +00:00
<div class="ui hidden divider" />
<router-view
v-if="object"
:object="object"
@tracks-loaded="totalTracks = $event"
/>
2020-02-05 14:06:07 +00:00
</div>
</div>
</section>
</template>
</main>
</template>
<script>
2021-12-06 10:35:20 +00:00
import axios from 'axios'
2022-04-23 07:37:43 +00:00
import PlayButton from '~/components/audio/PlayButton.vue'
import EmbedWizard from '~/components/audio/EmbedWizard.vue'
import Modal from '~/components/semantic/Modal.vue'
import TagsList from '~/components/tags/List.vue'
import ReportMixin from '~/components/mixins/Report.vue'
2020-02-05 14:06:07 +00:00
2022-04-23 07:37:43 +00:00
import SubscribeButton from '~/components/channels/SubscribeButton.vue'
import ChannelForm from '~/components/audio/ChannelForm.vue'
2020-02-05 14:06:07 +00:00
export default {
components: {
PlayButton,
EmbedWizard,
Modal,
TagsList,
SubscribeButton,
2021-12-06 10:35:20 +00:00
ChannelForm
},
mixins: [ReportMixin],
beforeRouteUpdate (to, from, next) {
to.meta.preserveScrollPosition = true
next()
2020-02-05 14:06:07 +00:00
},
2021-12-06 10:50:04 +00:00
props: { id: { type: String, required: true } },
2021-12-06 10:35:20 +00:00
data () {
2020-02-05 14:06:07 +00:00
return {
isLoading: true,
object: null,
totalTracks: 0,
latestTracks: null,
showEmbedModal: false,
showEditModal: false,
showSubscribeModal: false,
edit: {
submittable: false,
2021-12-06 10:35:20 +00:00
loading: false
}
2020-02-05 14:06:07 +00:00
}
},
2021-12-06 10:35:20 +00:00
computed: {
externalDomain () {
const parser = document.createElement('a')
parser.href = this.object.url || this.object.rss_url
return parser.hostname
},
isOwner () {
return this.$store.state.auth.authenticated && this.object.attributed_to.full_username === this.$store.state.auth.fullUsername
},
isPodcast () {
return this.object.artist.content_category === 'podcast'
},
labels () {
return {
title: this.$pgettext('*/*/*', 'Channel')
}
},
contentFilter () {
return this.$store.getters['moderation/artistFilters']().filter((e) => {
return e.target.id === this.object.artist.id
})[0]
},
isPlayable () {
return this.totalTracks > 0
}
},
watch: {
id () {
this.fetchData()
2022-03-22 15:13:19 +00:00
},
'$store.state.channels.latestPublication' (v) {
if (v && v.uploads && v.channel.uuid === this.object.uuid) {
this.fetchData()
}
2021-12-06 10:35:20 +00:00
}
},
2021-12-06 10:35:20 +00:00
async created () {
2020-02-05 14:06:07 +00:00
await this.fetchData()
2021-12-06 10:35:20 +00:00
const authenticated = this.$store.state.auth.authenticated
if (!authenticated && this.$store.getters['instance/domain'] !== this.object.actor.domain) {
this.$router.push({ name: 'login', query: { next: this.$route.fullPath } })
}
2020-02-05 14:06:07 +00:00
},
methods: {
2021-12-06 10:35:20 +00:00
async fetchData () {
const self = this
this.showEditModal = false
this.edit.isLoading = false
2020-02-05 14:06:07 +00:00
this.isLoading = true
2021-12-06 10:35:20 +00:00
const channelPromise = axios.get(`channels/${this.id}`, { params: { refresh: 'true' } }).then(response => {
2020-02-05 14:06:07 +00:00
self.object = response.data
2021-12-06 10:35:20 +00:00
if ((self.id === response.data.uuid) && response.data.actor) {
// replace with the pretty channel url if possible
2021-12-06 10:35:20 +00:00
const actor = response.data.actor
if (actor.is_local) {
2021-12-06 10:35:20 +00:00
self.$router.replace({ name: 'channels.detail', params: { id: actor.preferred_username } })
} else {
2021-12-06 10:35:20 +00:00
self.$router.replace({ name: 'channels.detail', params: { id: actor.full_username } })
}
}
2022-03-22 15:13:19 +00:00
self.totalTracks = response.data.artist.tracks_count
self.isLoading = false
2020-02-05 14:06:07 +00:00
})
await channelPromise
},
remove () {
2021-12-06 10:35:20 +00:00
const self = this
self.isLoading = true
axios.delete(`channels/${this.object.uuid}`).then((response) => {
self.isLoading = false
self.$emit('deleted')
2021-12-06 10:35:20 +00:00
self.$router.push({ name: 'profile.overview', params: { username: self.$store.state.auth.username } })
}, error => {
self.isLoading = false
self.errors = error.backendErrors
})
2020-02-05 14:06:07 +00:00
}
}
}
</script>