diff --git a/FORMS.md b/FORMS.md index 65e9cfaeae0c..d80b2dff3310 100644 --- a/FORMS.md +++ b/FORMS.md @@ -210,4 +210,4 @@ Form.js will automatically provide the following props to any input with the inp - defaultValue: The input default value. - errorText: The translated error text that is returned by validate for that specific input. - onBlur: An onBlur handler that calls validate. -- onChange: An onChange handler that saves draft values and calls validate. +- onInputChange: An onChange handler that saves draft values and calls validate for that input (inputA). Passing an inputID as a second param allows inputA to manipulate the input value of the provided inputID (inputB). diff --git a/android/app/build.gradle b/android/app/build.gradle index 443f02db466b..47c964dc1874 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -152,8 +152,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001017001 - versionName "1.1.70-1" + versionCode 1001017002 + versionName "1.1.70-2" } splits { abi { diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 03b85a8b6e04..35bb5c0abe92 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -30,7 +30,7 @@ CFBundleVersion - 1.1.70.1 + 1.1.70.2 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 03f71a81554c..fe19515cd8f5 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.1.70.1 + 1.1.70.2 diff --git a/package-lock.json b/package-lock.json index 6e34320b54e1..2ac942b3c2d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.1.70-1", + "version": "1.1.70-2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 28c258a37659..196d773e4ae5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.1.70-1", + "version": "1.1.70-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/components/AddressSearch.js b/src/components/AddressSearch.js index 9458a85361f4..7ad5474ae06b 100644 --- a/src/components/AddressSearch.js +++ b/src/components/AddressSearch.js @@ -106,7 +106,11 @@ const AddressSearch = (props) => { if (_.size(values) === 0) { return; } - props.onInputChange(values); + if (props.inputID) { + _.each(values, (value, key) => props.onInputChange(value, key)); + } else { + props.onInputChange(values); + } }; return ( @@ -161,7 +165,7 @@ const AddressSearch = (props) => { label: props.label, containerStyles: props.containerStyles, errorText: props.errorText, - hint: props.hint, + hint: displayListViewBorder ? undefined : props.hint, value: props.value, defaultValue: props.defaultValue, inputID: props.inputID, diff --git a/src/components/Banner.js b/src/components/Banner.js index a7ac463646f1..a4ea17eec2c8 100644 --- a/src/components/Banner.js +++ b/src/components/Banner.js @@ -23,6 +23,7 @@ const Banner = props => ( styles.p5, styles.borderRadiusNormal, isHovered ? styles.activeComponentBG : styles.hoveredComponentBG, + styles.breakAll, ]} > diff --git a/src/components/Button.js b/src/components/Button.js index a315abc160fc..4fcc046e0f7c 100644 --- a/src/components/Button.js +++ b/src/components/Button.js @@ -173,14 +173,11 @@ class Button extends Component { return ; } - if (this.props.isLoading) { - return ; - } - const textComponent = ( {this.renderContent()} + {this.props.isLoading && ( + + )} )} diff --git a/src/components/Form.js b/src/components/Form.js index 41fe3bc1b480..e33d3a959274 100644 --- a/src/components/Form.js +++ b/src/components/Form.js @@ -158,10 +158,16 @@ class Form extends React.Component { this.setTouchedInput(inputID); this.validate(this.inputValues); }, - onInputChange: (value) => { - this.inputValues[inputID] = value; + onInputChange: (value, key) => { + const inputKey = key || inputID; + this.inputValues[inputKey] = value; + const inputRef = this.inputRefs[inputKey]; + + if (key && inputRef && _.isFunction(inputRef.setNativeProps)) { + inputRef.setNativeProps({value}); + } if (child.props.shouldSaveDraft) { - FormActions.setDraftValues(this.props.formID, {[inputID]: value}); + FormActions.setDraftValues(this.props.formID, {[inputKey]: value}); } this.validate(this.inputValues); }, diff --git a/src/components/Modal/BaseModal.js b/src/components/Modal/BaseModal.js index 3779304830f0..105d4cef3230 100644 --- a/src/components/Modal/BaseModal.js +++ b/src/components/Modal/BaseModal.js @@ -77,6 +77,7 @@ class BaseModal extends PureComponent { ); return ( { if (e && e.type === 'keydown' && e.key === 'Enter') { return; diff --git a/src/components/ReportTransaction.js b/src/components/ReportTransaction.js index 599fa482c4b0..c201434d9569 100644 --- a/src/components/ReportTransaction.js +++ b/src/components/ReportTransaction.js @@ -1,18 +1,16 @@ import React, {Component} from 'react'; import PropTypes from 'prop-types'; import lodashGet from 'lodash/get'; -import { - View, Pressable, ActivityIndicator, -} from 'react-native'; +import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import ONYXKEYS from '../ONYXKEYS'; import styles from '../styles/styles'; -import themeColors from '../styles/themes/default'; import * as IOU from '../libs/actions/IOU'; import reportActionPropTypes from '../pages/home/report/reportActionPropTypes'; import ReportActionItemSingle from '../pages/home/report/ReportActionItemSingle'; import Text from './Text'; +import Button from './Button'; const propTypes = { /** The chatReport which the transaction is associated with */ @@ -88,30 +86,13 @@ class ReportTransaction extends Component { {this.props.canBeRejected && ( - - { - this.isBeingRejected() - ? ( - - ) - : ( - - {this.props.rejectButtonLabelText} - - ) - } - + isLoading={this.isBeingRejected()} + /> )} diff --git a/src/libs/Pusher/pusher.js b/src/libs/Pusher/pusher.js index 84e8b6fe70db..600d428f0be8 100644 --- a/src/libs/Pusher/pusher.js +++ b/src/libs/Pusher/pusher.js @@ -102,11 +102,10 @@ function getChannel(channelName) { * @param {Pusher.Channel} channel * @param {String} eventName * @param {Function} [eventCallback] - * @param {Boolean} [isChunked] Do we expect this channel to send chunked/separate blocks of data that need recombining? * * @private */ -function bindEventToChannel(channel, eventName, eventCallback = () => {}, isChunked = false) { +function bindEventToChannel(channel, eventName, eventCallback = () => {}) { if (!eventName) { return; } @@ -120,7 +119,7 @@ function bindEventToChannel(channel, eventName, eventCallback = () => {}, isChun Log.alert('[Pusher] Unable to parse JSON response from Pusher', {error: err, eventData}); return; } - if (!isChunked) { + if (data.id === undefined || data.chunk === undefined || data.final === undefined) { eventCallback(data); return; } @@ -169,9 +168,6 @@ function bindEventToChannel(channel, eventName, eventCallback = () => {}, isChun * @param {String} channelName * @param {String} eventName * @param {Function} [eventCallback] - * @param {Boolean} [isChunked] This parameters tells us whether or not we expect the result to come in individual - * pieces/chunks (because it exceeds - * the 10kB limit that pusher has). * @param {Function} [onResubscribe] Callback to be called when reconnection happen * * @return {Promise} @@ -182,7 +178,6 @@ function subscribe( channelName, eventName, eventCallback = () => {}, - isChunked = false, onResubscribe = () => {}, ) { return new Promise((resolve, reject) => { @@ -201,7 +196,7 @@ function subscribe( channel.bind('pusher:subscription_succeeded', () => { // Check so that we do not bind another event with each reconnect attempt if (!isBound) { - bindEventToChannel(channel, eventName, eventCallback, isChunked); + bindEventToChannel(channel, eventName, eventCallback); resolve(); isBound = true; return; @@ -225,7 +220,7 @@ function subscribe( reject(error); }); } else { - bindEventToChannel(channel, eventName, eventCallback, isChunked); + bindEventToChannel(channel, eventName, eventCallback); resolve(); } }); diff --git a/src/libs/actions/Inbox.js b/src/libs/actions/Inbox.js index 84662dba8a2b..4bd92abdf0bf 100644 --- a/src/libs/actions/Inbox.js +++ b/src/libs/actions/Inbox.js @@ -1,5 +1,6 @@ import Onyx from 'react-native-onyx'; import ONYXKEYS from '../../ONYXKEYS'; +import * as API from '../API'; import * as DeprecatedAPI from '../deprecatedAPI'; import Growl from '../Growl'; import * as Localize from '../Localize'; @@ -46,14 +47,11 @@ function requestInboxCall({ }); } -function getInboxCallWaitTime() { - DeprecatedAPI.Inbox_CallUser_WaitTime() - .then((data) => { - Onyx.set(ONYXKEYS.INBOX_CALL_USER_WAIT_TIME, data.waitTime); - }); +function openRequestCallPage() { + API.read('OpenRequestCallPage'); } export { requestInboxCall, - getInboxCallWaitTime, + openRequestCallPage, }; diff --git a/src/libs/actions/Policy.js b/src/libs/actions/Policy.js index cb0bea164d01..76f52becc26f 100644 --- a/src/libs/actions/Policy.js +++ b/src/libs/actions/Policy.js @@ -526,9 +526,12 @@ function subscribeToPolicyEvents() { // Remove the members from the policy Onyx.set(key, policyWithoutEmployee); - // Refetch the policy expense chats to update their state + // Refetch the policy expense chats to update their state and their actions to get the archive reason if (!_.isEmpty(policyExpenseChatIDs)) { Report.fetchChatReportsByIDs(policyExpenseChatIDs); + _.each(policyExpenseChatIDs, (reportID) => { + Report.fetchActions(reportID); + }); } // Remove the default chats if we are one of the users getting removed diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index c64d9a630cbf..79438cb60f8a 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -728,9 +728,8 @@ function getReportChannelName(reportID) { * * @param {String} eventName * @param {Function} onEvent - * @param {Boolean} isChunked */ -function subscribeToPrivateUserChannelEvent(eventName, onEvent, isChunked = false) { +function subscribeToPrivateUserChannelEvent(eventName, onEvent) { const pusherChannelName = `private-encrypted-user-accountID-${currentUserAccountID}${CONFIG.PUSHER.SUFFIX}`; /** @@ -759,7 +758,7 @@ function subscribeToPrivateUserChannelEvent(eventName, onEvent, isChunked = fals Log.hmmm('[Report] Failed to subscribe to Pusher channel', false, {error, pusherChannelName, eventName}); } - Pusher.subscribe(pusherChannelName, eventName, onEventPush, isChunked, onPusherResubscribeToPrivateUserChannel) + Pusher.subscribe(pusherChannelName, eventName, onEventPush, onPusherResubscribeToPrivateUserChannel) .catch(onSubscriptionFailed); } @@ -781,17 +780,13 @@ function subscribeToUserEvents() { subscribeToPrivateUserChannelEvent(Pusher.TYPE.REPORT_COMMENT, pushJSON => updateReportWithNewAction(pushJSON.reportID, pushJSON.reportAction, pushJSON.notificationPreference)); // Live-update a report's actions when a 'chunked report comment' event is received. - subscribeToPrivateUserChannelEvent( - Pusher.TYPE.REPORT_COMMENT_CHUNK, - pushJSON => updateReportWithNewAction(pushJSON.reportID, pushJSON.reportAction, pushJSON.notificationPreference), - true, - ); + subscribeToPrivateUserChannelEvent(Pusher.TYPE.REPORT_COMMENT_CHUNK, pushJSON => updateReportWithNewAction(pushJSON.reportID, pushJSON.reportAction, pushJSON.notificationPreference)); // Live-update a report's actions when an 'edit comment' event is received. subscribeToPrivateUserChannelEvent(Pusher.TYPE.REPORT_COMMENT_EDIT, pushJSON => updateReportActionMessage(pushJSON.reportID, pushJSON.sequenceNumber, pushJSON.message)); // Live-update a report's actions when an 'edit comment chunk' event is received. - subscribeToPrivateUserChannelEvent(Pusher.TYPE.REPORT_COMMENT_EDIT_CHUNK, pushJSON => updateReportActionMessage(pushJSON.reportID, pushJSON.sequenceNumber, pushJSON.message), true); + subscribeToPrivateUserChannelEvent(Pusher.TYPE.REPORT_COMMENT_EDIT_CHUNK, pushJSON => updateReportActionMessage(pushJSON.reportID, pushJSON.sequenceNumber, pushJSON.message)); // Live-update a report's pinned state when a 'report toggle pinned' event is received. subscribeToPrivateUserChannelEvent(Pusher.TYPE.REPORT_TOGGLE_PINNED, pushJSON => updateReportPinnedState(pushJSON.reportID, pushJSON.isPinned)); diff --git a/src/libs/actions/User.js b/src/libs/actions/User.js index 5727c0b880a1..16220acb7c80 100644 --- a/src/libs/actions/User.js +++ b/src/libs/actions/User.js @@ -305,7 +305,7 @@ function subscribeToUserEvents() { // Live-update an user's preferred locale Pusher.subscribe(pusherChannelName, Pusher.TYPE.PREFERRED_LOCALE, (pushJSON) => { Onyx.merge(ONYXKEYS.NVP_PREFERRED_LOCALE, pushJSON.preferredLocale); - }, false, + }, () => { NetworkConnection.triggerReconnectionCallbacks('pusher re-subscribed to private user channel'); }) @@ -320,7 +320,7 @@ function subscribeToUserEvents() { // Subscribe to screen share requests sent by GuidesPlus agents Pusher.subscribe(pusherChannelName, Pusher.TYPE.SCREEN_SHARE_REQUEST, (pushJSON) => { Onyx.merge(ONYXKEYS.SCREEN_SHARE_REQUEST, pushJSON); - }, false, + }, () => { NetworkConnection.triggerReconnectionCallbacks('pusher re-subscribed to private user channel'); }) @@ -351,7 +351,7 @@ function subscribeToExpensifyCardUpdates() { } else { Onyx.merge(ONYXKEYS.USER, {isCheckingDomain: pushJSON.isCheckingDomain}); } - }, false, + }, () => { NetworkConnection.triggerReconnectionCallbacks('pusher re-subscribed to private user channel'); }) diff --git a/src/libs/deprecatedAPI.js b/src/libs/deprecatedAPI.js index d154ee6c0472..67333741f0a7 100644 --- a/src/libs/deprecatedAPI.js +++ b/src/libs/deprecatedAPI.js @@ -843,15 +843,6 @@ function Inbox_CallUser(parameters) { return Network.post(commandName, parameters); } -/** - * Get the current wait time in minutes for an inbox call - * @returns {Promise} - */ -function Inbox_CallUser_WaitTime() { - const commandName = 'Inbox_CallUser_WaitTime'; - return Network.post(commandName); -} - /** * @param {Object} parameters * @param {String} parameters.reportIDList @@ -952,7 +943,6 @@ export { GetRequestCountryCode, Graphite_Timer, Inbox_CallUser, - Inbox_CallUser_WaitTime, PayIOU, PayWithWallet, PersonalDetails_GetForEmails, diff --git a/src/pages/RequestCallPage.js b/src/pages/RequestCallPage.js index b42ecfc57332..84617412d833 100644 --- a/src/pages/RequestCallPage.js +++ b/src/pages/RequestCallPage.js @@ -234,7 +234,7 @@ class RequestCallPage extends Component { return; } - Inbox.getInboxCallWaitTime(); + Inbox.openRequestCallPage(); } validatePhoneInput() { diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.js b/src/pages/home/report/ContextMenu/ContextMenuActions.js index 7196a4ce1b2e..c06d0fea408b 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.js +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.js @@ -51,6 +51,9 @@ export default [ let {sourceURL} = attachmentDetails; sourceURL = addEncryptedAuthTokenToURL(sourceURL); fileDownload(sourceURL, originalFileName); + if (closePopover) { + hideContextMenu(true, ReportActionComposeFocusManager.focus); + } }, getDescription: () => {}, }, diff --git a/src/pages/signin/ResendValidationForm.js b/src/pages/signin/ResendValidationForm.js index 3a19e901cc15..701cc41d8561 100755 --- a/src/pages/signin/ResendValidationForm.js +++ b/src/pages/signin/ResendValidationForm.js @@ -143,7 +143,6 @@ class ResendValidationForm extends React.Component { text={this.props.translate('resendValidationForm.resendLink')} isLoading={this.props.account.loading} onPress={this.validateAndSubmitForm} - style={styles.resendLinkButton} /> diff --git a/src/stories/Form.stories.js b/src/stories/Form.stories.js index 6473750dac6d..926aa40fddc2 100644 --- a/src/stories/Form.stories.js +++ b/src/stories/Form.stories.js @@ -104,7 +104,7 @@ const Template = (args) => { /> @@ -179,8 +179,8 @@ const defaultArgs = { if (!values.pickAnotherFruit) { errors.pickAnotherFruit = 'Please select a fruit'; } - if (!values.pickState) { - errors.pickState = 'Please select a state'; + if (!values.state) { + errors.state = 'Please select a state'; } if (!values.checkbox) { errors.checkbox = 'You must accept the Terms of Service to continue'; @@ -204,7 +204,7 @@ const defaultArgs = { dob: '1990-01-01', pickFruit: 'orange', pickAnotherFruit: 'apple', - pickState: 'AL', + state: 'AL', checkbox: false, }, }; @@ -221,7 +221,7 @@ InputError.args = { pickFruit: '', dob: '', pickAnotherFruit: '', - pickState: '', + state: '', checkbox: false, }, }; diff --git a/src/styles/styles.js b/src/styles/styles.js index 60159bba44fa..d46612af0886 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -320,6 +320,10 @@ const styles = { backgroundColor: 'transparent', }, + opacity0: { + opacity: 0, + }, + opacity1: { opacity: 1, }, @@ -952,10 +956,6 @@ const styles = { height: '100%', }, - resendLinkButton: { - minWidth: 124, - }, - sidebarFooter: { alignItems: 'center', display: 'flex', diff --git a/src/styles/utilities/positioning.js b/src/styles/utilities/positioning.js index 455566b4655d..f25c757fbfc3 100644 --- a/src/styles/utilities/positioning.js +++ b/src/styles/utilities/positioning.js @@ -15,6 +15,12 @@ export default { tn8: { top: -32, }, + l0: { + left: 0, + }, + r0: { + right: 0, + }, r4: { right: 16, },