mirror of
https://github.com/trafficlunar/tomodachi-share.git
synced 2026-05-13 05:07:46 +00:00
fix: random bug fixes and a bit of metadata reimplementation
This commit is contained in:
parent
9e712530b0
commit
b6839c8d21
3 changed files with 68 additions and 12 deletions
|
|
@ -25,6 +25,7 @@ export default async function Reports({ searchParams }: { searchParams: { status
|
|||
]);
|
||||
|
||||
const totalPages = Math.ceil(total / PAGE_SIZE);
|
||||
const FRONTEND_URL = process.env.NEXT_PUBLIC_FRONTEND_URL;
|
||||
|
||||
const updateStatus = async (formData: FormData) => {
|
||||
"use server";
|
||||
|
|
@ -88,21 +89,24 @@ export default async function Reports({ searchParams }: { searchParams: { status
|
|||
<div className="grid grid-cols-4 text-xs text-zinc-600 mt-4 max-sm:grid-cols-2">
|
||||
<div>
|
||||
<p>Target ID</p>
|
||||
<a href={report.reportType === "MII" ? `/mii/${report.targetId}` : `/profile/${report.targetId}`} className="text-blue-600 text-sm">
|
||||
<a
|
||||
href={report.reportType === "MII" ? `${FRONTEND_URL}/mii/${report.targetId}` : `${FRONTEND_URL}/profile/${report.targetId}`}
|
||||
className="text-blue-600 text-sm"
|
||||
>
|
||||
{report.targetId}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p>Creator ID</p>
|
||||
<a href={`/profile/${report.creatorId}`} className="text-blue-600 text-sm">
|
||||
<a href={`${FRONTEND_URL}/profile/${report.creatorId}`} className="text-blue-600 text-sm">
|
||||
{report.creatorId}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p>Reporter</p>
|
||||
<a href={`/profile/${report.authorId}`} className="text-blue-600 text-sm">
|
||||
<a href={`${FRONTEND_URL}/profile/${report.authorId}`} className="text-blue-600 text-sm">
|
||||
{report.authorId}
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -54,8 +54,36 @@ export default function MiiPage() {
|
|||
if (loading || !mii) return <div className="p-6 text-center">Loading...</div>;
|
||||
const images = [...Array.from({ length: mii.imageCount ?? 0 }, (_, index) => `${API_URL}/mii/${mii.id}/image?type=image${index}`)];
|
||||
|
||||
const metaTitle = `${mii.name} - TomodachiShare`;
|
||||
const platformLabel = mii.platform === "SWITCH" ? "Switch Living the Dream" : "3DS";
|
||||
const metaDescription = `Check out '${mii.name}', a ${platformLabel} Tomodachi Life Mii created by ${mii.user.name} on TomodachiShare with ${mii.likeCount ?? 0} likes.`;
|
||||
const metaImage = `${import.meta.env.VITE_API_URL}/mii/${mii.id}/image?type=metadata`;
|
||||
const metaImageAlt = `${mii.name}, ${mii.tags?.join(", ")} ${mii.gender} Mii character`;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center">
|
||||
<title>{metaTitle}</title>
|
||||
<meta name="description" content={metaDescription} />
|
||||
<meta name="keywords" content={["mii", "tomodachi life", "nintendo", "tomodachishare", ...mii.tags].join(", ")} />
|
||||
<link rel="canonical" href={`${import.meta.env.VITE_BASE_URL}/mii/${mii.id}`} />
|
||||
|
||||
{/* Open Graph */}
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="og:title" content={metaTitle} />
|
||||
<meta property="og:description" content={metaDescription} />
|
||||
<meta property="og:image" content={metaImage} />
|
||||
<meta property="og:image:alt" content={metaImageAlt} />
|
||||
<meta property="article:published_time" content={new Date(mii.createdAt).toISOString()} />
|
||||
<meta property="article:author" content={`@${mii.user.name}`} />
|
||||
|
||||
{/* Twitter / X */}
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content={metaTitle} />
|
||||
<meta name="twitter:description" content={metaDescription} />
|
||||
<meta name="twitter:image" content={metaImage} />
|
||||
<meta name="twitter:image:alt" content={metaImageAlt} />
|
||||
<meta name="twitter:creator" content={`@${mii.user.name}`} />
|
||||
|
||||
<div className="max-w-5xl w-full flex flex-col gap-4">
|
||||
{mii.quarantined && (
|
||||
<div className="bg-red-100 border-2 border-red-400 rounded-2xl shadow-lg p-4 flex items-center gap-3 text-red-700">
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ export default function ProfileLayout() {
|
|||
const [loading, setLoading] = useState(true);
|
||||
const $session = useStore(session);
|
||||
|
||||
const userId = Number($session ? id : $session?.user?.id);
|
||||
|
||||
useEffect(() => {
|
||||
if ($session === undefined) return; // session still loading
|
||||
|
||||
const userId = id ?? $session?.user?.id;
|
||||
if (!userId) {
|
||||
navigate("/404");
|
||||
return;
|
||||
|
|
@ -44,14 +44,38 @@ export default function ProfileLayout() {
|
|||
return <div className="p-6 text-center">Loading...</div>;
|
||||
}
|
||||
|
||||
const sessionUserId = $session?.user?.id ? Number($session.user.id) : null;
|
||||
const page = location.pathname;
|
||||
const isAdmin = sessionUserId === Number(import.meta.env.VITE_ADMIN_USER_ID);
|
||||
const isAdmin = userId === Number(import.meta.env.VITE_ADMIN_USER_ID);
|
||||
const isContributor = import.meta.env.VITE_CONTRIBUTORS_USER_IDS?.split(",").includes(String(user?.id));
|
||||
const isOwnProfile = sessionUserId === user?.id;
|
||||
const isOwnProfile = userId === user?.id;
|
||||
|
||||
const joinDate = new Date(user.createdAt).toLocaleDateString("en-US", { month: "long", year: "numeric" });
|
||||
const metaTitle = `${user.name} - TomodachiShare`;
|
||||
const metaDescription = `View ${user.name}'s profile on TomodachiShare. Creator of ${user._count.miis} Miis. Member since ${joinDate}.`;
|
||||
const metaImage = user.image.startsWith("/profile")
|
||||
? `${import.meta.env.VITE_API_URL}${user.image}`
|
||||
: (user.image ?? `${import.meta.env.VITE_API_URL}/guest.png`);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<title>{metaTitle}</title>
|
||||
<meta name="description" content={metaDescription} />
|
||||
<meta name="keywords" content="mii, tomodachi life, nintendo, mii creator, mii collection, profile" />
|
||||
<link rel="canonical" href={`${import.meta.env.VITE_BASE_URL}/profile/${user.id}`} />
|
||||
|
||||
{/* Open Graph */}
|
||||
<meta property="og:type" content="profile" />
|
||||
<meta property="og:title" content={metaTitle} />
|
||||
<meta property="og:description" content={metaDescription} />
|
||||
<meta property="og:image" content={metaImage} />
|
||||
<meta property="og:profile:username" content={user.name} />
|
||||
|
||||
{/* Twitter / X */}
|
||||
<meta name="twitter:card" content="summary" />
|
||||
<meta name="twitter:title" content={metaTitle} />
|
||||
<meta name="twitter:description" content={metaDescription} />
|
||||
<meta name="twitter:image" content={metaImage} />
|
||||
<meta name="twitter:creator" content={user.name} />
|
||||
|
||||
<div className="bg-amber-50 border-2 border-amber-500 rounded-2xl shadow-lg p-4 flex gap-4 mb-2 max-md:flex-col">
|
||||
<div className="flex w-full gap-4 overflow-x-scroll">
|
||||
{/* Profile picture */}
|
||||
|
|
@ -110,19 +134,19 @@ export default function ProfileLayout() {
|
|||
<span>Admin</span>
|
||||
</a>
|
||||
)}
|
||||
{isOwnProfile && page !== "/profile/likes" && (
|
||||
{isOwnProfile && location.pathname !== "/profile/likes" && (
|
||||
<Link aria-label="Go to My Likes" to="/profile/likes">
|
||||
<Icon icon="icon-park-solid:like" />
|
||||
<span>My Likes</span>
|
||||
</Link>
|
||||
)}
|
||||
{isOwnProfile && page !== "/profile/settings" && (
|
||||
{isOwnProfile && location.pathname !== "/profile/settings" && (
|
||||
<Link aria-label="Go to Settings" to="/profile/settings">
|
||||
<Icon icon="material-symbols:settings-rounded" />
|
||||
<span>Settings</span>
|
||||
</Link>
|
||||
)}
|
||||
{(page === "/profile/likes" || page === "/profile/settings") && (
|
||||
{(location.pathname === "/profile/likes" || location.pathname === "/profile/settings") && (
|
||||
<Link aria-label="Go Back to Profile" to={`/profile/${user.id}`}>
|
||||
<Icon icon="tabler:chevron-left" />
|
||||
<span>Back</span>
|
||||
|
|
|
|||
Loading…
Reference in a new issue