"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 (
);
}
const SPRITE_HEIGHT = 32;
const SPRITE_WIDTH = 16;
/** Target rendered pet height as a fraction of LCD height */
const TARGET_HEIGHT_RATIO = 0.42;
/** Keep pet from spanning too much of the LCD width */
const TARGET_WIDTH_RATIO = 0.2;
function computeLandingPetScale(lcdWidth: number, lcdHeight: number): number {
if (lcdWidth <= 0 || lcdHeight <= 0) {
return 4;
}
const scaleFromHeight = Math.round(
(lcdHeight * TARGET_HEIGHT_RATIO) / SPRITE_HEIGHT,
);
const scaleFromWidth = Math.round(
(lcdWidth * TARGET_WIDTH_RATIO) / SPRITE_WIDTH,
);
return Math.min(5, Math.max(2, Math.min(scaleFromHeight, scaleFromWidth)));
}
export function LandingPetPreview() {
const resizeObserverRef = useRef(null);
const [isLoading, setIsLoading] = useState(true);
const [isSignedIn, setIsSignedIn] = useState(false);
const [petScale, setPetScale] = useState(4);
const [customization, setCustomization] = useState(
defaultAvatarCustomization,
);
const lcdRef = useCallback((node: HTMLDivElement | null) => {
resizeObserverRef.current?.disconnect();
resizeObserverRef.current = null;
if (!node) {
return;
}
const updateScale = (width: number, height: number) => {
setPetScale(computeLandingPetScale(width, height));
};
const rect = node.getBoundingClientRect();
updateScale(rect.width, rect.height);
const observer = new ResizeObserver((entries) => {
const entry = entries[0];
if (entry) {
updateScale(entry.contentRect.width, entry.contentRect.height);
}
});
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 ;
}
const preview = (
);
if (isSignedIn) {
return (
{preview}
);
}
return preview;
}