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))
|
- 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))
|
||||||
|
|
|
@ -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}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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')}
|
||||||
|
|
|
@ -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}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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} />
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue