diff --git a/components/account/AccountTabs.vue b/components/account/AccountTabs.vue new file mode 100644 index 00000000..ba3e3f5f --- /dev/null +++ b/components/account/AccountTabs.vue @@ -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> diff --git a/components/common/CommonRouteTabs.vue b/components/common/CommonRouteTabs.vue index 643568e2..65d8864b 100644 --- a/components/common/CommonRouteTabs.vue +++ b/components/common/CommonRouteTabs.vue @@ -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> diff --git a/locales/en-US.json b/locales/en-US.json index 2ea059c2..662cfa92 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -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", diff --git a/locales/es-ES.json b/locales/es-ES.json index 8e67dc6c..ef2fe170 100644 --- a/locales/es-ES.json +++ b/locales/es-ES.json @@ -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", diff --git a/pages/[[server]]/@[account]/index.vue b/pages/[[server]]/@[account]/index.vue index a6bbb4d5..59bda25c 100644 --- a/pages/[[server]]/@[account]/index.vue +++ b/pages/[[server]]/@[account]/index.vue @@ -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 diff --git a/pages/[[server]]/@[account]/index/followers.vue b/pages/[[server]]/@[account]/index/followers.vue index 0a749e59..3369f252 100644 --- a/pages/[[server]]/@[account]/index/followers.vue +++ b/pages/[[server]]/@[account]/index/followers.vue @@ -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> diff --git a/pages/[[server]]/@[account]/index/following.vue b/pages/[[server]]/@[account]/index/following.vue index 62e2011b..80e2b8c1 100644 --- a/pages/[[server]]/@[account]/index/following.vue +++ b/pages/[[server]]/@[account]/index/following.vue @@ -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> diff --git a/pages/[[server]]/@[account]/index/index.vue b/pages/[[server]]/@[account]/index/index.vue index 1187d420..b11a3bfd 100644 --- a/pages/[[server]]/@[account]/index/index.vue +++ b/pages/[[server]]/@[account]/index/index.vue @@ -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> diff --git a/pages/[[server]]/@[account]/index/media.vue b/pages/[[server]]/@[account]/index/media.vue new file mode 100644 index 00000000..385ba498 --- /dev/null +++ b/pages/[[server]]/@[account]/index/media.vue @@ -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> diff --git a/pages/[[server]]/@[account]/index/with_replies.vue b/pages/[[server]]/@[account]/index/with_replies.vue new file mode 100644 index 00000000..a3af2523 --- /dev/null +++ b/pages/[[server]]/@[account]/index/with_replies.vue @@ -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>