diff --git a/.github/workflows/cherryPick.yml b/.github/workflows/cherryPick.yml index 14f20f707d2b..f26bbd7148b2 100644 --- a/.github/workflows/cherryPick.yml +++ b/.github/workflows/cherryPick.yml @@ -190,7 +190,8 @@ jobs: channel: '#announce', attachments: [{ color: "#DB4545", - text: `@mobile-deployers 💥 Failed to CP https://github.com/Expensify/App/pull/${{ github.event.inputs.PULL_REQUEST_NUMBER }} to staging 💥`, + pretext: ``, + text: `💥 Failed to CP https://github.com/Expensify/App/pull/${{ github.event.inputs.PULL_REQUEST_NUMBER }} to staging 💥`, }] } env: diff --git a/.github/workflows/platformDeploy.yml b/.github/workflows/platformDeploy.yml index 93eb6980ca54..65976854ae58 100644 --- a/.github/workflows/platformDeploy.yml +++ b/.github/workflows/platformDeploy.yml @@ -223,11 +223,17 @@ jobs: APPLE_DEMO_EMAIL: ${{ secrets.APPLE_DEMO_EMAIL }} APPLE_DEMO_PASSWORD: ${{ secrets.APPLE_DEMO_PASSWORD }} + - name: Set iOS version in ENV + if: ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + run: | + echo "IOS_VERSION=$(echo '${{ github.event.release.tag_name }}' | tr '-' '.')" >> $GITHUB_ENV + echo "iOS version is: ${{ env.IOS_VERSION }}" + - name: Run Fastlane for App Store release if: ${{ env.SHOULD_DEPLOY_PRODUCTION == 'true' }} run: bundle exec fastlane ios production env: - VERSION: ${{ env.NEW_IOS_VERSION }} + VERSION: ${{ env.IOS_VERSION }} web: name: Build and deploy Web diff --git a/android/app/build.gradle b/android/app/build.gradle index c0dc4bb8f28e..4876e31ed2c2 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 1001009002 - versionName "1.0.90-2" + versionCode 1001009003 + versionName "1.0.90-3" } splits { abi { diff --git a/fastlane/Fastfile b/fastlane/Fastfile index f86243f49880..553d4a2bf228 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -142,7 +142,7 @@ platform :ios do api_key_path: "./ios/ios-fastlane-json-key.json", build_number: ENV["VERSION"], submit_for_review: true, - automatic_release: false, + automatic_release: true, skip_metadata: true, skip_screenshots: true, skip_binary_upload: true, diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 1df1d1dfeb3f..588af784d614 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -30,7 +30,7 @@ CFBundleVersion - 1.0.90.2 + 1.0.90.3 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index c5e85d08550c..330352b540c4 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.0.90.2 + 1.0.90.3 diff --git a/package-lock.json b/package-lock.json index 8b47f82dbc97..f99821e734fb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.0.90-2", + "version": "1.0.90-3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index ae0048ef0934..63cc8441ce7e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.0.90-2", + "version": "1.0.90-3", "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/components/Button.js b/src/components/Button.js index be1f13ac5131..52586b44d7e2 100644 --- a/src/components/Button.js +++ b/src/components/Button.js @@ -1,17 +1,21 @@ import _ from 'underscore'; import React, {Component} from 'react'; -import {Pressable, ActivityIndicator} from 'react-native'; +import {Pressable, ActivityIndicator, View} from 'react-native'; import PropTypes from 'prop-types'; import styles from '../styles/styles'; import themeColors from '../styles/themes/default'; import OpacityView from './OpacityView'; import Text from './Text'; import KeyboardShortcut from '../libs/KeyboardShortcut'; +import Icon from './Icon'; const propTypes = { /** The text for the button label */ text: PropTypes.string, + /** The icon asset to display to the left of the text */ + icon: PropTypes.func, + /** Small sized button */ small: PropTypes.bool, @@ -57,6 +61,7 @@ const propTypes = { const defaultProps = { text: '', + icon: null, isLoading: false, isDisabled: false, small: false, @@ -107,24 +112,42 @@ class Button extends Component { return ; } - return this.props.isLoading - ? ( - - ) : ( - - {this.props.text} - + if (this.props.isLoading) { + return ; + } + + const textComponent = ( + + {this.props.text} + + ); + + if (this.props.icon) { + return ( + + + + + {textComponent} + ); + } + + return textComponent; } render() { diff --git a/src/components/IOUConfirmationList.js b/src/components/IOUConfirmationList.js index 3be35546855a..b3e946a3f347 100755 --- a/src/components/IOUConfirmationList.js +++ b/src/components/IOUConfirmationList.js @@ -8,6 +8,7 @@ import styles from '../styles/styles'; import Text from './Text'; import themeColors from '../styles/themes/default'; import { + addSMSDomainIfPhoneNumber, getIOUConfirmationOptionsFromMyPersonalDetail, getIOUConfirmationOptionsFromParticipants, } from '../libs/OptionsListUtils'; @@ -225,7 +226,7 @@ class IOUConfirmationList extends Component { } const selectedParticipants = this.getSelectedParticipants(); const splits = _.map(selectedParticipants, participant => ({ - email: participant.login, + email: addSMSDomainIfPhoneNumber(participant.login), // We should send in cents to API // Cents is temporary and there must be support for other currencies in the future @@ -233,7 +234,7 @@ class IOUConfirmationList extends Component { })); splits.push({ - email: this.props.myPersonalDetails.login, + email: addSMSDomainIfPhoneNumber(this.props.myPersonalDetails.login), // The user is default and we should send in cents to API // USD is temporary and there must be support for other currencies in the future diff --git a/src/components/InboxCallButton.js b/src/components/InboxCallButton.js index ef271cfca57f..690a4e4bfd5c 100644 --- a/src/components/InboxCallButton.js +++ b/src/components/InboxCallButton.js @@ -1,18 +1,13 @@ import React from 'react'; -import { - View, Pressable, -} from 'react-native'; import PropTypes from 'prop-types'; -import Icon from './Icon'; -import {Phone} from './Icon/Expensicons'; import styles from '../styles/styles'; -import themeColors from '../styles/themes/default'; import withLocalize, {withLocalizePropTypes} from './withLocalize'; import compose from '../libs/compose'; import Navigation from '../libs/Navigation/Navigation'; import ROUTES from '../ROUTES'; -import Text from './Text'; import Tooltip from './Tooltip'; +import Button from './Button'; +import {Phone} from './Icon/Expensicons'; const propTypes = { ...withLocalizePropTypes, @@ -30,27 +25,14 @@ const InboxCallButton = props => ( text={props.translate('requestCallPage.needHelpTooltip')} containerStyles={[styles.justifyContentCenter, styles.alignItemsCenter]} > - { Navigation.navigate(ROUTES.getRequestCallRoute(props.taskID)); }} - style={[styles.button, styles.buttonSmall]} - > - - - - - - - {props.translate('requestCallPage.needHelp')} - - - - + text={props.translate('requestCallPage.needHelp')} + small + icon={Phone} + /> ); diff --git a/src/languages/en.js b/src/languages/en.js index 560e7d692f25..dfddc0518f81 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -336,7 +336,6 @@ export default { loginForm: { pleaseEnterEmailOrPhoneNumber: 'Please enter an email or phone number', phoneOrEmail: 'Phone or email', - enterYourPhoneOrEmail: 'Enter your phone or email:', }, resendValidationForm: { linkHasBeenResent: 'Link has been re-sent', diff --git a/src/languages/es.js b/src/languages/es.js index e7c6bedbe49f..e9b49d836755 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -336,7 +336,6 @@ export default { loginForm: { pleaseEnterEmailOrPhoneNumber: 'Por favor escribe un email o número de teléfono', phoneOrEmail: 'Número de teléfono o email', - enterYourPhoneOrEmail: 'Escribe tu número de teléfono o email:', }, resendValidationForm: { linkHasBeenResent: 'El enlace se ha reenviado', @@ -552,17 +551,17 @@ export default { forNextSteps: ' para conocer los próximos pasos para terminar de configurar su cuenta bancaria.', }, beneficialOwnersStep: { - beneficialOwners: 'Beneficial Owners', - additionalInformation: 'Additional Information', - checkAllThatApply: '(check all that apply, otherwise leave blank)', - iOwnMoreThan25Percent: 'I own more than 25% of ', - someoneOwnsMoreThan25Percent: 'Somebody else owns more than 25% of ', - additionalOwner: 'Additional Beneficial Owner', - removeOwner: 'Remove this beneficial owner', - addAnotherIndividual: 'Add another individual who owns more than 25% of ', - agreement: 'Agreement:', - termsAndConditions: 'terms and conditions', - certifyTrueAndAccurate: 'I certify that the information provided is true and accurate', + beneficialOwners: 'Beneficiario efectivo', + additionalInformation: 'Información adicional', + checkAllThatApply: '(marca todos los que apliquen, en caso de que ninguno aplique dejar en blanco)', + iOwnMoreThan25Percent: 'Soy dueño de mas de 25% de ', + someoneOwnsMoreThan25Percent: 'Otra persona es dueña de mas de 25% de ', + additionalOwner: 'Beneficiario efectivo adicional', + removeOwner: 'Eliminar este beneficiario efectivo', + addAnotherIndividual: 'Agregar otra persona que es dueña de mas de 25% de ', + agreement: 'Acuerdo:', + termsAndConditions: 'Términos y condiciones', + certifyTrueAndAccurate: 'Certifico que la información dada es correcta', error: { termsAndConditions: 'Debe aceptar términos y condiciones', certify: 'Debe certificar que la información es verdadera y precisa', diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 4123ef8e0ae7..7e7c38188cd9 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -106,7 +106,7 @@ function getPersonalDetailsForLogins(logins, personalDetails) { if (!personalDetail) { personalDetail = { - login: addSMSDomainIfPhoneNumber(login), + login, displayName: login, avatar: getDefaultAvatar(login), }; @@ -239,8 +239,7 @@ function createOption(personalDetailList, report, draftComments, { // It doesn't make sense to provide a login in the case of a report with multiple participants since // there isn't any one single login to refer to for a report. - // If single login is a mobile number, appending SMS domain - login: !hasMultipleParticipants ? addSMSDomainIfPhoneNumber(personalDetail.login) : null, + login: !hasMultipleParticipants ? personalDetail.login : null, reportID: report ? report.reportID : null, isUnread: report ? report.unreadActionCount > 0 : null, hasDraftComment: _.size(reportDraftComment) > 0, @@ -289,7 +288,7 @@ function isCurrentUser(userDetails) { return false; } - // If user login is mobile number, append sms domain if not appended already just a fail safe. + // If user login is mobile number, append sms domain if not appended already. const userDetailsLogin = addSMSDomainIfPhoneNumber(userDetails.login); // Initial check with currentUserLogin diff --git a/src/libs/actions/BankAccounts.js b/src/libs/actions/BankAccounts.js index ef1acaf27d6c..d961aa679203 100644 --- a/src/libs/actions/BankAccounts.js +++ b/src/libs/actions/BankAccounts.js @@ -346,6 +346,7 @@ function fetchFreePlanVerifiedBankAccount(stepToOpen) { // and determine which step to navigate to. Onyx.set(ONYXKEYS.REIMBURSEMENT_ACCOUNT, { loading: true, + error: '', // We temporarily keep the achData state to prevent UI changes while fetching. achData: {state: lodashGet(reimbursementAccountInSetup, 'state', '')}, diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js index 0a964289f5f4..6ce02391258c 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js @@ -6,7 +6,7 @@ import Str from 'expensify-common/lib/str'; import {View} from 'react-native'; import PropTypes from 'prop-types'; import ScreenWrapper from '../../components/ScreenWrapper'; -import {fetchFreePlanVerifiedBankAccount} from '../../libs/actions/BankAccounts'; +import {fetchFreePlanVerifiedBankAccount, hideBankAccountErrors} from '../../libs/actions/BankAccounts'; import ONYXKEYS from '../../ONYXKEYS'; import VBALoadingIndicator from '../../components/VBALoadingIndicator'; import Permissions from '../../libs/Permissions'; @@ -105,6 +105,7 @@ class ReimbursementAccountPage extends React.Component { // When the step changes we will navigate to update the route params. This is mostly cosmetic as we only use // the route params when the component first mounts to jump to a specific route instead of picking up where the // user left off in the flow. + hideBankAccountErrors(); Navigation.navigate(ROUTES.getBankAccountRoute(this.getRouteForCurrentStep(currentStep))); } diff --git a/src/pages/ReimbursementAccount/ValidationStep.js b/src/pages/ReimbursementAccount/ValidationStep.js index c10dd28ee004..60dcd7428ab1 100644 --- a/src/pages/ReimbursementAccount/ValidationStep.js +++ b/src/pages/ReimbursementAccount/ValidationStep.js @@ -142,7 +142,7 @@ class ValidationStep extends React.Component { value={this.state.amount3} onChangeText={amount3 => this.setState({amount3})} /> - {errorMessage && ( + {!_.isEmpty(errorMessage) && ( {errorMessage} diff --git a/src/pages/iou/IOUModal.js b/src/pages/iou/IOUModal.js index 1fd560f46fb5..f71ce27f4d88 100755 --- a/src/pages/iou/IOUModal.js +++ b/src/pages/iou/IOUModal.js @@ -3,6 +3,7 @@ import {View, TouchableOpacity} from 'react-native'; import PropTypes from 'prop-types'; import lodashGet from 'lodash/get'; import {withOnyx} from 'react-native-onyx'; +import Str from 'expensify-common/lib/str'; import IOUAmountPage from './steps/IOUAmountPage'; import IOUParticipantsPage from './steps/IOUParticipantsPage'; import IOUConfirmPage from './steps/IOUConfirmPage'; @@ -17,7 +18,7 @@ import Navigation from '../../libs/Navigation/Navigation'; import ONYXKEYS from '../../ONYXKEYS'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; import compose from '../../libs/compose'; -import {getPersonalDetailsForLogins} from '../../libs/OptionsListUtils'; +import {addSMSDomainIfPhoneNumber, getPersonalDetailsForLogins} from '../../libs/OptionsListUtils'; import FullScreenLoadingIndicator from '../../components/FullscreenLoadingIndicator'; import ScreenWrapper from '../../components/ScreenWrapper'; import Tooltip from '../../components/Tooltip'; @@ -109,7 +110,7 @@ class IOUModal extends Component { .map(personalDetails => ({ login: personalDetails.login, text: personalDetails.displayName, - alternateText: personalDetails.login, + alternateText: Str.isSMSLogin(personalDetails.login) ? Str.removeSMSDomain(personalDetails.login) : personalDetails.login, icons: [personalDetails.avatar], keyForList: personalDetails.login, })); @@ -270,7 +271,7 @@ class IOUModal extends Component { // should send in cents to API amount: Math.round(this.state.amount * 100), currency: this.props.iou.selectedCurrencyCode, - debtorEmail: this.state.participants[0].login, + debtorEmail: addSMSDomainIfPhoneNumber(this.state.participants[0].login), }); } diff --git a/src/pages/signin/LoginForm.js b/src/pages/signin/LoginForm.js index 9ffe5e491fc9..e3b4bfd3f09d 100755 --- a/src/pages/signin/LoginForm.js +++ b/src/pages/signin/LoginForm.js @@ -4,7 +4,6 @@ import {withOnyx} from 'react-native-onyx'; import PropTypes from 'prop-types'; import _ from 'underscore'; import styles from '../../styles/styles'; -import themeColors from '../../styles/themes/default'; import Button from '../../components/Button'; import Text from '../../components/Text'; import {fetchAccountDetails} from '../../libs/actions/Session'; @@ -83,8 +82,6 @@ class LoginForm extends React.Component { autoCapitalize="none" autoCorrect={false} keyboardType={getEmailKeyboardType()} - placeholder={this.props.translate('loginForm.enterYourPhoneOrEmail')} - placeholderTextColor={themeColors.placeholderText} autoFocus={canFocusInputOnScreenFocus()} translateX={-18} /> diff --git a/src/pages/workspace/WorkspacePeoplePage.js b/src/pages/workspace/WorkspacePeoplePage.js index a396454083fe..f32720f7cb40 100644 --- a/src/pages/workspace/WorkspacePeoplePage.js +++ b/src/pages/workspace/WorkspacePeoplePage.js @@ -198,7 +198,7 @@ class WorkspacePeoplePage extends React.Component { - Admin + {this.props.translate('common.admin')} diff --git a/tests/unit/OptionsListUtilsTest.js b/tests/unit/OptionsListUtilsTest.js index 0a16c61e642b..0bdcea4ca963 100644 --- a/tests/unit/OptionsListUtilsTest.js +++ b/tests/unit/OptionsListUtilsTest.js @@ -445,7 +445,7 @@ describe('OptionsListUtils', () => { expect(results.recentReports.length).toBe(0); expect(results.personalDetails.length).toBe(0); expect(results.userToInvite).not.toBe(null); - expect(results.userToInvite.login).toBe(`+15005550006${CONST.SMS.DOMAIN}`); + expect(results.userToInvite.login).toBe('+15005550006'); // When we add a search term for which no options exist and the searchValue itself // is a potential phone number with country code added @@ -456,7 +456,7 @@ describe('OptionsListUtils', () => { expect(results.recentReports.length).toBe(0); expect(results.personalDetails.length).toBe(0); expect(results.userToInvite).not.toBe(null); - expect(results.userToInvite.login).toBe(`+15005550006${CONST.SMS.DOMAIN}`); + expect(results.userToInvite.login).toBe('+15005550006'); // Test Concierge's existence in new group options results = OptionsListUtils.getNewGroupOptions(REPORTS_WITH_CONCIERGE, PERSONAL_DETAILS_WITH_CONCIERGE);