1
0
Fork 0
mirror of https://github.com/muerwre/vault-frontend.git synced 2025-04-25 21:06:42 +07:00

message notifications working

This commit is contained in:
Fedor Katurov 2019-11-13 12:53:29 +07:00
parent 83c9900af1
commit 7583a57b04
11 changed files with 137 additions and 32 deletions

View file

@ -7,6 +7,7 @@ import pick from 'ramda/es/pick';
import classNames from 'classnames';
import * as AUTH_ACTIONS from '~/redux/auth/actions';
import { NotificationBubble } from '../../notifications/NotificationBubble';
import { INotification, IMessageNotification } from '~/redux/types';
const mapStateToProps = state => ({
user: pick(['last_seen_messages'], selectAuthUser(state)),
@ -15,6 +16,7 @@ const mapStateToProps = state => ({
const mapDispatchToProps = {
authSetLastSeenMessages: AUTH_ACTIONS.authSetLastSeenMessages,
authOpenProfile: AUTH_ACTIONS.authOpenProfile,
};
type IProps = ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps & {};
@ -23,8 +25,9 @@ const NotificationsUnconnected: FC<IProps> = ({
updates: { last, notifications },
user: { last_seen_messages },
authSetLastSeenMessages,
authOpenProfile,
}) => {
const [visible, setVisible] = useState(true);
const [visible, setVisible] = useState(false);
const has_new = useMemo(
() =>
notifications.length &&
@ -35,21 +38,42 @@ const NotificationsUnconnected: FC<IProps> = ({
[last, last_seen_messages, notifications]
);
const onNotificationClick = useCallback(
(notification: INotification) => {
switch (notification.type) {
case 'message':
return authOpenProfile(
(notification as IMessageNotification).content.from.username,
'messages'
);
default:
return;
}
},
[authOpenProfile]
);
const showList = useCallback(() => setVisible(true), [setVisible]);
const hideList = useCallback(() => setVisible(false), [setVisible]);
useEffect(() => {
if (!visible || !has_new) return;
authSetLastSeenMessages(new Date().toISOString());
}, [visible]);
const showList = useCallback(() => setVisible(true), [setVisible]);
const hideList = useCallback(() => setVisible(false), [setVisible]);
return (
<div className={classNames(styles.wrap, { [styles.is_new]: has_new })}>
<div
className={classNames(styles.wrap, {
[styles.is_new]: has_new,
[styles.active]: notifications.length > 0,
})}
>
<div className={styles.icon} onFocus={showList} onBlur={hideList} tabIndex={-1}>
{has_new ? <Icon icon="bell_ring" size={24} /> : <Icon icon="bell" size={24} />}
</div>
{visible && <NotificationBubble notifications={notifications} />}
{visible && (
<NotificationBubble notifications={notifications} onClick={onNotificationClick} />
)}
</div>
);
};

View file

@ -21,9 +21,16 @@
position: relative;
outline: none;
&.active {
.icon {
opacity: 1;
}
}
&.is_new {
.icon {
animation: ring 1s infinite alternate;
opacity: 1;
svg {
fill: $red;
@ -35,4 +42,5 @@
.icon {
outline: none;
cursor: pointer;
opacity: 0.5;
}

View file

@ -2,28 +2,37 @@ import React, { FC, createElement } from 'react';
import { INotification, NOTIFICATION_TYPES } from '~/redux/types';
import styles from './styles.scss';
import { NotificationMessage } from '../NotificationMessage';
import { Icon } from '~/components/input/Icon';
interface IProps {
notifications: INotification[];
onClick: (notification: INotification) => void;
}
const NOTIFICATION_RENDERERS = {
[NOTIFICATION_TYPES.message]: NotificationMessage,
};
const NotificationBubble: FC<IProps> = ({ notifications }) => {
const NotificationBubble: FC<IProps> = ({ notifications, onClick }) => {
return (
<div className={styles.wrap}>
<div className={styles.list}>
{notifications
.filter(notification => notification.type && NOTIFICATION_RENDERERS[notification.type])
.map(notification =>
createElement(NOTIFICATION_RENDERERS[notification.type], {
notification,
onClick: console.log,
key: notification.content.id,
})
)}
{notifications.length === 0 && (
<div className={styles.placeholder}>
<Icon icon="bell_ring" />
<div>НЕТ УВЕДОМЛЕНИЙ</div>
</div>
)}
{notifications.length > 0 &&
notifications
.filter(notification => notification.type && NOTIFICATION_RENDERERS[notification.type])
.map(notification =>
createElement(NOTIFICATION_RENDERERS[notification.type], {
notification,
onClick,
key: notification.content.id,
})
)}
</div>
</div>
);

View file

@ -1,3 +1,5 @@
$notification_color: darken($content_bg, 2%);
@keyframes appear {
0% {
opacity: 0;
@ -6,15 +8,16 @@
opacity: 1;
}
}
.wrap {
position: absolute;
position: absolute;
background: $content_bg;
top: 50px;
background: $notification_color;
top: 42px;
left: 50%;
transform: translate(-50%, 0);
border-radius: $radius;
animation: appear 0.5s forwards;
animation: appear 0.25s forwards;
z-index: 2;
&::before {
content: ' ';
@ -22,7 +25,7 @@
height: 0;
border-style: solid;
border-width: 0 0 16px 16px;
border-color: transparent transparent $content_bg transparent;
border-color: transparent transparent $notification_color transparent;
position: absolute;
left: 50%;
top: -16px;
@ -45,6 +48,7 @@
flex-direction: column;
padding: $gap;
min-width: 0;
cursor: pointer;
svg {
fill: white;
@ -74,3 +78,23 @@
padding-left: 30px;
overflow: hidden;
}
.placeholder {
height: 200px;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
text-transform: uppercase;
font: $font_16_semibold;
svg {
width: 120px;
height: 120px;
opacity: 0.05;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}