1
0
Fork 0
mirror of https://github.com/muerwre/vault-frontend.git synced 2025-04-24 20:36:40 +07:00

notifications: added tabs to notification screen

This commit is contained in:
Fedor Katurov 2023-03-16 11:35:31 +06:00
parent 7135d06673
commit f9e0ecdd0c
10 changed files with 148 additions and 21 deletions

View file

@ -0,0 +1,25 @@
import React, { FC } from 'react';
import classNames from 'classnames';
import { LoaderCircle } from '../LoaderCircle';
import styles from './styles.module.scss';
interface LoaderScreenProps {
className?: string;
align?: 'top' | 'middle';
}
const LoaderScreen: FC<LoaderScreenProps> = ({
className,
align = 'middle',
}) => (
<div
className={classNames(styles.screen, styles[`align-${align}`], className)}
>
<LoaderCircle size={32} />
</div>
);
export { LoaderScreen };

View file

@ -0,0 +1,14 @@
@import 'src/styles/variables';
.screen {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
padding: $gap;
&.align-top {
align-items: flex-start;
}
}

View file

@ -14,6 +14,7 @@ interface HorizontalMenuItemProps {
icon?: string; icon?: string;
color?: 'green' | 'orange' | 'yellow'; color?: 'green' | 'orange' | 'yellow';
active?: boolean; active?: boolean;
stretchy?: boolean;
onClick?: () => void; onClick?: () => void;
} }
@ -31,6 +32,7 @@ HorizontalMenu.Item = ({
children, children,
isLoading, isLoading,
active, active,
stretchy,
onClick, onClick,
}: PropsWithChildren<HorizontalMenuItemProps>) => { }: PropsWithChildren<HorizontalMenuItemProps>) => {
if (isLoading) { if (isLoading) {
@ -44,7 +46,11 @@ HorizontalMenu.Item = ({
return ( return (
<div <div
className={classNames(styles.item, { [styles.active]: active }, styles[color])} className={classNames(
styles.item,
{ [styles.active]: active, [styles.stretchy]: stretchy },
styles[color],
)}
onClick={onClick} onClick={onClick}
> >
{!!icon && <Icon icon={icon} size={24} />} {!!icon && <Icon icon={icon} size={24} />}

View file

@ -57,6 +57,11 @@
background: $warning_gradient; background: $warning_gradient;
} }
} }
&.stretchy {
flex: 1;
justify-content: center;
}
} }
.text { .text {

View file

@ -1,5 +1,6 @@
import React, { FC, useCallback } from 'react'; import React, { FC, useCallback } from 'react';
import { Card } from '~/components/containers/Card';
import { Group } from '~/components/containers/Group'; import { Group } from '~/components/containers/Group';
import { Zone } from '~/components/containers/Zone'; import { Zone } from '~/components/containers/Zone';
import { Button } from '~/components/input/Button'; import { Button } from '~/components/input/Button';
@ -49,11 +50,16 @@ const NotificationSettingsForm: FC<NotificationSettingsFormProps> = ({
return ( return (
<Group> <Group>
<Zone title="Уведомления"> <Card>
<InputRow className={styles.row} input={toggle('enabled')}>
Получать уведомления
</InputRow>
</Card>
<div />
<Zone title="Типы уведомлений">
<Group> <Group>
<InputRow className={styles.row} input={toggle('enabled')}>
Включены
</InputRow>
<InputRow <InputRow
className={styles.row} className={styles.row}
input={toggle('flow', !values.enabled)} input={toggle('flow', !values.enabled)}
@ -70,7 +76,9 @@ const NotificationSettingsForm: FC<NotificationSettingsFormProps> = ({
</Group> </Group>
</Zone> </Zone>
<Zone title="Уведомления"> <div />
<Zone title="Способы доставки">
<Group> <Group>
<InputRow <InputRow
className={styles.row} className={styles.row}

View file

@ -1,25 +1,62 @@
import { VFC } from 'react'; import { useState, VFC } from 'react';
import { Padder } from '~/components/containers/Padder'; import { Group } from '~/components/containers/Group';
import { Button } from '~/components/input/Button';
import { Icon } from '~/components/input/Icon';
import { HorizontalMenu } from '~/components/menu/HorizontalMenu';
import { useStackContext } from '~/components/sidebar/SidebarStack'; import { useStackContext } from '~/components/sidebar/SidebarStack';
import { SidebarStackCard } from '~/components/sidebar/SidebarStackCard'; import { SidebarStackCard } from '~/components/sidebar/SidebarStackCard';
import { NotificationList } from '~/containers/notifications/NotificationList';
import { NotificationSettings } from '~/containers/notifications/NotificationSettings'; import { NotificationSettings } from '~/containers/notifications/NotificationSettings';
import { useNotificationSettings } from '~/hooks/notifications/useNotificationSettings';
import styles from './styles.module.scss';
interface ProfileSidebarNotificationsProps {} interface ProfileSidebarNotificationsProps {}
enum Tabs {
List,
Settings,
}
const ProfileSidebarNotifications: VFC< const ProfileSidebarNotifications: VFC<
ProfileSidebarNotificationsProps ProfileSidebarNotificationsProps
> = () => { > = () => {
const { closeAllTabs } = useStackContext(); const { closeAllTabs } = useStackContext();
const [tab, setTab] = useState(Tabs.List);
const { loading } = useNotificationSettings();
return ( return (
<SidebarStackCard <SidebarStackCard width={400} onBackPress={closeAllTabs}>
width={400} <div className={styles.grid}>
headerFeature="back" <Group className={styles.head} horizontal>
title="Уведомления" <HorizontalMenu className={styles.tabs}>
onBackPress={closeAllTabs} <HorizontalMenu.Item
> active={tab === Tabs.List}
<NotificationSettings /> isLoading={loading}
onClick={() => setTab(Tabs.List)}
stretchy
>
Уведомления
</HorizontalMenu.Item>
<HorizontalMenu.Item
active={tab === Tabs.Settings}
isLoading={loading}
onClick={() => setTab(Tabs.Settings)}
stretchy
>
Настройки
</HorizontalMenu.Item>
</HorizontalMenu>
<Button iconLeft="right" color="link" onClick={closeAllTabs} />
</Group>
<div className={styles.list}>
{tab === Tabs.List ? <NotificationList /> : <NotificationSettings />}
</div>
</div>
</SidebarStackCard> </SidebarStackCard>
); );
}; };

View file

@ -0,0 +1,31 @@
@import 'src/styles/variables';
.grid {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
z-index: 4;
height: 100%;
}
.head {
@include row_shadow;
width: 100%;
padding: $gap;
flex: 0;
}
.tabs {
flex: 1;
}
.list {
@include row_shadow;
overflow-y: auto;
flex: 1 1;
overflow: auto;
width: 100%;
}

View file

@ -1,6 +1,6 @@
import React, { FC, useEffect } from 'react'; import { FC, useEffect } from 'react';
import { LoaderCircle } from '~/components/input/LoaderCircle'; import { LoaderScreen } from '~/components/input/LoaderScreen';
import { NotificationComment } from '~/components/notifications/NotificationComment'; import { NotificationComment } from '~/components/notifications/NotificationComment';
import { useNotificationsList } from '~/hooks/notifications/useNotificationsList'; import { useNotificationsList } from '~/hooks/notifications/useNotificationsList';
import { useNotifications } from '~/utils/providers/NotificationProvider'; import { useNotifications } from '~/utils/providers/NotificationProvider';
@ -18,7 +18,7 @@ const NotificationList: FC<NotificationListProps> = () => {
}, []); }, []);
if (isLoading) { if (isLoading) {
return <LoaderCircle />; return <LoaderScreen align="top" />;
} }
return ( return (

View file

@ -14,13 +14,13 @@ export const useNotificationSettings = () => {
enabled: settingsEnabled, enabled: settingsEnabled,
lastSeen, lastSeen,
lastDate, lastDate,
isLoading: isLoadingSettings, isLoading,
update, update,
refresh, refresh,
settings, settings,
} = useNotificationSettingsRequest(); } = useNotificationSettingsRequest();
const enabled = !isLoadingSettings && !settingsError && settingsEnabled; const enabled = !isLoading && !settingsError && settingsEnabled;
const hasNew = const hasNew =
enabled && !!lastDate && (!lastSeen || isAfter(lastDate, lastSeen)); enabled && !!lastDate && (!lastSeen || isAfter(lastDate, lastSeen));
@ -47,5 +47,6 @@ export const useNotificationSettings = () => {
markAsRead, markAsRead,
refresh, refresh,
update, update,
loading: isLoading,
}; };
}; };

View file

@ -66,7 +66,7 @@ export const useNotificationSettingsRequest = () => {
const refresh = useCallback(() => mutate(), [mutate]); const refresh = useCallback(() => mutate(), [mutate]);
return { return {
isLoading, isLoading: isLoading && !data,
error, error,
lastSeen: lastSeen:
data?.lastSeen && isValid(parseISO(data.lastSeen)) data?.lastSeen && isValid(parseISO(data.lastSeen))