fix(front): regressions after rebase on `develop`

This commit is contained in:
upsiflu 2025-01-09 01:15:31 +01:00
parent d122f7cdf4
commit af239917bc
8 changed files with 70 additions and 108 deletions

View File

@ -1,31 +1,33 @@
<script setup lang="ts"> <script setup lang="ts">
// import LegacyLayout from '~/LegacyLayout.vue'
// TODO: see below (isUIv2)
import UiApp from '~/ui/App.vue'
import type { QueueTrack } from '~/composables/audio/queue'
import { watchEffect, computed, onMounted, nextTick } from 'vue' import { watchEffect, computed, onMounted, nextTick } from 'vue'
import { useQueue } from '~/composables/audio/queue' import { type QueueTrack, useQueue } from '~/composables/audio/queue'
import { useStore } from '~/store' import { useStore } from '~/store'
import useLogger from '~/composables/useLogger' import useLogger from '~/composables/useLogger'
import { useLocalStorage, useStyleTag, useIntervalFn} from '@vueuse/core'
import { color } from '~/composables/color';
import { generateTrackCreditStringFromQueue } from '~/utils/utils' import { generateTrackCreditStringFromQueue } from '~/utils/utils'
const ChannelUploadModal = defineAsyncComponent(() => import('~/components/channels/UploadModal.vue')) // TODO: Check if these modals are still needed:
const PlaylistModal = defineAsyncComponent(() => import('~/components/playlists/PlaylistModal.vue')) // import ChannelUploadModal from '~/components/channels/UploadModal.vue'
const FilterModal = defineAsyncComponent(() => import('~/components/moderation/FilterModal.vue')) // import PlaylistModal from '~/components/playlists/PlaylistModal.vue'
const ReportModal = defineAsyncComponent(() => import('~/components/moderation/ReportModal.vue')) // import FilterModal from '~/components/moderation/FilterModal.vue'
const ServiceMessages = defineAsyncComponent(() => import('~/components/ServiceMessages.vue')) // import ReportModal from '~/components/moderation/ReportModal.vue'
const ShortcutsModal = defineAsyncComponent(() => import('~/components/ShortcutsModal.vue')) // import ServiceMessages from '~/components/ServiceMessages.vue'
const AudioPlayer = defineAsyncComponent(() => import('~/components/audio/Player.vue')) // TODO: Modernise and add player/queue:
const Sidebar = defineAsyncComponent(() => import('~/components/Sidebar.vue')) // import AudioPlayer from '~/components/audio/Player.vue'
const Queue = defineAsyncComponent(() => import('~/components/Queue.vue')) // import Queue from '~/components/Queue.vue'
import Sidebar from '~/ui/components/Sidebar.vue'
import { useLocalStorage, useStyleTag, useIntervalFn, useToggle, useWindowSize } from '@vueuse/core' import ShortcutsModal from '~/ui/modals/Shortcuts.vue'
import LanguagesModal from '~/ui/modals/Languages.vue'
import UploadModal from '~/ui/modals/Upload.vue';
// Fake content
onMounted(async () => {
await nextTick()
document.getElementById('fake-app')?.remove()
})
const logger = useLogger() const logger = useLogger()
logger.debug('App setup()') logger.debug('App setup()')
@ -74,22 +76,34 @@ useIntervalFn(() => {
// because we want to learn if we are authenticated at all // because we want to learn if we are authenticated at all
store.dispatch('auth/fetchUser') store.dispatch('auth/fetchUser')
// TODO:
// Research...
// - "What is the use case for this toggle?"
// - "Is Local Storage (persistence on a specific browser
// on a specific machine) the right place?"
const isUIv2 = useLocalStorage('ui-v2', true)
</script> </script>
<template> <template>
<UiApp/> <div class="funkwhale responsive">
<!-- <LegacyLayout v-else /> --> <Sidebar/>
<RouterView v-bind="color({}, ['default', 'solid'])()" />
<LanguagesModal />
<ShortcutsModal />
<UploadModal />
</div>
</template> </template>
<style> <style scoped lang="scss">
html, body { .responsive {
font-size: 16px; display: grid !important;
} grid-template-columns: 100%;
grid-template-rows: min-content;
min-height: 100vh;
@media screen and (min-width: 1024px) {
grid-template-columns: 300px 5fr;
grid-template-rows: 100%;
}
}
</style>
<style>
/* Make inert pages (behind modals) unscrollable */
body:has(#app[inert="true"]) {
overflow:hidden;
}
</style> </style>

View File

@ -2,8 +2,11 @@
import type { Actor } from '~/types' import type { Actor } from '~/types'
import { computed } from 'vue' import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { useStore } from '~/store' import { useStore } from '~/store'
const { t } = useI18n()
interface Events { interface Events {
(e: 'unfollowed'): void (e: 'unfollowed'): void
(e: 'followed'): void (e: 'followed'): void
@ -39,13 +42,13 @@ const toggle = () => {
> >
<i class="heart icon" /> <i class="heart icon" />
<span v-if="isApproved"> <span v-if="isApproved">
{{ $t('components.audio.LibraryFollowButton.button.unfollow') }} {{ t('components.audio.LibraryFollowButton.button.unfollow') }}
</span> </span>
<span v-else-if="isPending"> <span v-else-if="isPending">
{{ $t('components.audio.LibraryFollowButton.button.cancel') }} {{ t('components.audio.LibraryFollowButton.button.cancel') }}
</span> </span>
<span v-else> <span v-else>
{{ $t('components.audio.LibraryFollowButton.button.follow') }} {{ t('components.audio.LibraryFollowButton.button.follow') }}
</span> </span>
</button> </button>
</template> </template>

View File

@ -95,7 +95,7 @@ const triggerFileInput = () => {
{{ labels.export }} {{ labels.export }}
</div> </div>
<div <div
v-if="$store.state.auth.authenticated && playlist.actor.full_username !== $store.state.auth.fullUsername" v-if="store.state.auth.authenticated && playlist.actor.full_username !== store.state.auth.fullUsername"
role="button" role="button"
class="basic item" class="basic item"
:title="t('components.playlists.PlaylistDropdown.button.import.description')" :title="t('components.playlists.PlaylistDropdown.button.import.description')"

View File

@ -1,5 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import type { RouterLinkProps } from 'vue-router' import type { RouterLinkProps } from 'vue-router'
import { useAttrs } from 'vue'
import Layout from '~/components/ui/Layout.vue' import Layout from '~/components/ui/Layout.vue'
import Spacer from '~/components/ui/layout/Spacer.vue' import Spacer from '~/components/ui/layout/Spacer.vue'
@ -20,13 +21,14 @@ const [headingLevel, title] =
const numberOfColumnsPerItem = const numberOfColumnsPerItem =
'noItems' in props && props.noItems ? 1 : 'tinyItems' in props && props.tinyItems ? 2 : 'smallItems' in props && props['smallItems'] ? 3 : 4 'noItems' in props && props.noItems ? 1 : 'tinyItems' in props && props.tinyItems ? 2 : 'smallItems' in props && props['smallItems'] ? 3 : 4
const { style, ...fallthroughProps } = useAttrs()
const headerGrid = const headerGrid =
`auto / repeat(auto-fit, calc(46px * ${numberOfColumnsPerItem} + 32px * ${numberOfColumnsPerItem - 1}))` `auto / repeat(auto-fit, calc(46px * ${numberOfColumnsPerItem} + 32px * ${numberOfColumnsPerItem - 1}))`
</script> </script>
<template> <template>
<section> <section style="flex-grow: 1;">
<Layout header :grid="headerGrid" <Layout header :grid="headerGrid"
:style="`${'alignLeft' in props && props.alignLeft ? 'justify-content: start' : ''}; :style="`${'alignLeft' in props && props.alignLeft ? 'justify-content: start' : ''};
margin-top: -64px;`" margin-top: -64px;`"
@ -49,10 +51,10 @@ const headerGrid =
<Spacer grow /> <Spacer grow />
<!-- Action! You can either specify `to` or `onClick`. --> <!-- Action! You can either specify `to` or `onClick`. -->
<Button v-if="props.action && 'onClick' in props.action" <Button v-if="props.action && 'onClick' in props.action"
ghost thin-font minWidth align-self="baseline" ghost thin-font min-content align-self="baseline"
:style="`margin-right: ${('primary' in props || 'secondary' in props || 'destructive' in props) ? '0px' : '-16px'}`" :style="`margin-right: ${('primary' in props || 'secondary' in props || 'destructive' in props) ? '0px' : '-16px'}`"
:onClick="props.action.onClick" :onClick="props.action.onClick"
v-bind="$attrs" v-bind="fallthroughProps"
> >
{{ props.action.text }} {{ props.action.text }}
</Button> </Button>
@ -60,7 +62,7 @@ const headerGrid =
ghost force-underline thinFont align-self="baseline" ghost force-underline thinFont align-self="baseline"
:style="`margin-right: ${('primary' in props || 'secondary' in props || 'destructive' in props) ? '0px' : '-16px'}`" :style="`margin-right: ${('primary' in props || 'secondary' in props || 'destructive' in props) ? '0px' : '-16px'}`"
:to="props.action.to" :to="props.action.to"
v-bind="$attrs" v-bind="fallthroughProps"
> >
{{ props.action.text }} {{ props.action.text }}
</Link> </Link>

View File

@ -18,7 +18,7 @@ export type WidthProps =
export type Key = KeysOfUnion<WidthProps> export type Key = KeysOfUnion<WidthProps>
const widths = { const widths = {
minContent: 'width: min-content;', minContent: 'width: min-content; flex-grow: 0;',
iconWidth: 'width: 40px;', iconWidth: 'width: 40px;',
tiny: "width: 124px; grid-column: span 2;", tiny: "width: 124px; grid-column: span 2;",
buttonWidth: "width: 136px; grid-column: span 2; flex-grow: 0; min-width: min-content;", buttonWidth: "width: 136px; grid-column: span 2; flex-grow: 0; min-width: min-content;",

View File

@ -1,45 +0,0 @@
<script setup lang="ts">
import { onMounted, nextTick } from 'vue'
import { color } from '~/composables/color';
import Sidebar from '~/ui/components/Sidebar.vue'
import ShortcutsModal from './modals/Shortcuts.vue'
import LanguagesModal from './modals/Languages.vue'
import UploadModal from './modals/Upload.vue';
// Fake content
onMounted(async () => {
await nextTick()
document.getElementById('fake-app')?.remove()
})
</script>
<template>
<div class="funkwhale responsive">
<Sidebar/>
<RouterView v-bind="color({}, ['default', 'solid'])()" />
<LanguagesModal />
<ShortcutsModal />
<UploadModal />
</div>
</template>
<style scoped lang="scss">
.responsive {
display: grid !important;
grid-template-columns: 100%;
grid-template-rows: min-content;
min-height: 100vh;
@media screen and (min-width: 1024px) {
grid-template-columns: 300px 5fr;
grid-template-rows: 100%;
}
}
</style>
<style>
body:has(#app[inert="true"]) {
overflow:hidden;
}
</style>

View File

@ -3,12 +3,10 @@ import { ref, onMounted, watch, computed } from 'vue'
import { useUploadsStore } from '../stores/upload' import { useUploadsStore } from '../stores/upload'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useStore } from '~/store' import { useStore } from '~/store'
import { color } from '~/composables/color'
import Logo from '~/components/Logo.vue' import Logo from '~/components/Logo.vue'
import Input from '~/components/ui/Input.vue' import Input from '~/components/ui/Input.vue'
import Link from '~/components/ui/Link.vue' import Link from '~/components/ui/Link.vue'
import ActorAvatar from '~/components/common/ActorAvatar.vue'
import UserMenu from './UserMenu.vue' import UserMenu from './UserMenu.vue'
import Button from '~/components/ui/Button.vue' import Button from '~/components/ui/Button.vue'
import Layout from '~/components/ui/Layout.vue' import Layout from '~/components/ui/Layout.vue'
@ -62,7 +60,6 @@ const logoUrl = computed(() => store.state.auth.authenticated ? 'library.index'
round round
square-small square-small
icon="bi-upload" icon="bi-upload"
:class="[$style.button]"
ghost ghost
@click="store.commit('ui/toggleModal', 'upload')" @click="store.commit('ui/toggleModal', 'upload')"
:aria-pressed="store.state.ui.modalsOpen.has('upload') || undefined" :aria-pressed="store.state.ui.modalsOpen.has('upload') || undefined"
@ -106,6 +103,8 @@ const logoUrl = computed(() => store.state.auth.authenticated ? 'library.index'
/> />
</form> </form>
<Spacer />
<!-- Sign up, Log in --> <!-- Sign up, Log in -->
<div style="display:contents;" v-if="!store.state.auth.authenticated"> <div style="display:contents;" v-if="!store.state.auth.authenticated">
<Layout flex grow style="--gap:16px;"> <Layout flex grow style="--gap:16px;">
@ -275,11 +274,6 @@ const logoUrl = computed(() => store.state.auth.authenticated ? 'library.index'
} }
} }
.search {
padding: 0 4px;
margin-bottom: 28px;
}
> h3 { > h3 {
margin: 0; margin: 0;
padding: 0 32px 8px; padding: 0 32px 8px;

View File

@ -36,6 +36,7 @@ const props = withDefaults(defineProps<Props>(), {
const { report, getReportableObjects } = useReport() const { report, getReportableObjects } = useReport()
const store = useStore() const store = useStore()
// We are working either with an Actor or null
const object = ref<Actor | null>(null) const object = ref<Actor | null>(null)
const displayName = computed(() => object.value?.name ?? object.value?.preferred_username) const displayName = computed(() => object.value?.name ?? object.value?.preferred_username)
@ -79,7 +80,6 @@ watch(props, fetchData, { immediate: true })
<template> <template>
<Layout stack main <Layout stack main
v-title="labels.usernameProfile" v-title="labels.usernameProfile"
class="page-profile"
> >
<Layout flex> <Layout flex>
<!-- Profile Picture --> <!-- Profile Picture -->
@ -136,7 +136,7 @@ watch(props, fetchData, { immediate: true })
</div> </div>
</button> </button>
<user-follow-button <user-follow-button
v-if="$store.state.auth.authenticated && object && object.full_username !== $store.state.auth.fullUsername" v-if="store.state.auth.authenticated && object && object.full_username !== store.state.auth.fullUsername"
:actor="object" :actor="object"
/> />
<h1 class="ui center aligned icon header"> <h1 class="ui center aligned icon header">
@ -204,21 +204,11 @@ watch(props, fetchData, { immediate: true })
</div> </div>
</div> </div>
</div> --> </div> -->
</Layout>
<Layout stack noGap style="flex-grow: 2;">
<Layout flex style="justify-content: space-between;">
<!-- <Section h1="{{ displayName }}" :action="{ text: 'Edit profile', to:'/settings' }" /> -->
<h1>{{ displayName }}</h1>
<Link ghost alignSelf="center" minContent forceUnderline primary style="color: var(--fw-primary);" to="/settings">Edit profile</Link>
</Layout>
<span>{{ object?.full_username }}<i class="bi bi-copy" style="margin-left: 8px;"/></span>
<rendered-description
>
<!-- Profile description --> <!-- Profile description -->
<Section no-items <Section no-items
:h1="store.state.auth.username" :h1="store.state.auth.username"
:action="{ text:'Edit profile', to:'/settings' }" :action="{ text:'Edit profile', to:'/settings' }"
style="flex-grow:1" style="flex-grow: 1; margin-top: 58px;"
> >
<span style="grid-column: 1 / -1"> <span style="grid-column: 1 / -1">
{{ object?.full_username }} {{ object?.full_username }}
@ -226,12 +216,16 @@ watch(props, fetchData, { immediate: true })
</span> </span>
<RenderedDescription <RenderedDescription
style="grid-column: 1 / -1" style="grid-column: 1 / -1"
:content="object?.summary" :content="object?.summary? {text: object.summary} : null"
:field-name="'summary'" :field-name="'summary'"
:update-url="`users/${store.state.auth.username}/`" :update-url="`users/${store.state.auth.username}/`"
:can-update="store.state.auth.authenticated && object?.full_username === store.state.auth.fullUsername" :can-update="store.state.auth.authenticated && object?.full_username === store.state.auth.fullUsername"
@updated="emit('updated', $event)" @updated="emit('updated', $event)"
/> />
<UserFollowButton
v-if="store.state.auth.authenticated && object && object.full_username !== store.state.auth.fullUsername"
:actor="object"
/>
</Section> </Section>
</Layout> </Layout>
<!-- TODO: Make routerlinks work for tabs --> <!-- TODO: Make routerlinks work for tabs -->