funkwhale/front/src/views/auth/ProfileBase.vue

180 lines
4.8 KiB
Vue

<script setup lang="ts">
import type { Actor } from '~/types'
import { onBeforeRouteUpdate } from 'vue-router'
import { computed, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { useStore } from '~/store'
import { hashCode, intToRGB } from '~/utils/color'
import UserFollowButton from '~/components/federation/UserFollowButton.vue'
import axios from 'axios'
import useErrorHandler from '~/composables/useErrorHandler'
import useReport from '~/composables/moderation/useReport'
import RenderedDescription from '~/components/common/RenderedDescription.vue'
import Layout from '~/components/ui/Layout.vue'
import Section from '~/components/ui/Section.vue'
import Tabs from '~/components/ui/Tabs.vue'
import Tab from '~/components/ui/Tab.vue'
interface Events {
(e: 'updated', value: Actor): void
}
interface Props {
username: string
domain?: string | null
}
const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), {
domain: null
})
const { report, getReportableObjects } = useReport()
const store = useStore()
// We are working either with an Actor or null
const object = ref<Actor | null>(null)
const actorColor = computed(() => intToRGB(hashCode(object.value?.full_username)))
const defaultAvatarStyle = computed(() => ({ backgroundColor: `#${actorColor.value}` }))
const displayName = computed(() => object.value?.name ?? object.value?.preferred_username)
const fullUsername = computed(() => props.domain
? `${props.username}@${props.domain}`
: `${props.username}@${store.getters['instance/domain']}`
)
const routerParams = computed(() => props.domain
? { username: props.username, domain: props.domain }
: { username: props.username }
)
const { t } = useI18n()
const labels = computed(() => ({
usernameProfile: t('views.auth.ProfileBase.title', { username: props.username })
}))
onBeforeRouteUpdate((to) => {
to.meta.preserveScrollPosition = true
})
const isLoading = ref(false)
const fetchData = async () => {
object.value = null
isLoading.value = true
try {
const response = await axios.get(`federation/actors/${fullUsername.value}/`)
object.value = response.data
} catch (error) {
useErrorHandler(error as Error)
}
isLoading.value = false
}
watch(props, fetchData, { immediate: true })
const recentActivity = ref(0)
</script>
<template>
<Layout stack main
v-title="labels.usernameProfile"
>
<Layout flex>
<!-- Profile Picture -->
<img
v-if="object?.icon"
v-lazy="store.getters['instance/absoluteUrl'](object.icon.urls.large_square_crop)"
alt=""
class="avatar"
/>
<span
v-else
:style="defaultAvatarStyle"
class="ui avatar circular label"
>
{{ store.state.auth.profile?.full_username?.[0] || "" }}
</span>
<!-- TODO: Translate Edit Link -->
<Section no-items
:h1="store.state.auth.username"
:action="{ text:'Edit profile', to:'/settings' }"
solid
primary
icon="bi-pencil-fill"
style="margin-top: 58px;"
>
<span style="grid-column: 1 / -1">
{{ object?.full_username }}
<i class="bi bi-copy" style="margin-left: 8px;"/>
</span>
<RenderedDescription
style="grid-column: 1 / -1"
:content="object?.summary? {text: object.summary} : null"
:field-name="'summary'"
:update-url="`users/${store.state.auth.username}/`"
:can-update="store.state.auth.authenticated && object?.full_username === store.state.auth.fullUsername"
@updated="emit('updated', $event)"
/>
<UserFollowButton
v-if="store.state.auth.authenticated && object && object.full_username !== store.state.auth.fullUsername"
:actor="object"
/>
</Section>
</Layout>
<Tabs>
<Tab :title="t('views.auth.ProfileBase.link.overview')" :to="{name: 'profile.overview', params: routerParams}">
<router-view
:object="object"
@updated="fetchData"
/>
</Tab>
<Tab :title="t('views.auth.ProfileBase.link.activity')" :to="{name: 'profile.activity', params: routerParams}">
<router-view
:object="object"
@updated="fetchData"
/>
</Tab>
</Tabs>
</Layout>
</template>
<style scoped lang="scss">
img.avatar {
width: 176px;
height: 176px;
border-radius: 50%;
}
span.avatar {
font-size: 100px;
font-style: normal;
font-weight: 800;
text-align: center;
align-content: center;
background-color: var(--fw-gray-500);
border-radius: 50%;
width: 176px;
height: 176px;
}
h1 {
font-size: 48px;
margin-bottom: 8px;
}
a.edit span {
color: var(--fw-primary);
&:hover {
color: var(--color);
}
}
</style>