Direct reacting from notifications #6935

Merged
saltrafael merged 4 commits from notifications_reacts into master 2021-08-28 14:52:56 +02:00
15 changed files with 264 additions and 67 deletions

View file

@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Add confirmation on comment removal _community pr!_ ([#6563](https://github.com/lbryio/lbry-desktop/pull/6563))
- Show on content page if a file is part of a playlist already _community pr!_([#6393](https://github.com/lbryio/lbry-desktop/pull/6393))
- Add filtering to playlists ([#6905](https://github.com/lbryio/lbry-desktop/pull/6905))
- Added direct replying to notifications _community pr!_ ([#6935](https://github.com/lbryio/lbry-desktop/pull/6935))
infinite-persistence commented 2021-08-24 08:57:59 +02:00 (Migrated from github.com)
Review

File thumbnail flickers a lot

Probably a non-blocker, but a bit annoying. Was present in the past, but now super obvious.

It flickers when:

  • you refresh the Notifications Page (or initial load)
  • On a comment notification, click "Reply" to expand the text box (notice other notifications with file thumbnails will flicker).
  • On a comment notification, click "Cancel" to close the text box (notice other notifications with file thumbnails will flicker).
## File thumbnail flickers a lot <sub>Probably a non-blocker, but a bit annoying. Was present in the past, but now super obvious.</sub> It flickers when: - you refresh the Notifications Page (or initial load) - On a comment notification, click "Reply" to expand the text box (notice other notifications with file thumbnails will flicker). - On a comment notification, click "Cancel" to close the text box (notice other notifications with file thumbnails will flicker).
infinite-persistence commented 2021-08-24 09:06:52 +02:00 (Migrated from github.com)
Review

[Non-blocker] Unknown error

Not sure how to reproduce this. Happens only on a few notifications when replying.

## [Non-blocker] Unknown error Not sure how to reproduce this. Happens only on a few notifications when replying. <img src="https://user-images.githubusercontent.com/64950861/130572293-9bd39920-00ac-47b2-9af3-77448d5fa340.png" width="300">
infinite-persistence commented 2021-08-24 09:08:47 +02:00 (Migrated from github.com)
Review

Ah, it happens when the actual comment has been deleted. Not sure how to handle this gracefully. Probably can ignore for now, unless you have some ideas.

Ah, it happens when the actual comment has been deleted. Not sure how to handle this gracefully. Probably can ignore for now, unless you have some ideas.
saltrafael commented 2021-08-27 13:53:26 +02:00 (Migrated from github.com)
Review

challenge accepted

Peek 2021-08-27 09-11

challenge accepted ![Peek 2021-08-27 09-11](https://user-images.githubusercontent.com/76502841/131125445-36435d22-5619-4633-9dff-5095efce21f6.gif)
kauffj commented 2021-08-27 18:06:09 +02:00 (Migrated from github.com)
Review

@infinite-persistence if this looks good on another review, please merge

@infinite-persistence if this looks good on another review, please merge
### Changed
- Use Canonical Url for copy link ([#6500](https://github.com/lbryio/lbry-desktop/pull/6500))

View file

@ -64,6 +64,9 @@ type Props = {
isModerator: boolean,
isGlobalMod: boolean,
isFiat: boolean,
supportDisabled: boolean,
setQuickReply: (any) => void,
quickReply: any,
};
const LENGTH_TO_COLLAPSE = 300;
@ -100,6 +103,9 @@ function Comment(props: Props) {
isModerator,
isGlobalMod,
isFiat,
supportDisabled,
setQuickReply,
quickReply,
} = props;
const {
@ -185,6 +191,7 @@ function Comment(props: Props) {
function handleSubmit() {
updateComment(commentId, editedMessage);
if (setQuickReply) setQuickReply({ ...quickReply, comment_id: commentId, comment: editedMessage });
setEditing(false);
}
@ -294,6 +301,7 @@ function Comment(props: Props) {
commentIsMine={commentIsMine}
handleEditComment={handleEditComment}
supportAmount={supportAmount}
setQuickReply={setQuickReply}
/>
</Menu>
</div>
@ -403,6 +411,7 @@ function Comment(props: Props) {
onCancelReplying={() => {
setReplying(false);
}}
supportDisabled={supportDisabled}
/>
)}
</>

View file

@ -7,7 +7,7 @@ import {
doSendTip,
} from 'lbry-redux';
import { doOpenModal, doSetActiveChannel } from 'redux/actions/app';
import { doCommentCreate, doFetchCreatorSettings } from 'redux/actions/comments';
import { doCommentCreate, doFetchCreatorSettings, doCommentById } from 'redux/actions/comments';
import { selectUserVerifiedEmail } from 'redux/selectors/user';
import { selectActiveChannelClaim } from 'redux/selectors/app';
import { selectSettingsByChannelId } from 'redux/selectors/comments';
@ -43,6 +43,7 @@ const perform = (dispatch, ownProps) => ({
sendTip: (params, callback, errorCallback) => dispatch(doSendTip(params, false, callback, errorCallback, false)),
doToast: (options) => dispatch(doToast(options)),
doFetchCreatorSettings: (channelClaimId) => dispatch(doFetchCreatorSettings(channelClaimId)),
fetchComment: (commentId) => dispatch(doCommentById(commentId, false)),
});
export default connect(select, perform)(CommentCreate);

View file

@ -47,8 +47,12 @@ type Props = {
claimIsMine: boolean,
sendTip: ({}, (any) => void, (any) => void) => void,
doToast: ({ message: string }) => void,
supportDisabled: boolean,
doFetchCreatorSettings: (channelId: string) => Promise<any>,
settingsByChannelId: { [channelId: string]: PerChannelSettings },
setQuickReply: (any) => void,
fetchComment: (commentId: string) => Promise<any>,
shouldFetchComment: boolean,
};
export function CommentCreate(props: Props) {
@ -71,6 +75,10 @@ export function CommentCreate(props: Props) {
doToast,
doFetchCreatorSettings,
settingsByChannelId,
supportDisabled,
setQuickReply,
fetchComment,
shouldFetchComment,
} = props;
const buttonRef: ElementRef<any> = React.useRef();
const {
@ -80,7 +88,7 @@ export function CommentCreate(props: Props) {
const [isSubmitting, setIsSubmitting] = React.useState(false);
const [commentFailure, setCommentFailure] = React.useState(false);
const [successTip, setSuccessTip] = React.useState({ txid: undefined, tipAmount: undefined });
const { claim_id: claimId } = claim;
const claimId = claim && claim.claim_id;
const [isSupportComment, setIsSupportComment] = React.useState();
const [isReviewingSupportComment, setIsReviewingSupportComment] = React.useState();
const [tipAmount, setTipAmount] = React.useState(1);
@ -90,7 +98,8 @@ export function CommentCreate(props: Props) {
const charCount = commentValue.length;
const [activeTab, setActiveTab] = React.useState('');
const [tipError, setTipError] = React.useState();
const disabled = isSubmitting || isFetchingChannels || !commentValue.length;
const [deletedComment, setDeletedComment] = React.useState(false);
const disabled = deletedComment || isSubmitting || isFetchingChannels || !commentValue.length;
const [shouldDisableReviewButton, setShouldDisableReviewButton] = React.useState();
const channelId = getChannelIdFromClaim(claim);
const channelSettings = channelId ? settingsByChannelId[channelId] : undefined;
@ -99,6 +108,15 @@ export function CommentCreate(props: Props) {
const minAmount = minTip || minSuper || 0;
const minAmountMet = minAmount === 0 || tipAmount >= minAmount;
// Fetch top-level comments to identify if it has been deleted and can reply to it
React.useEffect(() => {
if (shouldFetchComment && fetchComment) {
fetchComment(parentId).then((result) => {
setDeletedComment(String(result).includes('Error'));
});
}
}, [fetchComment, shouldFetchComment, parentId]);
const minAmountRef = React.useRef(minAmount);
minAmountRef.current = minAmount;
@ -322,6 +340,7 @@ export function CommentCreate(props: Props) {
createComment(commentValue, claimId, parentId, txid, payment_intent_id, environment)
.then((res) => {
setIsSubmitting(false);
if (setQuickReply) setQuickReply(res);
if (res && res.signature) {
setCommentValue('');
@ -522,32 +541,34 @@ export function CommentCreate(props: Props) {
requiresAuth={IS_WEB}
/>
)}
{!claimIsMine && (
<Button
disabled={disabled}
button="alt"
className="thatButton"
icon={ICONS.LBC}
onClick={() => {
setIsSupportComment(true);
setActiveTab(TAB_LBC);
}}
/>
{!supportDisabled && !claimIsMine && (
<>
<Button
disabled={disabled}
button="alt"
className="thatButton"
icon={ICONS.LBC}
onClick={() => {
setIsSupportComment(true);
setActiveTab(TAB_LBC);
}}
/>
{/* @if TARGET='web' */}
{stripeEnvironment && (
<Button
disabled={disabled}
button="alt"
className="thisButton"
icon={ICONS.FINANCE}
onClick={() => {
setIsSupportComment(true);
setActiveTab(TAB_FIAT);
}}
/>
)}
{/* @endif */}
</>
)}
{/* @if TARGET='web' */}
{!claimIsMine && stripeEnvironment && (
<Button
disabled={disabled}
button="alt"
className="thisButton"
icon={ICONS.FINANCE}
onClick={() => {
setIsSupportComment(true);
setActiveTab(TAB_FIAT);
}}
/>
)}
{/* @endif */}
{isReply && !minTip && (
<Button
button="link"
@ -561,6 +582,7 @@ export function CommentCreate(props: Props) {
)}
</>
)}
{deletedComment && <div className="error__text">{__('This comment has been deleted.')}</div>}
{MinAmountNotice}
</div>
</Form>

View file

@ -33,6 +33,7 @@ type Props = {
commentModBlockAsAdmin: (string, string) => void,
commentModBlockAsModerator: (string, string, string) => void,
commentModAddDelegate: (string, string, ChannelClaim) => void,
setQuickReply: (any) => void,
};
function CommentMenuList(props: Props) {
@ -59,6 +60,7 @@ function CommentMenuList(props: Props) {
moderationDelegatorsById,
openModal,
supportAmount,
setQuickReply,
} = props;
const contentChannelClaim = !claim
@ -86,7 +88,13 @@ function CommentMenuList(props: Props) {
if (playingUri && playingUri.source === 'comment') {
clearPlayingUri();
}
openModal(MODALS.CONFIRM_REMOVE_COMMENT, { commentId, commentIsMine, contentChannelPermanentUrl, supportAmount });
openModal(MODALS.CONFIRM_REMOVE_COMMENT, {
commentId,
commentIsMine,
contentChannelPermanentUrl,
supportAmount,
setQuickReply,
});
}
function handleCommentBlock() {

View file

@ -19,10 +19,21 @@ type Props = {
activeChannelId: ?string,
claim: ?ChannelClaim,
doToast: ({ message: string }) => void,
hideCreatorLike: boolean,
};
export default function CommentReactions(props: Props) {
const { myReacts, othersReacts, commentId, react, claimIsMine, claim, activeChannelId, doToast } = props;
const {
myReacts,
othersReacts,
commentId,
react,
claimIsMine,
claim,
activeChannelId,
doToast,
hideCreatorLike,
} = props;
const {
push,
location: { pathname },
@ -48,7 +59,7 @@ export default function CommentReactions(props: Props) {
}
return count;
};
const shouldHide = !canCreatorReact && hideCreatorLike;
const creatorLiked = getCountForReact(REACTION_TYPES.CREATOR_LIKE) > 0;
const likeIcon = SIMPLE_SITE
? myReacts.includes(REACTION_TYPES.LIKE)
@ -105,9 +116,8 @@ export default function CommentReactions(props: Props) {
label={<span className="comment__reaction-count">{getCountForReact(REACTION_TYPES.DISLIKE)}</span>}
/>
{ENABLE_CREATOR_REACTIONS && (canCreatorReact || creatorLiked) && (
{!shouldHide && ENABLE_CREATOR_REACTIONS && (canCreatorReact || creatorLiked) && (
<Button
iconOnly
disabled={!canCreatorReact || !claimIsMine}
requiresAuth={IS_WEB}
title={claimIsMine ? __('You loved this') : __('Creator loved this')}
@ -116,7 +126,7 @@ export default function CommentReactions(props: Props) {
onClick={() => react(commentId, REACTION_TYPES.CREATOR_LIKE)}
>
{creatorLiked && (
<ChannelThumbnail xsmall uri={authorUri} hideStakedIndicator className="comment__creator-like" />
<ChannelThumbnail xsmall uri={authorUri} hideStakedIndicator className="comment__creator-like" allowGifs />
)}
</Button>
)}

View file

@ -18,6 +18,7 @@ type Props = {
isFetchingByParentId: { [string]: boolean },
onShowMore?: () => void,
hasMore: boolean,
supportDisabled: boolean,
};
function CommentsReplies(props: Props) {
@ -34,6 +35,7 @@ function CommentsReplies(props: Props) {
isFetchingByParentId,
onShowMore,
hasMore,
supportDisabled,
} = props;
const [isExpanded, setExpanded] = React.useState(true);
@ -98,6 +100,7 @@ function CommentsReplies(props: Props) {
numDirectReplies={comment.replies}
isModerator={comment.is_moderator}
isGlobalMod={comment.is_global_mod}
supportDisabled={supportDisabled}
/>
);
})}

View file

@ -18,6 +18,9 @@ import NotificationContentChannelMenu from 'component/notificationContentChannel
import LbcMessage from 'component/common/lbc-message';
import UriIndicator from 'component/uriIndicator';
import { NavLink } from 'react-router-dom';
import CommentReactions from 'component/commentReactions';
import CommentCreate from 'component/commentCreate';
import CommentsReplies from 'component/commentsReplies';
type Props = {
notification: WebNotification,
@ -31,6 +34,8 @@ 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 [isReplying, setReplying] = React.useState(false);
const [quickReply, setQuickReply] = React.useState();
const isCommentNotification =
notification_rule === RULE.COMMENT ||
@ -119,7 +124,8 @@ export default function Notification(props: Props) {
fullTitle.push(message);
if (index === titleSplit.length - 1) {
return <LbcMessage>{fullTitle.join(' ')}</LbcMessage>;
const result = fullTitle.join(' ');
return <LbcMessage key={result}>{result}</LbcMessage>;
}
}
});
@ -147,11 +153,6 @@ export default function Notification(props: Props) {
}
}
function handleReadNotification(e) {
e.stopPropagation();
doReadNotifications([id]);
}
const Wrapper = menuButton
? (props: { children: any }) => (
<MenuItem className="menu__link--notification" onSelect={handleNotificationClick}>
@ -160,10 +161,8 @@ export default function Notification(props: Props) {
)
: notificationLink
? (props: { children: any }) => (
<NavLink {...navLinkProps}>
<a className="menu__link--notification" onClick={handleNotificationClick}>
{props.children}
</a>
<NavLink {...navLinkProps} className="menu__link--notification" onClick={handleNotificationClick}>
{props.children}
</NavLink>
)
: (props: { children: any }) => (
@ -176,12 +175,12 @@ export default function Notification(props: Props) {
);
return (
<Wrapper>
<div
className={classnames('notification__wrapper', {
'notification__wrapper--unread': !is_read,
})}
>
<div
className={classnames('notification__wrapper', {
'notification__wrapper--unread': !is_read,
})}
>
<Wrapper>
<div className="notification__icon">{icon}</div>
<div className="notification__content-wrapper">
@ -192,7 +191,7 @@ export default function Notification(props: Props) {
{isCommentNotification && commentText ? (
<>
<div className="notification__title">{title}</div>
<div title={commentText} className="notification__text mobile-hidden">
<div title={commentText} className="notification__text">
{commentText}
</div>
</>
@ -220,7 +219,15 @@ export default function Notification(props: Props) {
</div>
<div className="notification__extra">
{!is_read && <Button className="notification__mark-seen" onClick={handleReadNotification} />}
{!is_read && (
<Button
className="notification__mark-seen"
onClick={(e) => {
e.stopPropagation();
doReadNotifications([id]);
}}
/>
)}
<div className="notification__time">
<DateTime timeAgo date={notification.active_at} />
</div>
@ -249,7 +256,47 @@ export default function Notification(props: Props) {
</MenuList>
</Menu>
</div>
</div>
</Wrapper>
</Wrapper>
{isCommentNotification && (
<div>
<div className="notification__reactions">
<Button
label={__('Reply')}
className="comment__action"
onClick={() => setReplying(!isReplying)}
icon={ICONS.REPLY}
/>
<CommentReactions
uri={notificationTarget}
commentId={notification_parameters.dynamic.hash}
hideCreatorLike
/>
</div>
{isReplying && (
<CommentCreate
isReply
uri={notificationTarget}
infinite-persistence commented 2021-08-24 08:02:51 +02:00 (Migrated from github.com)
Review

Lots of console errors

## Lots of console errors <img src="https://user-images.githubusercontent.com/64950861/130564018-2a162341-1af1-4563-bac9-6ae9f8bc8bd6.png" width="300">
infinite-persistence commented 2021-08-28 14:43:03 +02:00 (Migrated from github.com)
Review

This one still exists. Can ship, although we should stop introducing console errors to a already-long list (e.g. Modal dismissal, Reach, videojs)

This one still exists. Can ship, although we should stop introducing console errors to a already-long list (e.g. Modal dismissal, Reach, videojs)
parentId={notification_parameters.dynamic.hash}
onDoneReplying={() => setReplying(false)}
onCancelReplying={() => setReplying(false)}
setQuickReply={setQuickReply}
infinite-persistence commented 2021-08-24 07:57:09 +02:00 (Migrated from github.com)
Review

2nd-level reply doesn't appear, with warning.

If you reply to your own reply, that comment doesn't appear, and we get a warning as well
image

But it does go through. If you tried to reply from the top again (clicking the notifications's "Reply"), then all comments appear correctly with the "Show/hide replies" button.

## 2nd-level reply doesn't appear, with warning. If you reply to your own reply, that comment doesn't appear, and we get a warning as well ![image](https://user-images.githubusercontent.com/64950861/130563743-c4ca5f63-3667-4302-924c-d62dd932fe84.png) But it does go through. If you tried to reply from the top again (clicking the notifications's "Reply"), then all comments appear correctly with the "Show/hide replies" button.
supportDisabled
shouldFetchComment
/>
)}
{quickReply && (
<CommentsReplies
uri={notificationTarget}
parentId={notification_parameters.dynamic.hash}
numDirectReplies={1}
supportDisabled
/>
)}
</div>
)}
</div>
);
}

View file

@ -11,10 +11,19 @@ type Props = {
closeModal: () => void,
deleteComment: (string, ?string) => void,
supportAmount?: any,
setQuickReply: (any) => void,
};
function ModalRemoveComment(props: Props) {
const { commentId, commentIsMine, contentChannelPermanentUrl, closeModal, deleteComment, supportAmount } = props;
const {
commentId,
commentIsMine,
contentChannelPermanentUrl,
closeModal,
deleteComment,
supportAmount,
setQuickReply,
} = props;
return (
<Modal isOpen contentLabel={__('Confirm Comment Deletion')} type="card" onAborted={closeModal}>
@ -24,7 +33,9 @@ function ModalRemoveComment(props: Props) {
<React.Fragment>
<p>{__('Are you sure you want to remove this comment?')}</p>
{Boolean(supportAmount) && (
<p className="help error__text"> {__('This comment has a tip associated with it which cannot be reverted.')}</p>
<p className="help error__text">
{__('This comment has a tip associated with it which cannot be reverted.')}
</p>
)}
</React.Fragment>
}
@ -35,8 +46,9 @@ function ModalRemoveComment(props: Props) {
button="primary"
label={__('Remove')}
onClick={() => {
deleteComment(commentId, commentIsMine ? undefined : contentChannelPermanentUrl);
closeModal();
deleteComment(commentId, commentIsMine ? undefined : contentChannelPermanentUrl);
if (setQuickReply) setQuickReply(undefined);
}}
/>
<Button button="link" label={__('Cancel')} onClick={closeModal} />

View file

@ -7,6 +7,8 @@ import {
selectUnseenNotificationCount,
selectNotificationCategories,
} from 'redux/selectors/notifications';
import { doCommentReactList } from 'redux/actions/comments';
import { selectActiveChannelClaim } from 'redux/selectors/app';
import { doReadNotifications, doNotificationList, doSeeAllNotifications } from 'redux/actions/notifications';
import NotificationsPage from './view';
@ -17,10 +19,12 @@ const select = (state) => ({
fetching: selectIsFetchingNotifications(state),
unreadCount: selectUnreadNotificationCount(state),
unseenCount: selectUnseenNotificationCount(state),
activeChannel: selectActiveChannelClaim(state),
});
export default connect(select, {
doReadNotifications,
doNotificationList,
doSeeAllNotifications,
doCommentReactList,
})(NotificationsPage);

View file

@ -10,6 +10,7 @@ import usePersistedState from 'effects/use-persisted-state';
import Yrbl from 'component/yrbl';
import * as NOTIFICATIONS from 'constants/notifications';
import useFetched from 'effects/use-fetched';
import { RULE } from 'constants/notifications';
type Props = {
notifications: Array<Notification>,
@ -21,6 +22,8 @@ type Props = {
doSeeAllNotifications: () => void,
doReadNotifications: () => void,
doNotificationList: (?Array<string>) => void,
activeChannel: ?ChannelClaim,
doCommentReactList: (Array<string>) => Promise<any>,
};
export default function NotificationsPage(props: Props) {
@ -34,12 +37,41 @@ export default function NotificationsPage(props: Props) {
doReadNotifications,
doNotificationList,
notificationCategories,
activeChannel,
doCommentReactList,
} = props;
const initialFetchDone = useFetched(fetching);
const [name, setName] = usePersistedState('notifications--rule', NOTIFICATIONS.NOTIFICATION_NAME_ALL);
const isFiltered = name !== NOTIFICATIONS.NOTIFICATION_NAME_ALL;
const list = isFiltered ? notificationsFiltered : notifications;
// Fetch reacts
React.useEffect(() => {
if (initialFetchDone && activeChannel) {
let idsForReactionFetch = [];
list.map((notification) => {
const { notification_rule, notification_parameters } = notification;
const isComment =
notification_rule === RULE.COMMENT ||
notification_rule === RULE.COMMENT_REPLY ||
notification_rule === RULE.CREATOR_COMMENT;
const commentId =
isComment &&
notification_parameters &&
notification_parameters.dynamic &&
notification_parameters.dynamic.hash;
if (commentId) {
idsForReactionFetch.push(commentId);
}
infinite-persistence commented 2021-08-24 07:30:06 +02:00 (Migrated from github.com)
Review

GUI hangs (maybe to excessive reaction-fetch, but doesn't make sense since it's background call)

Anyway, regarding reaction fetch:
scenarios where it should not be fetching:

  • When clicking "Reply" to open CommentCreate in a Hyperchat notification
  • When clicking "Cancel" to close CommentCreate in a Hyperchat notification
  • Few others (I'm seeing 4-5 fetches, not sure from where)

Should only fetch in the following:

  • On initial mount, one time (using current active channel)
  • When switching channels in "Replying as", one time per channel.

Without looking deeper, my current guess is that it should be looking at "is fetching reacts" instead of "is fetching notifications".

One example scenario: select "Comments", let it render, then open the combo-box again (I think this coincides with the fetch. The entire browser freezes:
image

## GUI hangs (maybe to excessive reaction-fetch, but doesn't make sense since it's background call) Anyway, regarding reaction fetch: ❌ scenarios where it should not be fetching: - When clicking "Reply" to open `CommentCreate` in a Hyperchat notification - When clicking "Cancel" to close `CommentCreate` in a Hyperchat notification - Few others (I'm seeing 4-5 fetches, not sure from where) Should only fetch in the following: - On initial mount, one time (using current active channel) - When switching channels in "Replying as", one time per channel. _Without looking deeper, my current guess is that it should be looking at "is fetching reacts" instead of "is fetching notifications"._ One example scenario: select "Comments", let it render, then open the combo-box again (I think this coincides with the fetch. The entire browser freezes: ![image](https://user-images.githubusercontent.com/64950861/130560760-ef0ad2d8-6935-4619-8dbf-dfe465c8a790.png)
});
if (idsForReactionFetch.length !== 0) {
doCommentReactList(idsForReactionFetch);
}
}
}, [initialFetchDone, doCommentReactList, list, activeChannel]);
React.useEffect(() => {
if (unseenCount > 0 || unreadCount > 0) {
// If there are unread notifications when entering the page, reset to All.
@ -106,7 +138,7 @@ export default function NotificationsPage(props: Props) {
{list && list.length > 0 && !(isFiltered && fetching) ? (
<div className="card">
<div className="notification_list">
{list.map((notification, index) => {
{list.map((notification) => {
return <Notification key={notification.id} notification={notification} />;
})}
</div>
@ -117,11 +149,9 @@ export default function NotificationsPage(props: Props) {
<Yrbl
title={__('No notifications')}
subtitle={
<p>
{isFiltered
? __('Try selecting another filter.')
: __("You don't have any notifications yet, but they will be here when you do!")}
</p>
isFiltered
? __('Try selecting another filter.')
: __("You don't have any notifications yet, but they will be here when you do!")
}
actions={
<div className="section__actions">

View file

@ -170,6 +170,8 @@ export function doCommentById(commentId: string, toastIfNotFound: boolean = true
} else {
devToast(dispatch, error.message);
}
return error;
});
};
}

View file

@ -249,6 +249,7 @@ $thumbnailWidthSmall: 1rem;
.comment__message {
word-break: break-word;
max-width: 35rem;
color: var(--color-text);
ul li,
ol li {
@ -298,6 +299,7 @@ $thumbnailWidthSmall: 1rem;
.comment__char-count {
font-size: var(--font-xsmall);
color: var(--color-text);
}
.comment__char-count-mde {

View file

@ -7,18 +7,25 @@ $contentMaxWidth: 60rem;
}
.notification_list {
> * {
border-bottom: 1px solid var(--color-border);
.notification__wrapper {
border-top: 1px solid var(--color-border);
&:last-of-type {
border-bottom: none;
&:first-of-type {
border-top: none;
}
}
.comment__create,
.comment__content {
margin: var(--spacing-m);
margin-bottom: 0;
}
}
.notification__icon {
display: flex;
align-items: flex-start;
margin: auto;
.icon__wrapper {
width: 1rem;
@ -36,6 +43,10 @@ $contentMaxWidth: 60rem;
align-items: center;
margin-left: var(--spacing-m);
}
@media (max-width: $breakpoint-medium) {
margin-top: var(--spacing-xxs);
}
}
.notification__wrapper {
@ -43,6 +54,7 @@ $contentMaxWidth: 60rem;
display: flex;
padding: var(--spacing-m) 0;
justify-content: space-between;
flex-direction: column;
.channel-thumbnail {
@include handleChannelGif(3rem);
@ -57,6 +69,16 @@ $contentMaxWidth: 60rem;
@media (max-width: $breakpoint-small) {
padding: var(--spacing-s);
}
.comment__creator-like {
height: 0.8rem;
width: 0.8rem;
margin-left: 3px;
z-index: 3;
position: absolute;
top: 0.4rem;
left: 0.4rem;
}
}
.notification__wrapper--unread {
@ -162,6 +184,29 @@ $contentMaxWidth: 60rem;
}
}
.notification__reactions {
display: flex;
margin: var(--spacing-m);
margin-bottom: 0;
@media (min-width: $breakpoint-small) {
margin-left: 5rem;
}
@media (max-width: $breakpoint-small) {
margin-left: 3rem;
}
> *:not(:last-of-type) {
margin-right: var(--spacing-m);
}
.button__label {
margin-left: var(--spacing-xs);
}
}
.notification__bubble {
height: 1.5rem;
width: 1.5rem;

View file

@ -119,6 +119,7 @@
}
.menu__link--notification {
width: 100%;
display: flex;
align-items: flex-start;