433 lines
16 KiB
Vue
433 lines
16 KiB
Vue
<script setup lang="ts">
|
|
import { humanSize } from '~/utils/filters'
|
|
import { useStore } from '~/store'
|
|
import { get } from 'lodash-es'
|
|
import { computed } from 'vue'
|
|
|
|
import axios from 'axios'
|
|
|
|
import useMarkdown from '~/composables/useMarkdown'
|
|
import type { NodeInfo } from '~/store/instance'
|
|
import { useI18n } from 'vue-i18n'
|
|
|
|
const store = useStore()
|
|
const nodeinfo = computed(() => store.state.instance.nodeinfo)
|
|
|
|
const fetchData = async () => {
|
|
const response = await axios.get('instance/nodeinfo/2.0/')
|
|
store.commit('instance/nodeinfo', response.data)
|
|
}
|
|
fetchData()
|
|
|
|
const { t } = useI18n()
|
|
const labels = computed(() => ({
|
|
title: t('components.AboutPod.title')
|
|
}))
|
|
|
|
const podName = computed(() => get(nodeinfo.value, 'metadata.nodeName') || 'Funkwhale')
|
|
const banner = computed(() => get(nodeinfo.value, 'metadata.banner'))
|
|
const longDescription = useMarkdown(() => get(nodeinfo.value, 'metadata.longDescription', ''))
|
|
const rules = useMarkdown(() => get(nodeinfo.value, 'metadata.rules', ''))
|
|
const terms = useMarkdown(() => get(nodeinfo.value, 'metadata.terms', ''))
|
|
const contactEmail = computed(() => get(nodeinfo.value, 'metadata.contactEmail'))
|
|
const anonymousCanListen = computed(() => get(nodeinfo.value, 'metadata.library.anonymousCanListen'))
|
|
const allowListEnabled = computed(() => get(nodeinfo.value, 'metadata.allowList.enabled'))
|
|
const version = computed(() => get(nodeinfo.value, 'software.version'))
|
|
const openRegistrations = computed(() => get(nodeinfo.value, 'openRegistrations'))
|
|
const defaultUploadQuota = computed(() => get(nodeinfo.value, 'metadata.defaultUploadQuota'))
|
|
const federationEnabled = computed(() => get(nodeinfo.value, 'metadata.library.federationEnabled'))
|
|
|
|
const onDesktop = computed(() => window.innerWidth > 800)
|
|
|
|
const stats = computed(() => {
|
|
const info = nodeinfo.value ?? {} as NodeInfo
|
|
|
|
const data = {
|
|
users: get(info, 'usage.users.activeMonth', null),
|
|
hours: get(info, 'metadata.library.music.hours', null),
|
|
artists: get(info, 'metadata.library.artists.total', null),
|
|
albums: get(info, 'metadata.library.albums.total', null),
|
|
tracks: get(info, 'metadata.library.tracks.total', null),
|
|
listenings: get(info, 'metadata.usage.listenings.total', null)
|
|
}
|
|
|
|
if (data.users === null || data.artists === null) {
|
|
return data
|
|
}
|
|
|
|
return data
|
|
})
|
|
|
|
const headerStyle = computed(() => {
|
|
if (!banner.value) {
|
|
return ''
|
|
}
|
|
|
|
return {
|
|
backgroundImage: `url(${store.getters['instance/absoluteUrl'](banner.value)})`
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<main
|
|
v-title="labels.title"
|
|
class="main pusher page-about"
|
|
>
|
|
<div
|
|
class="ui"
|
|
:class="{ container: onDesktop}"
|
|
>
|
|
<div class="ui horizontally fitted stripe basic segment">
|
|
<div class="ui basic vertically fitted stripe segment content">
|
|
<section
|
|
:class="['ui', 'head', {'with-background': banner}, 'vertical', 'center', 'aligned', 'stripe', 'segment']"
|
|
:style="headerStyle"
|
|
>
|
|
<h1>
|
|
<i class="music icon" />
|
|
{{ podName }}
|
|
</h1>
|
|
</section>
|
|
</div>
|
|
<div class="ui basic vertically fitted stripe segment content">
|
|
<!-- See layout in _about.scss -->
|
|
<div class="about-pod-info-container">
|
|
<div class="about-pod-info-toc">
|
|
<div class="ui vertical pointing secondary menu">
|
|
<router-link
|
|
to="/about/pod"
|
|
class="item"
|
|
>
|
|
{{ $t('components.AboutPod.aboutPod') }}
|
|
</router-link>
|
|
<router-link
|
|
to="/about/pod#rules"
|
|
class="item"
|
|
>
|
|
{{ $t('components.AboutPod.rules') }}
|
|
</router-link>
|
|
<router-link
|
|
to="/about/pod#terms"
|
|
class="item"
|
|
>
|
|
{{ $t('components.AboutPod.termsAndPrivacy') }}
|
|
</router-link>
|
|
<router-link
|
|
to="/about/pod#features"
|
|
class="item"
|
|
>
|
|
{{ $t('components.AboutPod.features') }}
|
|
</router-link>
|
|
<router-link
|
|
v-if="stats"
|
|
to="/about/pod#statistics"
|
|
class="item"
|
|
>
|
|
{{ $t('components.AboutPod.statistics') }}
|
|
</router-link>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="about-pod-info">
|
|
<h2
|
|
id="description about-this-pod"
|
|
class="ui header"
|
|
>
|
|
{{ $t('components.AboutPod.aboutPod') }}
|
|
</h2>
|
|
<sanitized-html
|
|
v-if="longDescription"
|
|
:html="longDescription"
|
|
/>
|
|
<p v-else>
|
|
{{ $t('components.AboutPod.noDescription') }}
|
|
</p>
|
|
|
|
<h3
|
|
id="rules"
|
|
class="ui header"
|
|
>
|
|
{{ $t('components.AboutPod.rules') }}
|
|
</h3>
|
|
<sanitized-html
|
|
v-if="rules"
|
|
:html="rules"
|
|
/>
|
|
<p v-else>
|
|
{{ $t('components.AboutPod.noRules') }}
|
|
</p>
|
|
|
|
<h3
|
|
id="terms"
|
|
class="ui header"
|
|
>
|
|
{{ $t('components.AboutPod.termsAndPrivacy') }}
|
|
</h3>
|
|
<sanitized-html
|
|
v-if="terms"
|
|
:html="terms"
|
|
/>
|
|
<p v-else>
|
|
{{ $t('components.AboutPod.noTerms') }}
|
|
</p>
|
|
|
|
<h3
|
|
id="features"
|
|
class="header"
|
|
>
|
|
{{ $t('components.AboutPod.features') }}
|
|
</h3>
|
|
<div class="features-container ui two column stackable grid">
|
|
<div class="column">
|
|
<table class="ui very basic table unstackable">
|
|
<tbody>
|
|
<tr>
|
|
<td>
|
|
{{ $t('components.AboutPod.funkwhaleVersion') }}
|
|
</td>
|
|
<td
|
|
v-if="version"
|
|
class="right aligned"
|
|
>
|
|
<span class="features-status ui text">
|
|
{{ version }}
|
|
</span>
|
|
</td>
|
|
<td
|
|
v-else
|
|
class="right aligned"
|
|
>
|
|
<span class="features-status ui text">
|
|
{{ $t('components.AboutPod.notApplicable') }}
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>
|
|
{{ $t('components.AboutPod.federation') }}
|
|
</td>
|
|
<td
|
|
v-if="federationEnabled"
|
|
class="right aligned"
|
|
>
|
|
<span class="features-status ui text">
|
|
<i class="check icon" />
|
|
{{ $t('components.AboutPod.enabled') }}
|
|
</span>
|
|
</td>
|
|
<td
|
|
v-else
|
|
class="right aligned"
|
|
>
|
|
<span class="features-status ui text">
|
|
<i class="x icon" />
|
|
{{ $t('components.AboutPod.disabled') }}
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>
|
|
{{ $t('components.AboutPod.allowList') }}
|
|
</td>
|
|
<td
|
|
v-if="allowListEnabled"
|
|
class="right aligned"
|
|
>
|
|
<span class="features-status ui text">
|
|
<i class="check icon" />
|
|
{{ $t('components.AboutPod.enabled') }}
|
|
</span>
|
|
</td>
|
|
<td
|
|
v-else
|
|
class="right aligned"
|
|
>
|
|
<span class="features-status ui text">
|
|
<i class="x icon" />
|
|
{{ $t('components.AboutPod.disabled') }}
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="column">
|
|
<table class="ui very basic table unstackable">
|
|
<tbody>
|
|
<tr>
|
|
<td>
|
|
{{ $t('components.AboutPod.anonymousAccess') }}
|
|
</td>
|
|
<td
|
|
v-if="anonymousCanListen"
|
|
class="right aligned"
|
|
>
|
|
<span class="features-status ui text">
|
|
<i class="check icon" />
|
|
{{ $t('components.AboutPod.enabled') }}
|
|
</span>
|
|
</td>
|
|
<td
|
|
v-else
|
|
class="right aligned"
|
|
>
|
|
<span class="features-status ui text">
|
|
<i class="x icon" />
|
|
{{ $t('components.AboutPod.disabled') }}
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>
|
|
{{ $t('components.AboutPod.registrations') }}
|
|
</td>
|
|
<td
|
|
v-if="openRegistrations"
|
|
class="right aligned"
|
|
>
|
|
<span class="features-status ui text">
|
|
<i class="check icon" />
|
|
{{ $t('components.AboutPod.open') }}
|
|
</span>
|
|
</td>
|
|
<td
|
|
v-else
|
|
class="right aligned"
|
|
>
|
|
<span class="features-status ui text">
|
|
<i class="x icon" />
|
|
{{ $t('components.AboutPod.closed') }}
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>
|
|
{{ $t('components.AboutPod.uploadQuota') }}
|
|
</td>
|
|
<td
|
|
v-if="defaultUploadQuota"
|
|
class="right aligned"
|
|
>
|
|
<span class="features-status ui text">
|
|
{{ humanSize(defaultUploadQuota * 1000 * 1000) }}
|
|
</span>
|
|
</td>
|
|
<td
|
|
v-else
|
|
class="right aligned"
|
|
>
|
|
<span class="features-status ui text">
|
|
{{ $t('components.AboutPod.notApplicable') }}
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<template v-if="stats">
|
|
<h3
|
|
id="statistics"
|
|
class="header"
|
|
>
|
|
{{ $t('components.AboutPod.statistics') }}
|
|
</h3>
|
|
<div class="statistics-container">
|
|
<div
|
|
v-if="stats.hours"
|
|
class="statistics-statistic"
|
|
>
|
|
<span class="statistics-figure ui text">
|
|
<span class="ui big text"><strong>{{ stats.hours.toLocaleString($store.state.ui.momentLocale) }}</strong></span>
|
|
<br>
|
|
{{ $t('components.AboutPod.hoursOfMusic', {hours: stats.hours}) }}
|
|
</span>
|
|
</div>
|
|
<div
|
|
v-if="stats.artists"
|
|
class="statistics-statistic"
|
|
>
|
|
<span class="statistics-figure ui text">
|
|
<span class="ui big text"><strong>{{ stats.artists.toLocaleString($store.state.ui.momentLocale) }}</strong></span>
|
|
<br>
|
|
{{ $t('components.AboutPod.artistsCount', {artists: stats.artists}) }}
|
|
</span>
|
|
</div>
|
|
<div
|
|
v-if="stats.albums"
|
|
class="statistics-statistic"
|
|
>
|
|
<span class="statistics-figure ui text">
|
|
<span class="ui big text"><strong>{{ stats.albums.toLocaleString($store.state.ui.momentLocale) }}</strong></span>
|
|
<br>
|
|
{{ $t('components.AboutPod.albumsCount', {albums: stats.albums}) }}
|
|
</span>
|
|
</div>
|
|
<div
|
|
v-if="stats.tracks"
|
|
class="statistics-statistic"
|
|
>
|
|
<span class="statistics-figure ui text">
|
|
<span class="ui big text"><strong>{{ stats.tracks.toLocaleString($store.state.ui.momentLocale) }}</strong></span>
|
|
<br>
|
|
{{ $t('components.AboutPod.tracksCount', {tracks: stats.tracks}) }}
|
|
</span>
|
|
</div>
|
|
<div
|
|
v-if="stats.users"
|
|
class="statistics-statistic"
|
|
>
|
|
<span class="statistics-figure ui text">
|
|
<span class="ui big text"><strong>{{ stats.users.toLocaleString($store.state.ui.momentLocale) }}</strong></span>
|
|
<br>
|
|
{{ $t('components.AboutPod.activeUsers', {users: stats.users}) }}
|
|
</span>
|
|
</div>
|
|
<div
|
|
v-if="stats.listenings"
|
|
class="statistics-statistic"
|
|
>
|
|
<span class="statistics-figure ui text">
|
|
<span class="ui big text"><strong>{{ stats.listenings.toLocaleString($store.state.ui.momentLocale) }}</strong></span>
|
|
<br>
|
|
{{ $t('components.AboutPod.listeningsCount', {listenings: stats.listenings}) }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template v-if="contactEmail">
|
|
<h3
|
|
id="contact"
|
|
class="ui header"
|
|
>
|
|
{{ $t('components.AboutPod.contactHeader') }}
|
|
</h3>
|
|
<a
|
|
v-if="contactEmail"
|
|
:href="`mailto:${contactEmail}`"
|
|
>
|
|
{{ $t('components.AboutPod.contactEmail', {contactEmail: contactEmail}) }}
|
|
</a>
|
|
</template>
|
|
|
|
<div class="ui hidden divider" />
|
|
<div class="ui fluid horizontally fitted basic clearing segment container">
|
|
<router-link
|
|
to="/about"
|
|
class="ui left floated basic secondary button"
|
|
>
|
|
<i class="icon arrow left" />
|
|
{{ $t('components.AboutPod.introductionLink') }}
|
|
</router-link>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
</template>
|