feat: add account tab pages (#422)
parent
8f8e65e9db
commit
089890677f
|
@ -0,0 +1,41 @@
|
|||
<script setup lang="ts">
|
||||
const { t } = useI18n()
|
||||
const route = useRoute()
|
||||
|
||||
const server = $(computedEager(() => route.params.server as string))
|
||||
const account = $(computedEager(() => route.params.account as string))
|
||||
|
||||
const tabs = $computed(() => [
|
||||
{
|
||||
name: 'account-index',
|
||||
to: {
|
||||
name: 'account-index',
|
||||
params: { server, account },
|
||||
},
|
||||
display: t('tab.posts'),
|
||||
icon: 'i-ri:file-list-2-line',
|
||||
},
|
||||
{
|
||||
name: 'account-replies',
|
||||
to: {
|
||||
name: 'account-replies',
|
||||
params: { server, account },
|
||||
},
|
||||
display: t('tab.posts_with_replies'),
|
||||
icon: 'i-ri:chat-3-line',
|
||||
},
|
||||
{
|
||||
name: 'account-media',
|
||||
to: {
|
||||
name: 'account-media',
|
||||
params: { server, account },
|
||||
},
|
||||
display: t('tab.media'),
|
||||
icon: 'i-ri:camera-2-line',
|
||||
},
|
||||
] as const)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CommonRouteTabs force :options="tabs" prevent-scroll-top command />
|
||||
</template>
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import type { RouteLocationRaw } from 'vue-router'
|
||||
|
||||
const { options, command, replace } = $defineProps<{
|
||||
const { options, command, replace, preventScrollTop = false } = $defineProps<{
|
||||
options: {
|
||||
to: RouteLocationRaw
|
||||
display: string
|
||||
|
@ -10,6 +10,7 @@ const { options, command, replace } = $defineProps<{
|
|||
}[]
|
||||
command?: boolean
|
||||
replace?: boolean
|
||||
preventScrollTop?: boolean
|
||||
}>()
|
||||
|
||||
const router = useRouter()
|
||||
|
@ -36,7 +37,7 @@ useCommands(() => command
|
|||
tabindex="1"
|
||||
hover:bg-active transition-100
|
||||
exact-active-class="children:(font-bold !border-primary !op100)"
|
||||
@click="$scrollToTop"
|
||||
@click="!preventScrollTop && $scrollToTop()"
|
||||
>
|
||||
<span ws-nowrap mxa sm:px2 sm:py3 py2 text-center border-b-3 op50 hover:op70 border-transparent>{{ option.display }}</span>
|
||||
</NuxtLink>
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"follow": "Follow",
|
||||
"follow_back": "Follow back",
|
||||
"follow_requested": "Requested",
|
||||
"followers": "Followers",
|
||||
"followers_count": "{0} Followers",
|
||||
"following": "Following",
|
||||
"following_count": "{0} Following",
|
||||
|
@ -18,6 +19,7 @@
|
|||
"muted_users": "Muted users",
|
||||
"mutuals": "Mutuals",
|
||||
"pinned": "Pinned",
|
||||
"posts": "Posts",
|
||||
"posts_count": "{0} Posts",
|
||||
"profile_description": "{0}'s profile header",
|
||||
"profile_unavailable": "Profile unavailable",
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"follow": "Seguir",
|
||||
"follow_back": "Seguir de vuelta",
|
||||
"follow_requested": "Enviado",
|
||||
"followers": "Seguidores",
|
||||
"followers_count": "{0} Seguidores|{0} Seguidor|{0} Seguidores",
|
||||
"following": "Siguiendo",
|
||||
"following_count": "{0} Siguiendo",
|
||||
|
@ -18,6 +19,7 @@
|
|||
"muted_users": "Usuarios silenciados",
|
||||
"mutuals": "Mutuo",
|
||||
"pinned": "Publicaciones fijadas",
|
||||
"posts": "Publicaciones",
|
||||
"posts_count": "{0} publicaciones|{0} publicación|{0} publicaciones",
|
||||
"profile_description": "Encabezado del perfil de {0}",
|
||||
"profile_unavailable": "Perfil no disponible",
|
||||
|
@ -172,7 +174,7 @@
|
|||
"tab": {
|
||||
"for_you": "Para tí",
|
||||
"hashtags": "Etiquetas",
|
||||
"media": "Medios de comunicación",
|
||||
"media": "Multimedia",
|
||||
"news": "Noticias",
|
||||
"notifications_all": "Todas",
|
||||
"notifications_mention": "Menciones",
|
||||
|
|
|
@ -11,12 +11,6 @@ const { t } = useI18n()
|
|||
const { data: account, refresh } = $(await useAsyncData(() => fetchAccountByHandle(accountName).catch(() => null)))
|
||||
const relationship = $computed(() => account ? useRelationship(account).value : undefined)
|
||||
|
||||
if (account) {
|
||||
useHeadFixed({
|
||||
title: () => `${getDisplayName(account)} (@${account.acct})`,
|
||||
})
|
||||
}
|
||||
|
||||
onReactivated(() => {
|
||||
// Silently update data when reentering the page
|
||||
// The user will see the previous content first, and any changes will be updated to the UI when the request is completed
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
const { t } = useI18n()
|
||||
const params = useRoute().params
|
||||
const handle = $(computedEager(() => params.account as string))
|
||||
|
||||
|
@ -6,6 +7,12 @@ definePageMeta({ name: 'account-followers' })
|
|||
|
||||
const account = await fetchAccountByHandle(handle)
|
||||
const paginator = account ? useMasto().accounts.iterateFollowers(account.id, {}) : null
|
||||
|
||||
if (account) {
|
||||
useHeadFixed({
|
||||
title: () => `${t('account.followers')} | ${getDisplayName(account)} (@${account})`,
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
const { t } = useI18n()
|
||||
const params = useRoute().params
|
||||
const handle = $(computedEager(() => params.account as string))
|
||||
|
||||
|
@ -6,6 +7,12 @@ definePageMeta({ name: 'account-following' })
|
|||
|
||||
const account = await fetchAccountByHandle(handle)
|
||||
const paginator = account ? useMasto().accounts.iterateFollowing(account.id, {}) : null
|
||||
|
||||
if (account) {
|
||||
useHeadFixed({
|
||||
title: () => `${t('account.following')} | ${getDisplayName(account)} (@${account})`,
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -1,52 +1,31 @@
|
|||
<script setup lang="ts">
|
||||
import type { Account } from 'masto'
|
||||
import AccountTabs from '~/components/account/AccountTabs.vue'
|
||||
|
||||
const params = useRoute().params
|
||||
const handle = $(computedEager(() => params.account as string))
|
||||
|
||||
definePageMeta({ name: 'account-index' })
|
||||
|
||||
const { data: account } = await useAsyncData(`account:${handle}`, async () => (
|
||||
window.history.state?.account as Account | undefined)
|
||||
?? await fetchAccountByHandle(handle),
|
||||
)
|
||||
const { t } = useI18n()
|
||||
|
||||
const paginatorPosts = useMasto().accounts.iterateStatuses(account.value!.id, { excludeReplies: true })
|
||||
const paginatorPostsWithReply = useMasto().accounts.iterateStatuses(account.value!.id, { excludeReplies: false })
|
||||
const paginatorMedia = useMasto().accounts.iterateStatuses(account.value!.id, { onlyMedia: true, excludeReplies: false })
|
||||
const { data: account } = await useAsyncData(`account:${handle}`, async () => (
|
||||
window.history.state?.account as Account | undefined)
|
||||
?? await fetchAccountByHandle(handle),
|
||||
)
|
||||
|
||||
const tabs = $computed(() => [
|
||||
{
|
||||
name: 'posts',
|
||||
display: t('tab.posts'),
|
||||
icon: 'i-ri:file-list-2-line',
|
||||
paginator: paginatorPosts,
|
||||
},
|
||||
{
|
||||
name: 'relies',
|
||||
display: t('tab.posts_with_replies'),
|
||||
icon: 'i-ri:chat-3-line',
|
||||
paginator: paginatorPostsWithReply,
|
||||
},
|
||||
{
|
||||
name: 'media',
|
||||
display: t('tab.media'),
|
||||
icon: 'i-ri:camera-2-line',
|
||||
paginator: paginatorMedia,
|
||||
},
|
||||
] as const)
|
||||
const paginator = useMasto().accounts.iterateStatuses(account.value!.id, { excludeReplies: true })
|
||||
|
||||
// Don't use local storage because it is better to default to Posts every time you visit a user's profile.
|
||||
const tab = $ref(tabs[0].name)
|
||||
const paginator = $computed(() => tabs.find(t => t.name === tab)!.paginator)
|
||||
if (account) {
|
||||
useHeadFixed({
|
||||
title: () => `${t('account.posts')} | ${getDisplayName(account.value!)} (@${account.value!.acct})`,
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<CommonTabs v-model="tab" :options="tabs" command />
|
||||
<KeepAlive>
|
||||
<TimelinePaginator :key="tab" :paginator="paginator" context="account" />
|
||||
</KeepAlive>
|
||||
<AccountTabs />
|
||||
<TimelinePaginator :paginator="paginator" context="account" />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
<script setup lang="ts">
|
||||
import type { Account } from 'masto'
|
||||
|
||||
definePageMeta({ name: 'account-media' })
|
||||
|
||||
const { t } = useI18n()
|
||||
const params = useRoute().params
|
||||
const handle = $(computedEager(() => params.account as string))
|
||||
|
||||
const { data: account } = await useAsyncData(`account:${handle}`, async () => (
|
||||
window.history.state?.account as Account | undefined)
|
||||
?? await fetchAccountByHandle(handle),
|
||||
)
|
||||
|
||||
const paginator = useMasto().accounts.iterateStatuses(account.value!.id, { onlyMedia: true, excludeReplies: false })
|
||||
|
||||
if (account) {
|
||||
useHeadFixed({
|
||||
title: () => `${t('tab.media')} | ${getDisplayName(account.value!)} (@${account.value!.acct})`,
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<AccountTabs />
|
||||
<TimelinePaginator :paginator="paginator" context="account" />
|
||||
</div>
|
||||
</template>
|
|
@ -0,0 +1,29 @@
|
|||
<script setup lang="ts">
|
||||
import type { Account } from 'masto'
|
||||
|
||||
definePageMeta({ name: 'account-replies' })
|
||||
|
||||
const { t } = useI18n()
|
||||
const params = useRoute().params
|
||||
const handle = $(computedEager(() => params.account as string))
|
||||
|
||||
const { data: account } = await useAsyncData(`account:${handle}`, async () => (
|
||||
window.history.state?.account as Account | undefined)
|
||||
?? await fetchAccountByHandle(handle),
|
||||
)
|
||||
|
||||
const paginator = useMasto().accounts.iterateStatuses(account.value!.id, { excludeReplies: false })
|
||||
|
||||
if (account) {
|
||||
useHeadFixed({
|
||||
title: () => `${t('tab.posts_with_replies')} | ${getDisplayName(account.value!)} (@${account.value!.acct})`,
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<AccountTabs />
|
||||
<TimelinePaginator :paginator="paginator" context="account" />
|
||||
</div>
|
||||
</template>
|
Loading…
Reference in New Issue