Merge branch 'dev' into feat/password-editing

This commit is contained in:
limes
2026-08-30 20:22:24 +02:00
31 changed files with 219 additions and 179 deletions

View File

@@ -0,0 +1,36 @@
import type { UseSeoMetaInput } from '@unhead/vue';
export type CustomSeoMetaOptions = UseSeoMetaInput & {
subsection?: string;
};
export default function (args?: CustomSeoMetaOptions) {
let newTitleTemplate = '';
switch (args?.subsection) {
case ('account'): {
newTitleTemplate = 'Account | Pretendo Network';
break;
}
case ('terms'): {
newTitleTemplate = 'Terms | Pretendo Network';
break;
}
case ('blog'): {
newTitleTemplate = 'Blog | Pretendo Network';
break;
}
case ('docs'): {
newTitleTemplate = 'Docs | Pretendo Network';
break;
}
default: {
newTitleTemplate = 'Pretendo Network';
}
}
const newTitle = args?.title ? `${args?.title} | ${newTitleTemplate}` : newTitleTemplate;
useHead({ title: newTitle });
useSeoMeta({ ...args, title: newTitle, ogTitle: newTitle, twitterTitle: newTitle, ogDescription: args?.description, twitterDescription: args?.description, twitterImage: args?.ogImage });
}

View File

@@ -3,9 +3,11 @@ import { watchImmediate } from '@vueuse/core';
import DefaultLayout from './layouts/default.vue';
const { error } = defineProps<{ error: any }>();
watchImmediate([error], () => {
console.error(error);
});
if (import.meta.client) {
watchImmediate([() => error], () => {
console.error(error);
});
}
</script>
<template>

View File

@@ -1,9 +1,4 @@
<script setup lang="ts">
useHead({
titleTemplate: (titleChunk) => {
return titleChunk ? `${titleChunk} | Pretendo Network` : 'Pretendo Network';
}
});
</script>
<template>

View File

@@ -3,14 +3,6 @@
const path = useRoute().fullPath;
const isSidebarOpen = ref(false);
useHead({
titleTemplate: (titleChunk) => {
return titleChunk
? `${titleChunk} | Docs | Pretendo Network`
: 'Docs | Pretendo Network';
}
});
</script>
<template>

View File

@@ -224,7 +224,7 @@
"nickname": "昵称",
"birthDate": "生日",
"gender": "性别",
"country": "国家/地区",
"country": "国家",
"timezone": "时区",
"serverEnv": "服务器环境",
"production": "生产环境",
@@ -244,7 +244,8 @@
"no_newsletter_notice": "电子报告目前不可用,请稍后再来查看。",
"userSettings": "用户设置",
"no_signins_notice": "目前没有任何登录历史。请稍后再试!",
"no_edit_from_dashboard": "目前无法从网站修改 PNID 设置。请通过已绑定的主机上修改用户设置。"
"no_edit_from_dashboard": "目前无法从网站修改 PNID 设置。请通过已绑定的主机上修改用户设置。",
"region": "地区"
},
"upgrade": "升级账号",
"unavailable": "无法使用",
@@ -309,7 +310,7 @@
"clickToSet": "点击设置",
"favorite": "设为最爱",
"sharing": "共享",
"copying": "复制中",
"copying": "允许复制",
"save": "保存",
"saveCaption": "保存你的 Mii 虚拟形象会删除之前的 Mii 虚拟形象。",
"noCanvas": "您的浏览器不支持 canvas 元素。",

View File

@@ -180,7 +180,7 @@
"profile": "個人檔案",
"birthDate": "生日",
"gender": "性別",
"country": "國家/地區",
"country": "國家",
"timezone": "時區",
"serverEnv": "伺服器環境",
"production": "生產",
@@ -204,7 +204,8 @@
"passwordPrompt": "輸入你的 PNID 密碼以下載 Cemu 檔案",
"no_signins_notice": "登入歷史紀錄目前未記錄。請稍後再來查看!",
"no_newsletter_notice": "電子報告目前無法使用。請稍後再來查看!",
"no_edit_from_dashboard": "目前無法從網站編輯 PNID 設定。請通過已連結的主機上編輯使用者設定。"
"no_edit_from_dashboard": "目前無法從網站編輯 PNID 設定。請通過已連結的主機上編輯使用者設定。",
"region": "地區"
},
"upgrade": "升級帳號",
"unavailable": "無法使用",
@@ -307,7 +308,7 @@
"miiEditor": {
"birthday": "生日",
"clickToSet": "點觸設定",
"copying": "複製中",
"copying": "允許複製",
"corruptedData": "找到已損壞的 Mii 資料。是否要從頭開始?",
"creator": "創作者",
"favorite": "設為最愛",

View File

@@ -48,6 +48,8 @@ const { execute, isLoading } = useAsync({
});
}
});
customSeoMeta({ subsection: 'account', title: 'Forgot password' });
</script>
<template>

