Files
Homarr/src/components/layout/header/AvatarMenu.tsx

152 lines
4.9 KiB
TypeScript
Raw Normal View History

2023-10-25 14:13:58 +02:00
import { Avatar, Badge, Indicator, Menu, UnstyledButton, useMantineTheme } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import {
IconDashboard,
2023-08-05 12:35:06 +02:00
IconHomeShare,
IconInfoCircle,
IconLogin,
IconLogout,
IconMoonStars,
IconSun,
IconUserCog,
} from '@tabler/icons-react';
2023-07-30 01:09:10 +02:00
import { useQuery } from '@tanstack/react-query';
import { User } from 'next-auth';
import { signOut, useSession } from 'next-auth/react';
2023-08-05 16:02:26 +02:00
import { useTranslation } from 'next-i18next';
import Link from 'next/link';
import { forwardRef } from 'react';
2023-08-05 12:26:42 +02:00
import { AboutModal } from '~/components/layout/header/About/AboutModal';
import { useColorScheme } from '~/hooks/use-colorscheme';
2023-07-30 01:09:10 +02:00
import { usePackageAttributesStore } from '~/tools/client/zustands/usePackageAttributesStore';
import { REPO_URL } from '../../../../data/constants';
2023-08-10 18:17:49 +02:00
import { useBoardLink } from '../Templates/BoardLayout';
export const AvatarMenu = () => {
2023-08-05 16:02:26 +02:00
const { t } = useTranslation('layout/header');
const [aboutModalOpened, aboutModal] = useDisclosure(false);
const { data: sessionData } = useSession();
const { colorScheme, toggleColorScheme } = useColorScheme();
2023-07-30 01:09:10 +02:00
const newVersionAvailable = useNewVersionAvailable();
const Icon = colorScheme === 'dark' ? IconSun : IconMoonStars;
2023-08-10 18:17:49 +02:00
const defaultBoardHref = useBoardLink('/board');
return (
<>
<UnstyledButton>
2023-08-01 12:06:39 +02:00
<Menu width={256}>
<Menu.Target>
2023-10-25 14:13:58 +02:00
<CurrentUserAvatar
newVersionAvailable={newVersionAvailable}
user={sessionData?.user ?? null}
/>
</Menu.Target>
<Menu.Dropdown>
2023-10-25 14:13:58 +02:00
<Menu.Item
closeMenuOnClick={false}
icon={<Icon size="1rem" />}
onClick={toggleColorScheme}
>
2023-08-05 16:02:26 +02:00
{t('actions.avatar.switchTheme')}
</Menu.Item>
2023-07-29 21:18:41 +02:00
{sessionData?.user && (
<>
<Menu.Item
component={Link}
passHref
2023-07-30 22:57:14 +02:00
href="/user/preferences"
icon={<IconUserCog size="1rem" />}
>
2023-08-05 16:02:26 +02:00
{t('actions.avatar.preferences')}
</Menu.Item>
2023-08-10 18:17:49 +02:00
<Menu.Item
component={Link}
href={defaultBoardHref}
icon={<IconDashboard size="1rem" />}
>
2023-08-05 16:02:26 +02:00
{t('actions.avatar.defaultBoard')}
</Menu.Item>
2023-08-05 12:35:06 +02:00
<Menu.Item component={Link} href="/manage" icon={<IconHomeShare size="1rem" />}>
2023-08-06 14:12:39 +02:00
{t('actions.avatar.manage')}
2023-08-05 12:35:06 +02:00
</Menu.Item>
2023-07-29 21:18:41 +02:00
<Menu.Divider />
</>
)}
<Menu.Item
icon={<IconInfoCircle size="1rem" />}
rightSection={
newVersionAvailable && (
<Badge variant="light" color="blue">
2023-08-05 16:02:26 +02:00
{t('actions.avatar.about.new')}
</Badge>
)
}
onClick={() => aboutModal.open()}
>
2023-08-05 16:02:26 +02:00
{t('actions.avatar.about.label')}
</Menu.Item>
{sessionData?.user ? (
2023-08-05 11:42:49 +02:00
<Menu.Item
icon={<IconLogout size="1rem" />}
color="red"
onClick={() =>
signOut({
2023-08-05 11:49:37 +02:00
redirect: false,
}).then(() => window.location.reload())
2023-08-05 11:42:49 +02:00
}
>
2023-08-05 16:02:26 +02:00
{t('actions.avatar.logout', {
username: sessionData.user.name,
})}
</Menu.Item>
) : (
<Menu.Item icon={<IconLogin size="1rem" />} component={Link} href="/auth/login">
2023-08-05 16:02:26 +02:00
{t('actions.avatar.login')}
</Menu.Item>
)}
</Menu.Dropdown>
</Menu>
</UnstyledButton>
<AboutModal
opened={aboutModalOpened}
closeModal={aboutModal.close}
newVersionAvailable={newVersionAvailable}
/>
</>
);
};
type CurrentUserAvatarProps = {
2023-10-25 14:13:58 +02:00
newVersionAvailable: boolean;
user: User | null;
};
2023-07-30 01:09:10 +02:00
const CurrentUserAvatar = forwardRef<HTMLDivElement, CurrentUserAvatarProps>(
2023-10-25 14:13:58 +02:00
({ user, newVersionAvailable, ...others }, ref) => {
const { primaryColor } = useMantineTheme();
if (!user) return <Avatar ref={ref} {...others} />;
return (
2023-10-25 14:13:58 +02:00
<Indicator color="blue" processing size={10} hidden={!newVersionAvailable}>
<Avatar ref={ref} color={primaryColor} {...others}>
{user.name?.slice(0, 2).toUpperCase()}
</Avatar>
</Indicator>
);
}
);
2023-07-30 01:09:10 +02:00
const useNewVersionAvailable = () => {
const { attributes } = usePackageAttributesStore();
const { data } = useQuery({
queryKey: ['github/latest'],
cacheTime: 1000 * 60 * 60 * 24,
staleTime: 1000 * 60 * 60 * 5,
queryFn: () =>
fetch(`https://api.github.com/repos/${REPO_URL}/releases/latest`).then((res) => res.json()),
});
return data?.tag_name > `v${attributes.packageVersion}` ? data?.tag_name : undefined;
};