Replace landing hero PixelAvatar with live PetHabitat preview.
Shows a tamagotchi-style mini pet screen for guests and signed-in users, with a skeleton while loading customization data.
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import { Suspense } from "react";
|
import { Suspense } from "react";
|
||||||
|
|
||||||
import { PixelAvatar } from "@/components/avatar/pixel-avatar";
|
import { LandingPetPreview } from "@/components/landing/landing-pet-preview";
|
||||||
import { LandingHeroActions } from "@/components/layout/landing-hero-actions";
|
import { LandingHeroActions } from "@/components/layout/landing-hero-actions";
|
||||||
import { SiteFooter, SiteHeader } from "@/components/layout/site-header";
|
import { SiteFooter, SiteHeader } from "@/components/layout/site-header";
|
||||||
import {
|
import {
|
||||||
@@ -37,7 +37,7 @@ export default function Home() {
|
|||||||
|
|
||||||
<Card className="border-dashed">
|
<Card className="border-dashed">
|
||||||
<CardContent className="flex flex-col items-center gap-6 pt-8">
|
<CardContent className="flex flex-col items-center gap-6 pt-8">
|
||||||
<PixelAvatar mood="happy" />
|
<LandingPetPreview />
|
||||||
<div className="space-y-2 text-center">
|
<div className="space-y-2 text-center">
|
||||||
<p className="text-sm font-medium">Your daily loop</p>
|
<p className="text-sm font-medium">Your daily loop</p>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
|
|||||||
96
components/landing/landing-pet-preview.tsx
Normal file
96
components/landing/landing-pet-preview.tsx
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import Link from "next/link";
|
||||||
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
|
import { PetHabitat } from "@/components/pet/pet-habitat";
|
||||||
|
import {
|
||||||
|
defaultAvatarCustomization,
|
||||||
|
getAvatarCustomization,
|
||||||
|
type AvatarCustomization,
|
||||||
|
} from "@/lib/avatar-customization-storage";
|
||||||
|
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 aspect-[4/3] w-full animate-pulse bg-muted/40" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function LandingPetPreview() {
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
const [isSignedIn, setIsSignedIn] = useState(false);
|
||||||
|
const [customization, setCustomization] = useState<AvatarCustomization>(
|
||||||
|
defaultAvatarCustomization,
|
||||||
|
);
|
||||||
|
|
||||||
|
const loadPreview = useCallback(async () => {
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
|
const supabase = createClient();
|
||||||
|
const {
|
||||||
|
data: { user },
|
||||||
|
} = await supabase.auth.getUser();
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
setIsSignedIn(false);
|
||||||
|
setCustomization(defaultAvatarCustomization);
|
||||||
|
setIsLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsSignedIn(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
setCustomization(await getAvatarCustomization());
|
||||||
|
} catch {
|
||||||
|
setCustomization(defaultAvatarCustomization);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
void loadPreview();
|
||||||
|
}, [loadPreview]);
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return <LandingPetPreviewSkeleton />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const preview = (
|
||||||
|
<PetHabitat
|
||||||
|
customization={customization}
|
||||||
|
className="w-full max-w-sm"
|
||||||
|
showStyleLink={false}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ type PetHabitatProps = {
|
|||||||
customization: AvatarCustomization;
|
customization: AvatarCustomization;
|
||||||
className?: string;
|
className?: string;
|
||||||
fillViewport?: boolean;
|
fillViewport?: boolean;
|
||||||
|
showStyleLink?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const MOOD_LABELS = {
|
const MOOD_LABELS = {
|
||||||
@@ -34,6 +35,7 @@ export function PetHabitat({
|
|||||||
customization,
|
customization,
|
||||||
className,
|
className,
|
||||||
fillViewport = false,
|
fillViewport = false,
|
||||||
|
showStyleLink = true,
|
||||||
}: PetHabitatProps) {
|
}: PetHabitatProps) {
|
||||||
const [condition, setCondition] = useState<AvatarCondition>(
|
const [condition, setCondition] = useState<AvatarCondition>(
|
||||||
defaultAvatarCondition,
|
defaultAvatarCondition,
|
||||||
@@ -87,9 +89,11 @@ export function PetHabitat({
|
|||||||
<PixelGauge type="heart" value={condition.health} variant="overlay" />
|
<PixelGauge type="heart" value={condition.health} variant="overlay" />
|
||||||
<PixelGauge type="energy" value={condition.energy} variant="overlay" />
|
<PixelGauge type="energy" value={condition.energy} variant="overlay" />
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute right-2 top-2 z-20">
|
{showStyleLink ? (
|
||||||
<PetStyleLink />
|
<div className="absolute right-2 top-2 z-20">
|
||||||
</div>
|
<PetStyleLink />
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
<div className="pointer-events-none absolute inset-x-0 bottom-0 z-[2] h-8 bg-gradient-to-t from-black/35 to-transparent" />
|
<div className="pointer-events-none absolute inset-x-0 bottom-0 z-[2] h-8 bg-gradient-to-t from-black/35 to-transparent" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user