Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
c0eb6d8
feat(periodic-background-updates): Initial setup for background fetch
mananjadhav Sep 13, 2021
b17d2ca
fix(background-fetch): WIP Adding headless task
mananjadhav Oct 19, 2021
eacf864
add close account option to security settings menu
Julesssss Dec 8, 2021
5111bf4
create basic CloseAccount page
Julesssss Dec 8, 2021
11e9f17
add all Close Account strings
Julesssss Dec 9, 2021
f433976
apply lint fixes
Julesssss Dec 9, 2021
7c21dc1
create basic close account page components
Julesssss Dec 9, 2021
87de73d
convert page to a class component
Julesssss Dec 9, 2021
3f91c4b
implement component logic and state
Julesssss Dec 9, 2021
e310f11
Merge branch 'main' into jules-closeAccount2
Julesssss Dec 10, 2021
5ea5f9e
implement User_Delete API, call when user presses button
Julesssss Dec 10, 2021
57ea2f3
redirect to sign in on successful close account request
Julesssss Dec 10, 2021
b7cb788
add Growl for unsuccessful close account request
Julesssss Dec 10, 2021
02ea413
Merge branch 'main' into jules-closeAccount2
Julesssss Dec 21, 2021
cdb5599
update ExpensifyButton component name
Julesssss Dec 21, 2021
b2af428
fix 'reason for leaving' param
Julesssss Dec 21, 2021
def8d9a
make close account button red ad use correct label
Julesssss Dec 21, 2021
e8bb4fc
Merge branch 'main' into jules-closeAccount3
Julesssss Jan 5, 2022
b81b462
update Expensi component names
Julesssss Jan 5, 2022
84bf827
add closed account icon
Julesssss Jan 5, 2022
8930d08
create Popover component
Julesssss Jan 5, 2022
e2fce53
center Popover component for web
Julesssss Jan 5, 2022
280ccbc
Merge branch 'main' into jules-closeAccount2
Julesssss Jan 6, 2022
25884d9
replace Popover component with ConfirmModal to simplify the feature
Julesssss Jan 6, 2022
29b2ca7
use correct strings in the hint modal
Julesssss Jan 6, 2022
1b0c4f5
update spanish split string
Julesssss Jan 6, 2022
77ea0e2
fix invalid translation key
Julesssss Jan 7, 2022
721152a
use Onyx to hold state of close account modal
Julesssss Jan 7, 2022
f08e9fb
remove unused state field
Julesssss Jan 7, 2022
ca99ddb
simplify the close account page and callbacks
Julesssss Jan 7, 2022
5789a0a
add success prop to ConfirmModal, to allow neutral coloured buttons
Julesssss Jan 7, 2022
91c5e52
emphasis part of the close account warning message
Julesssss Jan 7, 2022
43d022b
make the close account feedback form multiline to match design
Julesssss Jan 7, 2022
9e3aff1
check against actual user email, reorder Onyx props
Julesssss Jan 7, 2022
91a74ee
add missing comma
Julesssss Jan 7, 2022
5bc4f98
clear default values used for testing, update help link URL
Julesssss Jan 7, 2022
ebbb912
Merge branch 'main' into jules-closeAccount2
Julesssss Jan 10, 2022
36fa944
recognise SMS accounts correctly, to allow close account option
Julesssss Jan 10, 2022
ea0e1cc
give users the email/phone number which must be typed to close the ac…
Julesssss Jan 10, 2022
8fea58b
improve string formatting, use string template and unique IDs
Julesssss Jan 10, 2022
c39c62f
ignore case when comparing users primary login
Julesssss Jan 10, 2022
4295d23
add translation for close account input label
Julesssss Jan 10, 2022
ef94e7b
fix singlequote lint error
Julesssss Jan 10, 2022
15e8b2d
update input label from 'Type ...' to 'Enter' to align with usages
Julesssss Jan 10, 2022
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
10 changes: 10 additions & 0 deletions assets/images/closed-sign.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/ONYXKEYS.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@ export default {
// Is Keyboard shortcuts modal open?
IS_SHORTCUTS_MODAL_OPEN: 'isShortcutsModalOpen',

// Is close acount modal open?
IS_CLOSE_ACCOUNT_MODAL_OPEN: 'isCloseAccountModalOpen',

// Stores information about active wallet transfer amount, selectedAccountID, status, etc
WALLET_TRANSFER: 'walletTransfer',

Expand Down
1 change: 1 addition & 0 deletions src/ROUTES.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export default {
SETTINGS_PROFILE: 'settings/profile',
SETTINGS_PREFERENCES: 'settings/preferences',
SETTINGS_SECURITY: 'settings/security',
SETTINGS_CLOSE: 'settings/security/closeAccount',
SETTINGS_PASSWORD: 'settings/security/password',
SETTINGS_ABOUT: 'settings/about',
SETTINGS_APP_DOWNLOAD_LINKS: 'settings/about/app-download-links',
Expand Down
6 changes: 5 additions & 1 deletion src/components/ConfirmModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ const propTypes = {
/** Modal content text/element */
prompt: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),

/** Whether we should use the success button color */
success: PropTypes.bool,

/** Is the action destructive */
danger: PropTypes.bool,

Expand All @@ -49,6 +52,7 @@ const defaultProps = {
confirmText: '',
cancelText: '',
prompt: '',
success: true,
danger: false,
onCancel: () => {},
shouldShowCancelButton: true,
Expand Down Expand Up @@ -76,7 +80,7 @@ const ConfirmModal = props => (
) : (props.prompt)}

<Button
success
success={props.success}
danger={props.danger}
style={[styles.mt4]}
onPress={props.onConfirm}
Expand Down
2 changes: 2 additions & 0 deletions src/components/Icon/Expensicons.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import Checkmark from '../../../assets/images/checkmark.svg';
import CircleHourglass from '../../../assets/images/circle-hourglass.svg';
import Clipboard from '../../../assets/images/clipboard.svg';
import Close from '../../../assets/images/close.svg';
import ClosedSign from '../../../assets/images/closed-sign.svg';
import Concierge from '../../../assets/images/concierge.svg';
import CreditCard from '../../../assets/images/creditcard.svg';
import DownArrow from '../../../assets/images/down.svg';
Expand Down Expand Up @@ -86,6 +87,7 @@ export {
CircleHourglass,
Clipboard,
Close,
ClosedSign,
Concierge,
CreditCard,
DownArrow,
Expand Down
12 changes: 12 additions & 0 deletions src/languages/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,18 @@ export default {
phrase4: 'privacy policy',
},
},
closeAccountPage: {
closeAccount: 'Close account',
reasonForLeavingPrompt: 'We’d hate to see you go! Would you kindly tell us why, so we can improve?',
enterMessageHere: 'Enter message here',
closeAccountWarning: 'Closing your account cannot be undone.',
closeAccountPermanentlyDeleteData: 'This will permanently delete all of your unsubmitted expense data. Type your phone number or email address to confirm.',
closeAccountSuccess: 'Account closed successfully',
closeAccountActionRequired: 'Looks like you need to complete some actions before closing your account. Check out the guide',
closeAccountTryAgainAfter: 'and try again after.',
typeToConfirm: ({emailOrPhone}) => `Enter ${emailOrPhone} to confirm`,
okayGotIt: 'Okay, Got it',
},
passwordPage: {
changePassword: 'Change password',
changingYourPasswordPrompt: 'Changing your password will update your password for both your Expensify.com and New Expensify accounts.',
Expand Down
12 changes: 12 additions & 0 deletions src/languages/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,18 @@ export default {
phrase4: 'política de privacidad',
},
},
closeAccountPage: {
closeAccount: 'Cerrar cuenta',
reasonForLeavingPrompt: '¡Lamentamos verte partir! ¿Serías tan amable de decirnos por qué, para que podamos mejorar?',
enterMessageHere: 'Ingresa el mensaje aquí',
closeAccountWarning: 'Una vez cerrada tu cuenta no se puede revertir.',
closeAccountPermanentlyDeleteData: 'Esta acción eliminará permanentemente toda la información de tus gastos no enviados. Escribe tu número de teléfono o correo electrónico para confirmar',
closeAccountSuccess: 'Cuenta cerrada exitosamente',
closeAccountActionRequired: 'Parece que necesitas completar algunas acciones antes de cerrar tu cuenta. Mira la guía',
closeAccountTryAgainAfter: 'e intenta nuevamente',
typeToConfirm: ({emailOrPhone}) => `Ingresa ${emailOrPhone} para confirmar`,
okayGotIt: 'Ok, entendido',
},
passwordPage: {
changePassword: 'Cambiar contraseña',
changingYourPasswordPrompt: 'El cambio de contraseña va a afectar tanto a la cuenta de Expensify.com como la de Nuevo Expensify.',
Expand Down
11 changes: 11 additions & 0 deletions src/libs/API.js
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,16 @@ function UpdateAccount(parameters) {
return Network.post(commandName, parameters);
}

/**
* @param {Object} parameters
* @param {String} parameters.message
* @returns {Promise}
*/
function User_Delete(parameters) {
const commandName = 'User_Delete';
return Network.post(commandName, parameters);
}

/**
* @returns {Promise}
*/
Expand Down Expand Up @@ -1185,6 +1195,7 @@ export {
UpdateAccount,
UpdatePolicy,
User_SignUp,
User_Delete,
User_GetBetas,
User_IsFromPublicDomain,
User_IsUsingExpensifyCard,
Expand Down
5 changes: 5 additions & 0 deletions src/libs/Navigation/AppNavigator/ModalStackNavigators.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import SettingsAboutPage from '../../../pages/settings/AboutPage';
import SettingsAppDownloadLinks from '../../../pages/settings/AppDownloadLinks';
import SettingsPasswordPage from '../../../pages/settings/PasswordPage';
import SettingsSecurityPage from '../../../pages/settings/Security/SecuritySettingsPage';
import SettingsCloseAccountPage from '../../../pages/settings/Security/CloseAccountPage';
import SettingsPaymentsPage from '../../../pages/settings/Payments/PaymentsPage';
import SettingsAddPayPalMePage from '../../../pages/settings/Payments/AddPayPalMePage';
import SettingsAddDebitCardPage from '../../../pages/settings/Payments/AddDebitCardPage';
Expand Down Expand Up @@ -166,6 +167,10 @@ const SettingsModalStackNavigator = createModalStackNavigator([
Component: SettingsPasswordPage,
name: 'Settings_Password',
},
{
Component: SettingsCloseAccountPage,
name: 'Settings_Close',
},
{
Component: SettingsSecurityPage,
name: 'Settings_Security',
Expand Down
4 changes: 4 additions & 0 deletions src/libs/Navigation/linkingConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ export default {
path: ROUTES.SETTINGS_PREFERENCES,
exact: true,
},
Settings_Close: {
path: ROUTES.SETTINGS_CLOSE,
exact: true,
},
Settings_Password: {
path: ROUTES.SETTINGS_PASSWORD,
exact: true,
Expand Down
33 changes: 33 additions & 0 deletions src/libs/actions/CloseAccount.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import Onyx from 'react-native-onyx';
import ONYXKEYS from '../../ONYXKEYS';

let isCloseAccountModalOpen;
Onyx.connect({
key: ONYXKEYS.IS_CLOSE_ACCOUNT_MODAL_OPEN,
callback: flag => isCloseAccountModalOpen = flag,
});

/**
* Set CloseAccount flag to show modal
*/
function showCloseAccountModal() {
if (isCloseAccountModalOpen) {
return;
}
Onyx.set(ONYXKEYS.IS_CLOSE_ACCOUNT_MODAL_OPEN, true);
}

/**
* Unset CloseAccount flag to hide modal
*/
function hideCloseAccountModal() {
if (!isCloseAccountModalOpen) {
return;
}
Onyx.set(ONYXKEYS.IS_CLOSE_ACCOUNT_MODAL_OPEN, false);
}

export {
showCloseAccountModal,
hideCloseAccountModal,
};
25 changes: 25 additions & 0 deletions src/libs/actions/User.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ import ROUTES from '../../ROUTES';
import * as Pusher from '../Pusher/pusher';
import Log from '../Log';
import NetworkConnection from '../NetworkConnection';
import redirectToSignIn from './SignInRedirect';
import NameValuePair from './NameValuePair';
import Growl from '../Growl';
import * as Localize from '../Localize';
import getSkinToneEmojiFromIndex from '../../pages/home/report/EmojiPickerMenu/getSkinToneEmojiFromIndex';
import * as CloseAccountActions from './CloseAccount';
import * as Link from './Link';

let sessionAuthToken = '';
Expand Down Expand Up @@ -59,6 +63,26 @@ function changePasswordAndNavigate(oldPassword, password) {
});
}

/**
* Attempt to close the user's account
*
* @param {String} message optional reason for closing account
*/
function closeAccount(message) {
API.User_Delete({message}).then((response) => {
console.debug('User_Delete: ', JSON.stringify(response));

if (response.jsonCode === 200) {
Growl.show(Localize.translateLocal('closeAccountPage.closeAccountSuccess'), CONST.GROWL.SUCCESS);
redirectToSignIn();
return;
}

// Inform user that they are currently unable to close their account
CloseAccountActions.showCloseAccountModal();
});
}

function getBetas() {
API.User_GetBetas().then((response) => {
if (response.jsonCode !== 200) {
Expand Down Expand Up @@ -369,6 +393,7 @@ function joinScreenShare(accessToken, roomName) {

export {
changePasswordAndNavigate,
closeAccount,
getBetas,
getUserDetails,
resendValidateCode,
Expand Down
149 changes: 149 additions & 0 deletions src/pages/settings/Security/CloseAccountPage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import React, {Component} from 'react';
import {Linking, ScrollView} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import PropTypes from 'prop-types';
import Str from 'expensify-common/lib/str';
import HeaderWithCloseButton from '../../../components/HeaderWithCloseButton';
import Navigation from '../../../libs/Navigation/Navigation';
import ROUTES from '../../../ROUTES';
import * as User from '../../../libs/actions/User';
import compose from '../../../libs/compose';
import styles from '../../../styles/styles';
import ScreenWrapper from '../../../components/ScreenWrapper';
import TextInput from '../../../components/TextInput';
import Button from '../../../components/Button';
import Text from '../../../components/Text';
import FixedFooter from '../../../components/FixedFooter';
import ConfirmModal from '../../../components/ConfirmModal';
import KeyboardAvoidingView from '../../../components/KeyboardAvoidingView';
import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions';
import * as CloseAccountActions from '../../../libs/actions/CloseAccount';
import ONYXKEYS from '../../../ONYXKEYS';

const propTypes = {
/** Onyx Props */

/** Is the Close Account information modal open? */
isCloseAccoutModalOpen: PropTypes.bool,

/** Session of currently logged in user */
session: PropTypes.shape({
/** Email address */
email: PropTypes.string.isRequired,
}).isRequired,

...windowDimensionsPropTypes,
...withLocalizePropTypes,
};

const defaultProps = {
isCloseAccoutModalOpen: false,
};

class CloseAccountPage extends Component {
constructor(props) {
super(props);

this.state = {
reasonForLeaving: '',
phoneOrEmail: '',
};
}

render() {
const userEmailOrPhone = Str.removeSMSDomain(this.props.session.email);
return (
<ScreenWrapper>
<KeyboardAvoidingView>
<HeaderWithCloseButton
title={this.props.translate('closeAccountPage.closeAccount')}
shouldShowBackButton
onBackButtonPress={() => Navigation.navigate(ROUTES.SETTINGS_SECURITY)}
onCloseButtonPress={() => Navigation.dismissModal(true)}
/>
<ScrollView
contentContainerStyle={[
styles.flexGrow1,
styles.flexColumn,
styles.p5,
]}
>
<Text>{this.props.translate('closeAccountPage.reasonForLeavingPrompt')}</Text>
<TextInput
multiline
numberOfLines={6}
value={this.state.reasonForLeaving}
onChangeText={reasonForLeaving => this.setState({reasonForLeaving})}
label={this.props.translate('closeAccountPage.enterMessageHere')}
containerStyles={[styles.mt5]}
/>
<Text style={[styles.mt5]}>
<Text style={[styles.textStrong]}>
{this.props.translate('closeAccountPage.closeAccountWarning')}
</Text>
{' '}
{this.props.translate('closeAccountPage.closeAccountPermanentlyDeleteData')}
</Text>
<TextInput
autoCapitalize="none"
value={this.state.phoneOrEmail}
onChangeText={phoneOrEmail => this.setState({phoneOrEmail: phoneOrEmail.toLowerCase()})}
label={this.props.translate('closeAccountPage.typeToConfirm', {emailOrPhone: userEmailOrPhone})}
containerStyles={[styles.mt5]}
/>
</ScrollView>
<FixedFooter>
<Button
danger
style={[styles.mb5]}
text={this.props.translate('closeAccountPage.closeAccount')}
isLoading={this.state.loading}
onPress={() => User.closeAccount(this.state.reasonForLeaving)}
isDisabled={Str.removeSMSDomain(userEmailOrPhone).toLowerCase() !== this.state.phoneOrEmail.toLowerCase()}
/>
</FixedFooter>
<ConfirmModal
title=""
success={false}
confirmText={this.props.translate('closeAccountPage.okayGotIt')}
prompt={(
<Text>
{this.props.translate('closeAccountPage.closeAccountActionRequired')}
{' '}
<Text
style={styles.link}
onPress={() => { Linking.openURL('https://community.expensify.com/discussion/4724/faq-why-cant-i-close-my-account'); }}
>
{this.props.translate('common.here')}
</Text>
{' '}
{this.props.translate('closeAccountPage.closeAccountTryAgainAfter')}
</Text>
)}
onConfirm={CloseAccountActions.hideCloseAccountModal}
isVisible={this.props.isCloseAccoutModalOpen}
shouldShowCancelButton={false}
/>
</KeyboardAvoidingView>
</ScreenWrapper>
);
}
}

CloseAccountPage.propTypes = propTypes;
CloseAccountPage.defaultProps = defaultProps;
CloseAccountPage.displayName = 'CloseAccountPage';

export default compose(
withLocalize,
withWindowDimensions,
withOnyx({
isCloseAccoutModalOpen: {
key: ONYXKEYS.IS_CLOSE_ACCOUNT_MODAL_OPEN,
},
session: {
key: ONYXKEYS.SESSION,
},
}),
)(CloseAccountPage);
Loading