Skip to content

Commit

Permalink
Merge branch 'main' into tgolen-remove-sideloading0
Browse files Browse the repository at this point in the history
  • Loading branch information
tgolen committed Sep 14, 2023
2 parents 9aececd + 63a5776 commit 286adad
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 63 deletions.
66 changes: 26 additions & 40 deletions src/libs/actions/OnyxUpdates.js → src/libs/actions/OnyxUpdates.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,25 @@
import Onyx from 'react-native-onyx';
import _ from 'underscore';
import Onyx, {OnyxEntry} from 'react-native-onyx';
import {Merge} from 'type-fest';
import PusherUtils from '../PusherUtils';
import ONYXKEYS from '../../ONYXKEYS';
import * as QueuedOnyxUpdates from './QueuedOnyxUpdates';
import CONST from '../../CONST';
import {OnyxUpdatesFromServer, OnyxUpdateEvent, Request} from '../../types/onyx';
import Response from '../../types/onyx/Response';

// This key needs to be separate from ONYXKEYS.ONYX_UPDATES_FROM_SERVER so that it can be updated without triggering the callback when the server IDs are updated. If that
// callback were triggered it would lead to duplicate processing of server updates.
let lastUpdateIDAppliedToClient = 0;
let lastUpdateIDAppliedToClient: OnyxEntry<number> = 0;
Onyx.connect({
key: ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT,
callback: (val) => (lastUpdateIDAppliedToClient = val),
});

/**
* @param {Object} request
* @param {Object} response
* @returns {Promise}
*/
function applyHTTPSOnyxUpdates(request, response) {
function applyHTTPSOnyxUpdates(request: Request, response: Response) {
console.debug('[OnyxUpdateManager] Applying https update');
// For most requests we can immediately update Onyx. For write requests we queue the updates and apply them after the sequential queue has flushed to prevent a replay effect in
// the UI. See https://github.com/Expensify/App/issues/12775 for more info.
const updateHandler = request.data.apiRequestType === CONST.API_REQUEST_TYPE.WRITE ? QueuedOnyxUpdates.queueOnyxUpdates : Onyx.update;
const updateHandler = request?.data?.apiRequestType === CONST.API_REQUEST_TYPE.WRITE ? QueuedOnyxUpdates.queueOnyxUpdates : Onyx.update;

// First apply any onyx data updates that are being sent back from the API. We wait for this to complete and then
// apply successData or failureData. This ensures that we do not update any pending, loading, or other UI states contained
Expand All @@ -46,66 +43,55 @@ function applyHTTPSOnyxUpdates(request, response) {
});
}