View File

@@ -155,9 +155,7 @@ const { execute: executeUnlinkDiscord, isLoading: isLoadingUnlink } = useAsync({
}
});
useHead({
title: `Account`
});
customSeoMeta({ subsection: 'account' });
</script>
<template>
@@ -167,15 +165,27 @@ useHead({
>
<div class="account-sidebar">
<div class="user">
<a
href="/account/miieditor"
class="mii"
>
<img
:src="profile.mii?.imageUrl"
alt="Mii image"
<div class="mii-wrapper">
<a
href="/account/miieditor"
class="mii"
>
</a>
<img
:src="profile.mii?.imageUrl"
alt="Mii image"
>
</a>
<a
class="edit-button"
href="/account/miieditor"
>
<Icon
name="ph:pencil"
size="20"
/>
</a>
</div>
<p class="miiname">
{{ profile.mii?.name }}
</p>
@@ -641,36 +651,41 @@ useHead({
border-color: #5aff15;
}
.account-sidebar .user .mii-wrapper {
position: relative;
}
.account-sidebar .user a.mii {
position: relative;
display: block;
width: 128px;
height: 128px;
overflow: hidden;
border-radius: 100%;
border-radius: 9999px;
background: var(--bg-shade-3);
}
.account-sidebar .user a.mii::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background:
no-repeat center/50% url("@/public/assets/images/edit.svg"),
rgba(55, 60, 101, 0.7);
opacity: 0;
transition: opacity 150ms;
}
.account-sidebar .user a.mii:hover::after {
opacity: 1;
.account-sidebar .user a.mii:hover,
.account-sidebar .user .mii-wrapper a.edit-button:hover,
.account-sidebar .user:has(a.edit-button:hover) a.mii,
.account-sidebar .user:has(a.mii:hover) a.edit-button {
background: var(--bg-shade-4);
}
.account-sidebar .user .mii {
width: 100%;
height: 100%;
.account-sidebar .user .mii-wrapper a.edit-button {
color: inherit;
position: absolute;
right: 2px;
bottom: 2px;
display: flex;
align-items: center;
justify-content: center;
background: var(--bg-shade-3);
width: 32px;
height: 32px;
border: 2px solid var(--bg-shade-1);
border-radius: 9999px;
}
.account-sidebar .buttons {
display: grid;
grid-auto-flow: row;

View File

@@ -47,6 +47,8 @@ const { execute, isLoading } = useAsync({
});
}
});
customSeoMeta({ subsection: 'account', title: 'Login' });
</script>
<template>

View File

@@ -15,9 +15,7 @@ definePageMeta({
const { data: profile } = await useApiFetch('/api/auth/me');
useHead({
title: 'Mii Editor | Pretendo Network'
});
customSeoMeta({ subsection: 'account', title: 'Mii Editor' });
const beforeUnload = (e: BeforeUnloadEvent) => {
e?.preventDefault();

View File

@@ -55,6 +55,8 @@ const { execute, isLoading } = useAsync({
});
}
});
customSeoMeta({ subsection: 'account', title: 'Register' });
</script>
<template>

View File

@@ -30,6 +30,8 @@ const { execute, isLoading } = useAsync({
});
}
});
customSeoMeta({ subsection: 'account', title: 'Reset Password' });
</script>
<template>

View File

@@ -81,10 +81,7 @@ function hasSubscription(priceId: string) {
profile.value?.stripeTier && priceId === profile.value.stripeTier.priceId
);
}
useHead({
title: `Upgrade`
});
customSeoMeta({ subsection: 'account', title: 'Upgrade' });
</script>
<template>

View File

