diff --git a/native/profile/delete-account.react.js b/native/profile/delete-account.react.js index f8f348f12..ce6bb16cb 100644 --- a/native/profile/delete-account.react.js +++ b/native/profile/delete-account.react.js @@ -1,278 +1,167 @@ // @flow -import invariant from 'invariant'; import * as React from 'react'; -import { - Text, - View, - TextInput as BaseTextInput, - ActivityIndicator, -} from 'react-native'; +import { Text, View, ActivityIndicator } from 'react-native'; import { ScrollView } from 'react-native-gesture-handler'; import { deleteAccountActionTypes, deleteAccount, } from 'lib/actions/user-actions.js'; import { preRequestUserStateSelector } from 'lib/selectors/account-selectors.js'; import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js'; -import { accountHasPassword } from 'lib/shared/account-utils.js'; import type { LogOutResult } from 'lib/types/account-types.js'; import type { LoadingStatus } from 'lib/types/loading-types.js'; import type { PreRequestUserState } from 'lib/types/session-types.js'; import type { DispatchActionPromise } from 'lib/utils/action-utils.js'; import { useServerCall, useDispatchActionPromise, } from 'lib/utils/action-utils.js'; import { deleteNativeCredentialsFor } from '../account/native-credentials.js'; import Button from '../components/button.react.js'; -import TextInput from '../components/text-input.react.js'; import { useSelector } from '../redux/redux-utils.js'; -import { type Colors, useColors, useStyles } from '../themes/colors.js'; -import type { GlobalTheme } from '../types/themes.js'; +import { useStyles } from '../themes/colors.js'; import Alert from '../utils/alert.js'; type Props = { // Redux state - +isAccountWithPassword: boolean, +loadingStatus: LoadingStatus, +preRequestUserState: PreRequestUserState, - +activeTheme: ?GlobalTheme, - +colors: Colors, +styles: typeof unboundStyles, // Redux dispatch functions +dispatchActionPromise: DispatchActionPromise, // async functions that hit server APIs +deleteAccount: ( preRequestUserState: PreRequestUserState, ) => Promise, }; -type State = { - +password: ?string, -}; -class DeleteAccount extends React.PureComponent { - state: State = { - password: null, - }; - mounted = false; - passwordInput: ?React.ElementRef; - - componentDidMount() { - this.mounted = true; - } - - componentWillUnmount() { - this.mounted = false; - } - +class DeleteAccount extends React.PureComponent { render() { const buttonContent = this.props.loadingStatus === 'loading' ? ( ) : ( Delete account ); - const { panelForegroundTertiaryLabel } = this.props.colors; - let inputPasswordPrompt; - if (this.props.isAccountWithPassword) { - inputPasswordPrompt = ( - <> - PASSWORD - - - - - ); - } return ( Your account will be permanently deleted. There is no way to reverse this. - {inputPasswordPrompt} ); } - onChangePasswordText = (newPassword: string) => { - this.setState({ password: newPassword }); - }; - - passwordInputRef = ( - passwordInput: ?React.ElementRef, - ) => { - this.passwordInput = passwordInput; - }; - - focusPasswordInput = () => { - invariant(this.passwordInput, 'passwordInput should be set'); - this.passwordInput.focus(); - }; - submitDeletion = () => { this.props.dispatchActionPromise( deleteAccountActionTypes, this.deleteAccount(), ); }; async deleteAccount() { try { await deleteNativeCredentialsFor(); const result = await this.props.deleteAccount( this.props.preRequestUserState, ); return result; } catch (e) { if (e.message === 'invalid_credentials') { Alert.alert( 'Incorrect password', 'The password you entered is incorrect', - [{ text: 'OK', onPress: this.onErrorAlertAcknowledged }], + [{ text: 'OK' }], { cancelable: false }, ); } else { - Alert.alert( - 'Unknown error', - 'Uhh... try again?', - [{ text: 'OK', onPress: this.onErrorAlertAcknowledged }], - { cancelable: false }, - ); + Alert.alert('Unknown error', 'Uhh... try again?', [{ text: 'OK' }], { + cancelable: false, + }); } throw e; } } - - onErrorAlertAcknowledged = () => { - this.setState({ password: '' }, this.focusPasswordInput); - }; } const unboundStyles = { deleteButton: { backgroundColor: 'vibrantRedButton', borderRadius: 5, flex: 1, marginHorizontal: 24, marginVertical: 12, padding: 12, }, - header: { - color: 'panelBackgroundLabel', - fontSize: 12, - fontWeight: '400', - paddingBottom: 3, - paddingHorizontal: 24, - }, - input: { - color: 'panelForegroundLabel', - flex: 1, - fontFamily: 'Arial', - fontSize: 16, - paddingVertical: 0, - borderBottomColor: 'transparent', - }, lastWarningText: { marginBottom: 24, }, saveText: { color: 'white', fontSize: 18, textAlign: 'center', }, scrollView: { backgroundColor: 'panelBackground', }, scrollViewContentContainer: { paddingTop: 24, }, - section: { - backgroundColor: 'panelForeground', - borderBottomWidth: 1, - borderColor: 'panelForegroundBorder', - borderTopWidth: 1, - flexDirection: 'row', - justifyContent: 'space-between', - marginBottom: 24, - paddingHorizontal: 24, - paddingVertical: 12, - }, warningText: { color: 'panelForegroundLabel', fontSize: 16, marginHorizontal: 24, textAlign: 'center', }, }; const loadingStatusSelector = createLoadingStatusSelector( deleteAccountActionTypes, ); const ConnectedDeleteAccount: React.ComponentType<{ ... }> = React.memo<{ ... }>(function ConnectedDeleteAccount() { - const isAccountWithPassword = useSelector(state => - accountHasPassword(state.currentUserInfo), - ); const loadingStatus = useSelector(loadingStatusSelector); const preRequestUserState = useSelector(preRequestUserStateSelector); - const activeTheme = useSelector(state => state.globalThemeInfo.activeTheme); - const colors = useColors(); const styles = useStyles(unboundStyles); const dispatchActionPromise = useDispatchActionPromise(); const callDeleteAccount = useServerCall(deleteAccount); return ( ); }); export default ConnectedDeleteAccount; diff --git a/web/settings/account-delete-modal.css b/web/settings/account-delete-modal.css index 56625671b..306fe03eb 100644 --- a/web/settings/account-delete-modal.css +++ b/web/settings/account-delete-modal.css @@ -1,33 +1,26 @@ .modal_body { padding: 24px 32px; color: var(--fg); } .modal_body p { font-size: var(--s-font-14); } .deletion_warning { font-weight: var(--bold); margin-bottom: 16px; } -.confirm_password { - margin-bottom: 10px; -} -.form_title { - font-weight: var(--bold); - margin-bottom: 8px; -} .form_footer { display: flex; flex-direction: row-reverse; justify-content: space-between; padding-top: 24px; } .form_error { font-size: var(--xs-font-12); color: var(--error); font-style: italic; padding-left: 6px; align-self: center; } diff --git a/web/settings/account-delete-modal.react.js b/web/settings/account-delete-modal.react.js index d91269d05..ea944fa0b 100644 --- a/web/settings/account-delete-modal.react.js +++ b/web/settings/account-delete-modal.react.js @@ -1,211 +1,129 @@ // @flow -import invariant from 'invariant'; import * as React from 'react'; import { deleteAccount, deleteAccountActionTypes, } from 'lib/actions/user-actions.js'; import { useModalContext } from 'lib/components/modal-provider.react.js'; import SWMansionIcon from 'lib/components/SWMansionIcon.react.js'; import { preRequestUserStateSelector } from 'lib/selectors/account-selectors.js'; import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js'; -import { accountHasPassword } from 'lib/shared/account-utils.js'; import type { LogOutResult } from 'lib/types/account-types.js'; import type { PreRequestUserState } from 'lib/types/session-types.js'; import type { DispatchActionPromise } from 'lib/utils/action-utils.js'; import { useDispatchActionPromise, useServerCall, } from 'lib/utils/action-utils.js'; import css from './account-delete-modal.css'; import Button, { buttonThemes } from '../components/button.react.js'; -import Input from '../modals/input.react.js'; import Modal from '../modals/modal.react.js'; import { useSelector } from '../redux/redux-utils.js'; type Props = { - +isAccountWithPassword: boolean, +preRequestUserState: PreRequestUserState, +inputDisabled: boolean, +dispatchActionPromise: DispatchActionPromise, +deleteAccount: ( preRequestUserState: PreRequestUserState, ) => Promise, +popModal: () => void, }; type State = { - +currentPassword: ?string, +errorMessage: string, }; class AccountDeleteModal extends React.PureComponent { - currentPasswordInput: ?HTMLInputElement; - - constructor(props: Props) { - super(props); - this.state = { - currentPassword: props.isAccountWithPassword ? '' : null, - errorMessage: '', - }; - } - - componentDidMount() { - invariant( - !this.props.isAccountWithPassword || this.currentPasswordInput, - 'newPasswordInput ref unset', - ); - this.currentPasswordInput?.focus(); - } + state = { + errorMessage: '', + }; render() { - const { inputDisabled } = this.props; - let errorMsg; if (this.state.errorMessage) { errorMsg = (
{this.state.errorMessage}
); } - let passwordConfirmation; - if (this.props.isAccountWithPassword) { - invariant( - this.state.currentPassword !== null && - this.state.currentPassword !== undefined, - 'currentPassword must be set if isAccountWithPassword', - ); - passwordConfirmation = ( - <> -

- Please enter your account password to confirm your identity. -

-

Account password

- - - ); - } - + const { inputDisabled } = this.props; return (

Your account will be permanently deleted. There is no way to reverse this.

- - {passwordConfirmation} -
{errorMsg}
); } - currentPasswordInputRef = (currentPasswordInput: ?HTMLInputElement) => { - this.currentPasswordInput = currentPasswordInput; - }; - - onChangeCurrentPassword = (event: SyntheticEvent) => { - const target = event.target; - invariant(target instanceof HTMLInputElement, 'target not input'); - this.setState({ currentPassword: target.value }); - }; - onDelete = (event: SyntheticEvent) => { event.preventDefault(); this.props.dispatchActionPromise( deleteAccountActionTypes, this.deleteAction(), ); }; async deleteAction() { try { const response = await this.props.deleteAccount( this.props.preRequestUserState, ); this.props.popModal(); return response; } catch (e) { - const errorMessage = - e.message === 'invalid_credentials' - ? 'wrong password' - : 'unknown error'; - this.setState( - { - currentPassword: this.props.isAccountWithPassword ? '' : null, - errorMessage: errorMessage, - }, - () => { - invariant( - !this.props.isAccountWithPassword || this.currentPasswordInput, - 'currentPasswordInput ref unset', - ); - this.currentPasswordInput?.focus(); - }, - ); + this.setState({ errorMessage: 'unknown error' }); throw e; } } } const deleteAccountLoadingStatusSelector = createLoadingStatusSelector( deleteAccountActionTypes, ); const ConnectedAccountDeleteModal: React.ComponentType<{}> = React.memo<{}>( function ConnectedAccountDeleteModal(): React.Node { - const isAccountWithPassword = useSelector(state => - accountHasPassword(state.currentUserInfo), - ); const preRequestUserState = useSelector(preRequestUserStateSelector); const inputDisabled = useSelector( state => deleteAccountLoadingStatusSelector(state) === 'loading', ); const callDeleteAccount = useServerCall(deleteAccount); const dispatchActionPromise = useDispatchActionPromise(); const modalContext = useModalContext(); return ( ); }, ); export default ConnectedAccountDeleteModal;