Merge pull request #453 from PretendoNetwork/feat/fixed-seo
Some checks failed
Build and Publish Docker Image / Build and Publish (amd64) (push) Has been cancelled
Build and Publish Docker Image / Build and Publish (arm64) (push) Has been cancelled

feat: fix seo for social media previews
This commit is contained in:
mrjvs
2026-08-26 17:54:37 +02:00
committed by GitHub
23 changed files with 86 additions and 116 deletions

View File

@@ -116,7 +116,7 @@ Thanks to the dedicated work by developer [Arian Kordi](https://github.com/arian
While the last few months had a lot of focus on internal changes and Juxtaposition, game servers also saw a lot of attention!
- [Dani](https://github.com/DaniElectra) has begun implementing the [`MessageDelivery` and `Messaging` protocols into our "common" module](https://github.com/PretendoNetwork/nex-protocols-common-go/pull/54). Once complete, these protocols should be widely available to all games
- [Dani](https://github.com/DaniElectra) Has begun [implementing the `MatchmakeExtension::GetPlayingSession`](https://github.com/PretendoNetwork/nex-protocols-common-go/pull/55) NEX method for use in multiplayer games
- [Dani](https://github.com/DaniElectra) Has begun [implementing the `MatchmakeExtension::GetPlayingSession`](https://github.com/PretendoNetwork/nex-protocols-common-go/pull/55) NEX method for use in multiplayer games
![The Lobby area in Splatoon 1.](/assets/images/blogposts/december-25-2025/image6.png)
*Splatoon, for its part, calls GetPlayingSession every time you enter the multiplayer lobby.*
- [Trace](https://github.com/TraceEntertains) updated [Yo Kai Watch Blasters](https://github.com/PretendoNetwork/yo-kai-watch-blasters/pull/5) to the latest server libraries, including the above mentioned changes by [Dani](https://github.com/DaniElectra). With this update friend rooms and trading are now functional

View File

@@ -156,6 +156,7 @@ export default defineNuxtConfig({
{ name: 'application-name', content: 'Pretendo Network' },
{ name: 'msapplication-TileColor', content: '#1b1f3b' },
{ name: 'theme-color', content: '#1b1f3b' },
{ property: 'og:title', content: 'Pretendo Network' },
{
property: 'og:description',
content:

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

@@ -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

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

View File

@@ -154,9 +154,7 @@ const { execute: executeUnlinkDiscord, isLoading: isLoadingUnlink } = useAsync({
}
});
useHead({
title: `Account`
});
customSeoMeta({ subsection: 'account' });
</script>
<template>

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>