feat: 重构用户菜单组件,引入新的对话框组件以替代链接跳转方式,优化用户交互体验。

This commit is contained in:
zyh 2024-10-22 07:35:36 +00:00
parent df93f0d897
commit 26fa196bdb
4 changed files with 151 additions and 68 deletions

View File

@ -0,0 +1,38 @@
import { Dialog, DialogTitle, DialogDescription, DialogRoot } from '~/components/ui/Dialog';
import { useAuth } from '~/hooks/useAuth';
interface ProfileDialogProps {
isOpen: boolean;
onClose: () => void;
}
export function ProfileDialog({ isOpen, onClose }: ProfileDialogProps) {
const { user } = useAuth();
if (!user) return null;
return (
<DialogRoot open={isOpen}>
<Dialog onBackdrop={onClose} onClose={onClose}>
<DialogTitle></DialogTitle>
<DialogDescription>
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-bolt-elements-textPrimary">
</label>
<p className="mt-1 text-bolt-elements-textSecondary">{user.nickname}</p>
</div>
<div>
<label className="block text-sm font-medium text-bolt-elements-textPrimary">
</label>
<p className="mt-1 text-bolt-elements-textSecondary">{user.phone}</p>
</div>
{/* 可以根据需要添加更多用户信息 */}
</div>
</DialogDescription>
</Dialog>
</DialogRoot>
);
}

View File

@ -0,0 +1,24 @@
import { Dialog, DialogTitle, DialogDescription, DialogRoot } from '~/components/ui/Dialog';
interface SubscriptionDialogProps {
isOpen: boolean;
onClose: () => void;
}
export function SubscriptionDialog({ isOpen, onClose }: SubscriptionDialogProps) {
return (
<DialogRoot open={isOpen}>
<Dialog onBackdrop={onClose} onClose={onClose}>
<DialogTitle></DialogTitle>
<DialogDescription>
<div className="space-y-4">
<p className="text-bolt-elements-textSecondary">
</p>
{/* 可以添加更多订阅相关的信息 */}
</div>
</DialogDescription>
</Dialog>
</DialogRoot>
);
}

View File

@ -1,18 +1,22 @@
import { Menu, Transition } from '@headlessui/react';
import { Fragment } from 'react';
import { Fragment, useState } from 'react';
import { useAuth } from '~/hooks/useAuth';
import { Avatar } from '~/components/ui/Avatar';
import { Link } from '@remix-run/react';
import { ProfileDialog } from '~/components/auth/ProfileDialog';
import { SubscriptionDialog } from '~/components/auth/SubscriptionDialog';
export function UserMenu() {
const { user, logout } = useAuth();
const [isProfileOpen, setIsProfileOpen] = useState(false);
const [isSubscriptionOpen, setIsSubscriptionOpen] = useState(false);
if (!user) return null;
return (
<>
<Menu as="div" className="relative inline-block text-left">
<div>
<Menu.Button className="inline-flex items-center gap-x-1.5 rounded-md bg-bolt-elements-background-depth-2 px-3 py-2 text-sm font-medium text-bolt-elements-textPrimary hover:bg-bolt-elements-background-depth-3 transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-bolt-elements-button-primary-background">
<Menu.Button className="inline-flex items-center gap-x-1.5 rounded-md bg-bolt-elements-background-depth-1 px-3 py-2 text-sm font-medium text-bolt-elements-textPrimary hover:bg-bolt-elements-background-depth-2 transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-bolt-elements-button-primary-background">
<Avatar src={user.avatarUrl} alt={user.nickname} />
<span className="ml-2">{user.nickname}</span>
<div className="i-ph:caret-down-fill text-bolt-elements-textSecondary" aria-hidden="true" />
@ -28,30 +32,30 @@ export function UserMenu() {
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items className="absolute right-0 z-10 mt-2 w-56 origin-top-right rounded-md bg-bolt-elements-background-depth-2 shadow-lg ring-1 ring-bolt-elements-borderColor focus:outline-none">
<Menu.Items className="absolute right-0 z-10 mt-2 w-56 origin-top-right rounded-md bg-bolt-elements-background-depth-1 shadow-lg ring-1 ring-bolt-elements-borderColor focus:outline-none">
<div className="py-1">
<Menu.Item>
{({ active }) => (
<Link
to="/profile"
<button
onClick={() => setIsProfileOpen(true)}
className={`${
active ? 'bg-bolt-elements-button-primary-background text-bolt-elements-button-primary-text' : 'text-bolt-elements-textSecondary'
} block px-4 py-2 text-sm transition-colors duration-150 ease-in-out`}
active ? 'bg-bolt-elements-background-depth-2 text-bolt-elements-textPrimary' : 'text-bolt-elements-textSecondary'
} block w-full text-left px-4 py-2 text-sm transition-colors duration-150 ease-in-out`}
>
</Link>
</button>
)}
</Menu.Item>
<Menu.Item>
{({ active }) => (
<Link
to="/subscription"
<button
onClick={() => setIsSubscriptionOpen(true)}
className={`${
active ? 'bg-bolt-elements-button-primary-background text-bolt-elements-button-primary-text' : 'text-bolt-elements-textSecondary'
} block px-4 py-2 text-sm transition-colors duration-150 ease-in-out`}
active ? 'bg-bolt-elements-background-depth-2 text-bolt-elements-textPrimary' : 'text-bolt-elements-textSecondary'
} block w-full text-left px-4 py-2 text-sm transition-colors duration-150 ease-in-out`}
>
</Link>
</button>
)}
</Menu.Item>
<Menu.Item>
@ -59,7 +63,7 @@ export function UserMenu() {
<button
onClick={logout}
className={`${
active ? 'bg-bolt-elements-button-primary-background text-bolt-elements-button-primary-text' : 'text-bolt-elements-textSecondary'
active ? 'bg-bolt-elements-background-depth-2 text-bolt-elements-textPrimary' : 'text-bolt-elements-textSecondary'
} block w-full text-left px-4 py-2 text-sm transition-colors duration-150 ease-in-out`}
>
退
@ -70,5 +74,9 @@ export function UserMenu() {
</Menu.Items>
</Transition>
</Menu>
<ProfileDialog isOpen={isProfileOpen} onClose={() => setIsProfileOpen(false)} />
<SubscriptionDialog isOpen={isSubscriptionOpen} onClose={() => setIsSubscriptionOpen(false)} />
</>
);
}

View File

@ -15,13 +15,25 @@ export function useAuth() {
const navigate = useNavigate();
useEffect(() => {
const checkAuth = () => {
const token = localStorage.getItem('token');
const storedUser = localStorage.getItem('user');
if (token && storedUser) {
setIsAuthenticated(true);
setUser(JSON.parse(storedUser));
} else {
setIsAuthenticated(false);
setUser(null);
}
setIsLoading(false);
};
checkAuth();
window.addEventListener('storage', checkAuth);
return () => {
window.removeEventListener('storage', checkAuth);
};
}, []);
const login = (token: string, userData: User) => {
@ -36,6 +48,7 @@ export function useAuth() {
localStorage.removeItem('user');
setIsAuthenticated(false);
setUser(null);
navigate('/');
};
return { isAuthenticated, isLoading, user, login, logout };