[WIP] refactor(front): channel detail page
This commit is contained in:
parent
07fdf734a9
commit
a717d02133
|
@ -9,6 +9,7 @@ import { useRoute } from 'vue-router'
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
import LoginModal from '~/components/common/LoginModal.vue'
|
import LoginModal from '~/components/common/LoginModal.vue'
|
||||||
|
import Button from '~/components/ui/Button.vue'
|
||||||
|
|
||||||
interface Events {
|
interface Events {
|
||||||
(e: 'unsubscribed'): void
|
(e: 'unsubscribed'): void
|
||||||
|
@ -46,20 +47,21 @@ const loginModal = ref()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<button
|
<Button
|
||||||
v-if="store.state.auth.authenticated"
|
v-if="store.state.auth.authenticated"
|
||||||
:class="['ui', 'pink', {'inverted': isSubscribed}, {'favorited': isSubscribed}, 'icon', 'labeled', 'button']"
|
:class="['pink', {'inverted': isSubscribed}, {'favorited': isSubscribed}]"
|
||||||
|
outline
|
||||||
|
icon="bi-heart-fill"
|
||||||
@click.stop="toggle"
|
@click.stop="toggle"
|
||||||
>
|
>
|
||||||
<i class="heart icon" />
|
|
||||||
{{ title }}
|
{{ title }}
|
||||||
</button>
|
</Button>
|
||||||
<button
|
<Button
|
||||||
v-else
|
v-else
|
||||||
:class="['ui', 'pink', 'icon', 'labeled', 'button']"
|
outline
|
||||||
|
icon="bi-heart"
|
||||||
@click="loginModal.show = true"
|
@click="loginModal.show = true"
|
||||||
>
|
>
|
||||||
<i class="heart icon" />
|
|
||||||
{{ title }}
|
{{ title }}
|
||||||
<login-modal
|
<login-modal
|
||||||
ref="loginModal"
|
ref="loginModal"
|
||||||
|
@ -69,5 +71,5 @@ const loginModal = ref()
|
||||||
:cover="channel.artist?.cover!"
|
:cover="channel.artist?.cover!"
|
||||||
@created="loginModal.show = false"
|
@created="loginModal.show = false"
|
||||||
/>
|
/>
|
||||||
</button>
|
</Button>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -67,7 +67,7 @@ const url = computed(() => {
|
||||||
v-if="avatar"
|
v-if="avatar"
|
||||||
:actor="actor"
|
:actor="actor"
|
||||||
/>
|
/>
|
||||||
<slot>{{ repr }}</slot>
|
<slot>@{{ repr }}</slot>
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -3,7 +3,8 @@ import { truncate } from '~/utils/filters'
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
import TagsList from '~/components/tags/List.vue'
|
import Layout from '~/components/ui/Layout.vue'
|
||||||
|
import Pill from '~/components/ui/Pill.vue'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
tags: string[]
|
tags: string[]
|
||||||
|
@ -35,23 +36,23 @@ const tags = computed(() => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="component-tags-list">
|
<Layout flex style="gap: 8px" class="component-tags-list">
|
||||||
<router-link
|
<router-link
|
||||||
v-for="tag in tags"
|
v-for="tag in tags"
|
||||||
:key="tag"
|
:key="tag"
|
||||||
:to="{name: props.detailRoute, params: { id: tag } }"
|
:to="{name: props.detailRoute, params: { id: tag } }"
|
||||||
:class="['ui', 'circular', 'hashtag', 'label', props.labelClasses]"
|
:class="props.labelClasses"
|
||||||
>
|
>
|
||||||
<span class="hashtag symbol" />
|
<Pill solid raised secondary>
|
||||||
{{ truncate(tag, props.truncateSize) }}
|
<span class="hashtag symbol" />
|
||||||
|
{{ truncate(tag, props.truncateSize) }}
|
||||||
|
</Pill>
|
||||||
</router-link>
|
</router-link>
|
||||||
<div
|
<Pill
|
||||||
v-if="props.showMore && tags.length < props.tags.length"
|
v-if="props.showMore && tags.length < props.tags.length"
|
||||||
role="button"
|
|
||||||
class="ui circular inverted accent label"
|
|
||||||
@click.prevent="honorLimit = false"
|
@click.prevent="honorLimit = false"
|
||||||
>
|
>
|
||||||
{{ t('components.tags.List.button.more', props.tags.length - tags.length) }}
|
{{ t('components.tags.List.button.more', props.tags.length - tags.length) }}
|
||||||
</div>
|
</Pill>
|
||||||
</div>
|
</Layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -11,9 +11,21 @@ import axios from 'axios'
|
||||||
import SubscribeButton from '~/components/channels/SubscribeButton.vue'
|
import SubscribeButton from '~/components/channels/SubscribeButton.vue'
|
||||||
import ChannelForm from '~/components/audio/ChannelForm.vue'
|
import ChannelForm from '~/components/audio/ChannelForm.vue'
|
||||||
import EmbedWizard from '~/components/audio/EmbedWizard.vue'
|
import EmbedWizard from '~/components/audio/EmbedWizard.vue'
|
||||||
import SemanticModal from '~/components/semantic/Modal.vue'
|
import HumanDuration from '~/components/common/HumanDuration.vue'
|
||||||
import PlayButton from '~/components/audio/PlayButton.vue'
|
import PlayButton from '~/components/audio/PlayButton.vue'
|
||||||
import TagsList from '~/components/tags/List.vue'
|
import TagsList from '~/components/tags/List.vue'
|
||||||
|
import SemanticModal from '~/components/semantic/Modal.vue'
|
||||||
|
import RadioButton from '~/components/radios/Button.vue'
|
||||||
|
import Loader from '~/components/ui/Loader.vue'
|
||||||
|
import Button from '~/components/ui/Button.vue'
|
||||||
|
import Tabs from '~/components/ui/Tabs.vue'
|
||||||
|
import Tab from '~/components/ui/Tab.vue'
|
||||||
|
import OptionsButton from '~/components/ui/button/Options.vue'
|
||||||
|
import Popover from '~/components/ui/Popover.vue'
|
||||||
|
import PopoverItem from '~/components/ui/popover/PopoverItem.vue'
|
||||||
|
import Layout from '~/components/ui/Layout.vue'
|
||||||
|
import Modal from '~/components/ui/Modal.vue'
|
||||||
|
import Spacer from '~/components/ui/Spacer.vue'
|
||||||
|
|
||||||
import useErrorHandler from '~/composables/useErrorHandler'
|
import useErrorHandler from '~/composables/useErrorHandler'
|
||||||
import useReport from '~/composables/moderation/useReport'
|
import useReport from '~/composables/moderation/useReport'
|
||||||
|
@ -92,6 +104,7 @@ const fetchData = async () => {
|
||||||
|
|
||||||
watch(() => props.id, fetchData, { immediate: true })
|
watch(() => props.id, fetchData, { immediate: true })
|
||||||
|
|
||||||
|
|
||||||
const uuid = computed(() => store.state.channels.latestPublication?.channel.uuid)
|
const uuid = computed(() => store.state.channels.latestPublication?.channel.uuid)
|
||||||
watch([uuid, object], ([uuid, object], [lastUuid, lastObject]) => {
|
watch([uuid, object], ([uuid, object], [lastUuid, lastObject]) => {
|
||||||
if (object?.uuid && object.uuid === lastObject?.uuid) return
|
if (object?.uuid && object.uuid === lastObject?.uuid) return
|
||||||
|
@ -128,49 +141,36 @@ const updateSubscriptionCount = (delta: number) => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main
|
<Layout stack main
|
||||||
v-title="labels.title"
|
v-title="labels.title"
|
||||||
class="main"
|
|
||||||
>
|
>
|
||||||
<div
|
<Loader v-if="isLoading" />
|
||||||
v-if="isLoading"
|
|
||||||
class="ui vertical segment"
|
|
||||||
>
|
|
||||||
<div :class="['ui', 'centered', 'active', 'inline', 'loader']" />
|
|
||||||
</div>
|
|
||||||
<template v-if="object && !isLoading">
|
<template v-if="object && !isLoading">
|
||||||
<section
|
<section
|
||||||
v-title="object.artist?.name"
|
v-title="object.artist?.name"
|
||||||
class="ui head vertical stripe segment container"
|
|
||||||
>
|
>
|
||||||
<div class="ui stackable grid">
|
<Layout flex>
|
||||||
<div class="seven wide column">
|
<img
|
||||||
<div class="ui two column grid">
|
v-if="object.artist?.cover"
|
||||||
<div class="column">
|
alt=""
|
||||||
<img
|
class="huge channel-image"
|
||||||
v-if="object.artist?.cover"
|
:src="store.getters['instance/absoluteUrl'](object.artist.cover.urls.large_square_crop)"
|
||||||
alt=""
|
>
|
||||||
class="huge channel-image"
|
<i
|
||||||
:src="store.getters['instance/absoluteUrl'](object.artist.cover.urls.medium_square_crop)"
|
v-else
|
||||||
>
|
class="huge circular inverted bi bi-person-circle-fill violet"
|
||||||
<i
|
/>
|
||||||
v-else
|
<Layout stack noGap style="flex: 1;">
|
||||||
class="huge circular inverted users violet icon"
|
<h1>
|
||||||
/>
|
<div
|
||||||
|
:title="object.artist?.name"
|
||||||
|
>
|
||||||
|
{{ object.artist?.name }}
|
||||||
</div>
|
</div>
|
||||||
<div class="ui column right aligned">
|
</h1>
|
||||||
<TagsList
|
<Layout stack class="meta" style="gap: 0;">
|
||||||
v-if="object.artist?.tags && object.artist?.tags.length > 0"
|
<Layout flex noGap>
|
||||||
:tags="object.artist.tags"
|
|
||||||
/>
|
|
||||||
<actor-link
|
|
||||||
v-if="object.actor"
|
|
||||||
:avatar="false"
|
|
||||||
:actor="object.attributed_to"
|
|
||||||
:display-name="true"
|
|
||||||
/>
|
|
||||||
<template v-if="totalTracks > 0">
|
<template v-if="totalTracks > 0">
|
||||||
<div class="ui hidden very small divider" />
|
|
||||||
<span
|
<span
|
||||||
v-if="object.artist?.content_category === 'podcast'"
|
v-if="object.artist?.content_category === 'podcast'"
|
||||||
>
|
>
|
||||||
|
@ -183,213 +183,169 @@ const updateSubscriptionCount = (delta: number) => {
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="object.attributed_to.full_username === store.state.auth.fullUsername || store.getters['channels/isSubscribed'](object.uuid)">
|
<template v-if="object.attributed_to.full_username === store.state.auth.fullUsername || store.getters['channels/isSubscribed'](object.uuid)">
|
||||||
<br>
|
<i class="bi bi-dot" />
|
||||||
{{ t('views.channels.DetailBase.meta.subscribers', object?.subscriptions_count ?? 0) }}
|
{{ t('views.channels.DetailBase.meta.subscribers', object?.subscriptions_count ?? 0) }}
|
||||||
<br>
|
<i class="bi bi-dot" />
|
||||||
{{ t('views.channels.DetailBase.meta.listenings', object?.downloads_count ?? 0) }}
|
{{ t('views.channels.DetailBase.meta.listenings', object?.downloads_count ?? 0) }}
|
||||||
</template>
|
</template>
|
||||||
<div class="ui hidden small divider" />
|
|
||||||
<a
|
|
||||||
class="ui icon small basic button"
|
|
||||||
@click.stop.prevent="showSubscribeModal = true"
|
|
||||||
>
|
|
||||||
<i class="feed icon" />
|
|
||||||
</a>
|
|
||||||
<semantic-modal
|
|
||||||
v-model:show="showSubscribeModal"
|
|
||||||
class="tiny"
|
|
||||||
>
|
|
||||||
<h4 class="header">
|
|
||||||
{{ t('views.channels.DetailBase.modal.subscribe.header') }}
|
|
||||||
</h4>
|
|
||||||
<div class="scrollable content">
|
|
||||||
<div class="description">
|
|
||||||
<template v-if="store.state.auth.authenticated">
|
|
||||||
<h3>
|
|
||||||
<i class="user icon" />
|
|
||||||
{{ t('views.channels.DetailBase.modal.subscribe.funkwhale.header') }}
|
|
||||||
</h3>
|
|
||||||
<subscribe-button
|
|
||||||
:channel="object"
|
|
||||||
@subscribed="updateSubscriptionCount(1)"
|
|
||||||
@unsubscribed="updateSubscriptionCount(-1)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template v-if="object.rss_url">
|
|
||||||
<h3>
|
|
||||||
<i class="feed icon" />
|
|
||||||
{{ t('views.channels.DetailBase.modal.subscribe.rss.header') }}
|
|
||||||
</h3>
|
|
||||||
<p>
|
|
||||||
{{ t('views.channels.DetailBase.modal.subscribe.rss.content.help') }}
|
|
||||||
</p>
|
|
||||||
<copy-input :value="object.rss_url" />
|
|
||||||
</template>
|
|
||||||
<template v-if="object.actor">
|
|
||||||
<h3>
|
|
||||||
<i class="bell icon" />
|
|
||||||
{{ t('views.channels.DetailBase.modal.subscribe.fediverse.header') }}
|
|
||||||
</h3>
|
|
||||||
<p>
|
|
||||||
{{ t('views.channels.DetailBase.modal.subscribe.fediverse.content.help') }}
|
|
||||||
</p>
|
|
||||||
<copy-input
|
|
||||||
id="copy-tag"
|
|
||||||
:value="`@${object.actor.full_username}`"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="actions">
|
|
||||||
<button class="ui basic deny button">
|
|
||||||
{{ t('views.channels.DetailBase.button.cancel') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</semantic-modal>
|
|
||||||
<button
|
|
||||||
ref="dropdown"
|
|
||||||
v-dropdown="{direction: 'downward'}"
|
|
||||||
class="ui right floated pointing dropdown icon small basic button"
|
|
||||||
>
|
|
||||||
<i class="ellipsis vertical icon" />
|
|
||||||
<div class="menu">
|
|
||||||
<a
|
|
||||||
v-if="totalTracks > 0"
|
|
||||||
href=""
|
|
||||||
class="basic item"
|
|
||||||
@click.prevent="showEmbedModal = !showEmbedModal"
|
|
||||||
>
|
|
||||||
<i class="code icon" />
|
|
||||||
{{ t('views.channels.DetailBase.button.embed') }}
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
v-if="object.actor && object.actor.domain != store.getters['instance/domain']"
|
|
||||||
:href="object.url"
|
|
||||||
target="_blank"
|
|
||||||
class="basic item"
|
|
||||||
>
|
|
||||||
<i class="external icon" />
|
|
||||||
{{ t('views.channels.DetailBase.link.domainView', {domain: object.actor.domain}) }}
|
|
||||||
</a>
|
|
||||||
<div class="divider" />
|
|
||||||
<a
|
|
||||||
v-for="obj in getReportableObjects({account: object.attributed_to, channel: object})"
|
|
||||||
:key="obj.target.type + obj.target.id"
|
|
||||||
href=""
|
|
||||||
class="basic item"
|
|
||||||
@click.stop.prevent="report(obj)"
|
|
||||||
>
|
|
||||||
<i class="share icon" /> {{ obj.label }}
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<template v-if="isOwner">
|
<div v-if="totalTracks > 0">
|
||||||
<div class="divider" />
|
<i class="bi bi-dot" />
|
||||||
<a
|
<human-duration
|
||||||
class="item"
|
v-if="totalTracks > 0"
|
||||||
href=""
|
:duration="totalTracks"
|
||||||
@click.stop.prevent="showEditModal = true"
|
/>
|
||||||
>
|
|
||||||
<i class="edit icon" />
|
|
||||||
{{ t('views.channels.DetailBase.button.edit') }}
|
|
||||||
</a>
|
|
||||||
<dangerous-button
|
|
||||||
v-if="object"
|
|
||||||
:class="['ui', {loading: isLoading}, 'item']"
|
|
||||||
@confirm="remove()"
|
|
||||||
>
|
|
||||||
<i class="ui trash icon" />
|
|
||||||
{{ t('views.channels.DetailBase.button.delete') }}
|
|
||||||
<template #modal-header>
|
|
||||||
<p>
|
|
||||||
{{ t('views.channels.DetailBase.modal.delete.header') }}
|
|
||||||
</p>
|
|
||||||
</template>
|
|
||||||
<template #modal-content>
|
|
||||||
<div>
|
|
||||||
<p>
|
|
||||||
{{ t('views.channels.DetailBase.modal.delete.content.warning') }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #modal-confirm>
|
|
||||||
<p>
|
|
||||||
{{ t('views.channels.DetailBase.button.confirm') }}
|
|
||||||
</p>
|
|
||||||
</template>
|
|
||||||
</dangerous-button>
|
|
||||||
</template>
|
|
||||||
<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" />
|
|
||||||
{{ t('views.channels.DetailBase.link.moderation') }}
|
|
||||||
</router-link>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<h1 class="ui header">
|
|
||||||
<div
|
|
||||||
class="left aligned"
|
|
||||||
:title="object.artist?.name"
|
|
||||||
>
|
|
||||||
{{ object.artist?.name }}
|
|
||||||
<div class="ui hidden very small divider" />
|
|
||||||
<div
|
|
||||||
v-if="object.actor"
|
|
||||||
class="sub header ellipsis"
|
|
||||||
:title="object.actor.full_username"
|
|
||||||
>
|
|
||||||
{{ object.actor.full_username }}
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
</Layout>
|
||||||
v-else
|
<Layout flex noGap>
|
||||||
class="sub header ellipsis"
|
<span
|
||||||
|
v-if="object.artist?.content_category === 'podcast'"
|
||||||
>
|
>
|
||||||
|
{{ t('views.channels.DetailBase.header.podcastChannel') }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-if="!object.actor"
|
||||||
|
>
|
||||||
|
<i class="bi bi-dot" />
|
||||||
<a
|
<a
|
||||||
:href="object.url || object.rss_url"
|
:href="object.url || object.rss_url"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
<i class="external link icon" />
|
<i class="bi bi-box-arrow-up-right" />
|
||||||
{{ t('views.channels.DetailBase.link.mirrored', {domain: externalDomain}) }}
|
{{ t('views.channels.DetailBase.link.mirrored', {domain: externalDomain}) }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</span>
|
||||||
</div>
|
<span
|
||||||
</h1>
|
v-else
|
||||||
<div class="header-buttons">
|
|
||||||
<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" />
|
{{ t('views.channels.DetailBase.header.artistChannel') }}
|
||||||
{{ t('views.channels.DetailBase.button.upload') }}
|
</span>
|
||||||
</button>
|
<span v-if="object.actor">
|
||||||
</div>
|
<i class="bi bi-dot" />
|
||||||
<div class="ui buttons">
|
{{ t('views.library.LibraryBase.link.owner') }}
|
||||||
<play-button
|
</span>
|
||||||
:is-playable="isPlayable"
|
<Spacer h :size="4" />
|
||||||
class="vibrant"
|
<ActorLink
|
||||||
:artist="object.artist"
|
v-if="object.actor"
|
||||||
>
|
discrete
|
||||||
{{ t('views.channels.DetailBase.button.play') }}
|
:avatar="false"
|
||||||
</play-button>
|
:actor="object.attributed_to"
|
||||||
</div>
|
:display-name="true"
|
||||||
<div class="ui buttons">
|
|
||||||
<subscribe-button
|
|
||||||
:channel="object"
|
|
||||||
@subscribed="updateSubscriptionCount(1)"
|
|
||||||
@unsubscribed="updateSubscriptionCount(-1)"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</Layout>
|
||||||
|
</Layout>
|
||||||
|
<rendered-description
|
||||||
|
:content="object.artist?.description"
|
||||||
|
:update-url="`channels/${object.uuid}/`"
|
||||||
|
:can-update="false"
|
||||||
|
@updated="object = $event"
|
||||||
|
/>
|
||||||
|
<Layout flex class="header-buttons">
|
||||||
|
<Button
|
||||||
|
v-if="isOwner"
|
||||||
|
primary
|
||||||
|
icon="bi-upload"
|
||||||
|
@click.prevent.stop="store.commit('channels/showUploadModal', {show: true, config: {channel: object}})"
|
||||||
|
>
|
||||||
|
{{ t('views.channels.DetailBase.button.upload') }}
|
||||||
|
</Button>
|
||||||
|
<PlayButton
|
||||||
|
:is-playable="isPlayable"
|
||||||
|
split
|
||||||
|
class="vibrant"
|
||||||
|
:artist="object.artist"
|
||||||
|
>
|
||||||
|
{{ t('views.channels.DetailBase.button.play') }}
|
||||||
|
</PlayButton>
|
||||||
|
<RadioButton
|
||||||
|
type="artist"
|
||||||
|
:object-id="object.artist.id"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Popover>
|
||||||
|
<template #default="{ toggleOpen }">
|
||||||
|
<OptionsButton
|
||||||
|
@click="toggleOpen"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #items>
|
||||||
|
<PopoverItem
|
||||||
|
v-if="totalTracks > 0"
|
||||||
|
icon="bi-code-slash"
|
||||||
|
@click.prevent="showEmbedModal = !showEmbedModal"
|
||||||
|
>
|
||||||
|
{{ t('views.channels.DetailBase.button.embed') }}
|
||||||
|
</PopoverItem>
|
||||||
|
<PopoverItem
|
||||||
|
v-if="object.actor && object.actor.domain != store.getters['instance/domain']"
|
||||||
|
:href="object.url"
|
||||||
|
target="_blank"
|
||||||
|
icon="bi-box-arrow-up-right"
|
||||||
|
>
|
||||||
|
{{ t('views.channels.DetailBase.link.domainView', {domain: object.actor.domain}) }}
|
||||||
|
</PopoverItem>
|
||||||
|
<hr>
|
||||||
|
<PopoverItem
|
||||||
|
v-for="obj in getReportableObjects({account: object.attributed_to, channel: object})"
|
||||||
|
icon="bi-share"
|
||||||
|
:key="obj.target.type + obj.target.id"
|
||||||
|
@click.stop.prevent="report(obj)"
|
||||||
|
>
|
||||||
|
{{ obj.label }}
|
||||||
|
</PopoverItem>
|
||||||
|
|
||||||
|
<template v-if="isOwner">
|
||||||
|
<hr>
|
||||||
|
<PopoverItem
|
||||||
|
icon="bi-pencil"
|
||||||
|
@click.stop.prevent="showEditModal = true"
|
||||||
|
>
|
||||||
|
{{ t('views.channels.DetailBase.button.edit') }}
|
||||||
|
</PopoverItem>
|
||||||
|
<dangerous-button
|
||||||
|
v-if="object"
|
||||||
|
:class="['ui', {loading: isLoading}, 'item']"
|
||||||
|
@confirm="remove()"
|
||||||
|
>
|
||||||
|
<i class="bi bi-trash" />
|
||||||
|
{{ t('views.channels.DetailBase.button.delete') }}
|
||||||
|
<template #modal-header>
|
||||||
|
<p>
|
||||||
|
{{ t('views.channels.DetailBase.modal.delete.header') }}
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
<template #modal-content>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
{{ t('views.channels.DetailBase.modal.delete.content.warning') }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #modal-confirm>
|
||||||
|
<p>
|
||||||
|
{{ t('views.channels.DetailBase.button.confirm') }}
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
</dangerous-button>
|
||||||
|
</template>
|
||||||
|
<template v-if="store.state.auth.availablePermissions['library']">
|
||||||
|
<hr>
|
||||||
|
<PopoverItem
|
||||||
|
:to="{name: 'manage.channels.detail', params: {id: object.uuid}}"
|
||||||
|
icon="bi-wrench"
|
||||||
|
>
|
||||||
|
{{ t('views.channels.DetailBase.link.moderation') }}
|
||||||
|
</PopoverItem>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</Popover>
|
||||||
|
<Spacer h grow />
|
||||||
|
<subscribe-button
|
||||||
|
:channel="object"
|
||||||
|
@subscribed="updateSubscriptionCount(1)"
|
||||||
|
@unsubscribed="updateSubscriptionCount(-1)"
|
||||||
|
/>
|
||||||
|
|
||||||
<semantic-modal
|
<semantic-modal
|
||||||
v-if="totalTracks > 0"
|
v-if="totalTracks > 0"
|
||||||
|
@ -451,51 +407,103 @@ const updateSubscriptionCount = (delta: number) => {
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</semantic-modal>
|
</semantic-modal>
|
||||||
</div>
|
<Button
|
||||||
<div v-if="store.getters['ui/layoutVersion'] === 'large'">
|
secondary
|
||||||
<rendered-description
|
icon="bi-rss"
|
||||||
:content="object.artist?.description"
|
@click.stop.prevent="showSubscribeModal = true"
|
||||||
:update-url="`channels/${object.uuid}/`"
|
|
||||||
:can-update="false"
|
|
||||||
@updated="object = $event"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
<semantic-modal
|
||||||
</div>
|
v-model:show="showSubscribeModal"
|
||||||
<div class="nine wide column">
|
class="tiny"
|
||||||
<div class="ui secondary pointing center aligned menu">
|
|
||||||
<router-link
|
|
||||||
class="item"
|
|
||||||
|
|
||||||
:to="{name: 'channels.detail', params: {id: id}}"
|
|
||||||
>
|
>
|
||||||
{{ t('views.channels.DetailBase.link.channelOverview') }}
|
<h4 class="header">
|
||||||
</router-link>
|
{{ t('views.channels.DetailBase.modal.subscribe.header') }}
|
||||||
<router-link
|
</h4>
|
||||||
class="item"
|
<div class="scrollable content">
|
||||||
|
<div class="description">
|
||||||
:to="{name: 'channels.detail.episodes', params: {id: id}}"
|
<template v-if="store.state.auth.authenticated">
|
||||||
>
|
<h3>
|
||||||
<span
|
<i class="user icon" />
|
||||||
v-if="isPodcast"
|
{{ t('views.channels.DetailBase.modal.subscribe.funkwhale.header') }}
|
||||||
>
|
</h3>
|
||||||
{{ t('views.channels.DetailBase.link.channelEpisodes') }}
|
<subscribe-button
|
||||||
</span>
|
:channel="object"
|
||||||
<span
|
@subscribed="updateSubscriptionCount(1)"
|
||||||
v-else
|
@unsubscribed="updateSubscriptionCount(-1)"
|
||||||
>
|
/>
|
||||||
{{ t('views.channels.DetailBase.link.channelTracks') }}
|
</template>
|
||||||
</span>
|
<template v-if="object.rss_url">
|
||||||
</router-link>
|
<h3>
|
||||||
</div>
|
<i class="feed icon" />
|
||||||
<div class="ui hidden divider" />
|
{{ t('views.channels.DetailBase.modal.subscribe.rss.header') }}
|
||||||
|
</h3>
|
||||||
|
<p>
|
||||||
|
{{ t('views.channels.DetailBase.modal.subscribe.rss.content.help') }}
|
||||||
|
</p>
|
||||||
|
<copy-input :value="object.rss_url" />
|
||||||
|
</template>
|
||||||
|
<template v-if="object.actor">
|
||||||
|
<h3>
|
||||||
|
<i class="bell icon" />
|
||||||
|
{{ t('views.channels.DetailBase.modal.subscribe.fediverse.header') }}
|
||||||
|
</h3>
|
||||||
|
<p>
|
||||||
|
{{ t('views.channels.DetailBase.modal.subscribe.fediverse.content.help') }}
|
||||||
|
</p>
|
||||||
|
<copy-input
|
||||||
|
id="copy-tag"
|
||||||
|
:value="`@${object.actor.full_username}`"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="actions">
|
||||||
|
<button class="ui basic deny button">
|
||||||
|
{{ t('views.channels.DetailBase.button.cancel') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</semantic-modal>
|
||||||
|
</Layout>
|
||||||
|
</Layout>
|
||||||
|
</Layout>
|
||||||
|
<hr>
|
||||||
|
<TagsList
|
||||||
|
v-if="object.artist?.tags && object.artist?.tags.length > 0"
|
||||||
|
:tags="object.artist.tags"
|
||||||
|
/>
|
||||||
|
<Tabs>
|
||||||
|
<Tab
|
||||||
|
:title="t('views.channels.DetailBase.link.channelOverview')"
|
||||||
|
:to="{name: 'channels.detail', params: {id: id}}"
|
||||||
|
>
|
||||||
<router-view
|
<router-view
|
||||||
v-if="object"
|
v-if="object"
|
||||||
:object="object"
|
:object="object"
|
||||||
@tracks-loaded="totalTracks = $event"
|
@tracks-loaded="totalTracks = $event"
|
||||||
/>
|
/>
|
||||||
</div>
|
</Tab>
|
||||||
</div>
|
<Tab
|
||||||
|
:title="t('views.channels.DetailBase.link.channelEpisodes')"
|
||||||
|
:to="{name: 'channels.detail.episodes', params: {id: id}}"
|
||||||
|
>
|
||||||
|
<router-view
|
||||||
|
v-if="object"
|
||||||
|
:object="object"
|
||||||
|
@tracks-loaded="totalTracks = $event"
|
||||||
|
/>
|
||||||
|
</Tab>
|
||||||
|
</Tabs>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
</main>
|
</Layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.channel-image {
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
.meta {
|
||||||
|
line-height: 24px;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue