-
Notifications
You must be signed in to change notification settings - Fork 448
Expand file tree
/
Copy pathauth-menu.tsx
More file actions
99 lines (87 loc) · 3.26 KB
/
auth-menu.tsx
File metadata and controls
99 lines (87 loc) · 3.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import {Avatar, AvatarFallback, AvatarImage} from '@campfirein/byterover-packages/components/avatar'
import {Button} from '@campfirein/byterover-packages/components/button'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@campfirein/byterover-packages/components/dropdown-menu'
import {useQueryClient} from '@tanstack/react-query'
import {LogOut, User} from 'lucide-react'
import {useEffect, useState} from 'react'
import {initials} from '../../../utils/initials'
import {getAuthStateQueryOptions} from '../api/get-auth-state'
import {useLogout} from '../api/logout'
import {useAuthStore} from '../stores/auth-store'
import {LoginDialog} from './login-dialog'
function UnauthorizedTrigger() {
const [isOpen, setIsOpen] = useState(false)
return (
<>
<Button className="h-auto rounded-full p-0" onClick={() => setIsOpen(true)} variant="ghost">
<Avatar>
<AvatarFallback className="bg-transparent">
<User className="size-4 shrink-0" />
</AvatarFallback>
</Avatar>
</Button>
<LoginDialog onOpenChange={setIsOpen} open={isOpen} />
</>
)
}
function AuthorizedMenu() {
const user = useAuthStore((s) => s.user)
const logoutMutation = useLogout()
const queryClient = useQueryClient()
useEffect(() => {
if (user) return
queryClient.invalidateQueries({queryKey: getAuthStateQueryOptions().queryKey}).catch(() => {})
}, [queryClient, user])
if (!user) {
return (
<Button disabled size="sm" variant="outline">
<User className="size-4 shrink-0 text-muted-foreground" />
<span>Signed in</span>
</Button>
)
}
const displayName = user.name ?? user.email
return (
<DropdownMenu>
<DropdownMenuTrigger>
<Avatar className="cursor-pointer">
<AvatarImage alt={displayName} src={user.avatarUrl} />
<AvatarFallback>{initials(displayName)}</AvatarFallback>
</Avatar>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-(--anchor-width) min-w-56" sideOffset={6}>
<DropdownMenuGroup>
<DropdownMenuLabel className="flex flex-col gap-0.5">
<span className="text-xs tracking-widest text-muted-foreground">Signed in</span>
<span className="truncate text-sm font-medium text-card-foreground!">{user.email}</span>
</DropdownMenuLabel>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem disabled={logoutMutation.isPending} onClick={() => logoutMutation.mutate()}>
<LogOut className="size-4 text-muted-foreground!" />
<span className="text-sm">{logoutMutation.isPending ? 'Logging out…' : 'Log out'}</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
}
export function AuthMenu() {
const isAuthorized = useAuthStore((s) => s.isAuthorized)
const isLoadingInitial = useAuthStore((s) => s.isLoadingInitial)
if (isLoadingInitial) {
return (
<Button disabled size="icon" variant="outline">
<User className="size-4 shrink-0 text-muted-foreground animate-pulse" />
</Button>
)
}
return isAuthorized ? <AuthorizedMenu /> : <UnauthorizedTrigger />
}