156 lines
5.0 KiB
TypeScript
156 lines
5.0 KiB
TypeScript
|
|
"use client"
|
||
|
|
import React, { useState, useEffect } from "react"
|
||
|
|
import { MenuItem } from "@/types/global"
|
||
|
|
import Image from "next/image"
|
||
|
|
import { cn } from "@/lib/utils"
|
||
|
|
import ChatSidebar from "./components/ChatSidebar"
|
||
|
|
import { Badge } from "../ui/badge"
|
||
|
|
import Link from "next/link"
|
||
|
|
import { usePathname, useRouter } from "next/navigation"
|
||
|
|
import { useMainLayout } from "@/context/mainLayout"
|
||
|
|
import { useCurrentUser } from "@/hooks/auth"
|
||
|
|
import Notice from "./components/Notice"
|
||
|
|
import useCreatorNavigation from "@/hooks/useCreatorNavigation"
|
||
|
|
|
||
|
|
// 菜单项接口
|
||
|
|
interface IMenuItem {
|
||
|
|
id: MenuItem
|
||
|
|
icon: string
|
||
|
|
selectedIcon: string
|
||
|
|
label: string
|
||
|
|
link: string
|
||
|
|
isSelected: boolean
|
||
|
|
}
|
||
|
|
|
||
|
|
// 主侧边栏组件
|
||
|
|
function Sidebar() {
|
||
|
|
const pathname = usePathname()
|
||
|
|
const router = useRouter()
|
||
|
|
const { isSidebarExpanded, dispatch } = useMainLayout();
|
||
|
|
const { data: user } = useCurrentUser();
|
||
|
|
const { routerToCreate } = useCreatorNavigation()
|
||
|
|
|
||
|
|
// 在 /create/image 页面时强制收起侧边栏
|
||
|
|
const isImageCreatePage = ['/generate/image', '/generate/image-2-image', '/generate/image-edit', '/generate/image-2-background'].includes(pathname)
|
||
|
|
const actualIsExpanded = isImageCreatePage ? false : isSidebarExpanded
|
||
|
|
|
||
|
|
const menuItems: IMenuItem[] = [
|
||
|
|
{
|
||
|
|
id: MenuItem.FOR_YOU,
|
||
|
|
icon: "/icons/explore.svg",
|
||
|
|
selectedIcon: "/icons/explore_selected.svg",
|
||
|
|
label: "Home",
|
||
|
|
link: '/',
|
||
|
|
isSelected: pathname === '/',
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id: MenuItem.CREATE,
|
||
|
|
icon: "/icons/create.svg",
|
||
|
|
selectedIcon: "/icons/create_selected.svg",
|
||
|
|
label: "Create a Character",
|
||
|
|
link: '/create',
|
||
|
|
isSelected: pathname.startsWith('/create'),
|
||
|
|
},
|
||
|
|
// {
|
||
|
|
// id: MenuItem.EXPLORE,
|
||
|
|
// icon: "/icons/explore.svg",
|
||
|
|
// selectedIcon: "/icons/explore_selected.svg",
|
||
|
|
// label: "Explore",
|
||
|
|
// link: '/explore',
|
||
|
|
// isSelected: pathname.startsWith('/explore'),
|
||
|
|
// },
|
||
|
|
{
|
||
|
|
id: MenuItem.CHAT,
|
||
|
|
icon: "/icons/contact.svg",
|
||
|
|
selectedIcon: "/icons/contact_selected.svg",
|
||
|
|
label: "My Crushes",
|
||
|
|
link: '/contact',
|
||
|
|
isSelected: pathname.startsWith('/contact'),
|
||
|
|
},
|
||
|
|
]
|
||
|
|
|
||
|
|
const toggleExpanded = () => {
|
||
|
|
// 在 /create/image 页面时禁用展开功能
|
||
|
|
if (isImageCreatePage) return
|
||
|
|
dispatch({
|
||
|
|
type: "updateState",
|
||
|
|
payload: {
|
||
|
|
isSidebarExpanded: !isSidebarExpanded,
|
||
|
|
},
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
return (
|
||
|
|
<aside className={cn(
|
||
|
|
"sticky top-0 left-0 bottom-0 z-10 h-screen flex-shrink-0 bg-background-default border-r border-outline-normal transition-all duration-300 ease-in-out",
|
||
|
|
isImageCreatePage ? "w-0 overflow-hidden" : (actualIsExpanded ? "w-80" : "w-20")
|
||
|
|
)}>
|
||
|
|
<div className="flex flex-col h-full py-4">
|
||
|
|
{/* 顶部导航 */}
|
||
|
|
<div className="flex flex-col gap-2 px-4">
|
||
|
|
{/* 展开/收起按钮 */}
|
||
|
|
<div className={cn(
|
||
|
|
"flex items-center justify-start p-2 select-none",
|
||
|
|
isImageCreatePage ? "cursor-default" : "cursor-pointer"
|
||
|
|
)} onClick={toggleExpanded}>
|
||
|
|
<div className="w-8 h-8 flex items-center justify-center flex-shrink-0">
|
||
|
|
{actualIsExpanded ? (
|
||
|
|
<Image src="/icons/fold.svg" alt="Fold" width={32} height={32} />
|
||
|
|
) : (
|
||
|
|
<Image src="/icons/expand.svg" alt="Expand" width={32} height={32} />
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* 菜单项 */}
|
||
|
|
{menuItems.map((item) => {
|
||
|
|
const menuItemContent = (
|
||
|
|
<div
|
||
|
|
className={cn(
|
||
|
|
"flex items-center gap-2 p-2 rounded-full cursor-pointer transition-colors hover:bg-surface-element-hover",
|
||
|
|
item.isSelected && "bg-surface-element-normal"
|
||
|
|
)}
|
||
|
|
>
|
||
|
|
<div className="w-8 h-8 flex items-center justify-center flex-shrink-0">
|
||
|
|
<Image
|
||
|
|
src={item.isSelected ? item.selectedIcon : item.icon}
|
||
|
|
alt={item.label}
|
||
|
|
width={32}
|
||
|
|
height={32}
|
||
|
|
className="w-full h-full"
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
{actualIsExpanded && (
|
||
|
|
<span className="txt-label-m text-txt-primary-normal flex-1 truncate">
|
||
|
|
{item.label}
|
||
|
|
</span>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
|
||
|
|
// 对于 create 链接,使用 div + onClick 避免触发 ProgressProvider
|
||
|
|
if (item.link.startsWith('/create')) {
|
||
|
|
return (
|
||
|
|
<div key={item.id} onClick={routerToCreate}>
|
||
|
|
{menuItemContent}
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 其他链接使用 Link 组件以便 SEO
|
||
|
|
return (
|
||
|
|
<Link href={item.link} key={item.id}>
|
||
|
|
{menuItemContent}
|
||
|
|
</Link>
|
||
|
|
);
|
||
|
|
})}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<ChatSidebar isExpanded={actualIsExpanded} />
|
||
|
|
<Notice actualIsExpanded={actualIsExpanded} />
|
||
|
|
</div>
|
||
|
|
</aside>
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
export default Sidebar
|