Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 82 additions & 51 deletions src/components/postComments/container/postComments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -171,59 +171,64 @@ const PostComments = forwardRef(
[navigation],
);

const _handleDeleteComment = useCallback(
// Mutation only, no confirmation. The options sheet confirms before invoking
// its onDelete, so a handler that prompts again would ask twice for one
// action, and let the user cancel the second after confirming the first.
const _deleteCommentConfirmed = useCallback(
async (_permlink, _parentPermlink?, _parentAuthor?, _rootAuthor?, _rootPermlink?) => {
const _onConfirmDelete = async () => {
const deletedKey = `${currentAccountName}/${_permlink}`;
const extractErrorDetail = (error: any) => {
const detail =
error?.message ||
error?.response?.message ||
error?.response?.data?.message ||
error?.data?.message ||
error?.error_description ||
error?.jse_shortmsg;
return typeof detail === 'string' ? detail : JSON.stringify(error);
};

setHiddenCommentKeys((prev) => {
const next = new Set(prev);
next.add(deletedKey);
return next;
});
const deletedKey = `${currentAccountName}/${_permlink}`;
const extractErrorDetail = (error: any) => {
const detail =
error?.message ||
error?.response?.message ||
error?.response?.data?.message ||
error?.data?.message ||
error?.error_description ||
error?.jse_shortmsg;
return typeof detail === 'string' ? detail : JSON.stringify(error);
};

setHiddenCommentKeys((prev) => {
const next = new Set(prev);
next.add(deletedKey);
return next;
});

try {
await deleteComment({
author: currentAccountName,
permlink: _permlink,
parentAuthor: _parentAuthor,
parentPermlink: _parentPermlink || permlink,
rootAuthor: _rootAuthor || author,
rootPermlink: _rootPermlink || permlink,
try {
await deleteComment({
author: currentAccountName,
permlink: _permlink,
parentAuthor: _parentAuthor,
parentPermlink: _parentPermlink || permlink,
rootAuthor: _rootAuthor || author,
rootPermlink: _rootPermlink || permlink,
});
console.log('deleted comment', `${currentAccountName}/${_permlink}`);
} catch (err) {
const stillExists = !!discussionQuery.data?.[deletedKey];
if (stillExists) {
setHiddenCommentKeys((prev) => {
const next = new Set(prev);
next.delete(deletedKey);
return next;
});
console.log('deleted comment', `${currentAccountName}/${_permlink}`);
} catch (err) {
const stillExists = !!discussionQuery.data?.[deletedKey];
if (stillExists) {
setHiddenCommentKeys((prev) => {
const next = new Set(prev);
next.delete(deletedKey);
return next;
});
}
const errorDetail = extractErrorDetail(err);
if (stillExists) {
dispatch(toastNotification(`Failed to delete comment: ${errorDetail}`));
} else {
console.log(
'delete returned error but comment is already absent in cache',
deletedKey,
);
}
console.warn('Failed to delete comment', err);
}
};
const errorDetail = extractErrorDetail(err);
if (stillExists) {
dispatch(toastNotification(`Failed to delete comment: ${errorDetail}`));
} else {
console.log('delete returned error but comment is already absent in cache', deletedKey);
}
console.warn('Failed to delete comment', err);
}
},
[author, currentAccountName, deleteComment, dispatch, discussionQuery.data, intl, permlink],
);

// Confirms, then mutates. Used by the inline delete button, which has no
// confirmation of its own.
const _handleDeleteComment = useCallback(
async (_permlink, _parentPermlink?, _parentAuthor?, _rootAuthor?, _rootPermlink?) => {
const action = await SheetManager.show(SheetNames.ACTION_MODAL, {
payload: {
title: intl.formatMessage({ id: 'delete.confirm_delete_title' }),
Expand All @@ -241,10 +246,16 @@ const PostComments = forwardRef(
});

if (action === 'confirm') {
_onConfirmDelete();
_deleteCommentConfirmed(
_permlink,
_parentPermlink,
_parentAuthor,
_rootAuthor,
_rootPermlink,
);
}
},
[author, currentAccountName, deleteComment, dispatch, discussionQuery.data, intl, permlink],
[_deleteCommentConfirmed, intl],
);

const _openReplyThread = useCallback(
Expand All @@ -270,6 +281,22 @@ const PostComments = forwardRef(
});
}, []);

// The sheet is opened for comments here, so its own delete path would call
// navigation.goBack() and leave the post the user is reading, and would skip
// the error handling and deleted-key cache work this screen already owns.
// Same arguments the inline delete button passes.
const _handleDeleteFromMenu = useCallback(
(comment) =>
_deleteCommentConfirmed(
comment.permlink,
Comment on lines +289 to +291

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid showing a second delete confirmation

When deleting a comment from this options menu, PostOptionsModal._deletePost has already shown and received confirmation from its action modal before invoking onDelete. Delegating here to _handleDeleteComment opens the same confirmation flow again, so users must confirm deletion twice (and can cancel the second prompt after confirming the first); delegate to a mutation-only handler or otherwise let only one layer own confirmation.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, fixed in f12935f. PostOptionsModal._deletePost confirms via ACTION_MODAL before invoking onDelete, and _handleDeleteComment then confirmed again, so one delete needed two confirmations and could be cancelled at the second after being confirmed at the first.

Split the handler in two rather than suppressing either prompt:

  • _deleteCommentConfirmed mutates with no prompt, and is what the sheet delegates to
  • _handleDeleteComment prompts and then calls it, for the inline delete button which has no confirmation of its own

That keeps exactly one confirmation on each path and leaves the inline button's behaviour unchanged.

Checked the equivalent path added in #3405: commentsContainer has no confirmation step at all, so commentsView delegating to it was already single-prompt and is unaffected.

comment.parent_permlink,
comment.parent_author,
comment.root_author,
comment.root_permlink,
),
[_deleteCommentConfirmed],
);

const _handleShowOptionsMenu = useCallback((comment) => {
if (postOptionsModalRef.current) {
postOptionsModalRef.current.show(comment);
Expand Down Expand Up @@ -454,7 +481,11 @@ const PostComments = forwardRef(
overScrollMode="never"
/>
<PostHtmlInteractionHandler ref={postInteractionRef} />
<PostOptionsModal ref={postOptionsModalRef} isVisibleTranslateModal={true} />
<PostOptionsModal
ref={postOptionsModalRef}
isVisibleTranslateModal={true}
onDelete={_handleDeleteFromMenu}
/>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
</Fragment>
);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,14 @@ const PostOptionsModal = (
parentAuthor: content.parent_author || '',
parentPermlink: content.parent_permlink || '',
});
// Always pops, because only the caller knows whether the deleted content
// *is* the screen. postScreen renders comments and waves as primary
// content too, so `parent_author` cannot stand in for that: gating on it
// left a comment's own detail screen showing deleted content.
//
// Consumers that own a surrounding list must therefore pass `onDelete`
// and handle removal themselves. See #3407 for inverting this into an
// explicit opt-in, which fails safe in the other direction.
navigation.goBack();
dispatch(
toastNotification(
Expand Down
Loading