feat: page metadata
This commit is contained in:
parent
2e9e4db6cb
commit
dc38d2bc28
12 changed files with 182 additions and 4 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
DATABASE_URL="postgresql://postgres:frieren@localhost:5432/tomodachi-share?schema=public"
|
DATABASE_URL="postgresql://postgres:frieren@localhost:5432/tomodachi-share?schema=public"
|
||||||
|
BASE_URL=https://tomodachi-share.trafficlunar.net
|
||||||
|
|
||||||
NEXTAUTH_URL=https://tomodachi-share.trafficlunar.net
|
NEXTAUTH_URL=https://tomodachi-share.trafficlunar.net # This should be the same as BASE_URL
|
||||||
AUTH_SECRET=XXXXXXXXXXXXXXXX
|
AUTH_SECRET=XXXXXXXXXXXXXXXX
|
||||||
|
|
||||||
AUTH_DISCORD_ID=XXXXXXXXXXXXXXXX
|
AUTH_DISCORD_ID=XXXXXXXXXXXXXXXX
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,13 @@
|
||||||
|
import { Metadata } from "next";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { auth } from "@/lib/auth";
|
import { auth } from "@/lib/auth";
|
||||||
import UsernameForm from "@/components/username-form";
|
import UsernameForm from "@/components/username-form";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Create your Username - TomodachiShare",
|
||||||
|
description: "Pick a unique username to start using TomodachiShare",
|
||||||
|
};
|
||||||
|
|
||||||
export default async function CreateUsernamePage() {
|
export default async function CreateUsernamePage() {
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { Metadata, ResolvingMetadata } from "next";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
import { auth } from "@/lib/auth";
|
import { auth } from "@/lib/auth";
|
||||||
|
|
@ -8,6 +9,21 @@ interface Props {
|
||||||
params: Promise<{ slug: string }>;
|
params: Promise<{ slug: string }>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function generateMetadata({ params }: Props, parent: ResolvingMetadata): Promise<Metadata> {
|
||||||
|
const { slug } = await params;
|
||||||
|
|
||||||
|
const mii = await prisma.mii.findUnique({
|
||||||
|
where: {
|
||||||
|
id: Number(slug),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: `${mii?.name} - TomodachiShare`,
|
||||||
|
description: `Edit the name, tags, and images of '${mii?.name}'`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export default async function MiiPage({ params }: Props) {
|
export default async function MiiPage({ params }: Props) {
|
||||||
const { slug } = await params;
|
const { slug } = await params;
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,10 @@ import Script from "next/script";
|
||||||
import { Lexend } from "next/font/google";
|
import { Lexend } from "next/font/google";
|
||||||
|
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
|
|
||||||
|
import Providers from "./provider";
|
||||||
import Header from "@/components/header";
|
import Header from "@/components/header";
|
||||||
import Footer from "@/components/footer";
|
import Footer from "@/components/footer";
|
||||||
import Providers from "./provider";
|
|
||||||
|
|
||||||
const lexend = Lexend({
|
const lexend = Lexend({
|
||||||
subsets: ["latin"],
|
subsets: ["latin"],
|
||||||
|
|
@ -13,7 +14,7 @@ const lexend = Lexend({
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "TomodachiShare",
|
title: "TomodachiShare",
|
||||||
description: "Share your Tomodachi Life Miis",
|
description: "Discover and share Mii residents for your Tomodachi Life island!",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,13 @@
|
||||||
|
import { Metadata } from "next";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { auth } from "@/lib/auth";
|
import { auth } from "@/lib/auth";
|
||||||
import LoginButtons from "@/components/login-buttons";
|
import LoginButtons from "@/components/login-buttons";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Login - TomodachiShare",
|
||||||
|
description: "Sign in with Discord or GitHub to upload Miis, and like others' creations",
|
||||||
|
};
|
||||||
|
|
||||||
export default async function LoginPage() {
|
export default async function LoginPage() {
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { Metadata, ResolvingMetadata } from "next";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
|
|
@ -15,6 +16,65 @@ interface Props {
|
||||||
params: Promise<{ slug: string }>;
|
params: Promise<{ slug: string }>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function generateMetadata({ params }: Props, parent: ResolvingMetadata): Promise<Metadata> {
|
||||||
|
const { slug } = await params;
|
||||||
|
|
||||||
|
const mii = await prisma.mii.findUnique({
|
||||||
|
where: {
|
||||||
|
id: Number(slug),
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
user: {
|
||||||
|
select: {
|
||||||
|
username: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
_count: {
|
||||||
|
select: { likedBy: true }, // Get total like count
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Bots get redirected anyways
|
||||||
|
if (!mii) return {};
|
||||||
|
|
||||||
|
const miiImageUrl = `/mii/${mii.id}/mii.webp`;
|
||||||
|
const qrCodeUrl = `/mii/${mii.id}/qrcode.webp`;
|
||||||
|
|
||||||
|
const username = `@${mii.user.username}`;
|
||||||
|
|
||||||
|
return {
|
||||||
|
metadataBase: new URL(process.env.BASE_URL!),
|
||||||
|
title: `${mii.name} - TomodachiShare`,
|
||||||
|
description: `Check out '${mii.name}', a Tomodachi Life Mii created by ${username} on TomodachiShare. From ${mii.islandName} Island with ${mii._count.likedBy} likes.`,
|
||||||
|
keywords: [`mii`, `tomodachi life`, `nintendo`, ...mii.tags],
|
||||||
|
creator: username,
|
||||||
|
category: "Gaming",
|
||||||
|
openGraph: {
|
||||||
|
locale: "en_US",
|
||||||
|
type: "article",
|
||||||
|
images: [miiImageUrl, qrCodeUrl],
|
||||||
|
siteName: "TomodachiShare",
|
||||||
|
publishedTime: mii.createdAt.toISOString(),
|
||||||
|
authors: username,
|
||||||
|
},
|
||||||
|
twitter: {
|
||||||
|
card: "summary_large_image",
|
||||||
|
title: `${mii.name} - TomodachiShare`,
|
||||||
|
description: `Check out '${mii.name}', a Tomodachi Life Mii created by ${username} on TomodachiShare. From ${mii.islandName} Island with ${mii._count.likedBy} likes.`,
|
||||||
|
images: [miiImageUrl, qrCodeUrl],
|
||||||
|
creator: username,
|
||||||
|
},
|
||||||
|
alternates: {
|
||||||
|
canonical: `/mii/${mii.id}`,
|
||||||
|
},
|
||||||
|
robots: {
|
||||||
|
index: true,
|
||||||
|
follow: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export default async function MiiPage({ params }: Props) {
|
export default async function MiiPage({ params }: Props) {
|
||||||
const { slug } = await params;
|
const { slug } = await params;
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
|
|
@ -26,7 +86,6 @@ export default async function MiiPage({ params }: Props) {
|
||||||
include: {
|
include: {
|
||||||
user: {
|
user: {
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
|
||||||
username: true,
|
username: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,11 @@
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { Icon } from "@iconify/react";
|
import { Icon } from "@iconify/react";
|
||||||
|
import { Metadata } from "next";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Not Found - TomodachiShare",
|
||||||
|
description: "The requested page could not be found on TomodachiShare",
|
||||||
|
};
|
||||||
|
|
||||||
export default function NotFound() {
|
export default function NotFound() {
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,10 @@
|
||||||
|
import { Metadata } from "next";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Privacy Policy - TomodachiShare",
|
||||||
|
description: "Learn how TomodachiShare collects, uses, and protects your data",
|
||||||
|
};
|
||||||
|
|
||||||
export default function PrivacyPage() {
|
export default function PrivacyPage() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { Metadata, ResolvingMetadata } from "next";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
@ -13,6 +14,62 @@ interface Props {
|
||||||
params: Promise<{ slug: string }>;
|
params: Promise<{ slug: string }>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function generateMetadata({ params }: Props, parent: ResolvingMetadata): Promise<Metadata> {
|
||||||
|
const { slug } = await params;
|
||||||
|
|
||||||
|
const user = await prisma.user.findUnique({
|
||||||
|
where: {
|
||||||
|
id: Number(slug),
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
_count: {
|
||||||
|
select: {
|
||||||
|
miis: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Bots get redirected anyways
|
||||||
|
if (!user) return {};
|
||||||
|
|
||||||
|
const joinDate = user.createdAt.toLocaleDateString("en-US", {
|
||||||
|
month: "long",
|
||||||
|
year: "numeric",
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
metadataBase: new URL(process.env.BASE_URL!),
|
||||||
|
title: `${user.name} (@${user.username}) - TomodachiShare`,
|
||||||
|
description: `View ${user.name}'s profile on TomodachiShare. Creator of ${user._count.miis} Miis. Member since ${joinDate}.`,
|
||||||
|
keywords: [`tomodachi life`, `mii creator`, `nintendo`, `mii collection`, `profile`],
|
||||||
|
creator: user.username,
|
||||||
|
category: "Gaming",
|
||||||
|
openGraph: {
|
||||||
|
locale: "en_US",
|
||||||
|
type: "profile",
|
||||||
|
images: [user.image ?? "/missing.webp"],
|
||||||
|
siteName: "TomodachiShare",
|
||||||
|
username: user.username,
|
||||||
|
firstName: user.name,
|
||||||
|
},
|
||||||
|
twitter: {
|
||||||
|
card: "summary",
|
||||||
|
title: `${user.name} (@${user.username}) - TomodachiShare`,
|
||||||
|
description: `View ${user.name}'s profile on TomodachiShare. Creator of ${user._count.miis} Miis. Member since ${joinDate}.`,
|
||||||
|
images: [user.image ?? "/missing.webp"],
|
||||||
|
creator: user.username!,
|
||||||
|
},
|
||||||
|
alternates: {
|
||||||
|
canonical: `/profile/${user.id}`,
|
||||||
|
},
|
||||||
|
robots: {
|
||||||
|
index: true,
|
||||||
|
follow: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export default async function ProfilePage({ params }: Props) {
|
export default async function ProfilePage({ params }: Props) {
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
const { slug } = await params;
|
const { slug } = await params;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { Metadata } from "next";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
@ -9,6 +10,11 @@ import { prisma } from "@/lib/prisma";
|
||||||
|
|
||||||
import ProfileSettings from "@/components/profile-settings";
|
import ProfileSettings from "@/components/profile-settings";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Profile Settings - TomodachiShare",
|
||||||
|
description: "Change your account info or delete it",
|
||||||
|
};
|
||||||
|
|
||||||
export default async function ProfileSettingsPage() {
|
export default async function ProfileSettingsPage() {
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,13 @@
|
||||||
|
import { Metadata } from "next";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { auth } from "@/lib/auth";
|
import { auth } from "@/lib/auth";
|
||||||
import SubmitForm from "@/components/submit-form";
|
import SubmitForm from "@/components/submit-form";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Submit a Mii - TomodachiShare",
|
||||||
|
description: "Upload your Tomodachi Life Mii through its QR code and share it with others",
|
||||||
|
};
|
||||||
|
|
||||||
export default async function SubmitPage() {
|
export default async function SubmitPage() {
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,10 @@
|
||||||
|
import { Metadata } from "next";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Terms of Service - TomodachiShare",
|
||||||
|
description: "Review the rules and guidelines for using TomodachiShare",
|
||||||
|
};
|
||||||
|
|
||||||
export default function PrivacyPage() {
|
export default function PrivacyPage() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue