diff --git a/android/app/build.gradle b/android/app/build.gradle
index be5d8d0aba6c..a0f9f25292d2 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -156,8 +156,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
- versionCode 1001028001
- versionName "1.2.80-1"
+ versionCode 1001028002
+ versionName "1.2.80-2"
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
if (isNewArchitectureEnabled()) {
diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist
index 4898f9cd9679..9f319b4fcb79 100644
--- a/ios/NewExpensify/Info.plist
+++ b/ios/NewExpensify/Info.plist
@@ -30,7 +30,7 @@
CFBundleVersion
- 1.2.80.1
+ 1.2.80.2
ITSAppUsesNonExemptEncryption
LSApplicationQueriesSchemes
diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist
index cc003d99ef93..419a9a37a8b4 100644
--- a/ios/NewExpensifyTests/Info.plist
+++ b/ios/NewExpensifyTests/Info.plist
@@ -19,6 +19,6 @@
CFBundleSignature
????
CFBundleVersion
- 1.2.80.1
+ 1.2.80.2
diff --git a/package-lock.json b/package-lock.json
index ba1e6c359d93..0b4897abca4f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "new.expensify",
- "version": "1.2.80-1",
+ "version": "1.2.80-2",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "new.expensify",
- "version": "1.2.80-1",
+ "version": "1.2.80-2",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
diff --git a/package.json b/package.json
index 42243a000275..4b46717518d2 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "1.2.80-1",
+ "version": "1.2.80-2",
"author": "Expensify, Inc.",
"homepage": "https://new.expensify.com",
"description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",
diff --git a/src/ROUTES.js b/src/ROUTES.js
index 65add35fbc58..74f3ba233bdf 100644
--- a/src/ROUTES.js
+++ b/src/ROUTES.js
@@ -51,8 +51,6 @@ export default {
SETTINGS_PERSONAL_DETAILS_DATE_OF_BIRTH: `${SETTINGS_PERSONAL_DETAILS}/date-of-birth`,
SETTINGS_PERSONAL_DETAILS_ADDRESS: `${SETTINGS_PERSONAL_DETAILS}/address`,
SETTINGS_CONTACT_METHODS,
- SETTINGS_CONTACT_METHOD_DETAILS: `${SETTINGS_CONTACT_METHODS}/:contactMethod/details`,
- getEditContactMethodRoute: contactMethod => `${SETTINGS_CONTACT_METHODS}/${encodeURIComponent(contactMethod)}/details`,
NEW_GROUP: 'new/group',
NEW_CHAT: 'new/chat',
REPORT,
diff --git a/src/components/AvatarWithIndicator.js b/src/components/AvatarWithIndicator.js
index f2a92f9df98c..6430d463a712 100644
--- a/src/components/AvatarWithIndicator.js
+++ b/src/components/AvatarWithIndicator.js
@@ -15,8 +15,6 @@ import {policyPropTypes} from '../pages/workspace/withPolicy';
import walletTermsPropTypes from '../pages/EnablePayments/walletTermsPropTypes';
import * as PolicyUtils from '../libs/PolicyUtils';
import * as PaymentMethods from '../libs/actions/PaymentMethods';
-import * as UserUtils from '../libs/UserUtils';
-import themeColors from '../styles/themes/default';
const propTypes = {
/** URL for the avatar */
@@ -28,8 +26,6 @@ const propTypes = {
/** To show a tooltip on hover */
tooltipText: PropTypes.string,
- /* Onyx Props */
-
/** The employee list of all policies (coming from Onyx) */
policiesMemberList: PropTypes.objectOf(policyMemberPropType),
@@ -47,15 +43,6 @@ const propTypes = {
/** Information about the user accepting the terms for payments */
walletTerms: walletTermsPropTypes,
-
- /** Login list for the user that is signed in */
- loginList: PropTypes.shape({
- /** Date login was validated, used to show info indicator status */
- validatedDate: PropTypes.string,
-
- /** Field-specific server side errors keyed by microtime */
- errorFields: PropTypes.objectOf(PropTypes.objectOf(PropTypes.string)),
- }),
};
const defaultProps = {
@@ -67,50 +54,45 @@ const defaultProps = {
cardList: {},
userWallet: {},
walletTerms: {},
- loginList: {},
};
const AvatarWithIndicator = (props) => {
+ const isLarge = props.size === 'large';
+ const indicatorStyles = [
+ styles.alignItemsCenter,
+ styles.justifyContentCenter,
+ isLarge ? styles.statusIndicatorLarge : styles.statusIndicator,
+ ];
+
// If a policy was just deleted from Onyx, then Onyx will pass a null value to the props, and
// those should be cleaned out before doing any error checking
const cleanPolicies = _.pick(props.policies, policy => policy);
const cleanPolicyMembers = _.pick(props.policiesMemberList, member => member);
- // All of the error & info-checking methods are put into an array. This is so that using _.some() will return
- // early as soon as the first error / info condition is returned. This makes the checks very efficient since
- // we only care if a single error / info condition exists anywhere.
+ // All of the error-checking methods are put into an array. This is so that using _.some() will return
+ // early as soon as the first error is returned. This makes the error checking very efficient since
+ // we only care if a single error exists anywhere.
const errorCheckingMethods = [
() => !_.isEmpty(props.userWallet.errors),
() => PaymentMethods.hasPaymentMethodError(props.bankAccountList, props.cardList),
() => _.some(cleanPolicies, PolicyUtils.hasPolicyError),
() => _.some(cleanPolicies, PolicyUtils.hasCustomUnitsError),
() => _.some(cleanPolicyMembers, PolicyUtils.hasPolicyMemberError),
- () => UserUtils.hasLoginListError(props.loginList),
// Wallet term errors that are not caused by an IOU (we show the red brick indicator for those in the LHN instead)
() => !_.isEmpty(props.walletTerms.errors) && !props.walletTerms.chatReportID,
];
- const infoCheckingMethods = [
- () => UserUtils.hasLoginListInfo(props.loginList),
- ];
- const shouldShowErrorIndicator = _.some(errorCheckingMethods, errorCheckingMethod => errorCheckingMethod());
- const shouldShowInfoIndicator = !shouldShowErrorIndicator && _.some(infoCheckingMethods, infoCheckingMethod => infoCheckingMethod());
-
- const indicatorColor = shouldShowErrorIndicator ? themeColors.danger : themeColors.success;
- const indicatorStyles = [
- styles.alignItemsCenter,
- styles.justifyContentCenter,
- styles.statusIndicator(indicatorColor),
- ];
+ const shouldShowIndicator = _.some(errorCheckingMethods, errorCheckingMethod => errorCheckingMethod());
return (
-
+
- {(shouldShowErrorIndicator || shouldShowInfoIndicator) && (
+ {shouldShowIndicator && (
)}
@@ -141,7 +123,4 @@ export default withOnyx({
walletTerms: {
key: ONYXKEYS.WALLET_TERMS,
},
- loginList: {
- key: ONYXKEYS.LOGIN_LIST,
- },
})(AvatarWithIndicator);
diff --git a/src/components/MenuItem.js b/src/components/MenuItem.js
index b23cf1ba6f8c..33563bb029be 100644
--- a/src/components/MenuItem.js
+++ b/src/components/MenuItem.js
@@ -62,7 +62,7 @@ const MenuItem = (props) => {
const descriptionTextStyle = StyleUtils.combineStyles([
styles.textLabelSupporting,
(props.icon ? styles.ml3 : undefined),
- styles.breakWord,
+ styles.breakAll,
styles.lineHeightNormal,
], props.style);
diff --git a/src/languages/en.js b/src/languages/en.js
index 0e5afed9a424..515f6ff252e3 100755
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -118,8 +118,6 @@ export default {
youAppearToBeOffline: 'You appear to be offline.',
thisFeatureRequiresInternet: 'This feature requires an active internet connection to be used.',
areYouSure: 'Are you sure?',
- verify: 'Verify',
- yesContinue: 'Yes, continue',
zipCodeExample: 'e.g. 12345, 12345-1234, 12345 1234',
websiteExample: 'e.g. https://www.expensify.com',
},
@@ -343,20 +341,6 @@ export default {
contacts: {
contactMethod: 'Contact method',
contactMethods: 'Contact methods',
- helpTextBeforeEmail: 'Add more ways for people to find you, and forward receipts to ',
- helpTextAfterEmail: ' from multiple email addresses.',
- pleaseVerify: 'Please verify this contact method',
- getInTouch: "Whenever we need to get in touch with you, we'll use this contact method.",
- enterMagicCode: ({contactMethod}) => `Please enter the magic code sent to ${contactMethod}`,
- yourDefaultContactMethod: 'This is your current default contact method. You will not be able to delete this contact method until you set an alternative default by selecting another contact method and pressing “Set as default”.',
- removeContactMethod: 'Remove contact method',
- removeAreYouSure: 'Are you sure you want to remove this contact method? This action cannot be undone.',
- resendMagicCode: 'Resend magic code',
- genericFailureMessages: {
- requestContactMethodValidateCode: 'Failed to send a new magic code. Please wait a bit and try again.',
- validateSecondaryLogin: 'Failed to validate contact method with given magic code. Please request a new code and try again.',
- deleteContactMethod: 'Failed to delete contact method. Please reach out to Concierge for help.',
- },
},
pronouns: {
coCos: 'Co / Cos',
diff --git a/src/languages/es.js b/src/languages/es.js
index f282242cecac..4394b6937524 100644
--- a/src/languages/es.js
+++ b/src/languages/es.js
@@ -118,8 +118,6 @@ export default {
youAppearToBeOffline: 'Parece que estás desconectado.',
thisFeatureRequiresInternet: 'Esta función requiere una conexión a Internet activa para ser utilizada.',
areYouSure: '¿Estás seguro?',
- verify: 'Verifique',
- yesContinue: 'Sí, Continuar',
zipCodeExample: 'p. ej. 12345, 12345-1234, 12345 1234',
websiteExample: 'p. ej. https://www.expensify.com',
},
@@ -343,20 +341,6 @@ export default {
contacts: {
contactMethod: 'Método de contacto',
contactMethods: 'Métodos de contacto',
- helpTextBeforeEmail: 'Añade más formas de que la gente te encuentre y reenvía los recibos a ',
- helpTextAfterEmail: ' desde varias direcciones de correo electrónico.',
- pleaseVerify: 'Por favor verifica este método de contacto',
- getInTouch: 'Utilizaremos este método de contacto cuando necesitemos contactarte.',
- enterMagicCode: ({contactMethod}) => `Por favor, introduce el código mágico enviado a ${contactMethod}`,
- yourDefaultContactMethod: 'Este es tu método de contacto predeterminado. No podrás eliminarlo hasta que añadas otro método de contacto y lo marques como predeterminado pulsando "Establecer como predeterminado".',
- removeContactMethod: 'Eliminar método de contacto',
- removeAreYouSure: '¿Estás seguro de que quieres eliminar este método de contacto? Esta acción no se puede deshacer.',
- resendMagicCode: 'Reenviar código mágico',
- genericFailureMessages: {
- requestContactMethodValidateCode: 'No se ha podido enviar un nuevo código mágico. Espera un rato y vuelve a intentarlo.',
- validateSecondaryLogin: 'No se ha podido validar el método de contacto con el código mágico provisto. Solicita un nuevo código y vuelve a intentarlo.',
- deleteContactMethod: 'No se ha podido eliminar el método de contacto. Por favor contacta con Concierge para obtener ayuda.',
- },
},
pronouns: {
coCos: 'Co / Cos',
diff --git a/src/libs/ErrorUtils.js b/src/libs/ErrorUtils.js
index 8173c57ad8a6..e1ca7233ba8a 100644
--- a/src/libs/ErrorUtils.js
+++ b/src/libs/ErrorUtils.js
@@ -1,5 +1,4 @@
import _ from 'underscore';
-import lodashGet from 'lodash/get';
import CONST from '../CONST';
/**
@@ -55,30 +54,8 @@ function getLatestErrorMessage(onyxData) {
.value();
}
-/**
- * @param {Object} onyxData
- * @param {Object} onyxData.errorFields
- * @param {String} fieldName
- * @returns {Object}
- */
-function getLatestErrorField(onyxData, fieldName) {
- const errorsForField = lodashGet(onyxData, ['errorFields', fieldName], {});
-
- if (_.isEmpty(errorsForField)) {
- return {};
- }
- return _.chain(errorsForField)
- .keys()
- .sortBy()
- .reverse()
- .map(key => ({[key]: errorsForField[key]}))
- .first()
- .value();
-}
-
export {
// eslint-disable-next-line import/prefer-default-export
getAuthenticateErrorMessage,
getLatestErrorMessage,
- getLatestErrorField,
};
diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js
index d1d11fc0f23c..07c67bec110e 100644
--- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js
+++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js
@@ -217,20 +217,6 @@ const SettingsModalStackNavigator = createModalStackNavigator([
},
name: 'Settings_Profile',
},
- {
- getComponent: () => {
- const SettingsContactMethodDetailsPage = require('../../../pages/settings/Profile/Contacts/ContactMethodDetailsPage').default;
- return SettingsContactMethodDetailsPage;
- },
- name: 'Settings_ContactMethodDetails',
- },
- {
- getComponent: () => {
- const SettingsContactMethodsPage = require('../../../pages/settings/Profile/Contacts/ContactMethodsPage').default;
- return SettingsContactMethodsPage;
- },
- name: 'Settings_ContactMethods',
- },
{
getComponent: () => {
const SettingsPronounsPage = require('../../../pages/settings/Profile/PronounsPage').default;
@@ -287,6 +273,13 @@ const SettingsModalStackNavigator = createModalStackNavigator([
},
name: 'Settings_PersonalDetails_Address',
},
+ {
+ getComponent: () => {
+ const SettingsContactMethodsPage = require('../../../pages/settings/Profile/Contacts/ContactMethodsPage').default;
+ return SettingsContactMethodsPage;
+ },
+ name: 'Settings_ContactMethods',
+ },
{
getComponent: () => {
const SettingsAddSecondaryLoginPage = require('../../../pages/settings/Profile/Contacts/AddSecondaryLoginPage').default;
diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js
index 80e043c1b40d..361655c31439 100644
--- a/src/libs/Navigation/linkingConfig.js
+++ b/src/libs/Navigation/linkingConfig.js
@@ -124,9 +124,6 @@ export default {
path: ROUTES.SETTINGS_CONTACT_METHODS,
exact: true,
},
- Settings_ContactMethodDetails: {
- path: ROUTES.SETTINGS_CONTACT_METHOD_DETAILS,
- },
Settings_Add_Secondary_Login: {
path: ROUTES.SETTINGS_ADD_LOGIN,
},
diff --git a/src/libs/UserUtils.js b/src/libs/UserUtils.js
deleted file mode 100644
index 748ad18dac69..000000000000
--- a/src/libs/UserUtils.js
+++ /dev/null
@@ -1,68 +0,0 @@
-import _ from 'underscore';
-import lodashGet from 'lodash/get';
-import CONST from '../CONST';
-
-/**
- * Searches through given loginList for any contact method / login with an error.
- *
- * Example that should return false:
- * {{
- * test@test.com: {
- * errorFields: {
- * validateCodeSent: null
- * }
- * }
- * }}
- *
- * Example that should return true:
- * {{
- * test@test.com: {
- * errorFields: {
- * validateCodeSent: { 18092081290: 'An error' }
- * }
- * }
- * }}
- *
- * @param {Object} loginList
- * @param {Object} loginList.errorFields
- * @returns {Boolean}
- */
-function hasLoginListError(loginList) {
- return _.some(loginList, login => _.some(lodashGet(login, 'errorFields', {}), field => !_.isEmpty(field)));
-}
-
-/**
- * Searches through given loginList for any contact method / login that requires
- * an Info brick road status indicator. Currently this only applies if the user
- * has an unvalidated contact method.
- *
- * @param {Object} loginList
- * @param {String} loginList.validatedDate
- * @returns {Boolean}
- */
-function hasLoginListInfo(loginList) {
- return _.some(loginList, login => _.isEmpty(login.validatedDate));
-}
-
-/**
- * Gets the appropriate brick road indicator status for a given loginList.
- * Error status is higher priority, so we check for that first.
- *
- * @param {Object} loginList
- * @returns {String}
- */
-function getLoginListBrickRoadIndicator(loginList) {
- if (hasLoginListError(loginList)) {
- return CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR;
- }
- if (hasLoginListInfo(loginList)) {
- return CONST.BRICK_ROAD_INDICATOR_STATUS.INFO;
- }
- return '';
-}
-
-export {
- hasLoginListError,
- hasLoginListInfo,
- getLoginListBrickRoadIndicator,
-};
diff --git a/src/libs/actions/User.js b/src/libs/actions/User.js
index 01daca6f7a28..7e7b51e93d3d 100644
--- a/src/libs/actions/User.js
+++ b/src/libs/actions/User.js
@@ -19,7 +19,6 @@ import * as SequentialQueue from '../Network/SequentialQueue';
import PusherUtils from '../PusherUtils';
import * as Report from './Report';
import * as ReportActionsUtils from '../ReportActionsUtils';
-import DateUtils from '../DateUtils';
import * as Session from './Session';
let currentUserAccountID = '';
@@ -101,61 +100,6 @@ function resendValidateCode(login) {
Session.resendValidateCode(login);
}
-/**
- * Requests a new validate code be sent for the passed contact method
- *
- * @param {String} contactMethod - the new contact method that the user is trying to verify
- */
-function requestContactMethodValidateCode(contactMethod) {
- const optimisticData = [{
- onyxMethod: CONST.ONYX.METHOD.MERGE,
- key: ONYXKEYS.LOGIN_LIST,
- value: {
- [contactMethod]: {
- validateCodeSent: true,
- errorFields: {
- validateCodeSent: null,
- },
- pendingFields: {
- validateCodeSent: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
- },
- },
- },
- }];
- const successData = [{
- onyxMethod: CONST.ONYX.METHOD.MERGE,
- key: ONYXKEYS.LOGIN_LIST,
- value: {
- [contactMethod]: {
- pendingFields: {
- validateCodeSent: null,
- },
- },
- },
- }];
- const failureData = [{
- onyxMethod: CONST.ONYX.METHOD.MERGE,
- key: ONYXKEYS.LOGIN_LIST,
- value: {
- [contactMethod]: {
- validateCodeSent: false,
- errorFields: {
- validateCodeSent: {
- [DateUtils.getMicroseconds()]: Localize.translateLocal('contacts.genericFailureMessages.requestContactMethodValidateCode'),
- },
- },
- pendingFields: {
- validateCodeSent: null,
- },
- },
- },
- }];
-
- API.write('RequestContactMethodValidateCode', {
- email: contactMethod,
- }, {optimisticData, successData, failureData});
-}
-
/**
* Sets whether or not the user is subscribed to Expensify news
*
@@ -218,77 +162,6 @@ function setSecondaryLoginAndNavigate(login, password) {
});
}
-/**
- * Delete a specific contact method
- *
- * @param {String} contactMethod - the contact method being deleted
- * @param {Object} oldLoginData
- */
-function deleteContactMethod(contactMethod, oldLoginData) {
- const optimisticData = [{
- onyxMethod: CONST.ONYX.METHOD.MERGE,
- key: ONYXKEYS.LOGIN_LIST,
- value: {
- [contactMethod]: {
- partnerUserID: '',
- errorFields: {
- deletedLogin: null,
- },
- pendingFields: {
- deletedLogin: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE,
- },
- },
- },
- }];
- const successData = [{
- onyxMethod: CONST.ONYX.METHOD.MERGE,
- key: ONYXKEYS.LOGIN_LIST,
- value: {
- [contactMethod]: null,
- },
- }];
- const failureData = [{
- onyxMethod: CONST.ONYX.METHOD.MERGE,
- key: ONYXKEYS.LOGIN_LIST,
- value: {
- [contactMethod]: {
- ...oldLoginData,
- errorFields: {
- deletedLogin: {
- [DateUtils.getMicroseconds()]: Localize.translateLocal('contacts.genericFailureMessages.deleteContactMethod'),
- },
- },
- pendingFields: {
- deletedLogin: null,
- },
- },
- },
- }];
-
- API.write('DeleteContactMethod', {
- partnerUserID: contactMethod,
- }, {optimisticData, successData, failureData});
-}
-
-/**
- * Clears any possible stored errors for a specific field on a contact method
- *
- * @param {String} contactMethod
- * @param {String} fieldName
- */
-function clearContactMethodErrors(contactMethod, fieldName) {
- Onyx.merge(ONYXKEYS.LOGIN_LIST, {
- [contactMethod]: {
- errorFields: {
- [fieldName]: null,
- },
- pendingFields: {
- [fieldName]: null,
- },
- },
- });
-}
-
/**
* Validates a login given an accountID and validation code
*
@@ -314,62 +187,6 @@ function validateLogin(accountID, validateCode) {
Navigation.navigate(ROUTES.HOME);
}
-/**
- * Validates a secondary login / contact method
- *
- * @param {String} contactMethod - The contact method the user is trying to verify
- * @param {String} validateCode
- */
-function validateSecondaryLogin(contactMethod, validateCode) {
- const optimisticData = [{
- onyxMethod: CONST.ONYX.METHOD.MERGE,
- key: ONYXKEYS.LOGIN_LIST,
- value: {
- [contactMethod]: {
- errorFields: {
- validateLogin: null,
- },
- pendingFields: {
- validateLogin: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
- },
- },
- },
- }];
- const successData = [{
- onyxMethod: CONST.ONYX.METHOD.MERGE,
- key: ONYXKEYS.LOGIN_LIST,
- value: {
- [contactMethod]: {
- pendingFields: {
- validateLogin: null,
- },
- },
- },
- }];
- const failureData = [{
- onyxMethod: CONST.ONYX.METHOD.MERGE,
- key: ONYXKEYS.LOGIN_LIST,
- value: {
- [contactMethod]: {
- errorFields: {
- validateLogin: {
- [DateUtils.getMicroseconds()]: Localize.translateLocal('contacts.genericFailureMessages.validateSecondaryLogin'),
- },
- },
- pendingFields: {
- validateLogin: null,
- },
- },
- },
- }];
-
- API.write('ValidateSecondaryLogin', {
- partnerUserID: contactMethod,
- validateCode,
- }, {optimisticData, successData, failureData});
- Navigation.navigate(ROUTES.SETTINGS_CONTACT_METHODS);
-}
-
/**
* Checks the blockedFromConcierge object to see if it has an expiresAt key,
* and if so whether the expiresAt date of a user's ban is before right now
@@ -657,13 +474,9 @@ export {
updatePassword,
closeAccount,
resendValidateCode,
- requestContactMethodValidateCode,
updateNewsletterSubscription,
setSecondaryLoginAndNavigate,
- deleteContactMethod,
- clearContactMethodErrors,
validateLogin,
- validateSecondaryLogin,
isBlockedFromConcierge,
subscribeToUserEvents,
updatePreferredSkinTone,
diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js
index 160729b91f5b..a5696d3e02e5 100755
--- a/src/pages/settings/InitialSettingsPage.js
+++ b/src/pages/settings/InitialSettingsPage.js
@@ -34,7 +34,6 @@ import ConfirmModal from '../../components/ConfirmModal';
import * as ReportUtils from '../../libs/ReportUtils';
import * as Link from '../../libs/actions/Link';
import OfflineWithFeedback from '../../components/OfflineWithFeedback';
-import * as UserUtils from '../../libs/UserUtils';
const propTypes = {
/* Onyx Props */
@@ -81,15 +80,6 @@ const propTypes = {
/** Information about the user accepting the terms for payments */
walletTerms: walletTermsPropTypes,
- /** Login list for the user that is signed in */
- loginList: PropTypes.shape({
- /** Date login was validated, used to show brickroad info status */
- validatedDate: PropTypes.string,
-
- /** Field-specific server side errors keyed by microtime */
- errorFields: PropTypes.objectOf(PropTypes.objectOf(PropTypes.string)),
- }),
-
...withLocalizePropTypes,
...withCurrentUserPersonalDetailsPropTypes,
};
@@ -155,7 +145,6 @@ class InitialSettingsPage extends React.Component {
.filter(policy => policy && policy.type === CONST.POLICY.TYPE.FREE && policy.role === CONST.POLICY.ROLE.ADMIN)
.find(policy => PolicyUtils.hasPolicyError(policy) || PolicyUtils.getPolicyBrickRoadIndicatorStatus(policy, this.props.policyMembers))
.value() ? 'error' : null;
- const profileBrickRoadIndicator = UserUtils.getLoginListBrickRoadIndicator(this.props.loginList);
return ([
{
@@ -170,7 +159,6 @@ class InitialSettingsPage extends React.Component {
translationKey: 'common.profile',
icon: Expensicons.Profile,
action: () => { App.openProfile(); },
- brickRoadIndicator: profileBrickRoadIndicator,
},
{
translationKey: 'common.preferences',
@@ -221,6 +209,7 @@ class InitialSettingsPage extends React.Component {
iconType={item.iconType}
onPress={item.action}
iconStyles={item.iconStyles}
+ iconFill={item.iconFill}
shouldShowRightIcon
iconRight={item.iconRight}
badgeText={this.getWalletBalance(isPaymentItem)}
@@ -351,9 +340,6 @@ export default compose(
walletTerms: {
key: ONYXKEYS.WALLET_TERMS,
},
- loginList: {
- key: ONYXKEYS.LOGIN_LIST,
- },
}),
withNetwork(),
)(InitialSettingsPage);
diff --git a/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.js b/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.js
deleted file mode 100644
index 2ae32c9a4e3a..000000000000
--- a/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.js
+++ /dev/null
@@ -1,257 +0,0 @@
-import Str from 'expensify-common/lib/str';
-import lodashGet from 'lodash/get';
-import React, {Component} from 'react';
-import {View, ScrollView, TouchableOpacity} from 'react-native';
-import PropTypes from 'prop-types';
-import {withOnyx} from 'react-native-onyx';
-import Navigation from '../../../../libs/Navigation/Navigation';
-import ROUTES from '../../../../ROUTES';
-import ScreenWrapper from '../../../../components/ScreenWrapper';
-import HeaderWithCloseButton from '../../../../components/HeaderWithCloseButton';
-import compose from '../../../../libs/compose';
-import ONYXKEYS from '../../../../ONYXKEYS';
-import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize';
-import MenuItem from '../../../../components/MenuItem';
-import styles from '../../../../styles/styles';
-import * as Expensicons from '../../../../components/Icon/Expensicons';
-import Text from '../../../../components/Text';
-import OfflineWithFeedback from '../../../../components/OfflineWithFeedback';
-import ConfirmModal from '../../../../components/ConfirmModal';
-import * as User from '../../../../libs/actions/User';
-import TextInput from '../../../../components/TextInput';
-import CONST from '../../../../CONST';
-import Icon from '../../../../components/Icon';
-import colors from '../../../../styles/colors';
-import Button from '../../../../components/Button';
-import * as ErrorUtils from '../../../../libs/ErrorUtils';
-import themeColors from '../../../../styles/themes/default';
-
-const propTypes = {
- /* Onyx Props */
-
- /** Login list for the user that is signed in */
- loginList: PropTypes.shape({
- /** Value of partner name */
- partnerName: PropTypes.string,
-
- /** Phone/Email associated with user */
- partnerUserID: PropTypes.string,
-
- /** Date when login was validated */
- validatedDate: PropTypes.string,
-
- /** Field-specific server side errors keyed by microtime */
- errorFields: PropTypes.objectOf(PropTypes.objectOf(PropTypes.string)),
-
- /** Field-specific pending states for offline UI status */
- pendingFields: PropTypes.objectOf(PropTypes.objectOf(PropTypes.string)),
- }),
-
- /** Current user session */
- session: PropTypes.shape({
- email: PropTypes.string.isRequired,
- }).isRequired,
-
- /** Route params */
- route: PropTypes.shape({
- params: PropTypes.shape({
- /** passed via route /settings/profile/contact-methods/:contactMethod/details */
- contactMethod: PropTypes.string,
- }),
- }),
-
- ...withLocalizePropTypes,
-};
-
-const defaultProps = {
- loginList: {},
- route: {
- params: {
- contactMethod: '',
- },
- },
-};
-
-class ContactMethodDetailsPage extends Component {
- constructor(props) {
- super(props);
-
- this.toggleDeleteModal = this.toggleDeleteModal.bind(this);
- this.confirmDeleteAndHideModal = this.confirmDeleteAndHideModal.bind(this);
- this.resendValidateCode = this.resendValidateCode.bind(this);
- this.getContactMethod = this.getContactMethod.bind(this);
- this.validateContactMethod = this.validateContactMethod.bind(this);
-
- this.state = {
- isDeleteModalOpen: false,
- validateCode: '',
- };
- }
-
- componentDidMount() {
- if (this.getContactMethod()) {
- return;
- }
- Navigation.navigate(ROUTES.SETTINGS_CONTACT_METHODS);
- }
-
- /**
- * Gets the current contact method from the route params
- *
- * @returns {string}
- */
- getContactMethod() {
- return decodeURIComponent(lodashGet(this.props.route, 'params.contactMethod'));
- }
-
- /**
- * Toggle delete confirm modal visibility
- * @param {Boolean} shouldOpen
- */
- toggleDeleteModal(shouldOpen) {
- this.setState({isDeleteModalOpen: shouldOpen});
- }
-
- /**
- * Delete the contact method and hide the modal
- */
- confirmDeleteAndHideModal() {
- const contactMethod = this.getContactMethod();
- User.deleteContactMethod(contactMethod);
- this.toggleDeleteModal(false);
- Navigation.navigate(ROUTES.SETTINGS_CONTACT_METHODS);
- }
-
- /**
- * Request a validate code / magic code be sent to verify this contact method
- */
- resendValidateCode() {
- User.requestContactMethodValidateCode(this.getContactMethod());
- }
-
- /**
- * Attempt to validate this contact method
- */
- validateContactMethod() {
- User.validateSecondaryLogin(this.getContactMethod(), this.state.validateCode);
- }
-
- render() {
- const contactMethod = this.getContactMethod();
- const loginData = this.props.loginList[contactMethod];
- if (!contactMethod || !loginData) {
- Navigation.navigate(ROUTES.SETTINGS_CONTACT_METHODS);
- return null;
- }
-
- const isDefaultContactMethod = (this.props.session.email === loginData.partnerUserID);
- const hasMagicCodeBeenSent = lodashGet(this.props.loginList, [contactMethod, 'validateCodeSent'], false);
-
- return (
-
- Navigation.navigate(ROUTES.SETTINGS_CONTACT_METHODS)}
- onCloseButtonPress={() => Navigation.dismissModal(true)}
- />
-
- this.toggleDeleteModal(false)}
- prompt={this.props.translate('contacts.removeAreYouSure')}
- confirmText={this.props.translate('common.yesContinue')}
- isVisible={this.state.isDeleteModalOpen}
- danger
- />
- {!loginData.validatedDate && (
-
-
-
-
-
- {this.props.translate('contacts.enterMagicCode', {contactMethod})}
-
-
-
- this.setState({validateCode: text})}
- keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD}
- blurOnSubmit={false}
- />
- User.clearContactMethodErrors(contactMethod, 'validateCodeSent')}
- >
-
-
- {this.props.translate('contacts.resendMagicCode')}
-
- {hasMagicCodeBeenSent && (
-
- )}
-
-
- User.clearContactMethodErrors(contactMethod, 'validateLogin')}
- >
-
-
-
- )}
- {isDefaultContactMethod ? (
-
- {this.props.translate('contacts.yourDefaultContactMethod')}
-
- ) : (
- User.clearContactMethodErrors(contactMethod, 'deletedLogin')}
- >
-
- )}
-
-
- );
- }
-}
-
-ContactMethodDetailsPage.propTypes = propTypes;
-ContactMethodDetailsPage.defaultProps = defaultProps;
-
-export default compose(
- withLocalize,
- withOnyx({
- loginList: {
- key: ONYXKEYS.LOGIN_LIST,
- },
- session: {
- key: ONYXKEYS.SESSION,
- },
- }),
-)(ContactMethodDetailsPage);
diff --git a/src/pages/settings/Profile/Contacts/ContactMethodsPage.js b/src/pages/settings/Profile/Contacts/ContactMethodsPage.js
index 92165475f7f5..edb998047c11 100644
--- a/src/pages/settings/Profile/Contacts/ContactMethodsPage.js
+++ b/src/pages/settings/Profile/Contacts/ContactMethodsPage.js
@@ -1,13 +1,13 @@
import Str from 'expensify-common/lib/str';
import lodashGet from 'lodash/get';
import PropTypes from 'prop-types';
-import React from 'react';
-import {View} from 'react-native';
+import React, {Component} from 'react';
import {ScrollView} from 'react-native-gesture-handler';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import HeaderWithCloseButton from '../../../../components/HeaderWithCloseButton';
import ScreenWrapper from '../../../../components/ScreenWrapper';
+import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '../../../../components/withCurrentUserPersonalDetails';
import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize';
import CONST from '../../../../CONST';
import compose from '../../../../libs/compose';
@@ -15,11 +15,6 @@ import Navigation from '../../../../libs/Navigation/Navigation';
import ONYXKEYS from '../../../../ONYXKEYS';
import ROUTES from '../../../../ROUTES';
import LoginField from './LoginField';
-import MenuItem from '../../../../components/MenuItem';
-import Text from '../../../../components/Text';
-import styles from '../../../../styles/styles';
-import CopyTextToClipboard from '../../../../components/CopyTextToClipboard';
-import OfflineWithFeedback from '../../../../components/OfflineWithFeedback';
const propTypes = {
/* Onyx Props */
@@ -32,136 +27,112 @@ const propTypes = {
/** Phone/Email associated with user */
partnerUserID: PropTypes.string,
- /** Date login was validated, used to show brickroad info status */
+ /** Date of when login was validated */
validatedDate: PropTypes.string,
-
- /** Field-specific server side errors keyed by microtime */
- errorFields: PropTypes.objectOf(PropTypes.objectOf(PropTypes.string)),
-
- /** Field-specific pending states for offline UI status */
- pendingFields: PropTypes.objectOf(PropTypes.objectOf(PropTypes.string)),
}),
- /** Current user session */
- session: PropTypes.shape({
- email: PropTypes.string.isRequired,
- }).isRequired,
-
...withLocalizePropTypes,
+ ...withCurrentUserPersonalDetailsPropTypes,
};
const defaultProps = {
loginList: {},
+ ...withCurrentUserPersonalDetailsDefaultProps,
};
-const ContactMethodsPage = (props) => {
- let hasPhoneNumberLogin = false;
- let hasEmailLogin = false;
+class ContactMethodsPage extends Component {
+ constructor(props) {
+ super(props);
- const loginMenuItems = _.map(props.loginList, (login, loginName) => {
- const pendingAction = lodashGet(login, 'pendingFields.deletedLogin', null);
- if (!login.partnerUserID && _.isEmpty(pendingAction)) {
- return null;
- }
+ this.state = {
+ logins: this.getLogins(),
+ };
- let description = '';
- if (props.session.email === login.partnerUserID) {
- description = props.translate('contacts.getInTouch');
- } else if (!login.validatedDate) {
- description = props.translate('contacts.pleaseVerify');
- }
- let indicator = null;
- if (_.some(lodashGet(login, 'errorFields', {}), errorField => !_.isEmpty(errorField))) {
- indicator = CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR;
- } else if (!login.validatedDate) {
- indicator = CONST.BRICK_ROAD_INDICATOR_STATUS.INFO;
+ this.getLogins = this.getLogins.bind(this);
+ }
+
+ componentDidUpdate(prevProps) {
+ let stateToUpdate = {};
+
+ // Recalculate logins if loginList has changed
+ if (_.keys(this.props.loginList).length !== _.keys(prevProps.loginList).length) {
+ stateToUpdate = {logins: this.getLogins()};
}
- // Temporary checks to determine if we need to show specific LoginField
- // components. This check will be removed soon.
- // Also we still use login.partnerUserID here even though it could have been
- // deleted optimistically because if the deletion is pending, do want to show
- // the option to add a new phone or email login, so we don't want to find
- // that login type in the list here.
- if (Str.isValidPhone(Str.removeSMSDomain(login.partnerUserID))) {
- hasPhoneNumberLogin = true;
- } else if (Str.isValidEmail(login.partnerUserID)) {
- hasEmailLogin = true;
+ if (_.isEmpty(stateToUpdate)) {
+ return;
}
- // Default to using login key if we deleted login.partnerUserID optimistically
- // but still need to show the pending login being deleted while offline.
- const partnerUserID = login.partnerUserID || loginName;
+ // eslint-disable-next-line react/no-did-update-set-state
+ this.setState(stateToUpdate);
+ }
+
+ /**
+ * Get the most validated login of each type
+ *
+ * @returns {Object}
+ */
+ getLogins() {
+ return _.reduce(_.values(this.props.loginList), (logins, currentLogin) => {
+ const type = Str.isSMSLogin(currentLogin.partnerUserID) ? CONST.LOGIN_TYPE.PHONE : CONST.LOGIN_TYPE.EMAIL;
+ const login = Str.removeSMSDomain(currentLogin.partnerUserID);
+
+ // If there's already a login type that's validated and/or currentLogin isn't valid then return early
+ if ((login !== lodashGet(this.props.currentUserPersonalDetails, 'login')) && !_.isEmpty(logins[type])
+ && (logins[type].validatedDate || !currentLogin.validatedDate)) {
+ return logins;
+ }
+ return {
+ ...logins,
+ [type]: {
+ ...currentLogin,
+ type,
+ partnerUserID: Str.removeSMSDomain(currentLogin.partnerUserID),
+ },
+ };
+ }, {
+ phone: {},
+ email: {},
+ });
+ }
+
+ render() {
return (
-
-
- );
- });
-
- return (
-
- Navigation.navigate(ROUTES.SETTINGS_PROFILE)}
- onCloseButtonPress={() => Navigation.dismissModal(true)}
- />
-
-
-
- {props.translate('contacts.helpTextBeforeEmail')}
-
- {props.translate('contacts.helpTextAfterEmail')}
-
-
- {loginMenuItems}
- {/* The below fields will be removed soon, when we implement the new Add Contact Method page */}
- {!hasEmailLogin && (
+
- )}
- {!hasPhoneNumberLogin && (
- )}
-
-
- );
-};
+
+
+ );
+ }
+}
ContactMethodsPage.propTypes = propTypes;
ContactMethodsPage.defaultProps = defaultProps;
-ContactMethodsPage.displayName = 'ContactMethodsPage';
export default compose(
withLocalize,
+ withCurrentUserPersonalDetails,
withOnyx({
loginList: {
key: ONYXKEYS.LOGIN_LIST,
},
- session: {
- key: ONYXKEYS.SESSION,
- },
}),
)(ContactMethodsPage);
diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js
index dc43443bc4fd..6672198dc7db 100755
--- a/src/pages/settings/Profile/ProfilePage.js
+++ b/src/pages/settings/Profile/ProfilePage.js
@@ -2,8 +2,6 @@ import Str from 'expensify-common/lib/str';
import lodashGet from 'lodash/get';
import React from 'react';
import {View} from 'react-native';
-import PropTypes from 'prop-types';
-import {withOnyx} from 'react-native-onyx';
import {ScrollView} from 'react-native-gesture-handler';
import _ from 'underscore';
import AvatarWithImagePicker from '../../../components/AvatarWithImagePicker';
@@ -22,21 +20,8 @@ import * as ReportUtils from '../../../libs/ReportUtils';
import ROUTES from '../../../ROUTES';
import styles from '../../../styles/styles';
import * as Expensicons from '../../../components/Icon/Expensicons';
-import ONYXKEYS from '../../../ONYXKEYS';
-import * as UserUtils from '../../../libs/UserUtils';
const propTypes = {
- /* Onyx Props */
-
- /** Login list for the user that is signed in */
- loginList: PropTypes.shape({
- /** Date login was validated, used to show brickroad info status */
- validatedDate: PropTypes.string,
-
- /** Field-specific server side errors keyed by microtime */
- errorFields: PropTypes.objectOf(PropTypes.objectOf(PropTypes.string)),
- }),
-
...withLocalizePropTypes,
...withCurrentUserPersonalDetailsPropTypes,
};
@@ -54,8 +39,6 @@ const ProfilePage = (props) => {
return lodashGet(props.translate('pronouns'), pronounsKey, props.translate('profilePage.selectYourPronouns'));
};
const currentUserDetails = props.currentUserPersonalDetails || {};
- const contactMethodBrickRoadIndicator = UserUtils.getLoginListBrickRoadIndicator(props.loginList);
-
const profileSettingsOptions = [
{
description: props.translate('displayNamePage.headerTitle'),
@@ -66,7 +49,6 @@ const ProfilePage = (props) => {
description: props.translate('contacts.contactMethod'),
title: Str.removeSMSDomain(lodashGet(currentUserDetails, 'login', '')),
pageRoute: ROUTES.SETTINGS_CONTACT_METHODS,
- brickRoadIndicator: contactMethodBrickRoadIndicator,
},
{
description: props.translate('pronounsPage.pronouns'),
@@ -112,7 +94,6 @@ const ProfilePage = (props) => {
title={detail.title}
description={detail.description}
onPress={() => Navigation.navigate(detail.pageRoute)}
- brickRoadIndicator={detail.brickRoadIndicator}
/>
))}
@@ -134,9 +115,4 @@ ProfilePage.displayName = 'ProfilePage';
export default compose(
withLocalize,
withCurrentUserPersonalDetails,
- withOnyx({
- loginList: {
- key: ONYXKEYS.LOGIN_LIST,
- },
- }),
)(ProfilePage);
diff --git a/src/styles/styles.js b/src/styles/styles.js
index f95ad25f7b50..af7c92065caa 100644
--- a/src/styles/styles.js
+++ b/src/styles/styles.js
@@ -1003,18 +1003,35 @@ const styles = {
width: variables.componentSizeNormal,
},
- statusIndicator: (backgroundColor = themeColors.danger) => ({
+ statusIndicator: {
borderColor: themeColors.sidebar,
- backgroundColor,
+ backgroundColor: themeColors.danger,
+ borderRadius: 6,
+ borderWidth: 2,
+ position: 'absolute',
+ right: -1,
+ bottom: -1,
+ height: 12,
+ width: 12,
+ zIndex: 10,
+ },
+
+ statusIndicatorLarge: {
+ borderColor: themeColors.componentBG,
+ backgroundColor: themeColors.danger,
borderRadius: 8,
borderWidth: 2,
position: 'absolute',
- right: -2,
- top: -1,
+ right: 4,
+ bottom: 4,
height: 16,
width: 16,
zIndex: 10,
- }),
+ },
+
+ statusIndicatorOnline: {
+ backgroundColor: themeColors.success,
+ },
avatarWithIndicator: {
errorDot: {
diff --git a/src/styles/utilities/spacing.js b/src/styles/utilities/spacing.js
index bf21efb012d1..2283bd63d046 100644
--- a/src/styles/utilities/spacing.js
+++ b/src/styles/utilities/spacing.js
@@ -201,10 +201,6 @@ export default {
marginBottom: 24,
},
- mb7: {
- marginBottom: 28,
- },
-
mb8: {
marginBottom: 32,
},