@@ -8,16 +8,11 @@ if (!post.value) {
throw createError({ statusCode: 404, statusMessage: 'Page Not Found' });
}
useHead({
title: `${post.value.title} | Blog`,
meta: [
{ property: 'description', content: post.value.caption },
{ property: 'og:description', content: post.value.caption },
{ property: 'og:image', content: post.value.cover_image },
{ property: 'og:image:alt', content: '' },
{ name: 'twitter:description', content: post.value.caption },
{ name: 'twitter:image', content: post.value.cover_image }
]
customSeoMeta({
subsection: 'blog',
title: post.value.title,
description: post.value.caption,
ogImage: post.value.cover_image
});
</script>

View File

@@ -1,13 +1,12 @@
<script setup lang="ts">
useHead({
title: 'Blog'
});
customSeoMeta({ subsection: 'blog' });
/* eslint-disable vue/no-v-html -- we might wanna avoid this by rewriting the locales to use variables */
const { data: allPosts } = await useAsyncData('blog', () => queryCollection('blog').all());
const posts = computed(() => allPosts.value?.filter(p => !p.path.startsWith('/blog/_')).sort((a, b) => {
return new Date(b.date).getTime() - new Date(a.date).getTime();
}));
</script>
<template>

View File

@@ -7,15 +7,10 @@ const { data: doc } = await useAsyncData(`docs-${slug}`, () => {
if (!doc.value) {
throw createError({ statusCode: 404, statusMessage: 'Page Not Found' });
}
useHead({
title: `${doc.value.title}`,
meta: [
{ property: 'description', content: doc.value.description },
{ property: 'og:description', content: doc.value.description },
{ property: 'og:image:alt', content: '' },
{ name: 'twitter:description', content: doc.value.description }
]
customSeoMeta({
subsection: 'docs',
title: doc.value.title,
description: doc.value.description
});
definePageMeta({

View File

@@ -8,14 +8,10 @@ if (!error.value) {
throw createError({ statusCode: 404, statusMessage: 'Page Not Found' });
}
useHead({
title: `${error.value.code} | Docs`,
meta: [
{ property: 'description', content: error.value.message },
{ property: 'og:description', content: error.value.message },
{ property: 'og:image:alt', content: '' },
{ name: 'twitter:description', content: error.value.message }
]
customSeoMeta({
subsection: 'docs',
title: error.value.code,
description: error.value.message
});
definePageMeta({

View File

@@ -36,25 +36,10 @@ const { data: searchResults } = await useAsyncData(
);
const results = computed(() => searchResults.value ?? []);
useHead({
customSeoMeta({
subsection: 'docs',
title: 'Search',
meta: [
{
name: 'description',
content:
'Got an error code? Find solutions here.'
},
{
name: 'og:description',
content:
'Got an error code? Find solutions here.'
},
{
name: 'twitter:description',
content:
'Got an error code? Find solutions here.'
}
]
description: 'Got an error code? Find solutions here.'
});
definePageMeta({

View File

@@ -1,23 +1,9 @@
<script setup lang="ts">
useHead({
customSeoMeta({
subsection: 'docs',
title: 'Install',
meta: [
{
name: 'description',
content:
'Learn how to set up Pretendo on a variety of consoles and emulators.'
},
{
name: 'og:description',
content:
'Learn how to set up Pretendo on a variety of consoles and emulators.'
},
{
name: 'twitter:description',
content:
'Learn how to set up Pretendo on a variety of consoles and emulators.'
}
]
description: 'Learn how to set up Pretendo Network on a variety of consoles and emulators.'
});
definePageMeta({

View File

@@ -1,23 +1,8 @@
<script setup lang="ts">
useHead({
meta: [
{
name: 'description',
content:
'Learn how to set up Pretendo on a variety of consoles and emulators.'
},
{
name: 'og:description',
content:
'Learn how to set up Pretendo on a variety of consoles and emulators.'
},
{
name: 'twitter:description',
content:
'Learn how to set up Pretendo on a variety of consoles and emulators.'
}
]
customSeoMeta({
subsection: 'docs',
description: 'Learn how to set up Pretendo Network on a variety of consoles and emulators.'
});
definePageMeta({

View File

@@ -32,6 +32,8 @@ function arraySplit<T>(array: Array<T>, groupCount: number): Array<Array<T>> {
function titleSuffixHandler(path: string) {
return te(path) ? t(path) : null;
}
customSeoMeta();
</script>
<template>

View File

@@ -4,6 +4,7 @@ useHead({
link: [{ rel: 'stylesheet', href: 'https://use.typekit.net/gok5tsu.css' }]
});
customSeoMeta();
</script>
<template>

View File

@@ -1,6 +1,11 @@
<script setup lang="ts">
const { t } = useI18n();
customSeoMeta({
title: t('progressPage.title'),
description: t('progressPage.description')
});
const progress = await useFetch('/api/progress');
const projects = computed(() => progress.data.value?.items ?? []);
const donations = computed(() => progress.data.value?.donations);

View File

@@ -8,14 +8,10 @@ if (!termContent.value) {
throw createError({ statusCode: 404, statusMessage: 'Page Not Found' });
}
useHead({
title: `${termContent.value.title} | Terms`,
meta: [
{ property: 'description', content: termContent.value.description },
{ property: 'og:description', content: termContent.value.description },
{ property: 'og:image:alt', content: '' },
{ name: 'twitter:description', content: termContent.value.description }
]
customSeoMeta({
subsection: 'terms',
title: termContent.value.title,
description: termContent.value.description
});
</script>