2020-07-23 16:22:57 +02:00
|
|
|
// @flow
|
2021-12-21 13:42:28 +01:00
|
|
|
import 'scss/component/_header.scss';
|
|
|
|
|
2021-12-20 13:32:32 +01:00
|
|
|
import { ENABLE_UI_NOTIFICATIONS } from 'config';
|
|
|
|
import { useHistory } from 'react-router';
|
2020-07-23 16:22:57 +02:00
|
|
|
import * as ICONS from 'constants/icons';
|
2021-12-20 13:32:32 +01:00
|
|
|
import * as PAGES from 'constants/pages';
|
2020-07-23 16:22:57 +02:00
|
|
|
import Icon from 'component/common/icon';
|
2020-08-11 22:32:03 +02:00
|
|
|
import NotificationBubble from 'component/notificationBubble';
|
2021-12-20 13:32:32 +01:00
|
|
|
import React from 'react';
|
2021-12-20 13:38:12 +01:00
|
|
|
import Tooltip from 'component/common/tooltip';
|
2022-06-09 16:29:56 +02:00
|
|
|
import { formatLbryUrlForWeb } from 'util/url';
|
|
|
|
import Notification from 'component/notification';
|
|
|
|
import DateTime from 'component/dateTime';
|
|
|
|
import ChannelThumbnail from 'component/channelThumbnail';
|
|
|
|
import { Menu as MuiMenu } from '@mui/material';
|
|
|
|
import Button from 'component/button';
|
|
|
|
import ClickAwayListener from '@mui/material/ClickAwayListener';
|
|
|
|
import { RULE } from 'constants/notifications';
|
|
|
|
import UriIndicator from 'component/uriIndicator';
|
|
|
|
import { generateNotificationTitle } from '../notification/helpers/title';
|
|
|
|
import { generateNotificationText } from '../notification/helpers/text';
|
|
|
|
import { parseURI } from 'util/lbryURI';
|
2020-07-23 16:22:57 +02:00
|
|
|
|
|
|
|
type Props = {
|
2022-06-09 16:29:56 +02:00
|
|
|
notifications: Array<Notification>,
|
2020-12-14 19:52:17 +01:00
|
|
|
unseenCount: number,
|
2020-07-23 16:22:57 +02:00
|
|
|
user: ?User,
|
2022-06-09 16:29:56 +02:00
|
|
|
authenticated: boolean,
|
|
|
|
readNotification: (Array<number>) => void,
|
|
|
|
seeNotification: (Array<number>) => void,
|
|
|
|
deleteNotification: (number) => void,
|
2021-12-20 13:32:32 +01:00
|
|
|
doSeeAllNotifications: () => void,
|
2020-07-23 16:22:57 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
export default function NotificationHeaderButton(props: Props) {
|
2022-06-09 16:29:56 +02:00
|
|
|
const {
|
|
|
|
notifications,
|
|
|
|
unseenCount,
|
|
|
|
user,
|
|
|
|
authenticated,
|
|
|
|
readNotification,
|
|
|
|
seeNotification,
|
|
|
|
deleteNotification,
|
|
|
|
doSeeAllNotifications,
|
|
|
|
} = props;
|
2022-06-11 20:56:43 +02:00
|
|
|
const list = notifications.slice(0, 20);
|
2021-12-20 13:32:32 +01:00
|
|
|
|
2020-07-23 16:22:57 +02:00
|
|
|
const { push } = useHistory();
|
2022-06-09 16:29:56 +02:00
|
|
|
const notificationsEnabled = authenticated && (ENABLE_UI_NOTIFICATIONS || (user && user.experimental_ui));
|
|
|
|
|
|
|
|
const [anchorEl, setAnchorEl] = React.useState(null);
|
|
|
|
const [clicked, setClicked] = React.useState(false);
|
|
|
|
const open = Boolean(anchorEl);
|
|
|
|
const handleClick = (event) => setAnchorEl(!anchorEl ? event.currentTarget : null);
|
|
|
|
const handleClose = () => setAnchorEl(null);
|
|
|
|
|
|
|
|
const menuProps = {
|
|
|
|
id: 'notification-menu',
|
|
|
|
anchorEl,
|
|
|
|
open,
|
|
|
|
onClose: handleClose,
|
|
|
|
MenuListProps: {
|
|
|
|
'aria-labelledby': 'basic-button',
|
|
|
|
sx: { padding: 'var(--spacing-xs)' },
|
|
|
|
},
|
|
|
|
className: 'menu__list--header menu__list--notifications',
|
|
|
|
sx: { 'z-index': 2 },
|
|
|
|
PaperProps: { className: 'MuiMenu-list--paper' },
|
|
|
|
disableScrollLock: true,
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleClickAway = () => {
|
|
|
|
if (!clicked) {
|
|
|
|
if (open) setClicked(true);
|
|
|
|
} else {
|
|
|
|
setAnchorEl(null);
|
|
|
|
setClicked(false);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const creatorIcon = (channelUrl, channelThumbnail) => (
|
|
|
|
<UriIndicator uri={channelUrl} link showAtSign channelInfo={{ uri: channelUrl, name: '' }}>
|
|
|
|
<ChannelThumbnail small thumbnailPreview={channelThumbnail} uri={channelThumbnail ? undefined : channelUrl} />
|
|
|
|
</UriIndicator>
|
|
|
|
);
|
2020-07-23 16:22:57 +02:00
|
|
|
|
|
|
|
function handleMenuClick() {
|
2021-12-20 13:32:32 +01:00
|
|
|
if (unseenCount > 0) doSeeAllNotifications();
|
2020-07-23 16:22:57 +02:00
|
|
|
push(`/$/${PAGES.NOTIFICATIONS}`);
|
|
|
|
}
|
|
|
|
|
2022-06-09 16:29:56 +02:00
|
|
|
function handleNotificationDelete(e, id) {
|
|
|
|
e.stopPropagation();
|
|
|
|
deleteNotification(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
if (!open) setClicked(false);
|
|
|
|
}, [open]);
|
|
|
|
|
2021-12-20 13:32:32 +01:00
|
|
|
if (!notificationsEnabled) return null;
|
2020-07-23 16:22:57 +02:00
|
|
|
|
2022-06-09 16:29:56 +02:00
|
|
|
function handleNotificationClick(notification) {
|
|
|
|
const { id, notification_parameters, is_read } = notification;
|
|
|
|
|
|
|
|
if (!is_read) {
|
|
|
|
seeNotification([id]);
|
|
|
|
readNotification([id]);
|
|
|
|
}
|
|
|
|
let notificationLink = formatLbryUrlForWeb(notification_parameters.device.target);
|
|
|
|
if (notification_parameters.dynamic.hash) {
|
|
|
|
notificationLink += '?lc=' + notification_parameters.dynamic.hash + '&view=discussion';
|
|
|
|
}
|
|
|
|
push(notificationLink);
|
|
|
|
}
|
|
|
|
|
|
|
|
function menuEntry(notification) {
|
|
|
|
const { id, active_at, notification_rule, notification_parameters, is_read, type } = notification;
|
|
|
|
|
|
|
|
let channelUrl;
|
|
|
|
let icon;
|
|
|
|
switch (notification_rule) {
|
|
|
|
case RULE.CREATOR_SUBSCRIBER:
|
|
|
|
icon = <Icon icon={ICONS.SUBSCRIBE} sectionIcon />;
|
|
|
|
break;
|
|
|
|
case RULE.COMMENT:
|
|
|
|
case RULE.CREATOR_COMMENT:
|
|
|
|
channelUrl = notification_parameters.dynamic.comment_author;
|
|
|
|
icon = creatorIcon(channelUrl, notification_parameters?.dynamic?.comment_author_thumbnail);
|
|
|
|
break;
|
|
|
|
case RULE.COMMENT_REPLY:
|
|
|
|
channelUrl = notification_parameters.dynamic.reply_author;
|
|
|
|
icon = creatorIcon(channelUrl, notification_parameters?.dynamic?.comment_author_thumbnail);
|
|
|
|
break;
|
|
|
|
case RULE.NEW_CONTENT:
|
|
|
|
channelUrl = notification_parameters.dynamic.channel_url;
|
|
|
|
icon = creatorIcon(channelUrl, notification_parameters?.dynamic?.channel_thumbnail);
|
|
|
|
break;
|
|
|
|
case RULE.NEW_LIVESTREAM:
|
|
|
|
channelUrl = notification_parameters.dynamic.channel_url;
|
|
|
|
icon = creatorIcon(channelUrl, notification_parameters?.dynamic?.channel_thumbnail);
|
|
|
|
break;
|
|
|
|
case RULE.WEEKLY_WATCH_REMINDER:
|
|
|
|
case RULE.DAILY_WATCH_AVAILABLE:
|
|
|
|
case RULE.DAILY_WATCH_REMIND:
|
|
|
|
case RULE.MISSED_OUT:
|
|
|
|
case RULE.REWARDS_APPROVAL_PROMPT:
|
|
|
|
icon = <Icon icon={ICONS.LBC} sectionIcon />;
|
|
|
|
break;
|
|
|
|
case RULE.FIAT_TIP:
|
|
|
|
icon = <Icon icon={ICONS.FINANCE} sectionIcon />;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
icon = <Icon icon={ICONS.NOTIFICATION} sectionIcon />;
|
|
|
|
}
|
|
|
|
|
|
|
|
let channelName;
|
|
|
|
if (channelUrl) {
|
|
|
|
try {
|
|
|
|
({ claimName: channelName } = parseURI(channelUrl));
|
|
|
|
} catch (e) {}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
2022-06-16 15:22:45 +02:00
|
|
|
<a onClick={() => handleNotificationClick(notification)} key={id}>
|
2022-06-10 06:48:28 +02:00
|
|
|
<div
|
|
|
|
className={is_read ? 'menu__list--notification' : 'menu__list--notification menu__list--notification-unread'}
|
|
|
|
key={id}
|
|
|
|
>
|
|
|
|
<div className="notification__icon">{icon}</div>
|
|
|
|
<div className="menu__list--notification-info">
|
|
|
|
<div className="menu__list--notification-type">
|
|
|
|
{generateNotificationTitle(notification_rule, notification_parameters, channelName)}
|
2022-06-09 16:29:56 +02:00
|
|
|
</div>
|
2022-06-10 06:48:28 +02:00
|
|
|
<div
|
|
|
|
className={
|
|
|
|
type === 'comments' ? 'menu__list--notification-title blockquote' : 'menu__list--notification-title'
|
|
|
|
}
|
|
|
|
>
|
|
|
|
{generateNotificationText(notification_rule, notification_parameters)}
|
2022-06-09 16:29:56 +02:00
|
|
|
</div>
|
2022-06-10 06:48:28 +02:00
|
|
|
{!is_read && <span>•</span>}
|
|
|
|
<DateTime timeAgo date={active_at} />
|
|
|
|
</div>
|
|
|
|
<div className="delete-notification" onClick={(e) => handleNotificationDelete(e, id)}>
|
|
|
|
<Icon icon={ICONS.DELETE} sectionIcon />
|
2022-06-09 16:29:56 +02:00
|
|
|
</div>
|
2022-06-10 06:48:28 +02:00
|
|
|
</div>
|
2022-06-16 15:22:45 +02:00
|
|
|
</a>
|
2022-06-09 16:29:56 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-07-23 16:22:57 +02:00
|
|
|
return (
|
2022-06-09 16:29:56 +02:00
|
|
|
notificationsEnabled && (
|
|
|
|
<>
|
|
|
|
<Tooltip title={__('Notifications')}>
|
|
|
|
<Button className="header__navigationItem--icon" onClick={handleClick}>
|
|
|
|
<Icon size={18} icon={ICONS.NOTIFICATION} aria-hidden />
|
|
|
|
<NotificationBubble />
|
|
|
|
</Button>
|
|
|
|
</Tooltip>
|
|
|
|
|
|
|
|
<ClickAwayListener onClickAway={handleClickAway}>
|
|
|
|
<MuiMenu {...menuProps}>
|
|
|
|
<div className="menu__list--notifications-header" />
|
|
|
|
<div className="menu__list--notifications-list">
|
|
|
|
{list.map((notification) => {
|
|
|
|
return menuEntry(notification);
|
|
|
|
})}
|
|
|
|
{list.length === 0 && (
|
|
|
|
<div className="menu__list--notification-empty">
|
|
|
|
<div className="menu__list--notification-empty-title">{__('No notifications')}</div>
|
|
|
|
<div className="menu__list--notification-empty-text">
|
|
|
|
{__("You don't have any notifications yet, but they will be here when you do!")}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
|
2022-06-16 15:22:45 +02:00
|
|
|
<a onClick={handleMenuClick}>
|
2022-06-09 16:29:56 +02:00
|
|
|
<div className="menu__list--notifications-more">{__('View all')}</div>
|
2022-06-16 15:22:45 +02:00
|
|
|
</a>
|
2022-06-09 16:29:56 +02:00
|
|
|
</MuiMenu>
|
|
|
|
</ClickAwayListener>
|
|
|
|
</>
|
|
|
|
)
|
2020-07-23 16:22:57 +02:00
|
|
|
);
|
|
|
|
}
|