Notifications improve links #6711
|
@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Update lighthouse search api _community pr!_ ([#6731](https://github.com/lbryio/lbry-desktop/pull/6731))
|
||||
- Update sockety api _community pr!_ ([#6747](https://github.com/lbryio/lbry-desktop/pull/6747))
|
||||
- Use resolve for OG metadata instead of chainquery _community pr!_ ([#6787](https://github.com/lbryio/lbry-desktop/pull/6787))
|
||||
- Improved clickability of notification links _community pr!_ ([#6711](https://github.com/lbryio/lbry-desktop/pull/6711))
|
||||
|
||||
### Fixed
|
||||
- App now supports '#' and ':' for claimId separator ([#6496](https://github.com/lbryio/lbry-desktop/pull/6496))
|
||||
|
|
|
@ -16,6 +16,8 @@ import FileThumbnail from 'component/fileThumbnail';
|
|||
import { Menu, MenuList, MenuButton, MenuItem } from '@reach/menu-button';
|
||||
import NotificationContentChannelMenu from 'component/notificationContentChannelMenu';
|
||||
import LbcMessage from 'component/common/lbc-message';
|
||||
import UriIndicator from 'component/uriIndicator';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
|
||||
type Props = {
|
||||
notification: WebNotification,
|
||||
|
@ -29,13 +31,12 @@ export default function Notification(props: Props) {
|
|||
const { notification, menuButton = false, doReadNotifications, doDeleteNotification } = props;
|
||||
const { push } = useHistory();
|
||||
const { notification_rule, notification_parameters, is_read, id } = notification;
|
||||
|
||||
const isCommentNotification =
|
||||
notification_rule === RULE.COMMENT ||
|
||||
notification_rule === RULE.COMMENT_REPLY ||
|
||||
notification_rule === RULE.CREATOR_COMMENT;
|
||||
const commentText = isCommentNotification && notification_parameters.dynamic.comment;
|
||||
const channelUrl =
|
||||
(notification_rule === RULE.NEW_CONTENT && notification.notification_parameters.dynamic.channel_url) || '';
|
||||
|
||||
let notificationTarget;
|
||||
switch (notification_rule) {
|
||||
|
@ -51,21 +52,14 @@ export default function Notification(props: Props) {
|
|||
notificationTarget = notification_parameters.device.target;
|
||||
}
|
||||
|
||||
let notificationLink = formatLbryUrlForWeb(notificationTarget);
|
||||
let urlParams = new URLSearchParams();
|
||||
if (isCommentNotification && notification_parameters.dynamic.hash) {
|
||||
urlParams.append('lc', notification_parameters.dynamic.hash);
|
||||
}
|
||||
|
||||
try {
|
||||
const { isChannel } = parseURI(notificationTarget);
|
||||
if (isChannel) {
|
||||
urlParams.append(PAGE_VIEW_QUERY, DISCUSSION_PAGE);
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
notificationLink += `?${urlParams.toString()}`;
|
||||
|
||||
const creatorIcon = (channelUrl) => {
|
||||
return (
|
||||
|
||||
<UriIndicator uri={channelUrl} link>
|
||||
<ChannelThumbnail small uri={channelUrl} />
|
||||
</UriIndicator>
|
||||
);
|
||||
};
|
||||
This one is probably not robust as it relies on how Commentron phrases the suggested title. It might be better to inspect Aside: It is also possible to reconstruct the title ourselve from This one is probably not robust as it relies on how Commentron phrases the suggested title.
(e.g. will fail if phrasing changed to `"@@Welcome to my post@@" received a new comment from @mike`).
It might be better to inspect `notification_parameters.dynamic` for the author, but you'll need to do it differently for each notification type that has a channel-mention. For example, the author for a comment-notification can be parsed from `notification_parameters.dynamic.comment_author`.
Aside: It is also possible to reconstruct the title ourselve from `notification_parameters`'s info. `device.title` is just Commentron's default.
@jessopb, I believe this block is trying replace the channel name with something that's clickable. I recall this issue also affects livestream chat and maybe other places. Do you have a recommendation on how to address this issue in general? The above breaks up the string. This will break any hopes of localizing the notifications page in the future. Maybe something like how But anyway, since Commentron doesn't support a i18n-friendly format yet, this solution is still better than current state. @jessopb, I believe this block is trying replace the channel name with something that's clickable. I recall this issue also affects livestream chat and maybe other places. Do you have a recommendation on how to address this issue in general?
The above breaks up the string. This will break any hopes of localizing the notifications page in the future.
- <img src="https://user-images.githubusercontent.com/64950861/127441224-22c8ef63-71e6-4938-8c3e-fd4692bae5a4.png" width="400">
- <img src="https://user-images.githubusercontent.com/64950861/127441451-c35e5f70-8657-4196-b502-9948032f7d01.png" width="400">
Maybe something like how `LbcMessage` does the replacement will be a cleaner approach?
But anyway, since Commentron doesn't support a i18n-friendly format yet, this solution is still better than current state.
Or maybe something like this, which also fixes the i18n problem:
Or maybe something like this, which also fixes the i18n problem:
```
const commentedOnClaimMsg = (
<I18nMessage
tokens={{
comment_author: <UriIndicator uri={notification_parameters.dynamic.comment_author} link />,
claim_title: notification_parameters.dynamic.claim_title,
}}
>
%comment_author% commented on %claim_title%
</I18nMessage>
);
```
|
||||
let channelUrl;
|
||||
let icon;
|
||||
switch (notification_rule) {
|
||||
case RULE.CREATOR_SUBSCRIBER:
|
||||
|
@ -73,16 +67,20 @@ export default function Notification(props: Props) {
|
|||
break;
|
||||
case RULE.COMMENT:
|
||||
case RULE.CREATOR_COMMENT:
|
||||
icon = <ChannelThumbnail small uri={notification_parameters.dynamic.comment_author} />;
|
||||
channelUrl = notification_parameters.dynamic.comment_author;
|
||||
icon = creatorIcon(channelUrl);
|
||||
break;
|
||||
case RULE.COMMENT_REPLY:
|
||||
icon = <ChannelThumbnail small uri={notification_parameters.dynamic.reply_author} />;
|
||||
channelUrl = notification_parameters.dynamic.reply_author;
|
||||
icon = creatorIcon(channelUrl);
|
||||
break;
|
||||
case RULE.NEW_CONTENT:
|
||||
icon = <ChannelThumbnail small uri={notification_parameters.dynamic.channel_url} />;
|
||||
channelUrl = notification_parameters.dynamic.channel_url;
|
||||
icon = creatorIcon(channelUrl);
|
||||
break;
|
||||
case RULE.NEW_LIVESTREAM:
|
||||
icon = <ChannelThumbnail small uri={notification_parameters.dynamic.channel_url} />;
|
||||
channelUrl = notification_parameters.dynamic.channel_url;
|
||||
icon = creatorIcon(channelUrl);
|
||||
break;
|
||||
case RULE.DAILY_WATCH_AVAILABLE:
|
||||
case RULE.DAILY_WATCH_REMIND:
|
||||
|
@ -97,12 +95,54 @@ export default function Notification(props: Props) {
|
|||
icon = <Icon icon={ICONS.NOTIFICATION} sectionIcon />;
|
||||
}
|
||||
|
||||
let notificationLink = formatLbryUrlForWeb(notificationTarget);
|
||||
let urlParams = new URLSearchParams();
|
||||
if (isCommentNotification && notification_parameters.dynamic.hash) {
|
||||
urlParams.append('lc', notification_parameters.dynamic.hash);
|
||||
}
|
||||
|
||||
let channelName = channelUrl && '@' + channelUrl.split('@')[1].split('#')[0];
|
||||
|
||||
const notificationTitle = notification_parameters.device.title;
|
||||
Feels like can be improved, but will pass for now so as not to stall the PR further. With a little more work, I think we can avoid the unnecessary loop and also enable localization at the same time using something like the following (incomplete example). It'll also prevent the need to put a blanket
Feels like can be improved, but will pass for now so as not to stall the PR further.
With a little more work, I think we can avoid the unnecessary loop and also enable localization at the same time using something like the following (incomplete example). It'll also prevent the need to put a blanket `LbcMessage` for notifications that don't need it. We can also customize the phrase like adding quotes to the title for readability.
```
function generateTitle(rule, params) {
switch (rule) {
case RULE.COMMENT:
return (
<I18nMessage
tokens={{
commenter: <UriIndicator uri={notification_parameters.dynamic.comment_author} link />,
title: notification_parameters.dynamic.claim_title,
}}
>
%commenter% commented on "%title%"
</I18nMessage>
);
case RULE.CREATOR_SUBSCRIBER:
// TODO
break;
case RULE.CREATOR_COMMENT:
// TODO
break;
case RULE.COMMENT_REPLY:
// TODO
break;
case RULE.NEW_CONTENT:
case RULE.NEW_LIVESTREAM:
case RULE.DAILY_WATCH_AVAILABLE:
case RULE.DAILY_WATCH_REMIND:
case RULE.MISSED_OUT:
case RULE.REWARDS_APPROVAL_PROMPT:
case RULE.FIAT_TIP:
default:
// Use Commentron default
return params.device.title;
}
}
const title = generateTitle(notification_rule, notification_parameters);
```
|
||||
const titleSplit = notificationTitle.split(' ');
|
||||
let fullTitle = [' '];
|
||||
let uriIndicator;
|
||||
const title = titleSplit.map((message, index) => {
|
||||
if (channelName === message) {
|
||||
uriIndicator = <UriIndicator uri={channelUrl} link />;
|
||||
fullTitle.push(' ');
|
||||
const resultTitle = fullTitle;
|
||||
fullTitle = [' '];
|
||||
|
||||
return [resultTitle.join(' '), uriIndicator];
|
||||
} else {
|
||||
fullTitle.push(message);
|
||||
|
||||
if (index === titleSplit.length - 1) {
|
||||
return <LbcMessage>{fullTitle.join(' ')}</LbcMessage>;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
const { isChannel } = parseURI(notificationTarget);
|
||||
if (isChannel) {
|
||||
urlParams.append(PAGE_VIEW_QUERY, DISCUSSION_PAGE);
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
notificationLink += `?${urlParams.toString()}`;
|
||||
const navLinkProps = {
|
||||
to: notificationLink,
|
||||
onClick: (e) => e.stopPropagation(),
|
||||
};
|
||||
|
||||
function handleNotificationClick() {
|
||||
if (!is_read) {
|
||||
doReadNotifications([id]);
|
||||
}
|
||||
|
||||
if (notificationLink) {
|
||||
if (menuButton && notificationLink) {
|
||||
push(notificationLink);
|
||||
}
|
||||
}
|
||||
|
@ -120,9 +160,11 @@ export default function Notification(props: Props) {
|
|||
)
|
||||
: notificationLink
|
||||
? (props: { children: any }) => (
|
||||
<a className="menu__link--notification" onClick={handleNotificationClick}>
|
||||
{props.children}
|
||||
</a>
|
||||
<NavLink {...navLinkProps}>
|
||||
<a className="menu__link--notification" onClick={handleNotificationClick}>
|
||||
{props.children}
|
||||
</a>
|
||||
</NavLink>
|
||||
)
|
||||
: (props: { children: any }) => (
|
||||
<span
|
||||
|
@ -145,17 +187,11 @@ export default function Notification(props: Props) {
|
|||
<div className="notification__content-wrapper">
|
||||
<div className="notification__content">
|
||||
<div className="notification__text-wrapper">
|
||||
{!isCommentNotification && (
|
||||
<div className="notification__title">
|
||||
<LbcMessage>{notification_parameters.device.title}</LbcMessage>
|
||||
</div>
|
||||
)}
|
||||
{!isCommentNotification && <div className="notification__title">{title}</div>}
|
||||
|
||||
{isCommentNotification && commentText ? (
|
||||
<>
|
||||
<div className="notification__title">
|
||||
<LbcMessage>{notification_parameters.device.title}</LbcMessage>
|
||||
</div>
|
||||
<div className="notification__title">{title}</div>
|
||||
<div title={commentText} className="notification__text mobile-hidden">
|
||||
{commentText}
|
||||
</div>
|
||||
|
@ -193,7 +229,13 @@ export default function Notification(props: Props) {
|
|||
|
||||
<div className="notification__menu">
|
||||
<Menu>
|
||||
<MenuButton className={'menu__button notification__menu-button'} onClick={(e) => e.stopPropagation()}>
|
||||
<MenuButton
|
||||
className={'menu__button notification__menu-button'}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<Icon size={18} icon={ICONS.MORE_VERTICAL} />
|
||||
</MenuButton>
|
||||
<MenuList className="menu__list">
|
||||
|
|
|
@ -42,6 +42,7 @@ $contentMaxWidth: 60rem;
|
|||
width: 100%;
|
||||
display: flex;
|
||||
padding: var(--spacing-m) 0;
|
||||
justify-content: space-between;
|
||||
|
||||
.channel-thumbnail {
|
||||
@include handleChannelGif(3rem);
|
||||
|
@ -60,6 +61,7 @@ $contentMaxWidth: 60rem;
|
|||
|
||||
.notification__wrapper--unread {
|
||||
background-color: var(--color-card-background-highlighted);
|
||||
justify-content: space-between;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-button-secondary-bg);
|
||||
|
@ -120,6 +122,7 @@ $contentMaxWidth: 60rem;
|
|||
}
|
||||
|
||||
.notification__title {
|
||||
position: relative;
|
||||
font-size: var(--font-small);
|
||||
color: var(--color-text);
|
||||
margin-bottom: var(--spacing-s);
|
||||
|
@ -135,6 +138,7 @@ $contentMaxWidth: 60rem;
|
|||
|
||||
.notification__text {
|
||||
font-size: var(--font-body);
|
||||
color: var(--color-text);
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
|
|
reaction.List
calls (see Network Tab in Dev Tools). The backend team will scold us again, lol.Suggestions:
[]
dependency list on theuseEffect
would work, assuming we have all the variables that we need on mount.Notification
level will create lots ofreaction.List
calls (1 for eachNotification
). It will be more efficient to do it atNotificationsPage
, since we have the full list comments to fetch for already. But we leave this up to you -- we can also make the change toNotificationsPage
at a later time when we see huge spikes inreaction.List
calls.