diff --git a/changes/changelog.d/2091.feature b/changes/changelog.d/2091.feature new file mode 100644 index 000000000..43a00d298 --- /dev/null +++ b/changes/changelog.d/2091.feature @@ -0,0 +1 @@ +Improve visuals & layout (#2091) diff --git a/front/src/components/About.vue b/front/src/components/About.vue index a40cf6da2..cf9984f59 100644 --- a/front/src/components/About.vue +++ b/front/src/components/About.vue @@ -5,8 +5,16 @@ import { get } from 'lodash-es' import { humanSize } from '~/utils/filters' import { computed } from 'vue' +import type { components } from '~/generated/types.ts' + import SignupForm from '~/components/auth/SignupForm.vue' import LogoText from '~/components/LogoText.vue' +import useMarkdown from '~/composables/useMarkdown' + +import Link from '~/components/ui/Link.vue' +import Card from '~/components/ui/Card.vue' +import Button from '~/components/ui/Button.vue' +import Layout from '~/components/ui/Layout.vue' const store = useStore() const nodeinfo = computed(() => store.state.instance.nodeinfo) @@ -16,7 +24,8 @@ const labels = computed(() => ({ title: t('components.About.title') })) -const podName = computed(() => get(nodeinfo.value, 'metadata.nodeName') ?? 'Funkwhale') +const podName = computed(() => (n => n === '' ? 'No name' : n ?? 'Funkwhale')(get(nodeinfo.value, 'metadata.nodeName'))) + const banner = computed(() => get(nodeinfo.value, 'metadata.banner')) const shortDescription = computed(() => get(nodeinfo.value, 'metadata.shortDescription')) @@ -28,10 +37,22 @@ const stats = computed(() => { return null } - return { users, hours } + const info = nodeinfo.value ?? {} as components['schemas']['NodeInfo21'] + + const data = { + users: info.usage.users.activeMonth || null, + hours: info.metadata.content.local.hoursOfContent || null, + artists: info.metadata.content.local.artists || null, + albums: info.metadata.content.local.releases || null, + tracks: info.metadata.content.local.recordings || null, + listenings: info.metadata.usage?.listenings.total || null + } + + return { users, hours, data } }) const openRegistrations = computed(() => get(nodeinfo.value, 'openRegistrations')) + const defaultUploadQuota = computed(() => humanSize(get(nodeinfo.value, 'metadata.defaultUploadQuota', 0) * 1000 * 1000)) const headerStyle = computed(() => { @@ -43,219 +64,554 @@ const headerStyle = computed(() => { backgroundImage: `url(${store.getters['instance/absoluteUrl'](banner.value)})` } }) + +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(() => { + const features = get(nodeinfo.value, 'metadata.metadata.feature', []) as string[] + const hasAnonymousCanListen = features.includes('anonymousCanListen') + return hasAnonymousCanListen +}) +const allowListEnabled = computed(() => get(nodeinfo.value, 'metadata.allowList.enabled')) +const version = computed(() => get(nodeinfo.value, 'software.version')) +const federationEnabled = computed(() => { + const features = get(nodeinfo.value, 'metadata.metadata.feature', []) as string[] + const hasAnonymousCanListen = features.includes('federation') + return hasAnonymousCanListen +}) + + + +
+

+ +

+
+
+

+ {{ t('components.About.header.aboutPod') }} +

+
+ {{ shortDescription }}
+

+ {{ t('components.About.placeholder.noDescription') }} +

+ + +
+ + +
+ + + + + + {{ t('components.About.description.publicContent') }} + + + + {{ t('components.About.description.publicContent') }} + + + + {{ t('components.About.description.findApp') }} + + + +
+

+ + {{ podName }} +

+
+ + +
+
+
+ +
+

+ {{ t('components.AboutPod.header.about') }} +

+ +

+ {{ t('components.AboutPod.placeholder.noDescription') }} +

+ +

+ {{ t('components.AboutPod.header.rules') }} +

+ +

+ {{ t('components.AboutPod.placeholder.noRules') }} +

+ +

+ {{ t('components.AboutPod.header.terms') }} +

+ +

+ {{ t('components.AboutPod.placeholder.noTerms') }} +

+ +

+ {{ t('components.AboutPod.header.features') }} +

+
+
+ + + + + + + + + + + + + + + + + + +
+ {{ t('components.AboutPod.feature.version') }} + + + {{ version }} + + + + {{ t('components.AboutPod.notApplicable') }} + +
+ {{ t('components.AboutPod.feature.federation') }} + + + + {{ t('components.AboutPod.feature.status.enabled') }} + + + + + {{ t('components.AboutPod.feature.status.disabled') }} + +
+ {{ t('components.AboutPod.feature.allowList') }} + + + + {{ t('components.AboutPod.feature.status.enabled') }} + + + + + {{ t('components.AboutPod.feature.status.disabled') }} + +
+
+
+ + + + + + + + + + + + + + + + + + +
+ {{ t('components.AboutPod.feature.anonymousAccess') }} + + + + {{ t('components.AboutPod.feature.status.enabled') }} + + + + + {{ t('components.AboutPod.feature.status.disabled') }} + +
+ {{ t('components.AboutPod.feature.registrations') }} + + + + {{ t('components.AboutPod.feature.status.open') }} + + + + + {{ t('components.AboutPod.feature.status.closed') }} + +
+ {{ t('components.AboutPod.feature.quota') }} + + + {{ defaultUploadQuota }} + + + + {{ t('components.AboutPod.notApplicable') }} + +
+
+
+ + + + + +
- + diff --git a/front/src/components/AboutPod.vue b/front/src/components/AboutPod.vue index a4bbe09f6..f59eb6209 100644 --- a/front/src/components/AboutPod.vue +++ b/front/src/components/AboutPod.vue @@ -5,7 +5,6 @@ import { get } from 'lodash-es' import { computed } from 'vue' import useMarkdown from '~/composables/useMarkdown' -import type { NodeInfo } from '~/store/instance' import { useI18n } from 'vue-i18n' const store = useStore() @@ -39,24 +38,14 @@ const federationEnabled = computed(() => { 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.content.local.hoursOfContent', null), - artists: get(info, 'metadata.content.local.artists.total', null), - albums: get(info, 'metadata.content.local.albums.total', null), - tracks: get(info, 'metadata.content.local.tracks.total', null), - listenings: get(info, 'metadata.usage.listenings.total', null) - } - - if (data.users === null || data.artists === null) { - return data - } - - return data -}) +const stats = computed(() => ({ + users: nodeinfo.value?.usage.users.activeMonth, + hours: nodeinfo.value?.metadata.content.local.hoursOfContent, + artists: nodeinfo.value?.metadata.content.local.artists, + albums: nodeinfo.value?.metadata.content.local.releases, // TODO: Check where to get 'metadata.content.local.albums.total' + tracks: nodeinfo.value?.metadata.content.local.recordings, // TODO: 'metadata.content.local.tracks.total' + listenings: nodeinfo.value?.metadata.usage?.listenings.total +})) const headerStyle = computed(() => { if (!banner.value) { @@ -72,7 +61,7 @@ const headerStyle = computed(() => { @@ -420,7 +409,7 @@ const headerStyle = computed(() => { class="ui left floated basic secondary button" > - {{ $t('components.AboutPod.link.introduction') }} + {{ t('components.AboutPod.link.introduction') }}
diff --git a/front/src/components/Home.vue b/front/src/components/Home.vue index 9b6cd4d80..4e10082a9 100644 --- a/front/src/components/Home.vue +++ b/front/src/components/Home.vue @@ -1,6 +1,6 @@ diff --git a/front/src/components/PageNotFound.vue b/front/src/components/PageNotFound.vue index 07d249fec..92a473726 100644 --- a/front/src/components/PageNotFound.vue +++ b/front/src/components/PageNotFound.vue @@ -12,7 +12,7 @@ const labels = computed(() => ({ - +

- {{ labels.queue }}
@@ -420,7 +421,10 @@ if (!isWebGLSupported) { - + {{ endsIn }} @@ -451,30 +455,31 @@ if (!isWebGLSupported) { diff --git a/front/src/components/QueueItem.vue b/front/src/components/QueueItem.vue index ce2b384ad..86e4b8271 100644 --- a/front/src/components/QueueItem.vue +++ b/front/src/components/QueueItem.vue @@ -3,6 +3,11 @@ import type { QueueItemSource } from '~/types' import time from '~/utils/time' import { generateTrackCreditStringFromQueue } from '~/utils/utils' +import { useStore } from '~/store' + +import Button from '~/components/ui/Button.vue' + +const store = useStore() interface Events { (e: 'play', index: number): void @@ -24,7 +29,7 @@ defineProps() tabindex="0" >
- +
() >
- +
- - + />
diff --git a/front/src/components/RemoteSearchForm.vue b/front/src/components/RemoteSearchForm.vue index 00e0c858b..122a7f0ed 100644 --- a/front/src/components/RemoteSearchForm.vue +++ b/front/src/components/RemoteSearchForm.vue @@ -8,6 +8,11 @@ import { useStore } from '~/store' import axios from 'axios' +import Layout from '~/components/ui/Layout.vue' +import Button from '~/components/ui/Button.vue' +import Input from '~/components/ui/Input.vue' +import Alert from '~/components/ui/Alert.vue' + import updateQueryString from '~/composables/updateQueryString' import useLogger from '~/composables/useLogger' @@ -165,85 +170,85 @@ watch(() => props.initialId, () => { diff --git a/front/src/components/ServiceMessages.vue b/front/src/components/ServiceMessages.vue index 4f16067ba..df9662033 100644 --- a/front/src/components/ServiceMessages.vue +++ b/front/src/components/ServiceMessages.vue @@ -1,7 +1,13 @@ + +