summaryrefslogtreecommitdiff
path: root/src/components/auth/user-button.tsx
blob: 7caea4c1df2ac3e603eafa57debc3c264a6faad3 (plain)
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
'use client';

import Link from 'next/link';

import { Icons } from '@/components/icons/icons';
import { Button } from '@/components/ui/button';
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { Skeleton } from '@/components/ui/skeleton';
import { signOut, useSession } from '@/lib/auth-client';
import { cn } from '@/lib/utils';

import type { UserAvatarClassNames } from './user-avatar';
import { UserAvatar } from './user-avatar';

export interface UserButtonClassNames {
  base?: string;
  skeleton?: string;
  trigger?: {
    base?: string;
    avatar?: UserAvatarClassNames;
    skeleton?: string;
  };
  content?: {
    base?: string;
    avatar?: UserAvatarClassNames;
    menuItem?: string;
    separator?: string;
  };
}

export interface UserButtonProps {
  className?: string;
  classNames?: UserButtonClassNames;
}

export function UserButton({ className, classNames }: UserButtonProps) {
  const { data: sessionData, isPending } = useSession();
  const user = sessionData?.user ?? null;

  return (
    <DropdownMenu>
      <DropdownMenuTrigger
        className={cn('rounded-md bg-transparent', classNames?.trigger?.base)}
        asChild
      >
        <Button
          variant='ghost'
          className='size-auto rounded-md border-none bg-transparent p-1.5 hover:bg-accent dark:hover:bg-accent'
          disabled={isPending}
        >
          {isPending ? (
            <Skeleton
              className={cn(
                'size-8 rounded-md',
                className,
                classNames?.base,
                classNames?.skeleton,
                classNames?.trigger?.skeleton,
              )}
            />
          ) : (
            <UserAvatar
              className={cn('size-5', className, classNames?.base)}
              classNames={classNames?.trigger?.avatar}
              user={user}
            />
          )}
        </Button>
      </DropdownMenuTrigger>

      <DropdownMenuContent
        className={cn('max-w-64', classNames?.content?.base)}
        onCloseAutoFocus={(e) => e.preventDefault()}
        align='end'
      >
        {user ? (
          <div className='flex items-center gap-2 p-2'>
            <UserAvatar classNames={classNames?.content?.avatar} user={user} />

            <div className='flex flex-col truncate'>
              <div className='truncate font-medium text-sm'>
                {user.name || user.email}
              </div>

              {user.name && (
                <div className='truncate text-muted-foreground text-xs'>
                  {user.email}
                </div>
              )}
            </div>
          </div>
        ) : (
          <div className='px-2 py-1 text-muted-foreground text-xs'>Account</div>
        )}

        <DropdownMenuSeparator className={classNames?.content?.separator} />

        {!user ? (
          <>
            <DropdownMenuItem className={classNames?.content?.menuItem} asChild>
              <Link href={'/login'}>
                <Icons.logIn className='size-4' />
                Sign In
              </Link>
            </DropdownMenuItem>
          </>
        ) : (
          <>
            <DropdownMenuItem
              className={classNames?.content?.menuItem}
              onClick={() => signOut()}
            >
              <Icons.logOut className='size-4' />
              Log Out
            </DropdownMenuItem>
          </>
        )}
      </DropdownMenuContent>
    </DropdownMenu>
  );
}