Skip to content

Commit

Permalink
[web] Warn the user when deleting a chat that has contained chats
Browse files Browse the repository at this point in the history
Summary:
Part of [ENG-4319](https://linear.app/comm/issue/ENG-4319/warn-the-user-when-deleting-a-chat-that-has-contained-chats).

Before deletion, the user is warned about possible contained chats. For subchannel deletion, the modal looks like this:
{F642476}

When deleting a whole community, message changes:
{F642488}

When there are no contained chats, the confirmation modal does not pop up.

Depends on D8526.

Test Plan:
This modal should pop up when deleting:
- Non-empty community
- Subchannel with threads

Modal should not pop up when deleting:
- Empty community
- Empty subchannel
- Thread

Note: right now we don't support deleting subchannels when the parent channel is deleted. What I mean is this situation:
-> Community
--> Subchannel 1
---> Subchannel 2
If we delete Subchannel 1, right now Subchannel 2 is not deleted, thus modal should not pop up.

Reviewers: bartek, tomek, inka, michal, ashoat

Reviewed By: ashoat

Subscribers: ashoat, tomek

Differential Revision: https://phab.comm.dev/D8530
  • Loading branch information
pklatka committed Aug 3, 2023
1 parent e7e6e8a commit 6421b30
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 4 deletions.
17 changes: 17 additions & 0 deletions lib/shared/thread-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -1605,6 +1605,22 @@ function useRoleUserSurfacedPermissions(
}, [threadInfo]);
}

function communityOrThreadNoun(threadInfo: RawThreadInfo | ThreadInfo): string {
return threadTypeIsCommunityRoot(threadInfo.type)
? 'community'
: threadNoun(threadInfo.type, threadInfo.parentThreadID);
}

function getThreadsToDeleteText(
threadInfo: RawThreadInfo | ThreadInfo,
): string {
return `${
threadTypeIsCommunityRoot(threadInfo.type)
? 'Subchannels and threads'
: 'Threads'
} within this ${communityOrThreadNoun(threadInfo)}`;
}

export {
threadHasPermission,
viewerIsMember,
Expand Down Expand Up @@ -1672,4 +1688,5 @@ export {
threadInfoInsideCommunity,
useRoleMemberCountsForCommunity,
useRoleUserSurfacedPermissions,
getThreadsToDeleteText,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.container {
padding: 0 40px 32px;
border-radius: 8px;
color: var(--modal-fg);
}
.text {
font-size: var(--xl-font-20);
padding: 5px 0px 20px;
}
.buttonContainer {
display: flex;
justify-content: flex-end;
gap: 24px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// @flow

import * as React from 'react';

import { useModalContext } from 'lib/components/modal-provider.react.js';
import { getThreadsToDeleteText } from 'lib/shared/thread-utils.js';
import type { ThreadInfo } from 'lib/types/thread-types';

import css from './thread-settings-delete-confirmation-modal.css';
import Button from '../../../components/button.react.js';
import Modal from '../../modal.react.js';

type BaseProps = {
+threadInfo: ThreadInfo,
+onConfirmation: () => mixed,
};

function ThreadDeleteConfirmationModal({
threadInfo,
onConfirmation,
}: BaseProps): React.Node {
const { popModal } = useModalContext();
const threadsToDeleteText = React.useMemo(
() => getThreadsToDeleteText(threadInfo),
[threadInfo],
);

return (
<Modal
size="large"
name="Warning"
icon="warning-circle"
withCloseButton={false}
onClose={popModal}
>
<div className={css.container}>
<p className={css.text}>
{threadsToDeleteText} will also be permanently deleted. Are you sure
you want to continue?
</p>
<div className={css.buttonContainer}>
<Button variant="outline" onClick={popModal}>
No
</Button>
<Button variant="filled" onClick={onConfirmation} type="submit">
Yes
</Button>
</div>
</div>
</Modal>
);
}

export default ThreadDeleteConfirmationModal;
47 changes: 43 additions & 4 deletions web/modals/threads/settings/thread-settings-delete-tab.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from 'lib/actions/thread-actions.js';
import { useModalContext } from 'lib/components/modal-provider.react.js';
import SWMansionIcon from 'lib/components/SWMansionIcon.react.js';
import { containedThreadInfos } from 'lib/selectors/thread-selectors.js';
import { type SetState } from 'lib/types/hook-types.js';
import { type ThreadInfo } from 'lib/types/thread-types.js';
import {
Expand All @@ -16,8 +17,10 @@ import {
} from 'lib/utils/action-utils.js';

import SubmitSection from './submit-section.react.js';
import ThreadDeleteConfirmationModal from './thread-settings-delete-confirmation-modal.react.js';
import css from './thread-settings-delete-tab.css';
import { buttonThemes } from '../../../components/button.react.js';
import { useSelector } from '../../../redux/redux-utils.js';

type ThreadSettingsDeleteTabProps = {
+threadSettingsOperationInProgress: boolean,
Expand All @@ -39,29 +42,65 @@ function ThreadSettingsDeleteTab(
const modalContext = useModalContext();
const dispatchActionPromise = useDispatchActionPromise();
const callDeleteThread = useServerCall(deleteThread);
const containedThreads = useSelector(
state => containedThreadInfos(state)[threadInfo.id],
);
const shouldUseDeleteConfirmationModal = React.useMemo(
() => containedThreads?.length > 0,
[containedThreads?.length],
);

const popThreadDeleteConfirmationModal = React.useCallback(() => {
if (shouldUseDeleteConfirmationModal) {
modalContext.popModal();
}
}, [modalContext, shouldUseDeleteConfirmationModal]);
const deleteThreadAction = React.useCallback(async () => {
try {
setErrorMessage('');
const response = await callDeleteThread(threadInfo.id);
popThreadDeleteConfirmationModal();
modalContext.popModal();
return response;
} catch (e) {
popThreadDeleteConfirmationModal();
setErrorMessage(
e.message === 'invalid_credentials'
? 'permission not granted'
: 'unknown error',
);
throw e;
}
}, [callDeleteThread, modalContext, setErrorMessage, threadInfo.id]);

}, [
callDeleteThread,
modalContext,
popThreadDeleteConfirmationModal,
setErrorMessage,
threadInfo.id,
]);
const dispatchDeleteThreadAction = React.useCallback(() => {
dispatchActionPromise(deleteThreadActionTypes, deleteThreadAction());
}, [dispatchActionPromise, deleteThreadAction]);
const onDelete = React.useCallback(
(event: SyntheticEvent<HTMLElement>) => {
event.preventDefault();
dispatchActionPromise(deleteThreadActionTypes, deleteThreadAction());
if (shouldUseDeleteConfirmationModal) {
modalContext.pushModal(
<ThreadDeleteConfirmationModal
threadInfo={threadInfo}
onConfirmation={dispatchDeleteThreadAction}
/>,
);
} else {
dispatchDeleteThreadAction();
}
},
[deleteThreadAction, dispatchActionPromise],
[
dispatchDeleteThreadAction,
modalContext,
shouldUseDeleteConfirmationModal,
threadInfo,
],
);

return (
Expand Down

0 comments on commit 6421b30

Please sign in to comment.