diff --git a/src/components/common/Avatar/index.tsx b/src/components/common/Avatar/index.tsx index 3f73ecea..442a7dff 100644 --- a/src/components/common/Avatar/index.tsx +++ b/src/components/common/Avatar/index.tsx @@ -14,22 +14,37 @@ interface Props extends DivProps { url?: string; username?: string; size?: number; + hasUpdates?: boolean; preset?: typeof imagePresets[keyof typeof imagePresets]; } const Avatar = forwardRef( ( - { url, username, size, className, preset = imagePresets.avatar, ...rest }, + { + url, + username, + size, + className, + preset = imagePresets.avatar, + hasUpdates, + ...rest + }, ref, ) => { return ( - + className={classNames(styles.container, { + [styles.has_dot]: hasUpdates, + })} + > + + ); }, ); diff --git a/src/components/common/Avatar/styles.module.scss b/src/components/common/Avatar/styles.module.scss index 4225c783..389a9772 100644 --- a/src/components/common/Avatar/styles.module.scss +++ b/src/components/common/Avatar/styles.module.scss @@ -1,5 +1,20 @@ @import 'src/styles/variables'; +.container { + &.has_dot::after { + content: ' '; + position: absolute; + bottom: 0; + right: 0; + width: 8px; + height: 8px; + border-radius: 8px; + background-color: $color_danger; + z-index: 1; + box-shadow: $content_bg 0 0 0 2px; + } +} + .avatar { @include outer_shadow; @@ -12,6 +27,7 @@ background-position: center; background-size: cover; cursor: pointer; + position: relative; img { object-fit: cover; diff --git a/src/components/main/UserButton/index.tsx b/src/components/main/UserButton/index.tsx index b7e6c3c4..85c7bf2d 100644 --- a/src/components/main/UserButton/index.tsx +++ b/src/components/main/UserButton/index.tsx @@ -2,7 +2,6 @@ import { FC } from 'react'; import { Avatar } from '~/components/common/Avatar'; import { Group } from '~/components/containers/Group'; -import { Icon } from '~/components/input/Icon'; import { imagePresets } from '~/constants/urls'; import { IFile } from '~/types'; import { getURL } from '~/utils/dom'; @@ -12,15 +11,21 @@ import styles from './styles.module.scss'; interface IProps { username: string; photo?: IFile; + hasUpdates?: boolean; + onClick?: () => void; } -const UserButton: FC = ({ username, photo, onClick }) => { +const UserButton: FC = ({ username, photo, hasUpdates, onClick }) => { return ( ); diff --git a/src/components/menu/VerticalMenu/index.tsx b/src/components/menu/VerticalMenu/index.tsx index 0b84065e..23c20e41 100644 --- a/src/components/menu/VerticalMenu/index.tsx +++ b/src/components/menu/VerticalMenu/index.tsx @@ -1,8 +1,8 @@ -import React, { PropsWithChildren } from 'react'; +import { PropsWithChildren } from 'react'; import classNames from 'classnames'; -import { Card } from '~/components/containers/Card'; +import { Anchor } from '~/components/common/Anchor'; import { DivProps, LinkProps } from '~/utils/types'; import styles from './styles.module.scss'; @@ -11,7 +11,9 @@ interface VerticalMenuProps extends DivProps { appearance?: 'inset' | 'flat' | 'default'; } -interface VerticalMenuItemProps extends Omit {} +interface VerticalMenuItemProps extends Omit { + hasUpdates?: boolean; +} function VerticalMenu({ children, @@ -28,8 +30,13 @@ function VerticalMenu({ ); } -VerticalMenu.Item = ({ ...props }: VerticalMenuItemProps) => ( - +VerticalMenu.Item = ({ hasUpdates, ...props }: VerticalMenuItemProps) => ( + ); export { VerticalMenu }; diff --git a/src/components/menu/VerticalMenu/styles.module.scss b/src/components/menu/VerticalMenu/styles.module.scss index 1c3e5d1d..81b66ffa 100644 --- a/src/components/menu/VerticalMenu/styles.module.scss +++ b/src/components/menu/VerticalMenu/styles.module.scss @@ -33,6 +33,19 @@ a.item { cursor: pointer; background-color: transparent; transition: background-color 0.25s; + position: relative; + + &.has_dot::after { + content: ' '; + position: absolute; + top: 50%; + right: 10px; + width: 8px; + height: 8px; + background: $color_danger; + border-radius: 8px; + transform: translate(0, -50%); + } &:hover { background-color: $content_bg_success; diff --git a/src/containers/main/Header/index.tsx b/src/containers/main/Header/index.tsx index 4d676f53..10b43801 100644 --- a/src/containers/main/Header/index.tsx +++ b/src/containers/main/Header/index.tsx @@ -9,16 +9,16 @@ import { Authorized } from '~/components/containers/Authorized'; import { Filler } from '~/components/containers/Filler'; import { Button } from '~/components/input/Button'; import { Logo } from '~/components/main/Logo'; -import { UserButton } from '~/components/main/UserButton'; import { Dialog } from '~/constants/modal'; -import { SidebarName } from '~/constants/sidebar'; import { URLS } from '~/constants/urls'; import { useAuth } from '~/hooks/auth/useAuth'; import { useScrollTop } from '~/hooks/dom/useScrollTop'; import { useFlow } from '~/hooks/flow/useFlow'; import { useModal } from '~/hooks/modal/useModal'; import { useUpdates } from '~/hooks/updates/useUpdates'; -import { useSidebar } from '~/utils/providers/SidebarProvider'; +import { useNotifications } from '~/utils/providers/NotificationProvider'; + +import { UserButtonWithNotifications } from '../UserButtonWithNotifications'; import styles from './styles.module.scss'; @@ -30,11 +30,7 @@ const Header: FC = observer(() => { const { isUser, user, fetched } = useAuth(); const { hasFlowUpdates, hasLabUpdates } = useFlow(); const { borisCommentedAt } = useUpdates(); - const { open } = useSidebar(); - - const openProfileSidebar = useCallback(() => { - open(SidebarName.Settings, {}); - }, [open]); + const { indicatorEnabled } = useNotifications(); const onLogin = useCallback(() => showModal(Dialog.Login, {}), [showModal]); @@ -43,6 +39,7 @@ const Header: FC = observer(() => { const hasBorisUpdates = useMemo( () => isUser && + !indicatorEnabled && borisCommentedAt && ((fetched && !user.last_seen_boris) || isBefore(new Date(user.last_seen_boris), new Date(borisCommentedAt))), @@ -67,14 +64,13 @@ const Header: FC = observer(() => { - {isUser && ( - - )} + {isUser && } {!isUser && (