diff --git a/.github/ISSUE_TEMPLATE/Standard.md b/.github/ISSUE_TEMPLATE/Standard.md index f728a9761a36..8f5d0c6b97e2 100644 --- a/.github/ISSUE_TEMPLATE/Standard.md +++ b/.github/ISSUE_TEMPLATE/Standard.md @@ -36,4 +36,4 @@ Where is this issue occurring? **Notes/Photos/Videos:** Any additional supporting documentation **Expensify/Expensify Issue URL:** -[View all open jobs on Upwork](https://www.upwork.com/ab/jobs/search/?q=Expensify%20React%20Native&sort=recency&user_location_match=2) +[View all open jobs on GitHub](https://github.com/Expensify/App/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+Wanted%22) diff --git a/android/app/build.gradle b/android/app/build.gradle index 6b2351ea4bd6..87a6aacb8bbf 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -150,8 +150,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001008506 - versionName "1.0.85-6" + versionCode 1001008601 + versionName "1.0.86-1" } splits { abi { diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 638712115c8a..121e687f2f42 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0.85 + 1.0.86 CFBundleSignature ???? CFBundleURLTypes @@ -30,7 +30,7 @@ CFBundleVersion - 1.0.85.6 + 1.0.86.1 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 1f8977d4e1bf..f352246bf05c 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -15,10 +15,10 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.0.85 + 1.0.86 CFBundleSignature ???? CFBundleVersion - 1.0.85.6 + 1.0.86.1 diff --git a/package-lock.json b/package-lock.json index e0afa2be99aa..8d14221c2894 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.0.85-6", + "version": "1.0.86-1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -23082,8 +23082,8 @@ } }, "expensify-common": { - "version": "git://github.com/Expensify/expensify-common.git#8f286b5826a041ecb739cff1ad6940ffb9324bee", - "from": "git://github.com/Expensify/expensify-common.git#8f286b5826a041ecb739cff1ad6940ffb9324bee", + "version": "git://github.com/Expensify/expensify-common.git#5fb22bd4a3619eb9fe9e2a9c0dc25e291863a2a2", + "from": "git://github.com/Expensify/expensify-common.git#5fb22bd4a3619eb9fe9e2a9c0dc25e291863a2a2", "requires": { "classnames": "2.3.1", "clipboard": "2.0.4", diff --git a/package.json b/package.json index ec81840a90f7..bd8878610a82 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.0.85-6", + "version": "1.0.86-1", "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.", @@ -60,7 +60,7 @@ "electron-log": "^4.3.5", "electron-serve": "^1.0.0", "electron-updater": "^4.3.4", - "expensify-common": "git://github.com/Expensify/expensify-common.git#8f286b5826a041ecb739cff1ad6940ffb9324bee", + "expensify-common": "git://github.com/Expensify/expensify-common.git#5fb22bd4a3619eb9fe9e2a9c0dc25e291863a2a2", "expo-haptics": "^10.0.0", "file-loader": "^6.0.0", "html-entities": "^1.3.1", diff --git a/src/components/ErrorBoundary/index.js b/src/components/ErrorBoundary/index.js index 7e8fdfdc2131..37daf447ad1a 100644 --- a/src/components/ErrorBoundary/index.js +++ b/src/components/ErrorBoundary/index.js @@ -3,7 +3,6 @@ import Log from '../../libs/Log'; BaseErrorBoundary.defaultProps.logError = (errorMessage, error, errorInfo) => { // Log the error to the server - Log.alert(errorMessage, 0, {error: error.message, errorInfo}, false); + Log.alert(errorMessage, {error: error.message, errorInfo}, false); }; - export default BaseErrorBoundary; diff --git a/src/components/ErrorBoundary/index.native.js b/src/components/ErrorBoundary/index.native.js index d320b46984e0..ae295a9d0c4d 100644 --- a/src/components/ErrorBoundary/index.native.js +++ b/src/components/ErrorBoundary/index.native.js @@ -5,7 +5,7 @@ import Log from '../../libs/Log'; BaseErrorBoundary.defaultProps.logError = (errorMessage, error, errorInfo) => { // Log the error to the server - Log.alert(errorMessage, 0, {error: error.message, errorInfo}, false); + Log.alert(errorMessage, {error: error.message, errorInfo}, false); /* On native we also log the error to crashlytics * Since the error was handled we need to manually tell crashlytics about it */ diff --git a/src/languages/en.js b/src/languages/en.js index 22a746f24070..107a1caee8fe 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -44,8 +44,9 @@ export default { here: 'here', dob: 'Date of Birth', ssnLast4: 'Last 4 Digits of SSN', - addressNoPO: 'Personal Address (PO Boxes and mail drop addresses are NOT allowed)', - companyAddressNoPO: 'Company Address (PO Boxes and mail drop addresses are NOT allowed)', + personalAddress: 'Personal Address', + companyAddress: 'Company Address', + noPO: '(PO Boxes and mail drop addresses are NOT allowed)', city: 'City', state: 'State', zip: 'Zip Code', diff --git a/src/languages/es.js b/src/languages/es.js index 0dfd38895d84..c65c9c7ee408 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -44,8 +44,9 @@ export default { here: 'aquí', dob: 'Fecha de Nacimiento', ssnLast4: 'Últimos 4 dígitos de su SSN', - addressNoPO: 'Dirección física personal (no se aceptan apartados ni direcciones postales)', - companyAddressNoPO: 'Dirección física de la empresa (no se aceptan apartados ni direcciones postales)', + personalAddress: 'Dirección física personal', + companyAddress: 'Dirección física de la empresa', + noPO: '(No se aceptan apartados ni direcciones postales)', city: 'Ciudad', state: 'Estado', zip: 'Código Postal', diff --git a/src/libs/API.js b/src/libs/API.js index 8a3c56b505e3..1f76c5cf9460 100644 --- a/src/libs/API.js +++ b/src/libs/API.js @@ -484,15 +484,13 @@ function GetRequestCountryCode() { /** * @param {Object} parameters - * @param {String} parameters.message - * @param {Object} parameters.parameters * @param {String} parameters.expensifyCashAppVersion - * @param {String} [parameters.email] + * @param {Object[]} parameters.logPacket * @returns {Promise} */ function Log(parameters) { const commandName = 'Log'; - requireParameters(['message', 'parameters', 'expensifyCashAppVersion'], + requireParameters(['logPacket', 'expensifyCashAppVersion'], parameters, commandName); // Note: We are forcing Log to run since it requires no authToken and should only be queued when we are offline. diff --git a/src/libs/HttpUtils.js b/src/libs/HttpUtils.js index 3e71518d0d37..dc4b2e817f33 100644 --- a/src/libs/HttpUtils.js +++ b/src/libs/HttpUtils.js @@ -2,6 +2,9 @@ import _ from 'underscore'; import CONFIG from '../CONFIG'; import CONST from '../CONST'; +// To avoid a circular dependency, we can't include Log here, so instead, we define an empty logging method and expose the setLogger method to set the logger from outside this file +let info = () => {}; + /** * Send an HTTP request, and attempt to resolve the json response. * If there is a network error, we'll set the application offline. @@ -28,10 +31,30 @@ function processHTTPRequest(url, method = 'get', body = null) { * @returns {Promise} */ function xhr(command, data, type = CONST.NETWORK.METHOD.POST, shouldUseSecure = false) { + if (command !== 'Log') { + info('Making API request', false, { + command, + type, + shouldUseSecure, + rvl: data.returnValueList, + }); + } const formData = new FormData(); _.each(data, (val, key) => formData.append(key, val)); const apiRoot = shouldUseSecure ? CONFIG.EXPENSIFY.URL_EXPENSIFY_SECURE : CONFIG.EXPENSIFY.URL_API_ROOT; - return processHTTPRequest(`${apiRoot}api?command=${command}`, type, formData); + return processHTTPRequest(`${apiRoot}api?command=${command}`, type, formData) + .then((response) => { + if (command !== 'Log') { + info('Finished API request', false, { + command, + type, + shouldUseSecure, + jsonCode: response.jsonCode, + requestID: response.requestID, + }); + } + return response; + }); } /** @@ -51,7 +74,12 @@ function download(relativePath) { return processHTTPRequest(`${siteRoot}${strippedRelativePath}`); } +function setLogger(logger) { + info = logger.info; +} + export default { + setLogger, download, xhr, }; diff --git a/src/libs/Log.js b/src/libs/Log.js index 1451ea25cc95..1bff8aa84fcb 100644 --- a/src/libs/Log.js +++ b/src/libs/Log.js @@ -4,22 +4,28 @@ import CONFIG from '../CONFIG'; import getPlatform from './getPlatform'; import {version} from '../../package.json'; import NetworkConnection from './NetworkConnection'; +import HttpUtils from './HttpUtils'; + +let timeout = null; /** * Network interface for logger. * + * @param {Logger} logger * @param {Object} params * @param {Object} params.parameters * @param {String} params.message + * @return {Promise} */ -function serverLoggingCallback(params) { - const requestParams = { - message: params.message, - parameters: JSON.stringify(params.parameters || {}), - expensifyCashAppVersion: `expensifyCash[${getPlatform()}]${version}`, - }; - - API.Log(requestParams); +function serverLoggingCallback(logger, params) { + const requestParams = params; + requestParams.expensifyCashAppVersion = `expensifyCash[${getPlatform()}]${version}`; + if (requestParams.parameters) { + requestParams.parameters = JSON.stringify(params.parameters); + } + clearTimeout(timeout); + timeout = setTimeout(() => logger.info('Flushing logs older than 10 minutes', true, {}, true), 10 * 60 * 1000); + return API.Log(requestParams); } // Note: We are importing Logger from expensify-common because it is @@ -33,6 +39,9 @@ const Log = new Logger({ }, isDebug: !CONFIG.IS_IN_PRODUCTION, }); +timeout = setTimeout(() => Log.info('Flushing logs older than 10 minutes', true, {}, true), 10 * 60 * 1000); NetworkConnection.registerLogInfoCallback(Log.info); +HttpUtils.setLogger(Log); + export default Log; diff --git a/src/libs/Navigation/NavigationRoot.js b/src/libs/Navigation/NavigationRoot.js index ec8745ec6cf1..903b71f161c4 100644 --- a/src/libs/Navigation/NavigationRoot.js +++ b/src/libs/Navigation/NavigationRoot.js @@ -6,6 +6,7 @@ import linkingConfig from './linkingConfig'; import AppNavigator from './AppNavigator'; import {setCurrentURL} from '../actions/App'; import FullScreenLoadingIndicator from '../../components/FullscreenLoadingIndicator'; +import Log from '../Log'; const propTypes = { /** Whether the current user is logged in with an authToken */ @@ -29,6 +30,7 @@ class NavigationRoot extends Component { } const path = getPathFromState(state, linkingConfig.config); + Log.info('Navigating to route', false, {path}); setCurrentURL(path); } diff --git a/src/libs/NetworkConnection.js b/src/libs/NetworkConnection.js index cb104b18d868..4e83f5a6d96c 100644 --- a/src/libs/NetworkConnection.js +++ b/src/libs/NetworkConnection.js @@ -19,7 +19,7 @@ const reconnectionCallbacks = []; * Loop over all reconnection callbacks and fire each one */ const triggerReconnectionCallbacks = _.throttle((reason) => { - logInfo(`[NetworkConnection] Firing reconnection callbacks because ${reason}`, true); + logInfo(`[NetworkConnection] Firing reconnection callbacks because ${reason}`); Onyx.set(ONYXKEYS.IS_LOADING_AFTER_RECONNECT, true); promiseAllSettled(_.map(reconnectionCallbacks, callback => callback())) .then(() => Onyx.set(ONYXKEYS.IS_LOADING_AFTER_RECONNECT, false)); @@ -49,7 +49,7 @@ function setOfflineStatus(isCurrentlyOffline) { * `disconnected` event which takes about 10-15 seconds to emit. */ function listenForReconnect() { - logInfo('[NetworkConnection] listenForReconnect called', true); + logInfo('[NetworkConnection] listenForReconnect called'); unsubscribeFromAppState = AppStateMonitor.addBecameActiveListener(() => { triggerReconnectionCallbacks('app became active'); @@ -67,7 +67,7 @@ function listenForReconnect() { * Tear down the event listeners when we are finished with them. */ function stopListeningForReconnect() { - logInfo('[NetworkConnection] stopListeningForReconnect called', true); + logInfo('[NetworkConnection] stopListeningForReconnect called'); if (unsubscribeFromNetInfo) { unsubscribeFromNetInfo(); unsubscribeFromNetInfo = undefined; diff --git a/src/libs/PusherConnectionManager.js b/src/libs/PusherConnectionManager.js index 164efb40d7b3..9873ed33d20c 100644 --- a/src/libs/PusherConnectionManager.js +++ b/src/libs/PusherConnectionManager.js @@ -7,7 +7,7 @@ import Log from './Log'; // reconnect each time when we only need to reconnect once. This way, if an authToken is expired and we try to // subscribe to a bunch of channels at once we will only reauthenticate and force reconnect Pusher once. const reauthenticate = _.throttle(() => { - Log.info('[Pusher] Re-authenticating and then reconnecting', true); + Log.info('[Pusher] Re-authenticating and then reconnecting'); API.reauthenticate('Push_Authenticate') .then(() => Pusher.reconnect()) .catch(() => { @@ -27,7 +27,7 @@ function init() { */ Pusher.registerCustomAuthorizer(channel => ({ authorize: (socketID, callback) => { - Log.info('[PusherConnectionManager] Attempting to authorize Pusher', true); + Log.info('[PusherConnectionManager] Attempting to authorize Pusher', false, {channelName: channel.name}); API.Push_Authenticate({ socket_id: socketID, @@ -44,11 +44,15 @@ function init() { return; } - Log.info('[PusherConnectionManager] Pusher authenticated successfully', true); + Log.info( + '[PusherConnectionManager] Pusher authenticated successfully', + false, + {channelName: channel.name}, + ); callback(null, data); }) .catch((error) => { - Log.info('[PusherConnectionManager] Unhandled error: ', error); + Log.info('[PusherConnectionManager] Unhandled error: ', false, {channelName: channel.name}); callback(error, {auth: ''}); }); }, @@ -63,14 +67,14 @@ function init() { Pusher.registerSocketEventCallback((eventName, data) => { switch (eventName) { case 'error': - Log.info('[PusherConnectionManager] error event', true, {error: data}); + Log.info('[PusherConnectionManager] error event', false, {error: data}); reauthenticate(); break; case 'connected': - Log.info('[PusherConnectionManager] connected event', true); + Log.info('[PusherConnectionManager] connected event'); break; case 'disconnected': - Log.info('[PusherConnectionManager] disconnected event', true); + Log.info('[PusherConnectionManager] disconnected event'); break; default: break; diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js index dcab886f39b2..642f826ddf80 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.js @@ -1,9 +1,10 @@ +import {AppState, Linking} from 'react-native'; import Onyx from 'react-native-onyx'; -import {Linking} from 'react-native'; import lodashGet from 'lodash/get'; import ONYXKEYS from '../../ONYXKEYS'; import * as API from '../API'; import CONST from '../../CONST'; +import Log from '../Log'; import CONFIG from '../../CONFIG'; import Firebase from '../Firebase'; import ROUTES from '../../ROUTES'; @@ -71,6 +72,14 @@ function setSidebarLoaded() { printPerformanceMetrics(); } +let appState; +AppState.addEventListener('change', (nextAppState) => { + if (nextAppState.match(/inactive|background/) && appState === 'active') { + Log.info('Flushing logs as app is going inactive', true, {}, true); + } + appState = nextAppState; +}); + export { setCurrentURL, setLocale, diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 1685997c382a..d9738e883bcf 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -309,7 +309,7 @@ function fetchChatReportsByIDs(chatList, shouldRedirectIfInacessible = false) { const simplifiedReports = {}; return API.GetReportSummaryList({reportIDList: chatList.join(',')}) .then(({reportSummaryList, jsonCode}) => { - Log.info('[Report] successfully fetched report data', true); + Log.info('[Report] successfully fetched report data', false, {chatList}); fetchedReports = reportSummaryList; // If we receive a 404 response while fetching a single report, treat that report as inacessible. @@ -329,7 +329,7 @@ function fetchChatReportsByIDs(chatList, shouldRedirectIfInacessible = false) { return; } if (participants.length === 0) { - Log.alert('[Report] Report with IOU action but does not have any participant.', true, { + Log.alert('[Report] Report with IOU action but does not have any participant.', { reportID: chatReport.reportID, participants, }); @@ -682,7 +682,7 @@ function subscribeToUserEvents() { // Live-update a report's actions when a 'report comment' event is received. Pusher.subscribe(pusherChannelName, Pusher.TYPE.REPORT_COMMENT, (pushJSON) => { Log.info( - `[Report] Handled ${Pusher.TYPE.REPORT_COMMENT} event sent by Pusher`, true, {reportID: pushJSON.reportID}, + `[Report] Handled ${Pusher.TYPE.REPORT_COMMENT} event sent by Pusher`, false, {reportID: pushJSON.reportID}, ); updateReportWithNewAction(pushJSON.reportID, pushJSON.reportAction, pushJSON.notificationPreference); }, false, @@ -692,7 +692,7 @@ function subscribeToUserEvents() { .catch((error) => { Log.info( '[Report] Failed to subscribe to Pusher channel', - true, + false, {error, pusherChannelName, eventName: Pusher.TYPE.REPORT_COMMENT}, ); }); @@ -700,7 +700,7 @@ function subscribeToUserEvents() { // Live-update a report's actions when an 'edit comment' event is received. Pusher.subscribe(pusherChannelName, Pusher.TYPE.REPORT_COMMENT_EDIT, (pushJSON) => { Log.info( - `[Report] Handled ${Pusher.TYPE.REPORT_COMMENT_EDIT} event sent by Pusher`, true, { + `[Report] Handled ${Pusher.TYPE.REPORT_COMMENT_EDIT} event sent by Pusher`, false, { reportActionID: pushJSON.reportActionID, }, ); @@ -712,7 +712,7 @@ function subscribeToUserEvents() { .catch((error) => { Log.info( '[Report] Failed to subscribe to Pusher channel', - true, + false, {error, pusherChannelName, eventName: Pusher.TYPE.REPORT_COMMENT_EDIT}, ); }); @@ -721,7 +721,7 @@ function subscribeToUserEvents() { Pusher.subscribe(pusherChannelName, Pusher.TYPE.REPORT_TOGGLE_PINNED, (pushJSON) => { Log.info( `[Report] Handled ${Pusher.TYPE.REPORT_TOGGLE_PINNED} event sent by Pusher`, - true, + false, {reportID: pushJSON.reportID}, ); updateReportPinnedState(pushJSON.reportID, pushJSON.isPinned); @@ -732,13 +732,13 @@ function subscribeToUserEvents() { .catch((error) => { Log.info( '[Report] Failed to subscribe to Pusher channel', - true, + false, {error, pusherChannelName, eventName: Pusher.TYPE.REPORT_TOGGLE_PINNED}, ); }); PushNotification.onReceived(PushNotification.TYPE.REPORT_COMMENT, ({reportID, reportAction}) => { - Log.info('[Report] Handled event sent by Airship', true, {reportID}); + Log.info('[Report] Handled event sent by Airship', false, {reportID}); updateReportWithNewAction(reportID, reportAction); }); @@ -810,7 +810,7 @@ function subscribeToReportTypingEvents(reportID) { }, 1500); }) .catch((error) => { - Log.info('[Report] Failed to initially subscribe to Pusher channel', true, {error, pusherChannelName}); + Log.info('[Report] Failed to initially subscribe to Pusher channel', false, {error, pusherChannelName}); }); } @@ -877,7 +877,7 @@ function fetchActions(reportID, offset) { const reportActionsOffset = !_.isUndefined(offset) ? offset : -1; if (!_.isNumber(reportActionsOffset)) { - Log.alert('[Report] Offset provided is not a number', true, { + Log.alert('[Report] Offset provided is not a number', { offset, reportActionsOffset, }); diff --git a/src/libs/actions/Session.js b/src/libs/actions/Session.js index eeb303a25c54..6313f3aac569 100644 --- a/src/libs/actions/Session.js +++ b/src/libs/actions/Session.js @@ -5,6 +5,7 @@ import ONYXKEYS from '../../ONYXKEYS'; import redirectToSignIn from './SignInRedirect'; import * as API from '../API'; import CONFIG from '../../CONFIG'; +import Log from '../Log'; import PushNotification from '../Notification/PushNotification'; import Timing from './Timing'; import CONST from '../../CONST'; @@ -63,6 +64,7 @@ function createAccount(login) { * Clears the Onyx store and redirects user to the sign in page */ function signOut() { + Log.info('Flushing logs before signing out', true, {}, true); if (credentials && credentials.autoGeneratedLogin) { // Clean up the login that we created API.DeleteLogin({ diff --git a/src/libs/actions/User.js b/src/libs/actions/User.js index e2428a124d04..c215f83c9f05 100644 --- a/src/libs/actions/User.js +++ b/src/libs/actions/User.js @@ -262,7 +262,7 @@ function subscribeToUserEvents() { .catch((error) => { Log.info( '[User] Failed to subscribe to Pusher channel', - true, + false, {error, pusherChannelName, eventName: Pusher.TYPE.PREFERRED_LOCALE}, ); }); diff --git a/src/libs/translate.js b/src/libs/translate.js index 4716a25841e9..d8f2e82bb049 100644 --- a/src/libs/translate.js +++ b/src/libs/translate.js @@ -46,7 +46,7 @@ function translate(locale = CONST.DEFAULT_LOCALE, phrase, variables = {}) { return Str.result(translationValue, variables); } if (localeLanguage !== 'en') { - Log.alert(`${phrase} was not found in the ${localeLanguage} locale`, 0, {}, false); + Log.alert(`${phrase} was not found in the ${localeLanguage} locale`); } // Phrase is not translated, search it in default language (en) @@ -59,7 +59,7 @@ function translate(locale = CONST.DEFAULT_LOCALE, phrase, variables = {}) { // on development throw an error if (Config.IS_IN_PRODUCTION) { const phraseString = Array.isArray(phrase) ? phrase.join('.') : phrase; - Log.alert(`${phraseString} was not found in the en locale`, 0, {}, false); + Log.alert(`${phraseString} was not found in the en locale`); return phraseString; } throw new Error(`${phrase} was not found in the default language`); diff --git a/src/pages/EnablePayments/AdditionalDetailsStep.js b/src/pages/EnablePayments/AdditionalDetailsStep.js index dd65d99b9a4c..bf943b4966fb 100644 --- a/src/pages/EnablePayments/AdditionalDetailsStep.js +++ b/src/pages/EnablePayments/AdditionalDetailsStep.js @@ -62,7 +62,7 @@ class AdditionalDetailsStep extends React.Component { fieldName: 'legalLastName', }, { - label: props.translate('common.addressNoPO'), + label: props.translate('common.personalAddress'), fieldName: 'addressStreet', }, { @@ -128,17 +128,20 @@ class AdditionalDetailsStep extends React.Component { {_.map(this.fields, field => ( - this.setState({[field.fieldName]: val})} - value={this.state[field.fieldName]} - errorText={errorFields.includes(field.fieldName) - ? `${field.label} ${this.requiredText}` - : ''} - // eslint-disable-next-line react/jsx-props-no-spreading - {..._.omit(field, ['label', 'fieldName'])} - /> + <> + this.setState({[field.fieldName]: val})} + value={this.state[field.fieldName]} + errorText={errorFields.includes(field.fieldName) + ? `${field.label} ${this.requiredText}` + : ''} + // eslint-disable-next-line react/jsx-props-no-spreading + {..._.omit(field, ['label', 'fieldName'])} + /> + {field.fieldName === 'addressStreet' && {this.props.translate('common.noPO')}} + ))} diff --git a/src/pages/ReimbursementAccount/CompanyStep.js b/src/pages/ReimbursementAccount/CompanyStep.js index c1ccb59f1cf7..de3fa93ae32a 100644 --- a/src/pages/ReimbursementAccount/CompanyStep.js +++ b/src/pages/ReimbursementAccount/CompanyStep.js @@ -170,7 +170,7 @@ class CompanyStep extends React.Component { disabled={shouldDisableCompanyName} /> { if (error === this.props.translate('bankAccount.error.addressStreet')) { @@ -183,6 +183,7 @@ class CompanyStep extends React.Component { ? this.props.translate('bankAccount.error.addressStreet') : ''} /> + {this.props.translate('common.noPO')} { @@ -124,6 +124,7 @@ const IdentityForm = ({ }} errorText={error === translateLocal('bankAccount.error.address') ? error : ''} /> + {translate('common.noPO')} + { + if (this.textInput) { + this.textInput.focus(); + } + }} + > this.toggleOption(item.currencyCode)} isSelected={ diff --git a/src/pages/settings/InitialPage.js b/src/pages/settings/InitialPage.js index ca3774b4cd91..8bfa58596188 100755 --- a/src/pages/settings/InitialPage.js +++ b/src/pages/settings/InitialPage.js @@ -174,7 +174,7 @@ const InitialSettingsPage = ({ /> - + {myPersonalDetails.displayName ? myPersonalDetails.displayName diff --git a/src/pages/settings/NewPasswordForm.js b/src/pages/settings/NewPasswordForm.js index a17d1bc7d560..8a6dce156b25 100644 --- a/src/pages/settings/NewPasswordForm.js +++ b/src/pages/settings/NewPasswordForm.js @@ -98,7 +98,7 @@ class NewPasswordForm extends React.Component { secureTextEntry autoCompleteType="password" textContentType="password" - value={this.state.password} + value={this.props.password} onChangeText={password => this.props.updatePassword(password)} onBlur={() => this.onBlurNewPassword()} /> diff --git a/src/pages/settings/PasswordPage.js b/src/pages/settings/PasswordPage.js index d959d39a5c7b..b1febd78bfa5 100755 --- a/src/pages/settings/PasswordPage.js +++ b/src/pages/settings/PasswordPage.js @@ -71,15 +71,15 @@ class PasswordPage extends Component { if (this.state.newPassword && this.state.confirmNewPassword && !this.doPasswordsMatch()) { stateToUpdate.shouldShowPasswordConfirmError = true; + } else { + stateToUpdate.shouldShowPasswordConfirmError = false; } - if (!isEmpty(stateToUpdate)) { - this.setState(stateToUpdate); - } + this.setState(stateToUpdate); } onBlurConfirmPassword() { - if (!this.state.confirmNewPassword || !this.doPasswordsMatch()) { + if ((this.state.newPassword && !this.state.confirmNewPassword) || !this.doPasswordsMatch()) { this.setState({shouldShowPasswordConfirmError: true}); } else { this.setState({shouldShowPasswordConfirmError: false}); diff --git a/src/pages/workspace/WorkspaceInvitePage.js b/src/pages/workspace/WorkspaceInvitePage.js index 0b6c2f88ab9e..e50a52266516 100644 --- a/src/pages/workspace/WorkspaceInvitePage.js +++ b/src/pages/workspace/WorkspaceInvitePage.js @@ -55,7 +55,9 @@ class WorkspaceInvitePage extends React.Component { welcomeNote: '', }; + this.focusEmailOrPhoneInput = this.focusEmailOrPhoneInput.bind(this); this.inviteUser = this.inviteUser.bind(this); + this.emailOrPhoneInputRef = null; } /** @@ -69,6 +71,13 @@ class WorkspaceInvitePage extends React.Component { }); } + focusEmailOrPhoneInput() { + if (!this.emailOrPhoneInputRef) { + return; + } + this.emailOrPhoneInputRef.focus(); + } + /** * Handle the invite button click */ @@ -93,7 +102,7 @@ class WorkspaceInvitePage extends React.Component { render() { return ( - + this.emailOrPhoneInputRef = el} label={this.props.translate('workspace.invite.enterEmailOrPhone')} autoCompleteType="email" autoCorrect={false} diff --git a/src/styles/styles.js b/src/styles/styles.js index d71e788b16a4..8905981ddb73 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -85,6 +85,12 @@ const styles = { lineHeight: 18, }, + mutedTextLabel: { + color: themeColors.textSupporting, + fontSize: variables.fontSizeLabel, + lineHeight: 18, + }, + textMicro: { fontFamily: fontFamily.GTA, fontSize: variables.fontSizeSmall, diff --git a/src/styles/utilities/sizing.js b/src/styles/utilities/sizing.js index 9724e9acf798..d823eb9856fa 100644 --- a/src/styles/utilities/sizing.js +++ b/src/styles/utilities/sizing.js @@ -24,6 +24,10 @@ export default { maxWidth: 'auto', }, + mw100: { + maxWidth: '100%', + }, + w1: { width: 4, }, diff --git a/test.md b/test.md new file mode 100644 index 000000000000..db0d8318b8f5 --- /dev/null +++ b/test.md @@ -0,0 +1 @@ +This is for testing purposes, I will remove this file in subsequent PR. \ No newline at end of file diff --git a/test_webhook.md b/test_webhook.md deleted file mode 100644 index 15b7e2b38138..000000000000 --- a/test_webhook.md +++ /dev/null @@ -1 +0,0 @@ -Just PR for testing a Webhook, I will remove this file in consequent PR. \ No newline at end of file