Pair Share Tech Mono body text with Press Start 2P display accents, and show a randomized dressed pixel character for signed-out homepage previews.
138 lines
3.4 KiB
TypeScript
138 lines
3.4 KiB
TypeScript
"use client";
|
|
|
|
import Link from "next/link";
|
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
|
|
import { PetHabitat } from "@/components/pet/pet-habitat";
|
|
import {
|
|
defaultAvatarCustomization,
|
|
getAvatarCustomization,
|
|
type AvatarCustomization,
|
|
} from "@/lib/avatar-customization-storage";
|
|
import { getGuestLandingPetCustomization } from "@/lib/landing-random-pet";
|
|
import { routes } from "@/lib/routes";
|
|
import { createClient } from "@/lib/supabase/client";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
function LandingPetPreviewSkeleton() {
|
|
return (
|
|
<div
|
|
aria-hidden="true"
|
|
className="tamagotchi-shell w-full max-w-sm overflow-hidden p-3 sm:p-4"
|
|
>
|
|
<div className="mb-3 space-y-2 border-b-2 border-border/60 pb-3">
|
|
<div className="h-4 w-24 animate-pulse rounded bg-muted" />
|
|
<div className="h-2 w-16 animate-pulse rounded bg-muted" />
|
|
</div>
|
|
<div className="tamagotchi-lcd tamagotchi-lcd-landing animate-pulse bg-muted/40" />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function computeLandingPetScale(lcdWidth: number): number {
|
|
return Math.min(7, Math.max(4, Math.round(lcdWidth / 36)));
|
|
}
|
|
|
|
export function LandingPetPreview() {
|
|
const resizeObserverRef = useRef<ResizeObserver | null>(null);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [isSignedIn, setIsSignedIn] = useState(false);
|
|
const [petScale, setPetScale] = useState(7);
|
|
const [customization, setCustomization] = useState<AvatarCustomization>(
|
|
defaultAvatarCustomization,
|
|
);
|
|
|
|
const lcdRef = useCallback((node: HTMLDivElement | null) => {
|
|
resizeObserverRef.current?.disconnect();
|
|
resizeObserverRef.current = null;
|
|
|
|
if (!node) {
|
|
return;
|
|
}
|
|
|
|
const updateScale = (width: number) => {
|
|
setPetScale(computeLandingPetScale(width));
|
|
};
|
|
|
|
updateScale(node.getBoundingClientRect().width);
|
|
|
|
const observer = new ResizeObserver((entries) => {
|
|
const entry = entries[0];
|
|
if (entry) {
|
|
updateScale(entry.contentRect.width);
|
|
}
|
|
});
|
|
|
|
observer.observe(node);
|
|
resizeObserverRef.current = observer;
|
|
}, []);
|
|
|
|
const loadPreview = useCallback(async () => {
|
|
setIsLoading(true);
|
|
|
|
const supabase = createClient();
|
|
const {
|
|
data: { user },
|
|
} = await supabase.auth.getUser();
|
|
|
|
if (!user) {
|
|
setIsSignedIn(false);
|
|
setCustomization(getGuestLandingPetCustomization());
|
|
setIsLoading(false);
|
|
return;
|
|
}
|
|
|
|
setIsSignedIn(true);
|
|
|
|
try {
|
|
setCustomization(await getAvatarCustomization());
|
|
} catch {
|
|
setCustomization(defaultAvatarCustomization);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
void loadPreview();
|
|
}, [loadPreview]);
|
|
|
|
useEffect(() => {
|
|
return () => {
|
|
resizeObserverRef.current?.disconnect();
|
|
};
|
|
}, []);
|
|
|
|
if (isLoading) {
|
|
return <LandingPetPreviewSkeleton />;
|
|
}
|
|
|
|
const preview = (
|
|
<PetHabitat
|
|
customization={customization}
|
|
className="w-full max-w-sm"
|
|
showStyleLink={false}
|
|
petScale={petScale}
|
|
lcdClassName="tamagotchi-lcd-landing"
|
|
lcdRef={lcdRef}
|
|
/>
|
|
);
|
|
|
|
if (isSignedIn) {
|
|
return (
|
|
<Link
|
|
href={routes.avatar}
|
|
aria-label="Go to your pet"
|
|
className={cn(
|
|
"block w-full max-w-sm rounded-sm transition-transform",
|
|
"hover:scale-[1.01] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
)}
|
|
>
|
|
{preview}
|
|
</Link>
|
|
);
|
|
}
|
|
|
|
return preview;
|
|
}
|