/**
* @param {Array} updates
* @returns {Promise}
*/
function applyPusherOnyxUpdates(updates) {
function applyPusherOnyxUpdates(updates: OnyxUpdateEvent[]) {
console.debug('[OnyxUpdateManager] Applying pusher update');
const pusherEventPromises = _.map(updates, (update) => PusherUtils.triggerMultiEventHandler(update.eventType, update.data));
const pusherEventPromises = updates.map((update) => PusherUtils.triggerMultiEventHandler(update.eventType, update.data));
return Promise.all(pusherEventPromises).then(() => {
console.debug('[OnyxUpdateManager] Done applying Pusher update');
});
}

/**
* @param {Object[]} updateParams
* @param {String} updateParams.type
* @param {Number} updateParams.lastUpdateID
* @param {Object} [updateParams.request] Exists if updateParams.type === 'https'
* @param {Object} [updateParams.response] Exists if updateParams.type === 'https'
* @param {Object} [updateParams.updates] Exists if updateParams.type === 'pusher'
* @returns {Promise}
* @param [updateParams.request] Exists if updateParams.type === 'https'
* @param [updateParams.response] Exists if updateParams.type === 'https'
* @param [updateParams.updates] Exists if updateParams.type === 'pusher'
*/
function apply({lastUpdateID, type, request, response, updates}) {
function apply({lastUpdateID, type, request, response, updates}: Merge<OnyxUpdatesFromServer, {updates: OnyxUpdateEvent[]; type: 'pusher'}>): Promise<void>;
function apply({lastUpdateID, type, request, response, updates}: Merge<OnyxUpdatesFromServer, {request: Request; response: Response; type: 'https'}>): Promise<Response>;
function apply({lastUpdateID, type, request, response, updates}: OnyxUpdatesFromServer): Promise<void | Response> | undefined {
console.debug(`[OnyxUpdateManager] Applying update type: ${type} with lastUpdateID: ${lastUpdateID}`, {request, response, updates});

if (lastUpdateID && lastUpdateID < lastUpdateIDAppliedToClient) {
if (lastUpdateID && lastUpdateIDAppliedToClient && Number(lastUpdateID) < lastUpdateIDAppliedToClient) {
console.debug('[OnyxUpdateManager] Update received was older than current state, returning without applying the updates');
return Promise.resolve();
}
if (lastUpdateID && lastUpdateID > lastUpdateIDAppliedToClient) {
Onyx.merge(ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT, lastUpdateID);
if (lastUpdateID && lastUpdateIDAppliedToClient && Number(lastUpdateID) > lastUpdateIDAppliedToClient) {
Onyx.merge(ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT, Number(lastUpdateID));
}
if (type === CONST.ONYX_UPDATE_TYPES.HTTPS) {
if (type === CONST.ONYX_UPDATE_TYPES.HTTPS && request && response) {
return applyHTTPSOnyxUpdates(request, response);
}
if (type === CONST.ONYX_UPDATE_TYPES.PUSHER) {
if (type === CONST.ONYX_UPDATE_TYPES.PUSHER && updates) {
return applyPusherOnyxUpdates(updates);
}
}

/**
* @param {Object[]} updateParams
* @param {String} updateParams.type
* @param {Object} [updateParams.request] Exists if updateParams.type === 'https'
* @param {Object} [updateParams.response] Exists if updateParams.type === 'https'
* @param {Object} [updateParams.updates] Exists if updateParams.type === 'pusher'
* @param {Number} [updateParams.lastUpdateID]
* @param {Number} [updateParams.previousUpdateID]
* @param [updateParams.request] Exists if updateParams.type === 'https'
* @param [updateParams.response] Exists if updateParams.type === 'https'
* @param [updateParams.updates] Exists if updateParams.type === 'pusher'
*/
function saveUpdateInformation(updateParams) {
function saveUpdateInformation(updateParams: OnyxUpdatesFromServer) {
// Always use set() here so that the updateParams are never merged and always unique to the request that came in
Onyx.set(ONYXKEYS.ONYX_UPDATES_FROM_SERVER, updateParams);
}

/**
* This function will receive the previousUpdateID from any request/pusher update that has it, compare to our current app state
* and return if an update is needed
* @param {Number} previousUpdateID The previousUpdateID contained in the response object
* @returns {Boolean}
* @param previousUpdateID The previousUpdateID contained in the response object
*/
function doesClientNeedToBeUpdated(previousUpdateID = 0) {
function doesClientNeedToBeUpdated(previousUpdateID = 0): boolean {
// If no previousUpdateID is sent, this is not a WRITE request so we don't need to update our current state
if (!previousUpdateID) {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,13 @@ import MoneyRequestParticipantsSplitSelector from './MoneyRequestParticipantsSpl
import MoneyRequestParticipantsSelector from './MoneyRequestParticipantsSelector';
import styles from '../../../../styles/styles';
import ScreenWrapper from '../../../../components/ScreenWrapper';
import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize';
import Navigation from '../../../../libs/Navigation/Navigation';
import compose from '../../../../libs/compose';
import * as DeviceCapabilities from '../../../../libs/DeviceCapabilities';
import HeaderWithBackButton from '../../../../components/HeaderWithBackButton';
import * as IOU from '../../../../libs/actions/IOU';
import * as MoneyRequestUtils from '../../../../libs/MoneyRequestUtils';
import {iouPropTypes, iouDefaultProps} from '../../propTypes';
import useLocalize from '../../../../hooks/useLocalize';

const propTypes = {
/** React Navigation route */
Expand All @@ -37,25 +36,39 @@ const propTypes = {

/** The current tab we have navigated to in the request modal. String that corresponds to the request type. */
selectedTab: PropTypes.oneOf([CONST.TAB.DISTANCE, CONST.TAB.MANUAL, CONST.TAB.SCAN]).isRequired,

...withLocalizePropTypes,
};

const defaultProps = {
iou: iouDefaultProps,
};

function MoneyRequestParticipantsPage(props) {
const {translate} = useLocalize();
const prevMoneyRequestId = useRef(props.iou.id);
const iouType = useRef(lodashGet(props.route, 'params.iouType', ''));
const reportID = useRef(lodashGet(props.route, 'params.reportID', ''));
const isNewReportIDSelectedLocally = useRef(false);
const optionsSelectorRef = useRef();
const isDistanceRequest = MoneyRequestUtils.isDistanceRequest(iouType.current, props.selectedTab);

const navigateToNextStep = () => {
const splitNavigateToNextStep = () => {
Navigation.navigate(ROUTES.getMoneyRequestConfirmationRoute(iouType.current, reportID.current));
};

const moneyRequestNavigateToNextStep = (option) => {
isNewReportIDSelectedLocally.current = true;

if (!option.reportID) {
IOU.setMoneyRequestId(iouType.current);
Navigation.navigate(ROUTES.getMoneyRequestConfirmationRoute(iouType.current, reportID.current));

return;
}

IOU.setMoneyRequestId(`${iouType.current}${option.reportID}`);
Navigation.navigate(ROUTES.getMoneyRequestConfirmationRoute(iouType.current, option.reportID));
};

const navigateBack = (forceFallback = false) => {
Navigation.goBack(ROUTES.getMoneyRequestRoute(iouType.current, reportID.current), forceFallback);
};
Expand All @@ -64,15 +77,15 @@ function MoneyRequestParticipantsPage(props) {
// ID in Onyx could change by initiating a new request in a separate browser tab or completing a request
if (prevMoneyRequestId.current !== props.iou.id) {
// The ID is cleared on completing a request. In that case, we will do nothing
if (!isDistanceRequest && props.iou.id) {
if (!isNewReportIDSelectedLocally.current && !isDistanceRequest && props.iou.id) {
navigateBack(true);
}
return;
}

// Reset the money request Onyx if the ID in Onyx does not match the ID from params
const moneyRequestId = `${iouType.current}${reportID.current}`;
const shouldReset = props.iou.id !== moneyRequestId;
const shouldReset = props.iou.id !== moneyRequestId && !isNewReportIDSelectedLocally.current;
if (shouldReset) {
IOU.resetMoneyRequestInfo(moneyRequestId);
}
Expand All @@ -94,20 +107,20 @@ function MoneyRequestParticipantsPage(props) {
{({safeAreaPaddingBottomStyle}) => (
<View style={styles.flex1}>
<HeaderWithBackButton
title={isDistanceRequest ? props.translate('common.distance') : props.translate('iou.cash')}
title={isDistanceRequest ? translate('common.distance') : translate('iou.cash')}
onBackButtonPress={navigateBack}
/>
{iouType.current === CONST.IOU.MONEY_REQUEST_TYPE.SPLIT ? (
<MoneyRequestParticipantsSplitSelector
onStepComplete={navigateToNextStep}
onStepComplete={splitNavigateToNextStep}
participants={props.iou.participants}
onAddParticipants={IOU.setMoneyRequestParticipants}
safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle}
/>
) : (
<MoneyRequestParticipantsSelector
ref={(el) => (optionsSelectorRef.current = el)}
onStepComplete={navigateToNextStep}
onStepComplete={moneyRequestNavigateToNextStep}
onAddParticipants={IOU.setMoneyRequestParticipants}
safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle}
iouType={iouType.current}
Expand All @@ -124,12 +137,11 @@ MoneyRequestParticipantsPage.displayName = 'IOUParticipantsPage';
MoneyRequestParticipantsPage.propTypes = propTypes;
MoneyRequestParticipantsPage.defaultProps = defaultProps;

export default compose(
withLocalize,
withOnyx({
iou: {key: ONYXKEYS.IOU},
selectedTab: {
key: `${ONYXKEYS.COLLECTION.SELECTED_TAB}${CONST.TAB.RECEIPT_TAB_ID}`,
},
}),
)(MoneyRequestParticipantsPage);
export default withOnyx({
iou: {
key: ONYXKEYS.IOU,
},
selectedTab: {
key: `${ONYXKEYS.COLLECTION.SELECTED_TAB}${CONST.TAB.RECEIPT_TAB_ID}`,
},
})(MoneyRequestParticipantsPage);
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ class MoneyRequestParticipantsSelector extends Component {
*/
addSingleParticipant(option) {
this.props.onAddParticipants([{accountID: option.accountID, login: option.login, isPolicyExpenseChat: option.isPolicyExpenseChat, reportID: option.reportID, selected: true}]);
this.props.onStepComplete();
this.props.onStepComplete(option);
}

render() {
Expand Down
9 changes: 7 additions & 2 deletions src/types/onyx/OnyxUpdatesFromServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@ import {OnyxUpdate} from 'react-native-onyx';
import Request from './Request';
import Response from './Response';

type OnyxUpdateEvent = {
eventType: string;
data: OnyxUpdate[];
};

type OnyxUpdatesFromServer = {
type: 'https' | 'pusher';
lastUpdateID: number | string;
previousUpdateID: number | string;
request?: Request;
response?: Response;
updates?: OnyxUpdate[];
updates?: OnyxUpdateEvent[];
};

export default OnyxUpdatesFromServer;
export type {OnyxUpdatesFromServer, OnyxUpdateEvent};
3 changes: 2 additions & 1 deletion src/types/onyx/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import ReimbursementAccountDraft from './ReimbursementAccountDraft';
import WalletTransfer from './WalletTransfer';
import ReceiptModal from './ReceiptModal';
import MapboxAccessToken from './MapboxAccessToken';
import OnyxUpdatesFromServer from './OnyxUpdatesFromServer';
import {OnyxUpdatesFromServer, OnyxUpdateEvent} from './OnyxUpdatesFromServer';
import Download from './Download';
import PolicyMember from './PolicyMember';
import Policy from './Policy';
Expand Down Expand Up @@ -97,6 +97,7 @@ export type {
Form,
AddDebitCardForm,
OnyxUpdatesFromServer,
OnyxUpdateEvent,
RecentWaypoints,
RecentlyUsedCategories,
RecentlyUsedTags,
Expand Down

0 comments on commit 286adad

Please sign in to comment.