Add direct reacting from notifications

This commit is contained in:
saltrafael 2021-08-27 07:29:58 -03:00 committed by infinite-persistence
parent 6637c7b98b
commit d7344f5047
No known key found for this signature in database
GPG key ID: B9C3252EDC3D0AA0
12 changed files with 226 additions and 63 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)) - 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)) - 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)) - 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))
### Changed ### Changed
- Use Canonical Url for copy link ([#6500](https://github.com/lbryio/lbry-desktop/pull/6500)) - 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, isModerator: boolean,
isGlobalMod: boolean, isGlobalMod: boolean,
isFiat: boolean, isFiat: boolean,
supportDisabled: boolean,
setQuickReply: (any) => void,
quickReply: any,
}; };
const LENGTH_TO_COLLAPSE = 300; const LENGTH_TO_COLLAPSE = 300;
@ -100,6 +103,9 @@ function Comment(props: Props) {
isModerator, isModerator,
isGlobalMod, isGlobalMod,
isFiat, isFiat,
supportDisabled,
setQuickReply,
quickReply,
} = props; } = props;
const { const {
@ -185,6 +191,7 @@ function Comment(props: Props) {
function handleSubmit() { function handleSubmit() {
updateComment(commentId, editedMessage); updateComment(commentId, editedMessage);
if (setQuickReply) setQuickReply({ ...quickReply, comment_id: commentId, comment: editedMessage });
setEditing(false); setEditing(false);
} }
@ -294,6 +301,7 @@ function Comment(props: Props) {
commentIsMine={commentIsMine} commentIsMine={commentIsMine}
handleEditComment={handleEditComment} handleEditComment={handleEditComment}
supportAmount={supportAmount} supportAmount={supportAmount}
setQuickReply={setQuickReply}
/> />
</Menu> </Menu>
</div> </div>
@ -403,6 +411,7 @@ function Comment(props: Props) {
onCancelReplying={() => { onCancelReplying={() => {
setReplying(false); setReplying(false);
}} }}
supportDisabled={supportDisabled}
/> />
)} )}
</> </>

View file

@ -47,8 +47,10 @@ type Props = {
claimIsMine: boolean, claimIsMine: boolean,
sendTip: ({}, (any) => void, (any) => void) => void, sendTip: ({}, (any) => void, (any) => void) => void,
doToast: ({ message: string }) => void, doToast: ({ message: string }) => void,
supportDisabled: boolean,
doFetchCreatorSettings: (channelId: string) => Promise<any>, doFetchCreatorSettings: (channelId: string) => Promise<any>,
settingsByChannelId: { [channelId: string]: PerChannelSettings }, settingsByChannelId: { [channelId: string]: PerChannelSettings },
setQuickReply: (any) => void,
}; };
export function CommentCreate(props: Props) { export function CommentCreate(props: Props) {
@ -71,6 +73,8 @@ export function CommentCreate(props: Props) {
doToast, doToast,
doFetchCreatorSettings, doFetchCreatorSettings,
settingsByChannelId, settingsByChannelId,
supportDisabled,
setQuickReply,
} = props; } = props;
const buttonRef: ElementRef<any> = React.useRef(); const buttonRef: ElementRef<any> = React.useRef();
const { const {
@ -80,7 +84,7 @@ export function CommentCreate(props: Props) {
const [isSubmitting, setIsSubmitting] = React.useState(false); const [isSubmitting, setIsSubmitting] = React.useState(false);
const [commentFailure, setCommentFailure] = React.useState(false); const [commentFailure, setCommentFailure] = React.useState(false);
const [successTip, setSuccessTip] = React.useState({ txid: undefined, tipAmount: undefined }); 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 [isSupportComment, setIsSupportComment] = React.useState();
const [isReviewingSupportComment, setIsReviewingSupportComment] = React.useState(); const [isReviewingSupportComment, setIsReviewingSupportComment] = React.useState();
const [tipAmount, setTipAmount] = React.useState(1); const [tipAmount, setTipAmount] = React.useState(1);
@ -322,6 +326,7 @@ export function CommentCreate(props: Props) {
createComment(commentValue, claimId, parentId, txid, payment_intent_id, environment) createComment(commentValue, claimId, parentId, txid, payment_intent_id, environment)
.then((res) => { .then((res) => {
setIsSubmitting(false); setIsSubmitting(false);
if (setQuickReply) setQuickReply(res);
if (res && res.signature) { if (res && res.signature) {
setCommentValue(''); setCommentValue('');
@ -522,32 +527,34 @@ export function CommentCreate(props: Props) {
requiresAuth={IS_WEB} requiresAuth={IS_WEB}
/> />
)} )}
{!claimIsMine && ( {!supportDisabled && !claimIsMine && (
<Button <>
disabled={disabled} <Button
button="alt" disabled={disabled}
className="thatButton" button="alt"
icon={ICONS.LBC} className="thatButton"
onClick={() => { icon={ICONS.LBC}
setIsSupportComment(true); onClick={() => {
setActiveTab(TAB_LBC); 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 && ( {isReply && !minTip && (
<Button <Button
button="link" button="link"

View file

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

View file

@ -19,10 +19,21 @@ type Props = {
activeChannelId: ?string, activeChannelId: ?string,
claim: ?ChannelClaim, claim: ?ChannelClaim,
doToast: ({ message: string }) => void, doToast: ({ message: string }) => void,
hideCreatorLike: boolean,
}; };
export default function CommentReactions(props: Props) { 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 { const {
push, push,
location: { pathname }, location: { pathname },
@ -48,7 +59,7 @@ export default function CommentReactions(props: Props) {
} }
return count; return count;
}; };
const shouldHide = !canCreatorReact && hideCreatorLike;
const creatorLiked = getCountForReact(REACTION_TYPES.CREATOR_LIKE) > 0; const creatorLiked = getCountForReact(REACTION_TYPES.CREATOR_LIKE) > 0;
const likeIcon = SIMPLE_SITE const likeIcon = SIMPLE_SITE
? myReacts.includes(REACTION_TYPES.LIKE) ? 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>} label={<span className="comment__reaction-count">{getCountForReact(REACTION_TYPES.DISLIKE)}</span>}
/> />
{ENABLE_CREATOR_REACTIONS && (canCreatorReact || creatorLiked) && ( {!shouldHide && ENABLE_CREATOR_REACTIONS && (canCreatorReact || creatorLiked) && (
<Button <Button
iconOnly
disabled={!canCreatorReact || !claimIsMine} disabled={!canCreatorReact || !claimIsMine}
requiresAuth={IS_WEB} requiresAuth={IS_WEB}
title={claimIsMine ? __('You loved this') : __('Creator loved this')} title={claimIsMine ? __('You loved this') : __('Creator loved this')}

View file

@ -18,6 +18,7 @@ type Props = {
isFetchingByParentId: { [string]: boolean }, isFetchingByParentId: { [string]: boolean },
onShowMore?: () => void, onShowMore?: () => void,
hasMore: boolean, hasMore: boolean,
supportDisabled: boolean,
}; };
function CommentsReplies(props: Props) { function CommentsReplies(props: Props) {
@ -34,6 +35,7 @@ function CommentsReplies(props: Props) {
isFetchingByParentId, isFetchingByParentId,
onShowMore, onShowMore,
hasMore, hasMore,
supportDisabled,
} = props; } = props;
const [isExpanded, setExpanded] = React.useState(true); const [isExpanded, setExpanded] = React.useState(true);
@ -98,6 +100,7 @@ function CommentsReplies(props: Props) {
numDirectReplies={comment.replies} numDirectReplies={comment.replies}
isModerator={comment.is_moderator} isModerator={comment.is_moderator}
isGlobalMod={comment.is_global_mod} 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 LbcMessage from 'component/common/lbc-message';
import UriIndicator from 'component/uriIndicator'; import UriIndicator from 'component/uriIndicator';
import { NavLink } from 'react-router-dom'; import { NavLink } from 'react-router-dom';
import CommentReactions from 'component/commentReactions';
import CommentCreate from 'component/commentCreate';
import CommentsReplies from 'component/commentsReplies';
type Props = { type Props = {
notification: WebNotification, notification: WebNotification,
@ -31,6 +34,8 @@ export default function Notification(props: Props) {
const { notification, menuButton = false, doReadNotifications, doDeleteNotification } = props; const { notification, menuButton = false, doReadNotifications, doDeleteNotification } = props;
const { push } = useHistory(); const { push } = useHistory();
const { notification_rule, notification_parameters, is_read, id } = notification; const { notification_rule, notification_parameters, is_read, id } = notification;
const [isReplying, setReplying] = React.useState(false);
const [quickReply, setQuickReply] = React.useState();
const isCommentNotification = const isCommentNotification =
notification_rule === RULE.COMMENT || notification_rule === RULE.COMMENT ||
@ -119,7 +124,8 @@ export default function Notification(props: Props) {
fullTitle.push(message); fullTitle.push(message);
if (index === titleSplit.length - 1) { 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 const Wrapper = menuButton
? (props: { children: any }) => ( ? (props: { children: any }) => (
<MenuItem className="menu__link--notification" onSelect={handleNotificationClick}> <MenuItem className="menu__link--notification" onSelect={handleNotificationClick}>
@ -160,10 +161,8 @@ export default function Notification(props: Props) {
) )
: notificationLink : notificationLink
? (props: { children: any }) => ( ? (props: { children: any }) => (
<NavLink {...navLinkProps} className="menu__link--notification"> <NavLink {...navLinkProps} className="menu__link--notification" onClick={handleNotificationClick}>
<a className="menu__link--notification" onClick={handleNotificationClick}> {props.children}
{props.children}
</a>
</NavLink> </NavLink>
) )
: (props: { children: any }) => ( : (props: { children: any }) => (
@ -176,12 +175,12 @@ export default function Notification(props: Props) {
); );
return ( return (
<Wrapper> <div
<div className={classnames('notification__wrapper', {
className={classnames('notification__wrapper', { 'notification__wrapper--unread': !is_read,
'notification__wrapper--unread': !is_read, })}
})} >
> <Wrapper>
<div className="notification__icon">{icon}</div> <div className="notification__icon">{icon}</div>
<div className="notification__content-wrapper"> <div className="notification__content-wrapper">
@ -220,7 +219,15 @@ export default function Notification(props: Props) {
</div> </div>
<div className="notification__extra"> <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"> <div className="notification__time">
<DateTime timeAgo date={notification.active_at} /> <DateTime timeAgo date={notification.active_at} />
</div> </div>
@ -249,7 +256,46 @@ export default function Notification(props: Props) {
</MenuList> </MenuList>
</Menu> </Menu>
</div> </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}
parentId={notification_parameters.dynamic.hash}
onDoneReplying={() => setReplying(false)}
onCancelReplying={() => setReplying(false)}
setQuickReply={setQuickReply}
supportDisabled
/>
)}
{quickReply && (
<CommentsReplies
uri={notificationTarget}
parentId={notification_parameters.dynamic.hash}
numDirectReplies={1}
supportDisabled
/>
)}
</div>
)}
</div>
); );
} }

View file

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

View file

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

View file

@ -10,6 +10,7 @@ import usePersistedState from 'effects/use-persisted-state';
import Yrbl from 'component/yrbl'; import Yrbl from 'component/yrbl';
import * as NOTIFICATIONS from 'constants/notifications'; import * as NOTIFICATIONS from 'constants/notifications';
import useFetched from 'effects/use-fetched'; import useFetched from 'effects/use-fetched';
import { RULE } from 'constants/notifications';
type Props = { type Props = {
notifications: Array<Notification>, notifications: Array<Notification>,
@ -21,6 +22,8 @@ type Props = {
doSeeAllNotifications: () => void, doSeeAllNotifications: () => void,
doReadNotifications: () => void, doReadNotifications: () => void,
doNotificationList: (?Array<string>) => void, doNotificationList: (?Array<string>) => void,
activeChannel: ?ChannelClaim,
doCommentReactList: (Array<string>) => Promise<any>,
}; };
export default function NotificationsPage(props: Props) { export default function NotificationsPage(props: Props) {
@ -34,12 +37,41 @@ export default function NotificationsPage(props: Props) {
doReadNotifications, doReadNotifications,
doNotificationList, doNotificationList,
notificationCategories, notificationCategories,
activeChannel,
doCommentReactList,
} = props; } = props;
const initialFetchDone = useFetched(fetching); const initialFetchDone = useFetched(fetching);
const [name, setName] = usePersistedState('notifications--rule', NOTIFICATIONS.NOTIFICATION_NAME_ALL); const [name, setName] = usePersistedState('notifications--rule', NOTIFICATIONS.NOTIFICATION_NAME_ALL);
const isFiltered = name !== NOTIFICATIONS.NOTIFICATION_NAME_ALL; const isFiltered = name !== NOTIFICATIONS.NOTIFICATION_NAME_ALL;
const list = isFiltered ? notificationsFiltered : notifications; 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);
}
});
if (idsForReactionFetch.length !== 0) {
doCommentReactList(idsForReactionFetch);
}
}
}, [initialFetchDone, doCommentReactList, list, activeChannel]);
React.useEffect(() => { React.useEffect(() => {
if (unseenCount > 0 || unreadCount > 0) { if (unseenCount > 0 || unreadCount > 0) {
// If there are unread notifications when entering the page, reset to All. // 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) ? ( {list && list.length > 0 && !(isFiltered && fetching) ? (
<div className="card"> <div className="card">
<div className="notification_list"> <div className="notification_list">
{list.map((notification, index) => { {list.map((notification) => {
return <Notification key={notification.id} notification={notification} />; return <Notification key={notification.id} notification={notification} />;
})} })}
</div> </div>
@ -117,11 +149,9 @@ export default function NotificationsPage(props: Props) {
<Yrbl <Yrbl
title={__('No notifications')} title={__('No notifications')}
subtitle={ subtitle={
<p> isFiltered
{isFiltered ? __('Try selecting another filter.')
? __('Try selecting another filter.') : __("You don't have any notifications yet, but they will be here when you do!")
: __("You don't have any notifications yet, but they will be here when you do!")}
</p>
} }
actions={ actions={
<div className="section__actions"> <div className="section__actions">

View file

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

View file

@ -7,18 +7,24 @@ $contentMaxWidth: 60rem;
} }
.notification_list { .notification_list {
> * { .notification__wrapper {
border-bottom: 1px solid var(--color-border); border-top: 1px solid var(--color-border);
&:last-of-type { &:first-of-type {
border-bottom: none; border-top: none;
} }
} }
.comment__create,
.comment__content {
margin: var(--spacing-m);
}
} }
.notification__icon { .notification__icon {
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
margin-top: var(--spacing-xs);
.icon__wrapper { .icon__wrapper {
width: 1rem; width: 1rem;
@ -43,6 +49,7 @@ $contentMaxWidth: 60rem;
display: flex; display: flex;
padding: var(--spacing-m) 0; padding: var(--spacing-m) 0;
justify-content: space-between; justify-content: space-between;
flex-direction: column;
.channel-thumbnail { .channel-thumbnail {
@include handleChannelGif(3rem); @include handleChannelGif(3rem);
@ -162,6 +169,30 @@ $contentMaxWidth: 60rem;
} }
} }
.notification__reactions {
display: flex;
margin: var(--spacing-m);
margin-bottom: 0;
> *:not(:last-of-type) {
margin-right: var(--spacing-m);
}
.button__label {
margin-left: var(--spacing-xs);
}
.comment__creator-like {
height: 0.8rem;
width: 0.8rem;
margin-left: 3px;
z-index: 3;
position: absolute;
top: 0.4rem;
left: 0.4rem;
}
}
.notification__bubble { .notification__bubble {
height: 1.5rem; height: 1.5rem;
width: 1.5rem; width: 1.5rem;