group comment notifications and clean up notification style

This commit is contained in:
Sean Yesmunt 2020-07-30 16:14:12 -04:00
parent 9fc3d10d4b
commit 9acbdf9825
6 changed files with 99 additions and 13 deletions

View file

@ -1,5 +1,6 @@
[ignore] [ignore]
.*\.typeface\.json .*\.typeface\.json
.*/node_modules/findup/.*
[include] [include]

View file

@ -21,6 +21,8 @@ declare type WebNotification = {
}, },
dynamic: { dynamic: {
comment_author: string, comment_author: string,
hash: string,
claim_title: string,
}, },
email: {}, email: {},
}, },
@ -28,4 +30,5 @@ declare type WebNotification = {
type: string, type: string,
updated_at: string, updated_at: string,
user_id: number, user_id: number,
group_count?: number,
}; };

View file

@ -1,4 +1,6 @@
// @flow // @flow
import { NOTIFICATION_CREATOR_SUBSCRIBER, NOTIFICATION_COMMENT } from 'constants/notifications';
import * as ICONS from 'constants/icons'; import * as ICONS from 'constants/icons';
import React from 'react'; import React from 'react';
import Icon from 'component/common/icon'; import Icon from 'component/common/icon';
@ -14,14 +16,14 @@ type Props = {
children: any, children: any,
}; };
const NOTIFICATION_CREATOR_SUBSCRIBER = 'creator_subscriber';
const NOTIFICATION_COMMENT = 'comment';
export default function Notification(props: Props) { export default function Notification(props: Props) {
const { notification, menuButton = false } = props; const { notification, menuButton = false } = props;
const notificationTarget = notification && notification.notification_parameters.device.target;
const notificationLink = formatLbryUrlForWeb(notificationTarget);
const { push } = useHistory(); const { push } = useHistory();
const notificationTarget = notification && notification.notification_parameters.device.target;
let notificationLink = formatLbryUrlForWeb(notificationTarget);
if (notification.notification_rule === NOTIFICATION_COMMENT && notification.notification_parameters.dynamic.hash) {
notificationLink += `?lc=${notification.notification_parameters.dynamic.hash}`;
}
let icon; let icon;
switch (notification.notification_rule) { switch (notification.notification_rule) {
@ -29,7 +31,7 @@ export default function Notification(props: Props) {
icon = <Icon icon={ICONS.SUBSCRIBE} sectionIcon className="notification__icon" />; icon = <Icon icon={ICONS.SUBSCRIBE} sectionIcon className="notification__icon" />;
break; break;
case NOTIFICATION_COMMENT: case NOTIFICATION_COMMENT:
icon = <ChannelThumbnail uri={notification.notification_parameters.dynamic.comment_author} />; icon = <ChannelThumbnail small uri={notification.notification_parameters.dynamic.comment_author} />;
break; break;
default: default:
icon = <Icon icon={ICONS.NOTIFICATION} sectionIcon className="notification__icon" />; icon = <Icon icon={ICONS.NOTIFICATION} sectionIcon className="notification__icon" />;
@ -52,8 +54,20 @@ export default function Notification(props: Props) {
<div className="notification__wrapper"> <div className="notification__wrapper">
<div className="notification__icon">{icon}</div> <div className="notification__icon">{icon}</div>
<div className="notification__content"> <div className="notification__content">
<div className="notification__title">{notification.notification_parameters.device.title}</div> <div>
<div className="notification__text">{notification.notification_parameters.device.text}</div> {notification.notification_rule !== NOTIFICATION_COMMENT && (
<div className="notification__title">{notification.notification_parameters.device.title}</div>
)}
<div className="notification__text">
{notification.notification_parameters.device.text.replace(
// This is terrible and will be replaced when I make the comment channel clickable
'commented on',
notification.group_count ? `left ${notification.group_count} comments on` : 'commented on'
)}
</div>
</div>
<div className="notification__time"> <div className="notification__time">
<DateTime timeAgo date={notification.created_at} /> <DateTime timeAgo date={notification.created_at} />
</div> </div>

View file

@ -0,0 +1,2 @@
export const NOTIFICATION_CREATOR_SUBSCRIBER = 'creator_subscriber';
export const NOTIFICATION_COMMENT = 'comment';

View file

@ -1,4 +1,5 @@
// @flow // @flow
import { NOTIFICATION_COMMENT } from 'constants/notifications';
import React from 'react'; import React from 'react';
import Page from 'component/page'; import Page from 'component/page';
import Card from 'component/common/card'; import Card from 'component/common/card';
@ -13,6 +14,51 @@ type Props = {
export default function NotificationsPage(props: Props) { export default function NotificationsPage(props: Props) {
const { notifications, fetching } = props; const { notifications, fetching } = props;
// Group sequential comment notifications if they are by the same author
let groupedCount = 1;
const groupedNotifications =
notifications &&
notifications.reduce((list, notification, index) => {
if (index === 0) {
return [notification];
}
const previousNotification = notifications[index - 1];
const isCommentNotification = notification.notification_rule === NOTIFICATION_COMMENT;
const previousIsCommentNotification = previousNotification.notification_rule === NOTIFICATION_COMMENT;
if (isCommentNotification && previousIsCommentNotification) {
const notificationTarget = notification.notification_parameters.device.target;
const previousTarget = previousNotification && previousNotification.notification_parameters.device.target;
const author = notification.notification_parameters.dynamic.comment_author;
const previousAuthor = previousNotification.notification_parameters.dynamic.comment_author;
if (author === previousAuthor && notificationTarget === previousTarget) {
const newList = [...list];
newList.pop();
groupedCount += 1;
const newNotification = {
...previousNotification,
group_count: groupedCount,
};
newList[index - groupedCount] = newNotification;
return newList;
} else {
if (groupedCount > 1) {
groupedCount = 1;
}
return [...list, notification];
}
} else {
if (groupedCount > 1) {
groupedCount = 1;
}
return [...list, notification];
}
}, []);
return ( return (
<Page> <Page>
{fetching && ( {fetching && (
@ -20,13 +66,17 @@ export default function NotificationsPage(props: Props) {
<Spinner delayed /> <Spinner delayed />
</div> </div>
)} )}
{notifications && notifications.length > 0 ? ( {groupedNotifications && groupedNotifications.length > 0 ? (
<Card <Card
isBodyList isBodyList
title={__('Notifications')} title={__('Notifications')}
body={ body={
<div className="notification_list"> <div className="notification_list">
{notifications.map((notification, index) => { {groupedNotifications.map((notification, index) => {
if (!notification) {
return null;
}
return <Notification key={notification.id} notification={notification} />; return <Notification key={notification.id} notification={notification} />;
})} })}
</div> </div>

View file

@ -13,25 +13,41 @@
} }
} }
.notification__icon {
.icon__wrapper {
margin-right: var(--spacing-m);
}
}
.notification__wrapper { .notification__wrapper {
width: 100%; width: 100%;
display: flex; display: flex;
.channel-thumbnail {
@include handleChannelGif(3rem);
}
} }
.notification__content { .notification__content {
margin-left: var(--spacing-m); display: flex;
flex: 1;
justify-content: space-between;
align-items: center;
} }
.notification__title { .notification__title {
font-size: var(--font-large); font-size: var(--font-small);
font-weight: bold;
color: var(--color-text);
margin-bottom: var(--spacing-s);
} }
.notification__text { .notification__text {
font-size: var(--font-body); font-size: var(--font-body);
margin-top: var(--spacing-s);
} }
.notification__time { .notification__time {
@extend .help; @extend .help;
margin-bottom: 0;
margin-top: 0; margin-top: 0;
} }