This repository has no description
5.3 kB
189 lines
1'use client';
2
3import {
4 Skeleton,
5 Avatar,
6 Group,
7 Alert,
8 Menu,
9 Card,
10 Text,
11 UnstyledButton,
12 Stack,
13} from '@mantine/core';
14import { useMantineColorScheme } from '@mantine/core';
15import useMyProfile from '../../lib/queries/useMyProfile';
16import {
17 MdOutlineBugReport,
18 MdOutlineCollectionsBookmark,
19 MdOutlineSmartphone,
20 MdOutlineDarkMode,
21 MdOutlineLightMode,
22} from 'react-icons/md';
23import { TbStackForward } from 'react-icons/tb';
24import { useAuth } from '@/hooks/useAuth';
25import { useRouter } from 'next/navigation';
26import Link from 'next/link';
27import { IoMdLogOut } from 'react-icons/io';
28import { useNavbarContext } from '@/providers/navbar';
29import { useOs } from '@mantine/hooks';
30import { BsThreeDots } from 'react-icons/bs';
31import styles from './ProfileMenu.module.css';
32import { sanitizeText } from '@/lib/utils/text';
33import { isBotAccount } from '@/features/platforms/bluesky/lib/utils/account';
34import BotLabel from '../botLabel/BotLabel';
35
36const schemes = ['light', 'dark', 'auto'] as const;
37type ColorScheme = (typeof schemes)[number];
38
39const schemeConfig: Record<
40 ColorScheme,
41 { icon: React.ReactNode; label: string; next: ColorScheme }
42> = {
43 light: {
44 icon: <MdOutlineLightMode size={22} />,
45 label: 'Light',
46 next: 'dark',
47 },
48 dark: { icon: <MdOutlineDarkMode size={22} />, label: 'Dark', next: 'auto' },
49 auto: {
50 icon: <MdOutlineSmartphone size={22} />,
51 label: 'Auto',
52 next: 'light',
53 },
54};
55
56export default function ProfileMenu() {
57 const router = useRouter();
58 const os = useOs();
59 const { toggleMobile } = useNavbarContext();
60 const { data, error, isPending } = useMyProfile();
61 const { logout } = useAuth();
62 const { colorScheme, setColorScheme } = useMantineColorScheme();
63
64 const current = schemeConfig[colorScheme as ColorScheme] ?? schemeConfig.auto;
65
66 const handleLogout = async () => {
67 try {
68 await logout();
69 router.push('/');
70 } catch (error) {
71 console.error('Error logging out:', error);
72 }
73 };
74
75 if (isPending || !data) {
76 return <Skeleton w={38} h={38} radius="md" ml={4} />;
77 }
78
79 if (error) {
80 return <Alert color="red" title="Could not load profile" />;
81 }
82
83 return (
84 <Group>
85 <Menu shadow="sm" width={280}>
86 <Menu.Target>
87 <Card px={5} py={5} flex={1} className={styles.root}>
88 <UnstyledButton>
89 <Group justify="space-between" wrap="nowrap">
90 <Group gap={'xs'} c={'bright'} wrap="nowrap">
91 <Avatar
92 src={data.avatarUrl?.replace('avatar', 'avatar_thumbnail')}
93 />
94 <Text fw={600} lineClamp={1}>
95 {data.name}
96 </Text>
97
98 {isBotAccount(data) && <BotLabel />}
99 </Group>
100 <BsThreeDots />
101 </Group>
102 </UnstyledButton>
103 </Card>
104 </Menu.Target>
105
106 <Menu.Dropdown>
107 <Menu.Item
108 component={Link}
109 href={`/profile/${data.handle}`}
110 onClick={toggleMobile}
111 >
112 <Group gap={'xs'} wrap="nowrap">
113 <Avatar
114 src={data.avatarUrl?.replace('avatar', 'avatar_thumbnail')}
115 alt={`${data.handle}'s avatar`}
116 />
117
118 <Stack gap={0}>
119 <Group gap={'xs'} wrap="nowrap">
120 <Text fw={600} c={'bright'} lineClamp={1}>
121 {sanitizeText(data.name) || data.handle}
122 </Text>
123 {isBotAccount(data) && <BotLabel />}
124 </Group>
125
126 <Text fw={600} c={'gray'} lineClamp={1}>
127 @{data.handle}
128 </Text>
129 </Stack>
130 </Group>
131 </Menu.Item>
132
133 <Menu.Divider />
134
135 <Menu.Item
136 color="gray"
137 closeMenuOnClick={false}
138 leftSection={current.icon}
139 onClick={() => setColorScheme(current.next)}
140 >
141 Theme: {current.label}
142 </Menu.Item>
143
144 <Menu.Item
145 component="a"
146 href="https://tangled.org/@cosmik.network/semble/issues"
147 target="_blank"
148 leftSection={<MdOutlineBugReport size={22} />}
149 color="gray"
150 >
151 Submit an issue
152 </Menu.Item>
153
154 <Menu.Item
155 color="gray"
156 leftSection={<MdOutlineCollectionsBookmark size={22} />}
157 component={Link}
158 href={'/bookmarklet'}
159 target="_blank"
160 >
161 Install bookmarklet
162 </Menu.Item>
163
164 {os === 'ios' && (
165 <Menu.Item
166 color="gray"
167 leftSection={<TbStackForward size={22} />}
168 component={Link}
169 href={
170 'https://www.icloud.com/shortcuts/9c4b4b4bc4ef4d6d93513c59373b0af6'
171 }
172 target="_blank"
173 >
174 Install iOS shortcut
175 </Menu.Item>
176 )}
177
178 <Menu.Item
179 color="red"
180 leftSection={<IoMdLogOut size={22} />}
181 onClick={handleLogout}
182 >
183 Log out
184 </Menu.Item>
185 </Menu.Dropdown>
186 </Menu>
187 </Group>
188 );
189}