Add direct reacting from notifications
This commit is contained in:
parent
6637c7b98b
commit
d7344f5047
12 changed files with 226 additions and 63 deletions
|
@ -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))
|
||||
|
||||
### Changed
|
||||
- Use Canonical Url for copy link ([#6500](https://github.com/lbryio/lbry-desktop/pull/6500))
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
|
|
@ -47,8 +47,10 @@ 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,
|
||||
};
|
||||
|
||||
export function CommentCreate(props: Props) {
|
||||
|
@ -71,6 +73,8 @@ export function CommentCreate(props: Props) {
|
|||
doToast,
|
||||
doFetchCreatorSettings,
|
||||
settingsByChannelId,
|
||||
supportDisabled,
|
||||
setQuickReply,
|
||||
} = props;
|
||||
const buttonRef: ElementRef<any> = React.useRef();
|
||||
const {
|
||||
|
@ -80,7 +84,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);
|
||||
|
@ -322,6 +326,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,7 +527,8 @@ export function CommentCreate(props: Props) {
|
|||
requiresAuth={IS_WEB}
|
||||
/>
|
||||
)}
|
||||
{!claimIsMine && (
|
||||
{!supportDisabled && !claimIsMine && (
|
||||
<>
|
||||
<Button
|
||||
disabled={disabled}
|
||||
button="alt"
|
||||
|
@ -533,9 +539,8 @@ export function CommentCreate(props: Props) {
|
|||
setActiveTab(TAB_LBC);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{/* @if TARGET='web' */}
|
||||
{!claimIsMine && stripeEnvironment && (
|
||||
{stripeEnvironment && (
|
||||
<Button
|
||||
disabled={disabled}
|
||||
button="alt"
|
||||
|
@ -548,6 +553,8 @@ export function CommentCreate(props: Props) {
|
|||
/>
|
||||
)}
|
||||
{/* @endif */}
|
||||
</>
|
||||
)}
|
||||
{isReply && !minTip && (
|
||||
<Button
|
||||
button="link"
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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')}
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
|
|
@ -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} className="menu__link--notification">
|
||||
<a className="menu__link--notification" onClick={handleNotificationClick}>
|
||||
<NavLink {...navLinkProps} className="menu__link--notification" onClick={handleNotificationClick}>
|
||||
{props.children}
|
||||
</a>
|
||||
</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,
|
||||
})}
|
||||
>
|
||||
<Wrapper>
|
||||
<div className="notification__icon">{icon}</div>
|
||||
|
||||
<div className="notification__content-wrapper">
|
||||
|
@ -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,46 @@ export default function Notification(props: Props) {
|
|||
</MenuList>
|
||||
</Menu>
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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} />
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
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
|
||||
isFiltered
|
||||
? __('Try selecting another filter.')
|
||||
: __("You don't have any notifications yet, but they will be here when you do!")}
|
||||
</p>
|
||||
: __("You don't have any notifications yet, but they will be here when you do!")
|
||||
}
|
||||
actions={
|
||||
<div className="section__actions">
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -7,18 +7,24 @@ $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);
|
||||
}
|
||||
}
|
||||
|
||||
.notification__icon {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-top: var(--spacing-xs);
|
||||
|
||||
.icon__wrapper {
|
||||
width: 1rem;
|
||||
|
@ -43,6 +49,7 @@ $contentMaxWidth: 60rem;
|
|||
display: flex;
|
||||
padding: var(--spacing-m) 0;
|
||||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
|
||||
.channel-thumbnail {
|
||||
@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 {
|
||||
height: 1.5rem;
|
||||
width: 1.5rem;
|
||||
|
|
Loading…
Reference in a new issue