feat(lovart-home): add hover avatar dropdown menu
This commit is contained in:
parent
310dd115b2
commit
b30ebc58b9
@ -2,7 +2,7 @@
|
||||
|
||||
import { Fragment, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { signIn } from "next-auth/react";
|
||||
import { signIn, signOut } from "next-auth/react";
|
||||
import useSWR from "swr";
|
||||
import useSWRInfinite from "swr/infinite";
|
||||
import {
|
||||
@ -12,11 +12,12 @@ import {
|
||||
ChevronDown,
|
||||
Clapperboard,
|
||||
Folder,
|
||||
Heart,
|
||||
Home,
|
||||
ImageIcon,
|
||||
Info,
|
||||
Loader2,
|
||||
LogOut,
|
||||
Mail,
|
||||
Monitor,
|
||||
Palette,
|
||||
Plus,
|
||||
@ -26,6 +27,7 @@ import {
|
||||
Star,
|
||||
Upload,
|
||||
User,
|
||||
Users,
|
||||
X,
|
||||
Zap,
|
||||
} from "lucide-react";
|
||||
@ -161,6 +163,12 @@ const PROMPT_TAGS = [
|
||||
const MASONRY_HEIGHTS = [420, 290, 340, 470, 300, 360, 430, 320];
|
||||
const FEATURED_PAGE_SIZE = 24;
|
||||
|
||||
function getUserInitials(user: HomeUser | null) {
|
||||
const source = (user?.name || user?.email || "mu").trim();
|
||||
if (!source) return "mu";
|
||||
return source.slice(0, 2).toLowerCase();
|
||||
}
|
||||
|
||||
function formatRelativeTime(value?: string) {
|
||||
if (!value) return "更新于今天";
|
||||
const date = new Date(value);
|
||||
@ -179,6 +187,29 @@ function formatPrice(currency: string, value: number) {
|
||||
return `${currency}${value}`;
|
||||
}
|
||||
|
||||
function AccountMenuItem({
|
||||
icon: Icon,
|
||||
label,
|
||||
onClick,
|
||||
}: {
|
||||
icon: typeof User;
|
||||
label: string;
|
||||
onClick: () => void;
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
className="flex w-full items-center gap-3 rounded-2xl px-3 py-2 text-left text-[15px] font-medium text-black/80 hover:bg-black/5"
|
||||
>
|
||||
<span className="flex h-9 w-9 items-center justify-center rounded-xl bg-black/5 text-black/70">
|
||||
<Icon className="h-[18px] w-[18px]" />
|
||||
</span>
|
||||
<span className="flex-1">{label}</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export function LovartHome({
|
||||
user,
|
||||
isAuthenticated,
|
||||
@ -669,6 +700,12 @@ export function LovartHome({
|
||||
previewCursorVisible ? " |" : ""
|
||||
}`;
|
||||
|
||||
const userInitials = useMemo(() => getUserInitials(user), [user]);
|
||||
const userDisplayName = useMemo(() => {
|
||||
if (!user) return "";
|
||||
return (user.name || user.email || "用户").toString();
|
||||
}, [user]);
|
||||
|
||||
return (
|
||||
<div className="min-h-svh bg-[#f4f4f4] text-[#171717]">
|
||||
<header className="fixed left-0 top-0 z-40 h-[68px] w-full bg-[#f4f4f4]/90 backdrop-blur">
|
||||
@ -754,9 +791,94 @@ export function LovartHome({
|
||||
</HoverCard>
|
||||
|
||||
{isAuthenticated ? (
|
||||
<button className="flex h-8 w-8 items-center justify-center rounded-full bg-[#d4551f] text-[14px] font-semibold text-white">
|
||||
{(user?.name || user?.email || "mu").slice(0, 2).toLowerCase()}
|
||||
</button>
|
||||
<HoverCard openDelay={80} closeDelay={120}>
|
||||
<HoverCardTrigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
className="flex h-8 w-8 items-center justify-center rounded-full bg-[#d4551f] text-[14px] font-semibold text-white"
|
||||
aria-label="Account menu"
|
||||
>
|
||||
{userInitials}
|
||||
</button>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent
|
||||
align="end"
|
||||
sideOffset={12}
|
||||
className="w-[360px] rounded-3xl border border-black/10 bg-white p-0 shadow-xl"
|
||||
>
|
||||
<div className="p-4">
|
||||
<div className="rounded-3xl border border-black/10 bg-white p-4">
|
||||
<div className="flex justify-center">
|
||||
<div className="flex h-14 w-14 items-center justify-center rounded-full bg-[#d4551f] text-[18px] font-semibold text-white">
|
||||
{userInitials}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-3 flex items-center justify-between gap-3">
|
||||
<div className="min-w-0">
|
||||
<div className="truncate text-[18px] font-semibold text-black">
|
||||
{userDisplayName}
|
||||
</div>
|
||||
<div className="truncate text-[14px] text-black/45">
|
||||
{user?.email || ""}
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
size="sm"
|
||||
className="h-9 rounded-2xl bg-[#2f3542] px-6 text-[14px] text-white hover:bg-[#242a36]"
|
||||
onClick={() => setUpgradeDialogOpen(true)}
|
||||
>
|
||||
升级
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 border-t border-black/10 pt-3">
|
||||
<button
|
||||
type="button"
|
||||
className="flex w-full items-center justify-between rounded-2xl px-1 py-1 text-[15px] font-semibold text-black/80 hover:bg-black/5"
|
||||
onClick={() => setUpgradeDialogOpen(true)}
|
||||
>
|
||||
<span>积分</span>
|
||||
<span className="inline-flex items-center gap-2 text-[15px] font-semibold text-black/60">
|
||||
{membershipData?.summary?.currentCredits ?? 0}
|
||||
<ArrowRight className="h-4 w-4" />
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-3 space-y-1">
|
||||
<AccountMenuItem
|
||||
icon={Users}
|
||||
label="创建团队"
|
||||
onClick={() => toast.info("团队功能后续开放")}
|
||||
/>
|
||||
<AccountMenuItem
|
||||
icon={User}
|
||||
label="账户管理"
|
||||
onClick={() => toast.info("账户管理后续开放")}
|
||||
/>
|
||||
<AccountMenuItem
|
||||
icon={Info}
|
||||
label="使用指南"
|
||||
onClick={() => toast.info("使用指南后续开放")}
|
||||
/>
|
||||
<AccountMenuItem
|
||||
icon={Mail}
|
||||
label="联系我们"
|
||||
onClick={() => toast.info("联系我们后续开放")}
|
||||
/>
|
||||
<AccountMenuItem
|
||||
icon={LogOut}
|
||||
label="退出登录"
|
||||
onClick={() => {
|
||||
void signOut({ redirect: true, callbackUrl: "/" });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user