From f41a929976714fb74214cce7567857c53d410a21 Mon Sep 17 00:00:00 2001 From: Lizzi Lindboe Date: Thu, 17 Aug 2023 15:33:57 -0700 Subject: [PATCH 001/856] Improve GSI errors Errors that didn't have specific handling weren't being logged properly. Update logging and print all status codes so we can also see errors we aren't specifically handling. --- .../SignInButtons/GoogleSignIn/index.native.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/components/SignInButtons/GoogleSignIn/index.native.js b/src/components/SignInButtons/GoogleSignIn/index.native.js index 9e638f0723cf..099fbfde22fd 100644 --- a/src/components/SignInButtons/GoogleSignIn/index.native.js +++ b/src/components/SignInButtons/GoogleSignIn/index.native.js @@ -25,14 +25,18 @@ function googleSignInRequest() { .then((response) => response.idToken) .then((token) => Session.beginGoogleSignIn(token)) .catch((error) => { + // Handle unexpected error shape + if (error === undefined || error.code === undefined) { + Log.alert(`[Google Sign In] Google sign in failed: ${error}`); + } + /** The logged code is useful for debugging any new errors that are not specifically handled. To decode, see: + - The common status codes documentation: https://developers.google.com/android/reference/com/google/android/gms/common/api/CommonStatusCodes + - The Google Sign In codes documentation: https://developers.google.com/android/reference/com/google/android/gms/auth/api/signin/GoogleSignInStatusCodes + */ if (error.code === statusCodes.SIGN_IN_CANCELLED) { - Log.alert('[Google Sign In] Google sign in cancelled', true, {error}); - } else if (error.code === statusCodes.IN_PROGRESS) { - Log.alert('[Google Sign In] Google sign in already in progress', true, {error}); - } else if (error.code === statusCodes.PLAY_SERVICES_NOT_AVAILABLE) { - Log.alert('[Google Sign In] Google play services not available or outdated', true, {error}); + Log.info('[Google Sign In] Google Sign In cancelled'); } else { - Log.alert('[Google Sign In] Unknown Google sign in error', true, {error}); + Log.alert(`[Google Sign In] Error Code: ${error.code}. ${error.message}`, {}, false); } }); } From b195e00ca248594ff6ce282903bd854f489f3817 Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Tue, 22 Aug 2023 12:08:44 +0200 Subject: [PATCH 002/856] Migrate ValidationStep.js to function component --- .../ReimbursementAccount/ValidationStep.js | 274 +++++++++--------- 1 file changed, 132 insertions(+), 142 deletions(-) diff --git a/src/pages/ReimbursementAccount/ValidationStep.js b/src/pages/ReimbursementAccount/ValidationStep.js index 0afa855f1ea6..64cdc82ecff0 100644 --- a/src/pages/ReimbursementAccount/ValidationStep.js +++ b/src/pages/ReimbursementAccount/ValidationStep.js @@ -51,23 +51,38 @@ const defaultProps = { }, }; -class ValidationStep extends React.Component { - constructor(props) { - super(props); +function ValidationStep({reimbursementAccount, translate, onBackButtonPress, account}) { + /** + * Filter input for validation amount + * Anything that isn't a number is returned as an empty string + * Any dollar amount (e.g. 1.12) will be returned as 112 + * + * @param {String} amount field input + * @returns {String} + */ + const filterInput = (amount) => { + let value = amount ? amount.toString().trim() : ''; + if (value === '' || !Math.abs(Str.fromUSDToNumber(value)) || _.isNaN(Number(value))) { + return ''; + } - this.submit = this.submit.bind(this); - this.validate = this.validate.bind(this); - } + // If the user enters the values in dollars, convert it to the respective cents amount + if (_.contains(value, '.')) { + value = Str.fromUSDToNumber(value); + } + + return value; + }; /** * @param {Object} values - form input values passed by the Form component * @returns {Object} */ - validate(values) { + const validate = (values) => { const errors = {}; _.each(values, (value, key) => { - const filteredValue = this.filterInput(value); + const filteredValue = filterInput(value); if (ValidationUtils.isRequiredFulfilled(filteredValue)) { return; } @@ -75,155 +90,130 @@ class ValidationStep extends React.Component { }); return errors; - } + }; /** * @param {Object} values - form input values passed by the Form component */ - submit(values) { - const amount1 = this.filterInput(values.amount1); - const amount2 = this.filterInput(values.amount2); - const amount3 = this.filterInput(values.amount3); + const submit = (values) => { + const amount1 = filterInput(values.amount1); + const amount2 = filterInput(values.amount2); + const amount3 = filterInput(values.amount3); const validateCode = [amount1, amount2, amount3].join(','); // Send valid amounts to BankAccountAPI::validateBankAccount in Web-Expensify - const bankaccountID = lodashGet(this.props.reimbursementAccount, 'achData.bankAccountID'); + const bankaccountID = lodashGet(reimbursementAccount, 'achData.bankAccountID'); BankAccounts.validateBankAccount(bankaccountID, validateCode); - } - - /** - * Filter input for validation amount - * Anything that isn't a number is returned as an empty string - * Any dollar amount (e.g. 1.12) will be returned as 112 - * - * @param {String} amount field input - * - * @returns {String} - */ - filterInput(amount) { - let value = amount ? amount.toString().trim() : ''; - if (value === '' || !Math.abs(Str.fromUSDToNumber(value)) || _.isNaN(Number(value))) { - return ''; - } + }; - // If the user enters the values in dollars, convert it to the respective cents amount - if (_.contains(value, '.')) { - value = Str.fromUSDToNumber(value); - } + const state = lodashGet(reimbursementAccount, 'achData.state'); - return value; + // If a user tries to navigate directly to the validate page we'll show them the EnableStep + if (state === BankAccount.STATE.OPEN) { + return ; } - render() { - const state = lodashGet(this.props.reimbursementAccount, 'achData.state'); - - // If a user tries to navigate directly to the validate page we'll show them the EnableStep - if (state === BankAccount.STATE.OPEN) { - return ; - } - - const maxAttemptsReached = lodashGet(this.props.reimbursementAccount, 'maxAttemptsReached'); - const isVerifying = !maxAttemptsReached && state === BankAccount.STATE.VERIFYING; - const requiresTwoFactorAuth = lodashGet(this.props, 'account.requiresTwoFactorAuth'); - - return ( - - - {maxAttemptsReached && ( - - - {this.props.translate('validationStep.maxAttemptsReached')} {this.props.translate('common.please')}{' '} - {this.props.translate('common.contactUs')}. - + const maxAttemptsReached = lodashGet(reimbursementAccount, 'maxAttemptsReached'); + const isVerifying = !maxAttemptsReached && state === BankAccount.STATE.VERIFYING; + const requiresTwoFactorAuth = lodashGet(account, 'requiresTwoFactorAuth'); + + return ( + + + {maxAttemptsReached && ( + + + {translate('validationStep.maxAttemptsReached')} {translate('common.please')}{' '} + {translate('common.contactUs')}. + + + )} + {!maxAttemptsReached && state === BankAccount.STATE.PENDING && ( +
+ + {translate('validationStep.description')} + {translate('validationStep.descriptionCTA')} - )} - {!maxAttemptsReached && state === BankAccount.STATE.PENDING && ( - - - {this.props.translate('validationStep.description')} - {this.props.translate('validationStep.descriptionCTA')} - - - - - + + + + + + {!requiresTwoFactorAuth && ( + + - {!requiresTwoFactorAuth && ( - - - - )} - - )} - {isVerifying && ( - -
- {this.props.translate('validationStep.letsChatText')} -
- {this.props.reimbursementAccount.shouldShowResetModal && } - {!requiresTwoFactorAuth && } -
- )} -
- ); - } + )} + + )} + {isVerifying && ( + +
+ {translate('validationStep.letsChatText')} +
+ {reimbursementAccount.shouldShowResetModal && } + {!requiresTwoFactorAuth && } +
+ )} +
+ ); } ValidationStep.propTypes = propTypes; From 6a458b7522aea7bc14eb6074af1accf82306371e Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Tue, 29 Aug 2023 17:01:01 +0300 Subject: [PATCH 003/856] Allow zIndex to be overridden --- src/components/PopoverWithoutOverlay/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/PopoverWithoutOverlay/index.js b/src/components/PopoverWithoutOverlay/index.js index 778f65349969..15924d97467c 100644 --- a/src/components/PopoverWithoutOverlay/index.js +++ b/src/components/PopoverWithoutOverlay/index.js @@ -50,7 +50,7 @@ function Popover(props) { return ( From c2aa62fdf2ca857136e2fb23c5897877474595aa Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Tue, 29 Aug 2023 17:17:02 +0300 Subject: [PATCH 004/856] Allow overriding some modal styles from HeaderWithBackButton --- src/components/AttachmentModal.js | 29 ++++++++++++------- .../headerWithBackButtonPropTypes.js | 3 ++ src/components/HeaderWithBackButton/index.js | 2 ++ src/components/PopoverMenu/index.js | 5 ++++ src/components/ThreeDotsMenu/index.js | 7 ++++- src/styles/styles.js | 8 +++-- 6 files changed, 41 insertions(+), 13 deletions(-) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index c07a4474a68b..56eb0de7c871 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -18,6 +18,7 @@ import compose from '../libs/compose'; import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions'; import Button from './Button'; import HeaderWithBackButton from './HeaderWithBackButton'; +import ReceiptAttachmentHeader from './ReceiptAttachmentHeader'; import fileDownload from '../libs/fileDownload'; import withLocalize, {withLocalizePropTypes} from './withLocalize'; import ConfirmModal from './ConfirmModal'; @@ -336,16 +337,24 @@ function AttachmentModal(props) { propagateSwipe > {props.isSmallScreenWidth && } - downloadAttachment(source)} - shouldShowCloseButton={!props.isSmallScreenWidth} - shouldShowBackButton={props.isSmallScreenWidth} - onBackButtonPress={closeModal} - onCloseButtonPress={closeModal} - /> + {isAttachmentReceipt ? ( + + ) : ( + downloadAttachment(source)} + shouldShowCloseButton={!props.isSmallScreenWidth} + shouldShowBackButton={props.isSmallScreenWidth} + onBackButtonPress={closeModal} + onCloseButtonPress={closeModal} + /> + )} {!_.isEmpty(props.report) ? ( )} {shouldShowCloseButton && ( diff --git a/src/components/PopoverMenu/index.js b/src/components/PopoverMenu/index.js index 67b9a0406aef..6484d24f2870 100644 --- a/src/components/PopoverMenu/index.js +++ b/src/components/PopoverMenu/index.js @@ -27,6 +27,9 @@ const propTypes = { /** Ref of the anchor */ anchorRef: refPropTypes, + /** Outer style of popover that passes down to Modal */ + outerStyle: PropTypes.oneOf([PropTypes.array, PropTypes.object]), + /** Where the popover should be positioned relative to the anchor points. */ anchorAlignment: PropTypes.shape({ horizontal: PropTypes.oneOf(_.values(CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL)), @@ -42,6 +45,7 @@ const defaultProps = { horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM, }, + outerStyle: {}, anchorRef: () => {}, withoutOverlay: false, }; @@ -89,6 +93,7 @@ function PopoverMenu(props) { disableAnimation={props.disableAnimation} fromSidebarMediumScreen={props.fromSidebarMediumScreen} withoutOverlay={props.withoutOverlay} + outerStyle={props.outerStyle} > {!_.isEmpty(props.headerText) && {props.headerText}} diff --git a/src/components/ThreeDotsMenu/index.js b/src/components/ThreeDotsMenu/index.js index b5637a4f3879..4d44f9eb6d7f 100644 --- a/src/components/ThreeDotsMenu/index.js +++ b/src/components/ThreeDotsMenu/index.js @@ -29,6 +29,9 @@ const propTypes = { /** Function to call on icon press */ onIconPress: PropTypes.func, + /** Outer styles that get passed down to Modal */ + outerStyle: PropTypes.oneOfType([PropTypes.array, PropTypes.object]), + /** menuItems that'll show up on toggle of the popup menu */ menuItems: ThreeDotsMenuItemPropTypes.isRequired, @@ -53,13 +56,14 @@ const defaultProps = { iconStyles: [], icon: Expensicons.ThreeDots, onIconPress: () => {}, + outerStyle: {}, anchorAlignment: { horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP, // we assume that popover menu opens below the button, anchor is at TOP }, }; -function ThreeDotsMenu({iconTooltip, icon, iconFill, iconStyles, onIconPress, menuItems, anchorPosition, anchorAlignment}) { +function ThreeDotsMenu({iconTooltip, icon, iconFill, iconStyles, onIconPress, outerStyle, menuItems, anchorPosition, anchorAlignment}) { const [isPopupMenuVisible, setPopupMenuVisible] = useState(false); const buttonRef = useRef(null); const {translate} = useLocalize(); @@ -108,6 +112,7 @@ function ThreeDotsMenu({iconTooltip, icon, iconFill, iconStyles, onIconPress, me menuItems={menuItems} withoutOverlay anchorRef={buttonRef} + outerStyle={outerStyle} /> ); diff --git a/src/styles/styles.js b/src/styles/styles.js index d6258da62cbc..1eb02a79f955 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -2148,6 +2148,10 @@ const styles = { width: '100%', }, + receiptAttachmentHeaderThreeDotsMenuStyles: { + zIndex: variables.popoverModalzIndex, + }, + defaultAttachmentView: { backgroundColor: themeColors.sidebar, borderRadius: variables.componentBorderRadiusNormal, @@ -3102,8 +3106,8 @@ const styles = { flex: 1, }, - threeDotsPopoverOffset: (windowWidth) => ({ - ...getPopOverVerticalOffset(60), + threeDotsPopoverOffset: (windowWidth, isWithinFullScreenModal) => ({ + ...getPopOverVerticalOffset(60 + (isWithinFullScreenModal ? 20 : 0)), horizontal: windowWidth - 60, }), From bfe63488873ea36356412e145e10520c1d461392 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Tue, 29 Aug 2023 17:17:28 +0300 Subject: [PATCH 005/856] New attachment view header component --- src/components/ReceiptAttachmentHeader.js | 86 +++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 src/components/ReceiptAttachmentHeader.js diff --git a/src/components/ReceiptAttachmentHeader.js b/src/components/ReceiptAttachmentHeader.js new file mode 100644 index 000000000000..11943e7a7fc7 --- /dev/null +++ b/src/components/ReceiptAttachmentHeader.js @@ -0,0 +1,86 @@ +import React, {useState, useCallback} from 'react'; +import {withOnyx} from 'react-native-onyx'; +import {View} from 'react-native'; +import HeaderWithBackButton from './HeaderWithBackButton'; +import iouReportPropTypes from '../pages/iouReportPropTypes'; +import * as Expensicons from './Icon/Expensicons'; +import styles from '../styles/styles'; +import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions'; +import compose from '../libs/compose'; +import ONYXKEYS from '../ONYXKEYS'; +import ConfirmModal from './ConfirmModal'; +import useLocalize from '../hooks/useLocalize'; + +const propTypes = { + /** The report currently being looked at */ + report: iouReportPropTypes.isRequired, + + /** The expense report or iou report (only will have a value if this is a transaction thread) */ + parentReport: iouReportPropTypes, + + ...windowDimensionsPropTypes, +}; + +const defaultProps = { + parentReport: {}, +}; + +function ReceiptAttachmentHeader(props) { + const {translate} = useLocalize(); + const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false); + + const deleteReceipt = useCallback(() => { + // IOU.deleteMoneyRequest(parentReportAction.originalMessage.IOUTransactionID, parentReportAction, true); + setIsDeleteModalVisible(false); + }, [props.parentReport, setIsDeleteModalVisible]); + + return ( + <> + + setIsDeleteModalVisible(true), + }, + ]} + threeDotsAnchorPosition={styles.threeDotsPopoverOffset(props.windowWidth, true)} + outerThreeDotsMenuStyle={styles.receiptAttachmentHeaderThreeDotsMenuStyles} + shouldShowBackButton={props.isSmallScreenWidth} + onBackButtonPress={props.onBackButtonPress} + onCloseButtonPress={props.onCloseButtonPress} + /> + + setIsDeleteModalVisible(false)} + prompt={translate('receipt.deleteConfirmation')} + confirmText={translate('common.delete')} + cancelText={translate('common.cancel')} + danger + /> + + ); +} + +ReceiptAttachmentHeader.displayName = 'ReceiptAttachmentHeader'; +ReceiptAttachmentHeader.propTypes = propTypes; +ReceiptAttachmentHeader.defaultProps = defaultProps; + +export default compose( + withWindowDimensions, + withOnyx({ + parentReport: { + key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`, + }, + }), +)(ReceiptAttachmentHeader); From 93738713966c5f4d4243dfe7cb62be1a8fb69ea8 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Tue, 29 Aug 2023 17:17:45 +0300 Subject: [PATCH 006/856] Forgot to commit new style var --- src/styles/variables.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/styles/variables.ts b/src/styles/variables.ts index f584e657c693..ae4fe6e4ee26 100644 --- a/src/styles/variables.ts +++ b/src/styles/variables.ts @@ -82,6 +82,7 @@ export default { sideBarWidth: 375, pdfPageMaxWidth: 992, tooltipzIndex: 10050, + popoverModalzIndex: 9999, gutterWidth: 12, popoverMenuShadow: '0px 4px 12px 0px rgba(0, 0, 0, 0.06)', optionRowHeight: 64, From 73ba5b668e5879eff61ddcc127962806608f0b4d Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Tue, 29 Aug 2023 18:22:31 +0300 Subject: [PATCH 007/856] Finish transaction-get logic --- src/components/ReceiptAttachmentHeader.js | 30 ++++++++++++----------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/components/ReceiptAttachmentHeader.js b/src/components/ReceiptAttachmentHeader.js index 11943e7a7fc7..37532d669095 100644 --- a/src/components/ReceiptAttachmentHeader.js +++ b/src/components/ReceiptAttachmentHeader.js @@ -1,22 +1,24 @@ import React, {useState, useCallback} from 'react'; -import {withOnyx} from 'react-native-onyx'; +import lodashGet from 'lodash/get'; +import PropTypes from 'prop-types'; import {View} from 'react-native'; import HeaderWithBackButton from './HeaderWithBackButton'; -import iouReportPropTypes from '../pages/iouReportPropTypes'; import * as Expensicons from './Icon/Expensicons'; import styles from '../styles/styles'; import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions'; import compose from '../libs/compose'; -import ONYXKEYS from '../ONYXKEYS'; import ConfirmModal from './ConfirmModal'; import useLocalize from '../hooks/useLocalize'; +import ReceiptActions from '../libs/actions/Receipt'; +import * as ReportActionsUtils from '../libs/ReportActionsUtils'; +import _ from 'lodash'; const propTypes = { /** The report currently being looked at */ - report: iouReportPropTypes.isRequired, - - /** The expense report or iou report (only will have a value if this is a transaction thread) */ - parentReport: iouReportPropTypes, + report: PropTypes.shape({ + parentReportID: PropTypes.string.isRequired, + parentReportActionID: PropTypes.string.isRequired, + }).isRequired, ...windowDimensionsPropTypes, }; @@ -30,9 +32,14 @@ function ReceiptAttachmentHeader(props) { const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false); const deleteReceipt = useCallback(() => { - // IOU.deleteMoneyRequest(parentReportAction.originalMessage.IOUTransactionID, parentReportAction, true); + // Get receipt attachment transaction ID + const parentReportAction = ReportActionsUtils.getParentReportAction(props.report); + const transactionID = lodashGet(parentReportAction, ['originalMessage', 'IOUTransactionID'], 0); + + // Detatch receipt & clear modal open state + ReceiptActions.detachReceipt(transactionID); setIsDeleteModalVisible(false); - }, [props.parentReport, setIsDeleteModalVisible]); + }, [props.receiptTransactions, setIsDeleteModalVisible]); return ( <> @@ -78,9 +85,4 @@ ReceiptAttachmentHeader.defaultProps = defaultProps; export default compose( withWindowDimensions, - withOnyx({ - parentReport: { - key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`, - }, - }), )(ReceiptAttachmentHeader); From fe013075d6fe4aa6bdc39a250ac7c5fdc8db459c Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Tue, 29 Aug 2023 18:50:25 +0300 Subject: [PATCH 008/856] Add optimistic and failure data --- src/libs/actions/Receipt.js | 44 +++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/libs/actions/Receipt.js b/src/libs/actions/Receipt.js index fbe9c22faaa2..dc25e28b22cc 100644 --- a/src/libs/actions/Receipt.js +++ b/src/libs/actions/Receipt.js @@ -1,5 +1,20 @@ import Onyx from 'react-native-onyx'; +import lodashGet from 'lodash/get'; import ONYXKEYS from '../../ONYXKEYS'; +import * as API from '../API'; +import * as CollectionUtils from '../CollectionUtils'; + +const allTransactionData = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.TRANSACTION, + callback: (data, key) => { + if (!key || !data) { + return; + } + const transactionID = CollectionUtils.extractCollectionItemID(key); + allTransactionData[transactionID] = data; + }, +}); /** * Sets the upload receipt error modal content when an invalid receipt is uploaded @@ -27,7 +42,36 @@ function clearUploadReceiptError() { }); } +/** + * Detaches the receipt from a transaction + * + * @param {String} transactionID + */ +function detachReceipt(transactionID) { + API.write('DetachReceipt', {transactionID}, { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, + value: { + receipt: {}, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, + value: { + receipt: lodashGet(allTransactionData, [transactionID, 'receipt'], {}), + }, + }, + ], + }); +} + export default { setUploadReceiptError, clearUploadReceiptError, + detachReceipt, }; From c0f11989f5f0055822e031fe1f0a92c0558a66b4 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Tue, 29 Aug 2023 18:59:15 +0300 Subject: [PATCH 009/856] Fixing up some lint errors --- src/components/ReceiptAttachmentHeader.js | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/components/ReceiptAttachmentHeader.js b/src/components/ReceiptAttachmentHeader.js index 37532d669095..e1af8473ff51 100644 --- a/src/components/ReceiptAttachmentHeader.js +++ b/src/components/ReceiptAttachmentHeader.js @@ -11,7 +11,6 @@ import ConfirmModal from './ConfirmModal'; import useLocalize from '../hooks/useLocalize'; import ReceiptActions from '../libs/actions/Receipt'; import * as ReportActionsUtils from '../libs/ReportActionsUtils'; -import _ from 'lodash'; const propTypes = { /** The report currently being looked at */ @@ -23,10 +22,6 @@ const propTypes = { ...windowDimensionsPropTypes, }; -const defaultProps = { - parentReport: {}, -}; - function ReceiptAttachmentHeader(props) { const {translate} = useLocalize(); const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false); @@ -39,7 +34,7 @@ function ReceiptAttachmentHeader(props) { // Detatch receipt & clear modal open state ReceiptActions.detachReceipt(transactionID); setIsDeleteModalVisible(false); - }, [props.receiptTransactions, setIsDeleteModalVisible]); + }, [props.report, setIsDeleteModalVisible]); return ( <> @@ -60,7 +55,6 @@ function ReceiptAttachmentHeader(props) { ]} threeDotsAnchorPosition={styles.threeDotsPopoverOffset(props.windowWidth, true)} outerThreeDotsMenuStyle={styles.receiptAttachmentHeaderThreeDotsMenuStyles} - shouldShowBackButton={props.isSmallScreenWidth} onBackButtonPress={props.onBackButtonPress} onCloseButtonPress={props.onCloseButtonPress} /> @@ -83,6 +77,4 @@ ReceiptAttachmentHeader.displayName = 'ReceiptAttachmentHeader'; ReceiptAttachmentHeader.propTypes = propTypes; ReceiptAttachmentHeader.defaultProps = defaultProps; -export default compose( - withWindowDimensions, -)(ReceiptAttachmentHeader); +export default withWindowDimensions(ReceiptAttachmentHeader); From e46307c6153e86f0fbe3ec892751845c3f7babbb Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Tue, 29 Aug 2023 19:08:13 +0300 Subject: [PATCH 010/856] Navigate user back to receipt report after detaching --- src/components/ReceiptAttachmentHeader.js | 3 +-- src/libs/actions/Receipt.js | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/ReceiptAttachmentHeader.js b/src/components/ReceiptAttachmentHeader.js index e1af8473ff51..da81aaa514e0 100644 --- a/src/components/ReceiptAttachmentHeader.js +++ b/src/components/ReceiptAttachmentHeader.js @@ -32,7 +32,7 @@ function ReceiptAttachmentHeader(props) { const transactionID = lodashGet(parentReportAction, ['originalMessage', 'IOUTransactionID'], 0); // Detatch receipt & clear modal open state - ReceiptActions.detachReceipt(transactionID); + ReceiptActions.detachReceipt(transactionID, props.report.reportID); setIsDeleteModalVisible(false); }, [props.report, setIsDeleteModalVisible]); @@ -75,6 +75,5 @@ function ReceiptAttachmentHeader(props) { ReceiptAttachmentHeader.displayName = 'ReceiptAttachmentHeader'; ReceiptAttachmentHeader.propTypes = propTypes; -ReceiptAttachmentHeader.defaultProps = defaultProps; export default withWindowDimensions(ReceiptAttachmentHeader); diff --git a/src/libs/actions/Receipt.js b/src/libs/actions/Receipt.js index dc25e28b22cc..db81a67bb521 100644 --- a/src/libs/actions/Receipt.js +++ b/src/libs/actions/Receipt.js @@ -3,6 +3,8 @@ import lodashGet from 'lodash/get'; import ONYXKEYS from '../../ONYXKEYS'; import * as API from '../API'; import * as CollectionUtils from '../CollectionUtils'; +import Navigation from '../Navigation/Navigation'; +import ROUTES from '../../ROUTES'; const allTransactionData = {}; Onyx.connect({ @@ -47,7 +49,7 @@ function clearUploadReceiptError() { * * @param {String} transactionID */ -function detachReceipt(transactionID) { +function detachReceipt(transactionID, reportID) { API.write('DetachReceipt', {transactionID}, { optimisticData: [ { @@ -68,6 +70,7 @@ function detachReceipt(transactionID) { }, ], }); + Navigation.navigate(ROUTES.getReportRoute(reportID)); } export default { From 802af8d1cbc8801cba5104b7040739662739084d Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Tue, 29 Aug 2023 19:09:08 +0300 Subject: [PATCH 011/856] Add translations --- src/languages/en.js | 3 ++- src/languages/es.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/languages/en.js b/src/languages/en.js index fa8ea84c141b..cd01c2680ccd 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -393,6 +393,8 @@ export default { flash: 'flash', shutter: 'shutter', gallery: 'gallery', + deleteReceipt: 'Delete receipt', + deleteConfirmation: 'Are you sure you want to delete this receipt?', }, iou: { amount: 'Amount', @@ -408,7 +410,6 @@ export default { pay: 'Pay', viewDetails: 'View details', pending: 'Pending', - deleteReceipt: 'Delete receipt', receiptScanning: 'Receipt scan in progress…', receiptStatusTitle: 'Scanning…', receiptStatusText: "Only you can see this receipt when it's scanning. Check back later or enter the details now.", diff --git a/src/languages/es.js b/src/languages/es.js index fd6fcd9b767f..6d37b5d0f89b 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -392,6 +392,8 @@ export default { flash: 'flash', shutter: 'obturador', gallery: 'galería', + deleteReceipt: 'Eliminar recibo', + deleteConfirmation: '¿Estás seguro de que quieres borrar este recibo?', }, iou: { amount: 'Importe', @@ -407,7 +409,6 @@ export default { pay: 'Pagar', viewDetails: 'Ver detalles', pending: 'Pendiente', - deleteReceipt: 'Eliminar recibo', receiptScanning: 'Escaneo de recibo en curso…', receiptStatusTitle: 'Escaneando…', receiptStatusText: 'Solo tú puedes ver este recibo cuando se está escaneando. Vuelve más tarde o introduce los detalles ahora.', From 6491074ea5fd5adc3f54fcb0c8ff0813060fea8c Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Tue, 29 Aug 2023 19:38:18 +0300 Subject: [PATCH 012/856] lint & prettify --- src/components/ReceiptAttachmentHeader.js | 1 - src/libs/actions/Receipt.js | 41 +++++++++++++---------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/components/ReceiptAttachmentHeader.js b/src/components/ReceiptAttachmentHeader.js index da81aaa514e0..7e66f784fdd0 100644 --- a/src/components/ReceiptAttachmentHeader.js +++ b/src/components/ReceiptAttachmentHeader.js @@ -6,7 +6,6 @@ import HeaderWithBackButton from './HeaderWithBackButton'; import * as Expensicons from './Icon/Expensicons'; import styles from '../styles/styles'; import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions'; -import compose from '../libs/compose'; import ConfirmModal from './ConfirmModal'; import useLocalize from '../hooks/useLocalize'; import ReceiptActions from '../libs/actions/Receipt'; diff --git a/src/libs/actions/Receipt.js b/src/libs/actions/Receipt.js index db81a67bb521..2222b8ff9caf 100644 --- a/src/libs/actions/Receipt.js +++ b/src/libs/actions/Receipt.js @@ -48,28 +48,33 @@ function clearUploadReceiptError() { * Detaches the receipt from a transaction * * @param {String} transactionID + * @param {String} reportID */ function detachReceipt(transactionID, reportID) { - API.write('DetachReceipt', {transactionID}, { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, - value: { - receipt: {}, + API.write( + 'DetachReceipt', + {transactionID}, + { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, + value: { + receipt: {}, + }, }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, - value: { - receipt: lodashGet(allTransactionData, [transactionID, 'receipt'], {}), + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, + value: { + receipt: lodashGet(allTransactionData, [transactionID, 'receipt'], {}), + }, }, - }, - ], - }); + ], + }, + ); Navigation.navigate(ROUTES.getReportRoute(reportID)); } From 776513ea65d424b2b0d022ac86cd554cd4829d6e Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Fri, 1 Sep 2023 18:56:05 +0300 Subject: [PATCH 013/856] Prop types fixes --- src/components/PopoverMenu/index.js | 2 +- src/components/ReceiptAttachmentHeader.js | 7 +++++++ src/components/ThreeDotsMenu/index.js | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/components/PopoverMenu/index.js b/src/components/PopoverMenu/index.js index 6484d24f2870..3e3fa247fe6e 100644 --- a/src/components/PopoverMenu/index.js +++ b/src/components/PopoverMenu/index.js @@ -28,7 +28,7 @@ const propTypes = { anchorRef: refPropTypes, /** Outer style of popover that passes down to Modal */ - outerStyle: PropTypes.oneOf([PropTypes.array, PropTypes.object]), + outerStyle: PropTypes.object, /** Where the popover should be positioned relative to the anchor points. */ anchorAlignment: PropTypes.shape({ diff --git a/src/components/ReceiptAttachmentHeader.js b/src/components/ReceiptAttachmentHeader.js index 7e66f784fdd0..4927c3fe458e 100644 --- a/src/components/ReceiptAttachmentHeader.js +++ b/src/components/ReceiptAttachmentHeader.js @@ -14,10 +14,17 @@ import * as ReportActionsUtils from '../libs/ReportActionsUtils'; const propTypes = { /** The report currently being looked at */ report: PropTypes.shape({ + reportID: PropTypes.string.isRequired, parentReportID: PropTypes.string.isRequired, parentReportActionID: PropTypes.string.isRequired, }).isRequired, + /** Method to trigger when pressing back button of the header */ + onBackButtonPress: PropTypes.func.isRequired, + + /** Method to trigger when pressing close button of the header */ + onCloseButtonPress: PropTypes.func.isRequired, + ...windowDimensionsPropTypes, }; diff --git a/src/components/ThreeDotsMenu/index.js b/src/components/ThreeDotsMenu/index.js index 4d44f9eb6d7f..93850b08a3bb 100644 --- a/src/components/ThreeDotsMenu/index.js +++ b/src/components/ThreeDotsMenu/index.js @@ -30,7 +30,7 @@ const propTypes = { onIconPress: PropTypes.func, /** Outer styles that get passed down to Modal */ - outerStyle: PropTypes.oneOfType([PropTypes.array, PropTypes.object]), + outerStyle: PropTypes.object, /** menuItems that'll show up on toggle of the popup menu */ menuItems: ThreeDotsMenuItemPropTypes.isRequired, From ba8a3c100bdd759d7ac318a340b1893055061f8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Mon, 4 Sep 2023 19:01:36 +0100 Subject: [PATCH 014/856] First version of styles refactor --- src/styles/addOutlineWidth/types.ts | 3 +- src/styles/codeStyles/types.ts | 9 +- src/styles/editedLabelStyles/index.ts | 8 +- src/styles/editedLabelStyles/types.ts | 3 +- src/styles/getModalStyles.ts | 9 +- .../getNavigationModalCardStyles/index.ts | 6 +- .../index.website.ts | 6 +- .../getNavigationModalCardStyles/types.ts | 3 +- .../getReportActionContextMenuStyles.ts | 11 +- src/styles/italic/types.ts | 3 +- src/styles/optionRowStyles/types.ts | 3 +- src/styles/overflowXHidden/types.ts | 4 +- src/styles/pointerEventsAuto/types.ts | 4 +- src/styles/pointerEventsNone/types.ts | 4 +- src/styles/utilities/cursor/types.ts | 4 +- src/styles/utilities/overflow.ts | 4 +- src/styles/utilities/overflowAuto/index.ts | 5 +- src/styles/utilities/overflowAuto/types.ts | 4 +- src/styles/utilities/textUnderline/types.ts | 6 +- src/styles/utilities/userSelect/types.ts | 4 +- src/styles/utilities/visibility/types.ts | 4 +- src/styles/utilities/whiteSpace/types.ts | 4 +- src/styles/utilities/wordBreak/types.ts | 4 +- src/types/modules/react-native.d.ts | 148 ++++++++++++++++++ 24 files changed, 210 insertions(+), 53 deletions(-) create mode 100644 src/types/modules/react-native.d.ts diff --git a/src/styles/addOutlineWidth/types.ts b/src/styles/addOutlineWidth/types.ts index 422d135ef759..fe5d1f5719b2 100644 --- a/src/styles/addOutlineWidth/types.ts +++ b/src/styles/addOutlineWidth/types.ts @@ -1,6 +1,5 @@ -import {CSSProperties} from 'react'; import {TextStyle} from 'react-native'; -type AddOutlineWidth = (obj: TextStyle | CSSProperties, val?: number, error?: boolean) => TextStyle | CSSProperties; +type AddOutlineWidth = (obj: TextStyle, val?: number, error?: boolean) => TextStyle; export default AddOutlineWidth; diff --git a/src/styles/codeStyles/types.ts b/src/styles/codeStyles/types.ts index 7397b3c6543c..25e35f492089 100644 --- a/src/styles/codeStyles/types.ts +++ b/src/styles/codeStyles/types.ts @@ -1,8 +1,7 @@ -import {CSSProperties} from 'react'; import {TextStyle, ViewStyle} from 'react-native'; -type CodeWordWrapperStyles = ViewStyle | CSSProperties; -type CodeWordStyles = TextStyle | CSSProperties; -type CodeTextStyles = TextStyle | CSSProperties; +type CodeWordWrapperStyles = ViewStyle; +type CodeWordStyles = TextStyle; +type CodeTextStyles = TextStyle; -export type {CodeWordWrapperStyles, CodeWordStyles, CodeTextStyles}; +export type {CodeTextStyles, CodeWordStyles, CodeWordWrapperStyles}; diff --git a/src/styles/editedLabelStyles/index.ts b/src/styles/editedLabelStyles/index.ts index 6e6164810b52..b3962e507757 100644 --- a/src/styles/editedLabelStyles/index.ts +++ b/src/styles/editedLabelStyles/index.ts @@ -1,7 +1,11 @@ -import EditedLabelStyles from './types'; +import {TextStyle} from 'react-native'; import display from '../utilities/display'; import flex from '../utilities/flex'; +import EditedLabelStyles from './types'; -const editedLabelStyles: EditedLabelStyles = {...display.dInlineFlex, ...flex.alignItemsBaseline}; +const editedLabelStyles: EditedLabelStyles = { + ...(display.dInlineFlex as TextStyle), + ...flex.alignItemsBaseline, +}; export default editedLabelStyles; diff --git a/src/styles/editedLabelStyles/types.ts b/src/styles/editedLabelStyles/types.ts index 8d96c9216ed2..20bcc8c55f15 100644 --- a/src/styles/editedLabelStyles/types.ts +++ b/src/styles/editedLabelStyles/types.ts @@ -1,6 +1,5 @@ -import {CSSProperties} from 'react'; import {TextStyle} from 'react-native'; -type EditedLabelStyles = CSSProperties | TextStyle; +type EditedLabelStyles = TextStyle; export default EditedLabelStyles; diff --git a/src/styles/getModalStyles.ts b/src/styles/getModalStyles.ts index 32e6cf6f98e7..ceea37ddb85b 100644 --- a/src/styles/getModalStyles.ts +++ b/src/styles/getModalStyles.ts @@ -1,11 +1,10 @@ -import {CSSProperties} from 'react'; import {ViewStyle} from 'react-native'; -import {ValueOf} from 'type-fest'; import {ModalProps} from 'react-native-modal'; +import {ValueOf} from 'type-fest'; import CONST from '../CONST'; -import variables from './variables'; -import themeColors from './themes/default'; import styles from './styles'; +import themeColors from './themes/default'; +import variables from './variables'; function getCenteredModalStyles(windowWidth: number, isSmallScreenWidth: boolean, isFullScreenWhenSmall = false): ViewStyle { return { @@ -26,7 +25,7 @@ type WindowDimensions = { type GetModalStyles = { modalStyle: ViewStyle; - modalContainerStyle: ViewStyle | Pick; + modalContainerStyle: ViewStyle; swipeDirection: ModalProps['swipeDirection']; animationIn: ModalProps['animationIn']; animationOut: ModalProps['animationOut']; diff --git a/src/styles/getNavigationModalCardStyles/index.ts b/src/styles/getNavigationModalCardStyles/index.ts index 8e2bffc1ecfc..d3cf49c6c89c 100644 --- a/src/styles/getNavigationModalCardStyles/index.ts +++ b/src/styles/getNavigationModalCardStyles/index.ts @@ -1,3 +1,7 @@ -export default () => ({ +import GetNavigationModalCardStyles from './types'; + +const getNavigationModalCardStyles: GetNavigationModalCardStyles = () => ({ height: '100%', }); + +export default getNavigationModalCardStyles; diff --git a/src/styles/getNavigationModalCardStyles/index.website.ts b/src/styles/getNavigationModalCardStyles/index.website.ts index 9879e4c1567b..ea76825e5bba 100644 --- a/src/styles/getNavigationModalCardStyles/index.website.ts +++ b/src/styles/getNavigationModalCardStyles/index.website.ts @@ -1,3 +1,4 @@ +import {ViewStyle} from 'react-native'; import GetNavigationModalCardStyles from './types'; const getNavigationModalCardStyles: GetNavigationModalCardStyles = () => ({ @@ -8,7 +9,10 @@ const getNavigationModalCardStyles: GetNavigationModalCardStyles = () => ({ // https://github.com/Expensify/App/issues/20709 width: '100%', height: '100%', - position: 'fixed', + + // NOTE: asserting "fixed" TS type to a valid type, because isn't possible + // to augment "position". + position: 'fixed' as ViewStyle['position'], }); export default getNavigationModalCardStyles; diff --git a/src/styles/getNavigationModalCardStyles/types.ts b/src/styles/getNavigationModalCardStyles/types.ts index 504b659c87b7..68453dc3c3de 100644 --- a/src/styles/getNavigationModalCardStyles/types.ts +++ b/src/styles/getNavigationModalCardStyles/types.ts @@ -1,9 +1,8 @@ -import {CSSProperties} from 'react'; import {ViewStyle} from 'react-native'; import {Merge} from 'type-fest'; type GetNavigationModalCardStylesParams = {isSmallScreenWidth: number}; -type GetNavigationModalCardStyles = (params: GetNavigationModalCardStylesParams) => Merge>; +type GetNavigationModalCardStyles = (params: GetNavigationModalCardStylesParams) => Merge>; export default GetNavigationModalCardStyles; diff --git a/src/styles/getReportActionContextMenuStyles.ts b/src/styles/getReportActionContextMenuStyles.ts index 17f0828ab80c..792213f2b54b 100644 --- a/src/styles/getReportActionContextMenuStyles.ts +++ b/src/styles/getReportActionContextMenuStyles.ts @@ -1,12 +1,11 @@ -import {CSSProperties} from 'react'; import {ViewStyle} from 'react-native'; import styles from './styles'; -import variables from './variables'; import themeColors from './themes/default'; +import variables from './variables'; -type StylesArray = Array; +type StylesArray = ViewStyle[]; -const defaultWrapperStyle: ViewStyle | CSSProperties = { +const defaultWrapperStyle: ViewStyle = { backgroundColor: themeColors.componentBG, }; @@ -18,7 +17,9 @@ const miniWrapperStyle: StylesArray = [ borderWidth: 1, borderColor: themeColors.border, // In Safari, when welcome messages use a code block (triple backticks), they would overlap the context menu below when there is no scrollbar without the transform style. - transform: 'translateZ(0)', + // NOTE: asserting "transform" TS type to a valid type, because isn't possible + // to augment "transform". + transform: 'translateZ(0)' as unknown as ViewStyle['transform'], }, ]; diff --git a/src/styles/italic/types.ts b/src/styles/italic/types.ts index 61e0328e52b6..0935c5844bb3 100644 --- a/src/styles/italic/types.ts +++ b/src/styles/italic/types.ts @@ -1,6 +1,5 @@ -import {CSSProperties} from 'react'; import {TextStyle} from 'react-native'; -type ItalicStyles = (TextStyle | CSSProperties)['fontStyle']; +type ItalicStyles = TextStyle['fontStyle']; export default ItalicStyles; diff --git a/src/styles/optionRowStyles/types.ts b/src/styles/optionRowStyles/types.ts index c08174470701..f645c6038397 100644 --- a/src/styles/optionRowStyles/types.ts +++ b/src/styles/optionRowStyles/types.ts @@ -1,6 +1,5 @@ -import {CSSProperties} from 'react'; import {ViewStyle} from 'react-native'; -type OptionRowStyles = CSSProperties | ViewStyle; +type OptionRowStyles = ViewStyle; export default OptionRowStyles; diff --git a/src/styles/overflowXHidden/types.ts b/src/styles/overflowXHidden/types.ts index 7ac572f0e651..0f13ba552c0c 100644 --- a/src/styles/overflowXHidden/types.ts +++ b/src/styles/overflowXHidden/types.ts @@ -1,5 +1,5 @@ -import {CSSProperties} from 'react'; +import {ViewStyle} from 'react-native'; -type OverflowXHiddenStyles = Partial>; +type OverflowXHiddenStyles = Pick; export default OverflowXHiddenStyles; diff --git a/src/styles/pointerEventsAuto/types.ts b/src/styles/pointerEventsAuto/types.ts index 7c9f0164c936..0ecbc851e000 100644 --- a/src/styles/pointerEventsAuto/types.ts +++ b/src/styles/pointerEventsAuto/types.ts @@ -1,5 +1,5 @@ -import {CSSProperties} from 'react'; +import {ViewStyle} from 'react-native'; -type PointerEventsAutoStyles = Partial>; +type PointerEventsAutoStyles = Pick; export default PointerEventsAutoStyles; diff --git a/src/styles/pointerEventsNone/types.ts b/src/styles/pointerEventsNone/types.ts index f2f5a0d88a41..766ac3f94349 100644 --- a/src/styles/pointerEventsNone/types.ts +++ b/src/styles/pointerEventsNone/types.ts @@ -1,5 +1,5 @@ -import {CSSProperties} from 'react'; +import {ViewStyle} from 'react-native'; -type PointerEventsNone = Partial>; +type PointerEventsNone = Pick; export default PointerEventsNone; diff --git a/src/styles/utilities/cursor/types.ts b/src/styles/utilities/cursor/types.ts index 9ca20c5ae123..98d661491c4b 100644 --- a/src/styles/utilities/cursor/types.ts +++ b/src/styles/utilities/cursor/types.ts @@ -1,4 +1,4 @@ -import {CSSProperties} from 'react'; +import {TextStyle, ViewStyle} from 'react-native'; type CursorStylesKeys = | 'cursorDefault' @@ -13,6 +13,6 @@ type CursorStylesKeys = | 'cursorInitial' | 'cursorText'; -type CursorStyles = Record>>; +type CursorStyles = Record>; export default CursorStyles; diff --git a/src/styles/utilities/overflow.ts b/src/styles/utilities/overflow.ts index 3961758e40bf..ac28df1c944f 100644 --- a/src/styles/utilities/overflow.ts +++ b/src/styles/utilities/overflow.ts @@ -1,4 +1,4 @@ -import {CSSProperties} from 'react'; +import {ViewStyle} from 'react-native'; import overflowAuto from './overflowAuto'; /** @@ -24,4 +24,4 @@ export default { }, overflowAuto, -} satisfies Record; +} satisfies Record; diff --git a/src/styles/utilities/overflowAuto/index.ts b/src/styles/utilities/overflowAuto/index.ts index 0eb19068738f..f958d8000c12 100644 --- a/src/styles/utilities/overflowAuto/index.ts +++ b/src/styles/utilities/overflowAuto/index.ts @@ -1,7 +1,10 @@ +import { ViewStyle } from 'react-native'; import OverflowAutoStyles from './types'; const overflowAuto: OverflowAutoStyles = { - overflow: 'auto', + // NOTE: asserting "auto" TS type to a valid type, because isn't possible + // to augment "overflow". + overflow: 'auto' as ViewStyle['overflow'], }; export default overflowAuto; diff --git a/src/styles/utilities/overflowAuto/types.ts b/src/styles/utilities/overflowAuto/types.ts index faba7c2cbdb8..da7548d49e7b 100644 --- a/src/styles/utilities/overflowAuto/types.ts +++ b/src/styles/utilities/overflowAuto/types.ts @@ -1,5 +1,5 @@ -import {CSSProperties} from 'react'; +import {ViewStyle} from 'react-native'; -type OverflowAutoStyles = Pick; +type OverflowAutoStyles = Pick; export default OverflowAutoStyles; diff --git a/src/styles/utilities/textUnderline/types.ts b/src/styles/utilities/textUnderline/types.ts index ecc09ed0fe09..f71d2bfdaa9a 100644 --- a/src/styles/utilities/textUnderline/types.ts +++ b/src/styles/utilities/textUnderline/types.ts @@ -1,8 +1,8 @@ -import {CSSProperties} from 'react'; +import {TextStyle} from 'react-native'; type TextUnderlineStyles = { - textUnderlinePositionUnder: Pick; - textDecorationSkipInkNone: Pick; + textUnderlinePositionUnder: Pick; + textDecorationSkipInkNone: Pick; }; export default TextUnderlineStyles; diff --git a/src/styles/utilities/userSelect/types.ts b/src/styles/utilities/userSelect/types.ts index 67a8c9c7b9b6..a177bac5a3e7 100644 --- a/src/styles/utilities/userSelect/types.ts +++ b/src/styles/utilities/userSelect/types.ts @@ -1,5 +1,5 @@ -import {CSSProperties} from 'react'; +import {TextStyle} from 'react-native'; -type UserSelectStyles = Record<'userSelectText' | 'userSelectNone', Partial>>; +type UserSelectStyles = Record<'userSelectText' | 'userSelectNone', Pick>; export default UserSelectStyles; diff --git a/src/styles/utilities/visibility/types.ts b/src/styles/utilities/visibility/types.ts index 9dab3d7c752e..872e35195edd 100644 --- a/src/styles/utilities/visibility/types.ts +++ b/src/styles/utilities/visibility/types.ts @@ -1,5 +1,5 @@ -import {CSSProperties} from 'react'; +import {TextStyle, ViewStyle} from 'react-native'; -type VisibilityStyles = Record<'visible' | 'hidden', Partial>>; +type VisibilityStyles = Record<'visible' | 'hidden', Pick>; export default VisibilityStyles; diff --git a/src/styles/utilities/whiteSpace/types.ts b/src/styles/utilities/whiteSpace/types.ts index c42dc14080b1..b10671f04977 100644 --- a/src/styles/utilities/whiteSpace/types.ts +++ b/src/styles/utilities/whiteSpace/types.ts @@ -1,5 +1,5 @@ -import {CSSProperties} from 'react'; +import {TextStyle} from 'react-native'; -type WhiteSpaceStyles = Record<'noWrap' | 'preWrap' | 'pre', Partial>>; +type WhiteSpaceStyles = Record<'noWrap' | 'preWrap' | 'pre', Pick>; export default WhiteSpaceStyles; diff --git a/src/styles/utilities/wordBreak/types.ts b/src/styles/utilities/wordBreak/types.ts index 003a5ca1c869..0ed520ae119d 100644 --- a/src/styles/utilities/wordBreak/types.ts +++ b/src/styles/utilities/wordBreak/types.ts @@ -1,5 +1,5 @@ -import {CSSProperties} from 'react'; +import {TextStyle} from 'react-native'; -type WordBreakStyles = Record<'breakWord' | 'breakAll', Partial>>; +type WordBreakStyles = Record<'breakWord' | 'breakAll', Pick>; export default WordBreakStyles; diff --git a/src/types/modules/react-native.d.ts b/src/types/modules/react-native.d.ts new file mode 100644 index 000000000000..1fc0617331ce --- /dev/null +++ b/src/types/modules/react-native.d.ts @@ -0,0 +1,148 @@ +/* eslint-disable @typescript-eslint/no-empty-interface */ +/* eslint-disable @typescript-eslint/consistent-type-definitions */ +import {CSSProperties} from 'react'; +import 'react-native'; +import {Merge, MergeDeep, OverrideProperties} from 'type-fest'; + +declare module 'react-native' { + // interface ViewStyle extends CSSProperties {} + // interface ViewProps { + // style?: number; // Doesn't make any difference + // onLayout?: number; + // href?: string; // It appear new prop as suggestion + // focusable?: string; + // } + type NumberOrString = number | string; + + type AnimationDirection = 'alternate' | 'alternate-reverse' | 'normal' | 'reverse'; + type AnimationFillMode = 'none' | 'forwards' | 'backwards' | 'both'; + type AnimationIterationCount = number | 'infinite'; + type AnimationKeyframes = string | object; + type AnimationPlayState = 'paused' | 'running'; + + type AnimationStyles = { + animationDelay?: string | string[]; + animationDirection?: AnimationDirection | AnimationDirection[]; + animationDuration?: string | string[]; + animationFillMode?: AnimationFillMode | AnimationFillMode[]; + animationIterationCount?: AnimationIterationCount | AnimationIterationCount[]; + animationKeyframes?: AnimationKeyframes | AnimationKeyframes[]; + animationPlayState?: AnimationPlayState | AnimationPlayState[]; + animationTimingFunction?: string | string[]; + transitionDelay?: string | string[]; + transitionDuration?: string | string[]; + transitionProperty?: string | string[]; + transitionTimingFunction?: string | string[]; + }; + + /** + * Image + */ + interface WebImageProps { + draggable?: boolean; + } + interface ImageProps extends WebImageProps {} + + /** + * Pressable + */ + interface WebPressableProps { + delayPressIn?: number; + delayPressOut?: number; + onPressMove?: null | ((event: GestureResponderEvent) => void); + onPressEnd?: null | ((event: GestureResponderEvent) => void); + } + interface WebPressableStateCallbackType { + readonly focused: boolean; + readonly hovered: boolean; + readonly pressed: boolean; + } + interface PressableProps extends WebPressableProps {} + interface PressableStateCallbackType extends WebPressableStateCallbackType {} + + /** + * Text + */ + interface WebTextProps { + dataSet?: Record; + dir?: 'auto' | 'ltr' | 'rtl'; + href?: string; + hrefAttrs?: { + download?: boolean; + rel?: string; + target?: string; + }; + lang?: string; + } + interface TextProps extends WebTextProps {} + + /** + * TextInput + */ + interface WebTextInputProps { + dataSet?: Record; + dir?: 'auto' | 'ltr' | 'rtl'; + lang?: string; + disabled?: boolean; + } + interface TextInputProps extends WebTextInputProps {} + + /** + * View + */ + interface WebViewProps { + dataSet?: Record; + dir?: 'ltr' | 'rtl'; + href?: string; + hrefAttrs?: { + download?: boolean; + rel?: string; + target?: string; + }; + tabIndex?: 0 | -1; + } + interface ViewProps extends WebViewProps {} + + interface WebStyle { + wordBreak?: CSSProperties['wordBreak']; + whiteSpace?: CSSProperties['whiteSpace']; + visibility?: CSSProperties['visibility']; + userSelect?: CSSProperties['userSelect']; + WebkitUserSelect?: CSSProperties['WebkitUserSelect']; + textUnderlinePosition?: CSSProperties['textUnderlinePosition']; + textDecorationSkipInk?: CSSProperties['textDecorationSkipInk']; + cursor?: CSSProperties['cursor']; + outlineWidth?: CSSProperties['outlineWidth']; + outlineStyle?: CSSProperties['outlineStyle']; + boxShadow?: CSSProperties['boxShadow']; + } + + interface WebViewStyle { + overscrollBehaviorX?: CSSProperties['overscrollBehaviorX']; + overflowX?: CSSProperties['overflowX']; + } + + // interface FlexStyle { + // overflow?: string; + // } + interface ViewStyle extends WebStyle, WebViewStyle {} + interface TextStyle extends WebStyle {} + interface ImageStyle extends WebStyle {} +} + +interface A { + overflow?: string; +} + +interface B { + overflow?: number; +} + +declare const a: A; + +// interface A extends B {} + +interface C extends Omit { + overflow?: number; +} +declare const a: C; From b1ee74b72cc5bad57fe70dc1be9740933ecefc25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Tue, 5 Sep 2023 16:12:01 +0100 Subject: [PATCH 015/856] Add more typings --- src/types/modules/react-native.d.ts | 197 +++++++++++++++++++++------- 1 file changed, 148 insertions(+), 49 deletions(-) diff --git a/src/types/modules/react-native.d.ts b/src/types/modules/react-native.d.ts index 1fc0617331ce..498529cf124a 100644 --- a/src/types/modules/react-native.d.ts +++ b/src/types/modules/react-native.d.ts @@ -1,40 +1,159 @@ +/* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/no-empty-interface */ /* eslint-disable @typescript-eslint/consistent-type-definitions */ -import {CSSProperties} from 'react'; +import {CSSProperties, FocusEventHandler, KeyboardEventHandler, MouseEventHandler} from 'react'; import 'react-native'; -import {Merge, MergeDeep, OverrideProperties} from 'type-fest'; declare module 'react-native' { - // interface ViewStyle extends CSSProperties {} - // interface ViewProps { - // style?: number; // Doesn't make any difference - // onLayout?: number; - // href?: string; // It appear new prop as suggestion - // focusable?: string; - // } + // Extracted from react-native-web, packages/react-native-web/src/exports/View/types.js type NumberOrString = number | string; + type OverscrollBehaviorValue = 'auto' | 'contain' | 'none'; + type idRef = string; + type idRefList = idRef | idRef[]; + + // https://necolas.github.io/react-native-web/docs/accessibility/#accessibility-props-api + // Extracted from react-native-web, packages/react-native-web/src/exports/View/types.js + type AccessibilityProps = { + 'aria-activedescendant'?: idRef; + 'aria-atomic'?: boolean; + 'aria-autocomplete'?: 'none' | 'list' | 'inline' | 'both'; + 'aria-busy'?: boolean; + 'aria-checked'?: boolean | 'mixed'; + 'aria-colcount'?: number; + 'aria-colindex'?: number; + 'aria-colspan'?: number; + 'aria-controls'?: idRef; + 'aria-current'?: boolean | 'page' | 'step' | 'location' | 'date' | 'time'; + 'aria-describedby'?: idRef; + 'aria-details'?: idRef; + 'aria-disabled'?: boolean; + 'aria-errormessage'?: idRef; + 'aria-expanded'?: boolean; + 'aria-flowto'?: idRef; + 'aria-haspopup'?: 'dialog' | 'grid' | 'listbox' | 'menu' | 'tree' | false; + 'aria-hidden'?: boolean; + 'aria-invalid'?: boolean; + 'aria-keyshortcuts'?: string[]; + 'aria-label'?: string; + 'aria-labelledby'?: idRef; + 'aria-level'?: number; + 'aria-live'?: 'assertive' | 'none' | 'polite'; + 'aria-modal'?: boolean; + 'aria-multiline'?: boolean; + 'aria-multiselectable'?: boolean; + 'aria-orientation'?: 'horizontal' | 'vertical'; + 'aria-owns'?: idRef; + 'aria-placeholder'?: string; + 'aria-posinset'?: number; + 'aria-pressed'?: boolean | 'mixed'; + 'aria-readonly'?: boolean; + 'aria-required'?: boolean; + 'aria-roledescription'?: string; + 'aria-rowcount'?: number; + 'aria-rowindex'?: number; + 'aria-rowspan'?: number; + 'aria-selected'?: boolean; + 'aria-setsize'?: number; + 'aria-sort'?: 'ascending' | 'descending' | 'none' | 'other'; + 'aria-valuemax'?: number; + 'aria-valuemin'?: number; + 'aria-valuenow'?: number; + 'aria-valuetext'?: string; + role?: string; + + // @deprecated + accessibilityActiveDescendant?: idRef; + accessibilityAtomic?: boolean; + accessibilityAutoComplete?: 'none' | 'list' | 'inline' | 'both'; + accessibilityBusy?: boolean; + accessibilityChecked?: boolean | 'mixed'; + accessibilityColumnCount?: number; + accessibilityColumnIndex?: number; + accessibilityColumnSpan?: number; + accessibilityControls?: idRefList; + accessibilityCurrent?: boolean | 'page' | 'step' | 'location' | 'date' | 'time'; + accessibilityDescribedBy?: idRefList; + accessibilityDetails?: idRef; + accessibilityDisabled?: boolean; + accessibilityErrorMessage?: idRef; + accessibilityExpanded?: boolean; + accessibilityFlowTo?: idRefList; + accessibilityHasPopup?: 'dialog' | 'grid' | 'listbox' | 'menu' | 'tree' | false; + accessibilityHidden?: boolean; + accessibilityInvalid?: boolean; + accessibilityKeyShortcuts?: string[]; + accessibilityLabel?: string; + accessibilityLabelledBy?: idRefList; + accessibilityLevel?: number; + accessibilityLiveRegion?: 'assertive' | 'none' | 'polite'; + accessibilityModal?: boolean; + accessibilityMultiline?: boolean; + accessibilityMultiSelectable?: boolean; + accessibilityOrientation?: 'horizontal' | 'vertical'; + accessibilityOwns?: idRefList; + accessibilityPlaceholder?: string; + accessibilityPosInSet?: number; + accessibilityPressed?: boolean | 'mixed'; + accessibilityReadOnly?: boolean; + accessibilityRequired?: boolean; + accessibilityRole?: string; + accessibilityRoleDescription?: string; + accessibilityRowCount?: number; + accessibilityRowIndex?: number; + accessibilityRowSpan?: number; + accessibilitySelected?: boolean; + accessibilitySetSize?: number; + accessibilitySort?: 'ascending' | 'descending' | 'none' | 'other'; + accessibilityValueMax?: number; + accessibilityValueMin?: number; + accessibilityValueNow?: number; + accessibilityValueText?: string; + }; + + // https://necolas.github.io/react-native-web/docs/interactions/#pointerevent-props-api + // Extracted from react-native-web, packages/react-native-web/src/exports/View/types.js + // Extracted from @types/react, index.d.ts + type PointerProps = { + onClick?: MouseEventHandler; + onClickCapture?: MouseEventHandler; + onContextMenu?: MouseEventHandler; + }; - type AnimationDirection = 'alternate' | 'alternate-reverse' | 'normal' | 'reverse'; - type AnimationFillMode = 'none' | 'forwards' | 'backwards' | 'both'; - type AnimationIterationCount = number | 'infinite'; - type AnimationKeyframes = string | object; - type AnimationPlayState = 'paused' | 'running'; - - type AnimationStyles = { - animationDelay?: string | string[]; - animationDirection?: AnimationDirection | AnimationDirection[]; - animationDuration?: string | string[]; - animationFillMode?: AnimationFillMode | AnimationFillMode[]; - animationIterationCount?: AnimationIterationCount | AnimationIterationCount[]; - animationKeyframes?: AnimationKeyframes | AnimationKeyframes[]; - animationPlayState?: AnimationPlayState | AnimationPlayState[]; - animationTimingFunction?: string | string[]; - transitionDelay?: string | string[]; - transitionDuration?: string | string[]; - transitionProperty?: string | string[]; - transitionTimingFunction?: string | string[]; + // TODO: Confirm + type FocusProps = { + onBlur?: FocusEventHandler; + onFocus?: FocusEventHandler; }; + // TODO: Confirm + type KeyboardProps = { + onKeyDown?: KeyboardEventHandler; + onKeyDownCapture?: KeyboardEventHandler; + onKeyUp?: KeyboardEventHandler; + onKeyUpCapture?: KeyboardEventHandler; + }; + + // type AnimationDirection = 'alternate' | 'alternate-reverse' | 'normal' | 'reverse'; + // type AnimationFillMode = 'none' | 'forwards' | 'backwards' | 'both'; + // type AnimationIterationCount = number | 'infinite'; + // type AnimationKeyframes = string | object; + // type AnimationPlayState = 'paused' | 'running'; + + // type AnimationStyles = { + // animationDelay?: string | string[]; + // animationDirection?: AnimationDirection | AnimationDirection[]; + // animationDuration?: string | string[]; + // animationFillMode?: AnimationFillMode | AnimationFillMode[]; + // animationIterationCount?: AnimationIterationCount | AnimationIterationCount[]; + // animationKeyframes?: AnimationKeyframes | AnimationKeyframes[]; + // animationPlayState?: AnimationPlayState | AnimationPlayState[]; + // animationTimingFunction?: string | string[]; + // transitionDelay?: string | string[]; + // transitionDuration?: string | string[]; + // transitionProperty?: string | string[]; + // transitionTimingFunction?: string | string[]; + // }; + /** * Image */ @@ -90,7 +209,7 @@ declare module 'react-native' { /** * View */ - interface WebViewProps { + interface WebViewProps extends AccessibilityProps, PointerProps, FocusProps, KeyboardProps { dataSet?: Record; dir?: 'ltr' | 'rtl'; href?: string; @@ -122,27 +241,7 @@ declare module 'react-native' { overflowX?: CSSProperties['overflowX']; } - // interface FlexStyle { - // overflow?: string; - // } interface ViewStyle extends WebStyle, WebViewStyle {} interface TextStyle extends WebStyle {} interface ImageStyle extends WebStyle {} } - -interface A { - overflow?: string; -} - -interface B { - overflow?: number; -} - -declare const a: A; - -// interface A extends B {} - -interface C extends Omit { - overflow?: number; -} -declare const a: C; From dc842449fdb1aabd252dc1ff56e310e8454bb830 Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Thu, 7 Sep 2023 15:13:16 +0200 Subject: [PATCH 016/856] [TS migration] Migrate 'KeyboardShortcuts.js' lib to TypeScript --- .../actions/{KeyboardShortcuts.js => KeyboardShortcuts.ts} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename src/libs/actions/{KeyboardShortcuts.js => KeyboardShortcuts.ts} (82%) diff --git a/src/libs/actions/KeyboardShortcuts.js b/src/libs/actions/KeyboardShortcuts.ts similarity index 82% rename from src/libs/actions/KeyboardShortcuts.js rename to src/libs/actions/KeyboardShortcuts.ts index d66e362890b2..3f12f9fc92e1 100644 --- a/src/libs/actions/KeyboardShortcuts.js +++ b/src/libs/actions/KeyboardShortcuts.ts @@ -1,7 +1,7 @@ import Onyx from 'react-native-onyx'; import ONYXKEYS from '../../ONYXKEYS'; -let isShortcutsModalOpen; +let isShortcutsModalOpen: boolean | null; Onyx.connect({ key: ONYXKEYS.IS_SHORTCUTS_MODAL_OPEN, callback: (flag) => (isShortcutsModalOpen = flag), @@ -11,7 +11,7 @@ Onyx.connect({ /** * Set keyboard shortcuts flag to show modal */ -function showKeyboardShortcutModal() { +function showKeyboardShortcutModal(): void { if (isShortcutsModalOpen) { return; } @@ -21,7 +21,7 @@ function showKeyboardShortcutModal() { /** * Unset keyboard shortcuts flag to hide modal */ -function hideKeyboardShortcutModal() { +function hideKeyboardShortcutModal(): void { if (!isShortcutsModalOpen) { return; } From fabe55fdbbec43e1b8a872d538afb42949277c3b Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Mon, 11 Sep 2023 15:48:37 -0700 Subject: [PATCH 017/856] WIP render an eReceipt for distance requests --- .../Attachments/AttachmentView/index.js | 20 +++++++ src/components/EReceipt.js | 54 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 src/components/EReceipt.js diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index 47353d915060..584827f499c6 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -17,6 +17,11 @@ import AttachmentViewPdf from './AttachmentViewPdf'; import addEncryptedAuthTokenToURL from '../../../libs/addEncryptedAuthTokenToURL'; import * as StyleUtils from '../../../styles/StyleUtils'; import {attachmentViewPropTypes, attachmentViewDefaultProps} from './propTypes'; +import {useRoute} from '@react-navigation/native'; +import * as ReportUtils from '../../../libs/ReportUtils'; +import * as TransactionUtils from '../../../libs/TransactionUtils'; +import EReceipt from '../../EReceipt'; +import * as ReportActionsUtils from '../../../libs/ReportActionsUtils'; const propTypes = { ...attachmentViewPropTypes, @@ -107,6 +112,21 @@ function AttachmentView({ ); } + const currentRoute = useRoute(); + const reportID = _.get(currentRoute, ['params', 'reportID']); + const report = ReportUtils.getReport(reportID); + + // Get the money request transaction + const parentReportAction = ReportActionsUtils.getParentReportAction(report); + const transactionID = _.get(parentReportAction, ['originalMessage', 'IOUTransactionID'], 0); + const transaction = TransactionUtils.getTransaction(transactionID); + console.log('transaction', transaction); + + const shouldShowEReceipt = TransactionUtils.isDistanceRequest(transaction); + if (shouldShowEReceipt) { + return + } + // For this check we use both source and file.name since temporary file source is a blob // both PDFs and images will appear as images when pasted into the the text field const isImage = Str.isImage(source); diff --git a/src/components/EReceipt.js b/src/components/EReceipt.js new file mode 100644 index 000000000000..406825926820 --- /dev/null +++ b/src/components/EReceipt.js @@ -0,0 +1,54 @@ +import React, {useEffect} from 'react'; +import PropTypes from 'prop-types'; +import {View} from 'react-native'; + +import {withOnyx} from 'react-native-onyx'; +import lodashGet from 'lodash/get'; +import _ from 'underscore'; +import ONYXKEYS from '../ONYXKEYS'; +import CONST from '../CONST'; +import * as MapboxToken from '../libs/actions/MapboxToken'; +import * as TransactionUtils from '../libs/TransactionUtils'; +import * as Expensicons from './Icon/Expensicons'; +import theme from '../styles/themes/default'; +import styles from '../styles/styles'; +import transactionPropTypes from './transactionPropTypes'; +import BlockingView from './BlockingViews/BlockingView'; +import useNetwork from '../hooks/useNetwork'; +import useLocalize from '../hooks/useLocalize'; + +const propTypes = { + /** The transaction for the eReceipt */ + transaction: transactionPropTypes, +}; + +const defaultProps = { + transaction: {}, +}; + + +function EReceipt({transaction}) { + const {isOffline} = useNetwork(); + const {translate} = useLocalize(); + const {route0: route} = transaction.routes || {}; + + useEffect(() => { + MapboxToken.init(); + return MapboxToken.stop; + }, []); + + return ( + <> + + ); +} + +export default withOnyx({ + transaction: { + key: ({transactionID}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, + }, +})(EReceipt); + +EReceipt.displayName = 'EReceipt'; +EReceipt.propTypes = propTypes; +EReceipt.defaultProps = defaultProps; From a6b83fedb4f1f1380cb433ee216da7169453a470 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Mon, 11 Sep 2023 16:26:00 -0700 Subject: [PATCH 018/856] Render the receipt image --- src/components/EReceipt.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/components/EReceipt.js b/src/components/EReceipt.js index 406825926820..20d4c3789838 100644 --- a/src/components/EReceipt.js +++ b/src/components/EReceipt.js @@ -16,6 +16,8 @@ import transactionPropTypes from './transactionPropTypes'; import BlockingView from './BlockingViews/BlockingView'; import useNetwork from '../hooks/useNetwork'; import useLocalize from '../hooks/useLocalize'; +import * as ReceiptUtils from '../libs/ReceiptUtils'; +import Image from './Image'; const propTypes = { /** The transaction for the eReceipt */ @@ -32,13 +34,19 @@ function EReceipt({transaction}) { const {translate} = useLocalize(); const {route0: route} = transaction.routes || {}; - useEffect(() => { - MapboxToken.init(); - return MapboxToken.stop; - }, []); - + const {thumbnail} = ReceiptUtils.getThumbnailAndImageURIs(transaction.receipt.source, transaction.filename); return ( <> + + + ); } From 9d7538c13ae968c886d6b266f91974a88fc7ef7f Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Mon, 11 Sep 2023 16:33:08 -0700 Subject: [PATCH 019/856] Style and clean up --- .../Attachments/AttachmentView/index.js | 2 +- src/components/EReceipt.js | 31 ++----------------- 2 files changed, 4 insertions(+), 29 deletions(-) diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index 584827f499c6..e7d1dd5abb6b 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -124,7 +124,7 @@ function AttachmentView({ const shouldShowEReceipt = TransactionUtils.isDistanceRequest(transaction); if (shouldShowEReceipt) { - return + return ; } // For this check we use both source and file.name since temporary file source is a blob diff --git a/src/components/EReceipt.js b/src/components/EReceipt.js index 20d4c3789838..2068a5e50f12 100644 --- a/src/components/EReceipt.js +++ b/src/components/EReceipt.js @@ -1,21 +1,8 @@ -import React, {useEffect} from 'react'; -import PropTypes from 'prop-types'; +import React from 'react'; import {View} from 'react-native'; - -import {withOnyx} from 'react-native-onyx'; -import lodashGet from 'lodash/get'; import _ from 'underscore'; -import ONYXKEYS from '../ONYXKEYS'; -import CONST from '../CONST'; -import * as MapboxToken from '../libs/actions/MapboxToken'; -import * as TransactionUtils from '../libs/TransactionUtils'; -import * as Expensicons from './Icon/Expensicons'; -import theme from '../styles/themes/default'; import styles from '../styles/styles'; import transactionPropTypes from './transactionPropTypes'; -import BlockingView from './BlockingViews/BlockingView'; -import useNetwork from '../hooks/useNetwork'; -import useLocalize from '../hooks/useLocalize'; import * as ReceiptUtils from '../libs/ReceiptUtils'; import Image from './Image'; @@ -28,18 +15,11 @@ const defaultProps = { transaction: {}, }; - function EReceipt({transaction}) { - const {isOffline} = useNetwork(); - const {translate} = useLocalize(); - const {route0: route} = transaction.routes || {}; - const {thumbnail} = ReceiptUtils.getThumbnailAndImageURIs(transaction.receipt.source, transaction.filename); return ( <> - + `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, - }, -})(EReceipt); - +export default EReceipt; EReceipt.displayName = 'EReceipt'; EReceipt.propTypes = propTypes; EReceipt.defaultProps = defaultProps; From 383b99130003ea542f4b55d0bcb0eebfea97d8ee Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Tue, 12 Sep 2023 11:16:20 +0200 Subject: [PATCH 020/856] Remove void return --- src/libs/actions/KeyboardShortcuts.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/KeyboardShortcuts.ts b/src/libs/actions/KeyboardShortcuts.ts index 3f12f9fc92e1..f401e3dd89aa 100644 --- a/src/libs/actions/KeyboardShortcuts.ts +++ b/src/libs/actions/KeyboardShortcuts.ts @@ -11,7 +11,7 @@ Onyx.connect({ /** * Set keyboard shortcuts flag to show modal */ -function showKeyboardShortcutModal(): void { +function showKeyboardShortcutModal() { if (isShortcutsModalOpen) { return; } @@ -21,7 +21,7 @@ function showKeyboardShortcutModal(): void { /** * Unset keyboard shortcuts flag to hide modal */ -function hideKeyboardShortcutModal(): void { +function hideKeyboardShortcutModal() { if (!isShortcutsModalOpen) { return; } From f730d9cb3f6e395b4258d77f7643f6c43d69f9a3 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Wed, 13 Sep 2023 12:30:06 +0530 Subject: [PATCH 021/856] upgrade rn-sdk to 8.3.0 --- ios/Podfile.lock | 10 +++++----- package-lock.json | 11 +++++++---- package.json | 2 +- ...4.0.patch => @onfido+react-native-sdk+8.3.0.patch} | 4 ++-- 4 files changed, 15 insertions(+), 12 deletions(-) rename patches/{@onfido+react-native-sdk+7.4.0.patch => @onfido+react-native-sdk+8.3.0.patch} (90%) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 2bea672171fe..76bd62b08c5c 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -251,9 +251,9 @@ PODS: - nanopb/encode (= 2.30908.0) - nanopb/decode (2.30908.0) - nanopb/encode (2.30908.0) - - Onfido (27.4.0) - - onfido-react-native-sdk (7.4.0): - - Onfido (= 27.4.0) + - Onfido (28.3.0) + - onfido-react-native-sdk (8.3.0): + - Onfido (~> 28.3.0) - React - OpenSSL-Universal (1.1.1100) - Permission-Camera (3.6.1): @@ -1219,8 +1219,8 @@ SPEC CHECKSUMS: MapboxMaps: af50ec61a7eb3b032c3f7962c6bd671d93d2a209 MapboxMobileEvents: de50b3a4de180dd129c326e09cd12c8adaaa46d6 nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96 - Onfido: e36f284b865adcf99d9c905590a64ac09d4a576b - onfido-react-native-sdk: 4ecde1a97435dcff9f00a878e3f8d1eb14fabbdc + Onfido: c7d010d9793790d44a07799d9be25aa8e3814ee7 + onfido-react-native-sdk: b346a620af5669f9fecb6dc3052314a35a94ad9f OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c Permission-Camera: bf6791b17c7f614b6826019fcfdcc286d3a107f6 Permission-LocationAccuracy: 76df17de5c6b8bc2eee34e61ee92cdd7a864c73d diff --git a/package-lock.json b/package-lock.json index 9336f8d44eee..4189d93a3b4a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "@gorhom/portal": "^1.0.14", "@invertase/react-native-apple-authentication": "^2.2.2", "@oguzhnatly/react-native-image-manipulator": "github:Expensify/react-native-image-manipulator#5cdae3d4455b03a04c57f50be3863e2fe6c92c52", - "@onfido/react-native-sdk": "7.4.0", + "@onfido/react-native-sdk": "8.3.0", "@react-native-async-storage/async-storage": "^1.17.10", "@react-native-camera-roll/camera-roll": "5.4.0", "@react-native-community/clipboard": "^1.5.1", @@ -5961,8 +5961,9 @@ } }, "node_modules/@onfido/react-native-sdk": { - "version": "7.4.0", - "license": "MIT", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@onfido/react-native-sdk/-/react-native-sdk-8.3.0.tgz", + "integrity": "sha512-nnhuvezd35v08WXUTQlX+gr4pbnNnwNV5KscC/jJrfjGikNUJnhnAHYxfnfJccTn44qUC6vRaKWq2GfpMUnqNA==", "peerDependencies": { "react": ">=17.0.0", "react-native": ">=0.68.2 <1.0.x" @@ -51793,7 +51794,9 @@ } }, "@onfido/react-native-sdk": { - "version": "7.4.0", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@onfido/react-native-sdk/-/react-native-sdk-8.3.0.tgz", + "integrity": "sha512-nnhuvezd35v08WXUTQlX+gr4pbnNnwNV5KscC/jJrfjGikNUJnhnAHYxfnfJccTn44qUC6vRaKWq2GfpMUnqNA==", "requires": {} }, "@pkgjs/parseargs": { diff --git a/package.json b/package.json index 021dba72e545..1b3ad1b64208 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "@gorhom/portal": "^1.0.14", "@invertase/react-native-apple-authentication": "^2.2.2", "@oguzhnatly/react-native-image-manipulator": "github:Expensify/react-native-image-manipulator#5cdae3d4455b03a04c57f50be3863e2fe6c92c52", - "@onfido/react-native-sdk": "7.4.0", + "@onfido/react-native-sdk": "8.3.0", "@react-native-async-storage/async-storage": "^1.17.10", "@react-native-camera-roll/camera-roll": "5.4.0", "@react-native-community/clipboard": "^1.5.1", diff --git a/patches/@onfido+react-native-sdk+7.4.0.patch b/patches/@onfido+react-native-sdk+8.3.0.patch similarity index 90% rename from patches/@onfido+react-native-sdk+7.4.0.patch rename to patches/@onfido+react-native-sdk+8.3.0.patch index b84225c0f667..12245cb58355 100644 --- a/patches/@onfido+react-native-sdk+7.4.0.patch +++ b/patches/@onfido+react-native-sdk+8.3.0.patch @@ -1,8 +1,8 @@ diff --git a/node_modules/@onfido/react-native-sdk/android/build.gradle b/node_modules/@onfido/react-native-sdk/android/build.gradle -index 781925b..9e16430 100644 +index b4c7106..d5083d3 100644 --- a/node_modules/@onfido/react-native-sdk/android/build.gradle +++ b/node_modules/@onfido/react-native-sdk/android/build.gradle -@@ -134,9 +134,9 @@ afterEvaluate { project -> +@@ -135,9 +135,9 @@ afterEvaluate { project -> group = "Reporting" description = "Generate Jacoco coverage reports after running tests." reports { From 4aef004335b01a621e6aba97119eacebeea64229 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Wed, 13 Sep 2023 15:14:07 +0100 Subject: [PATCH 022/856] Remove more CSSProperties usage from styles --- src/styles/StyleUtils.ts | 310 +++++++++--------- src/styles/cardStyles/index.ts | 4 +- src/styles/cardStyles/types.ts | 3 +- src/styles/editedLabelStyles/index.ts | 3 +- src/styles/fontWeight/bold/types.ts | 3 +- .../index.website.ts | 3 +- .../getReportActionContextMenuStyles.ts | 3 +- src/styles/getTooltipStyles.ts | 19 +- src/styles/utilities/display.ts | 12 +- src/styles/utilities/overflowAuto/index.ts | 5 +- 10 files changed, 185 insertions(+), 180 deletions(-) diff --git a/src/styles/StyleUtils.ts b/src/styles/StyleUtils.ts index 8945bc0be058..708a428b5633 100644 --- a/src/styles/StyleUtils.ts +++ b/src/styles/StyleUtils.ts @@ -1,19 +1,18 @@ +import {Animated, FlexStyle, PressableStateCallbackType, TextStyle, ViewStyle} from 'react-native'; import {EdgeInsets} from 'react-native-safe-area-context'; -import {Animated, PressableStateCallbackType, TextStyle, ViewStyle} from 'react-native'; -import {CSSProperties} from 'react'; import {ValueOf} from 'type-fest'; import CONST from '../CONST'; +import * as Browser from '../libs/Browser'; +import * as NumberUtils from '../libs/NumberUtils'; +import * as UserUtils from '../libs/UserUtils'; +import colors from './colors'; import fontFamily from './fontFamily'; +import styles from './styles'; import themeColors from './themes/default'; -import variables from './variables'; -import colors from './colors'; +import cursor from './utilities/cursor'; import positioning from './utilities/positioning'; -import styles from './styles'; import spacing from './utilities/spacing'; -import * as UserUtils from '../libs/UserUtils'; -import * as Browser from '../libs/Browser'; -import cursor from './utilities/cursor'; -import * as NumberUtils from '../libs/NumberUtils'; +import variables from './variables'; type ColorValue = ValueOf; type AvatarSizeName = ValueOf; @@ -38,7 +37,7 @@ type ButtonSizeValue = ValueOf; type EmptyAvatarSizeName = ValueOf>; type ButtonStateName = ValueOf; type AvatarSize = {width: number}; -type ParsableStyle = ViewStyle | CSSProperties | ((state: PressableStateCallbackType) => ViewStyle | CSSProperties); +type ParsableStyle = ViewStyle | ((state: PressableStateCallbackType) => ViewStyle); type WorkspaceColorStyle = {backgroundColor: ColorValue; fill: ColorValue}; @@ -115,7 +114,7 @@ const avatarSizes: Record = { [CONST.AVATAR_SIZE.SMALL_NORMAL]: variables.avatarSizeSmallNormal, }; -const emptyAvatarStyles: Record = { +const emptyAvatarStyles: Record = { [CONST.AVATAR_SIZE.SMALL]: styles.emptyAvatarSmall, [CONST.AVATAR_SIZE.MEDIUM]: styles.emptyAvatarMedium, [CONST.AVATAR_SIZE.LARGE]: styles.emptyAvatarLarge, @@ -155,21 +154,21 @@ function getAvatarSize(size: AvatarSizeName): number { /** * Return the height of magic code input container */ -function getHeightOfMagicCodeInput(): ViewStyle | CSSProperties { +function getHeightOfMagicCodeInput(): ViewStyle { return {height: styles.magicCodeInputContainer.minHeight - styles.textInputContainer.borderBottomWidth}; } /** * Return the style from an empty avatar size constant */ -function getEmptyAvatarStyle(size: EmptyAvatarSizeName): ViewStyle | CSSProperties | undefined { +function getEmptyAvatarStyle(size: EmptyAvatarSizeName): ViewStyle | undefined { return emptyAvatarStyles[size]; } /** * Return the width style from an avatar size constant */ -function getAvatarWidthStyle(size: AvatarSizeName): ViewStyle | CSSProperties { +function getAvatarWidthStyle(size: AvatarSizeName): ViewStyle { const avatarSize = getAvatarSize(size); return { width: avatarSize, @@ -179,7 +178,7 @@ function getAvatarWidthStyle(size: AvatarSizeName): ViewStyle | CSSProperties { /** * Return the style from an avatar size constant */ -function getAvatarStyle(size: AvatarSizeName): ViewStyle | CSSProperties { +function getAvatarStyle(size: AvatarSizeName): ViewStyle { const avatarSize = getAvatarSize(size); return { height: avatarSize, @@ -192,7 +191,7 @@ function getAvatarStyle(size: AvatarSizeName): ViewStyle | CSSProperties { /** * Get Font size of '+1' text on avatar overlay */ -function getAvatarExtraFontSizeStyle(size: AvatarSizeName): TextStyle | CSSProperties { +function getAvatarExtraFontSizeStyle(size: AvatarSizeName): TextStyle { return { fontSize: avatarFontSizes[size], }; @@ -201,7 +200,7 @@ function getAvatarExtraFontSizeStyle(size: AvatarSizeName): TextStyle | CSSPrope /** * Get Bordersize of Avatar based on avatar size */ -function getAvatarBorderWidth(size: AvatarSizeName): ViewStyle | CSSProperties { +function getAvatarBorderWidth(size: AvatarSizeName): ViewStyle { return { borderWidth: avatarBorderWidths[size], }; @@ -210,7 +209,7 @@ function getAvatarBorderWidth(size: AvatarSizeName): ViewStyle | CSSProperties { /** * Return the border radius for an avatar */ -function getAvatarBorderRadius(size: AvatarSizeName, type: string): ViewStyle | CSSProperties { +function getAvatarBorderRadius(size: AvatarSizeName, type: string): ViewStyle { if (type === CONST.ICON_TYPE_WORKSPACE) { return {borderRadius: avatarBorderSizes[size]}; } @@ -222,7 +221,7 @@ function getAvatarBorderRadius(size: AvatarSizeName, type: string): ViewStyle | /** * Return the border style for an avatar */ -function getAvatarBorderStyle(size: AvatarSizeName, type: string): ViewStyle | CSSProperties { +function getAvatarBorderStyle(size: AvatarSizeName, type: string): ViewStyle { return { overflow: 'hidden', ...getAvatarBorderRadius(size, type), @@ -232,7 +231,7 @@ function getAvatarBorderStyle(size: AvatarSizeName, type: string): ViewStyle | C /** * Helper method to return old dot default avatar associated with login */ -function getDefaultWorkspaceAvatarColor(workspaceName: string): ViewStyle | CSSProperties { +function getDefaultWorkspaceAvatarColor(workspaceName: string): ViewStyle { const colorHash = UserUtils.hashText(workspaceName.trim(), workspaceColorOptions.length); return workspaceColorOptions[colorHash]; @@ -241,7 +240,7 @@ function getDefaultWorkspaceAvatarColor(workspaceName: string): ViewStyle | CSSP /** * Takes safe area insets and returns padding to use for a View */ -function getSafeAreaPadding(insets?: EdgeInsets, insetsPercentage: number = variables.safeInsertPercentage): ViewStyle | CSSProperties { +function getSafeAreaPadding(insets?: EdgeInsets, insetsPercentage: number = variables.safeInsertPercentage): ViewStyle { return { paddingTop: insets?.top, paddingBottom: (insets?.bottom ?? 0) * insetsPercentage, @@ -253,11 +252,11 @@ function getSafeAreaPadding(insets?: EdgeInsets, insetsPercentage: number = vari /** * Takes safe area insets and returns margin to use for a View */ -function getSafeAreaMargins(insets?: EdgeInsets): ViewStyle | CSSProperties { +function getSafeAreaMargins(insets?: EdgeInsets): ViewStyle { return {marginBottom: (insets?.bottom ?? 0) * variables.safeInsertPercentage}; } -function getZoomCursorStyle(isZoomed: boolean, isDragging: boolean): ViewStyle | CSSProperties { +function getZoomCursorStyle(isZoomed: boolean, isDragging: boolean): ViewStyle { if (!isZoomed) { return styles.cursorZoomIn; } @@ -265,6 +264,7 @@ function getZoomCursorStyle(isZoomed: boolean, isDragging: boolean): ViewStyle | return isDragging ? styles.cursorGrabbing : styles.cursorZoomOut; } +// NOTE: asserting some web style properties to a valid type, because isn't possible to augment them. function getZoomSizingStyle( isZoomed: boolean, imgWidth: number, @@ -273,28 +273,28 @@ function getZoomSizingStyle( containerHeight: number, containerWidth: number, isLoading: boolean, -): ViewStyle | CSSProperties | undefined { +): ViewStyle | undefined { // Hide image until finished loading to prevent showing preview with wrong dimensions if (isLoading || imgWidth === 0 || imgHeight === 0) { return undefined; } - const top = `${Math.max((containerHeight - imgHeight) / 2, 0)}px`; - const left = `${Math.max((containerWidth - imgWidth) / 2, 0)}px`; + const top = `${Math.max((containerHeight - imgHeight) / 2, 0)}px` as ViewStyle['top']; + const left = `${Math.max((containerWidth - imgWidth) / 2, 0)}px` as ViewStyle['left']; // Return different size and offset style based on zoomScale and isZoom. if (isZoomed) { // When both width and height are smaller than container(modal) size, set the height by multiplying zoomScale if it is zoomed in. if (zoomScale >= 1) { return { - height: `${imgHeight * zoomScale}px`, - width: `${imgWidth * zoomScale}px`, + height: `${imgHeight * zoomScale}px` as FlexStyle['height'], + width: `${imgWidth * zoomScale}px` as FlexStyle['width'], }; } // If image height and width are bigger than container size, display image with original size because original size is bigger and position absolute. return { - height: `${imgHeight}px`, - width: `${imgWidth}px`, + height: `${imgHeight}px` as FlexStyle['height'], + width: `${imgWidth}px` as FlexStyle['width'], top, left, }; @@ -303,8 +303,8 @@ function getZoomSizingStyle( // If image is not zoomed in and image size is smaller than container size, display with original size based on offset and position absolute. if (zoomScale > 1) { return { - height: `${imgHeight}px`, - width: `${imgWidth}px`, + height: `${imgHeight}px` as FlexStyle['height'], + width: `${imgWidth}px` as FlexStyle['width'], top, left, }; @@ -312,11 +312,11 @@ function getZoomSizingStyle( // If image is bigger than container size, display full image in the screen with scaled size (fit by container size) and position absolute. // top, left offset should be different when displaying long or wide image. - const scaledTop = `${Math.max((containerHeight - imgHeight * zoomScale) / 2, 0)}px`; - const scaledLeft = `${Math.max((containerWidth - imgWidth * zoomScale) / 2, 0)}px`; + const scaledTop = `${Math.max((containerHeight - imgHeight * zoomScale) / 2, 0)}px` as ViewStyle['top']; + const scaledLeft = `${Math.max((containerWidth - imgWidth * zoomScale) / 2, 0)}px` as ViewStyle['left']; return { - height: `${imgHeight * zoomScale}px`, - width: `${imgWidth * zoomScale}px`, + height: `${imgHeight * zoomScale}px` as FlexStyle['height'], + width: `${imgWidth * zoomScale}px` as FlexStyle['width'], top: scaledTop, left: scaledLeft, }; @@ -325,7 +325,7 @@ function getZoomSizingStyle( /** * Returns auto grow text input style */ -function getWidthStyle(width: number): ViewStyle | CSSProperties { +function getWidthStyle(width: number): ViewStyle { return { width, }; @@ -334,7 +334,7 @@ function getWidthStyle(width: number): ViewStyle | CSSProperties { /** * Returns auto grow height text input style */ -function getAutoGrowHeightInputStyle(textInputHeight: number, maxHeight: number): ViewStyle | CSSProperties { +function getAutoGrowHeightInputStyle(textInputHeight: number, maxHeight: number): ViewStyle { if (textInputHeight > maxHeight) { return { ...styles.pr0, @@ -354,7 +354,7 @@ function getAutoGrowHeightInputStyle(textInputHeight: number, maxHeight: number) /** * Returns a style with backgroundColor and borderColor set to the same color */ -function getBackgroundAndBorderStyle(backgroundColor: string): ViewStyle | CSSProperties { +function getBackgroundAndBorderStyle(backgroundColor: string): ViewStyle { return { backgroundColor, borderColor: backgroundColor, @@ -364,7 +364,7 @@ function getBackgroundAndBorderStyle(backgroundColor: string): ViewStyle | CSSPr /** * Returns a style with the specified backgroundColor */ -function getBackgroundColorStyle(backgroundColor: string): ViewStyle | CSSProperties { +function getBackgroundColorStyle(backgroundColor: string): ViewStyle { return { backgroundColor, }; @@ -373,7 +373,7 @@ function getBackgroundColorStyle(backgroundColor: string): ViewStyle | CSSProper /** * Returns a style for text color */ -function getTextColorStyle(color: string): TextStyle | CSSProperties { +function getTextColorStyle(color: string): TextStyle { return { color, }; @@ -382,7 +382,7 @@ function getTextColorStyle(color: string): TextStyle | CSSProperties { /** * Returns a style with the specified borderColor */ -function getBorderColorStyle(borderColor: string): ViewStyle | CSSProperties { +function getBorderColorStyle(borderColor: string): ViewStyle { return { borderColor, }; @@ -391,7 +391,7 @@ function getBorderColorStyle(borderColor: string): ViewStyle | CSSProperties { /** * Returns the width style for the wordmark logo on the sign in page */ -function getSignInWordmarkWidthStyle(environment: string, isSmallScreenWidth: boolean): ViewStyle | CSSProperties { +function getSignInWordmarkWidthStyle(environment: string, isSmallScreenWidth: boolean): ViewStyle { if (environment === CONST.ENVIRONMENT.DEV) { return isSmallScreenWidth ? {width: variables.signInLogoWidthPill} : {width: variables.signInLogoWidthLargeScreenPill}; } @@ -423,7 +423,7 @@ function hexadecimalToRGBArray(hexadecimal: string): number[] | undefined { /** * Returns a background color with opacity style */ -function getBackgroundColorWithOpacityStyle(backgroundColor: string, opacity: number): ViewStyle | CSSProperties { +function getBackgroundColorWithOpacityStyle(backgroundColor: string, opacity: number): ViewStyle { const result = hexadecimalToRGBArray(backgroundColor); if (result !== undefined) { return { @@ -436,7 +436,7 @@ function getBackgroundColorWithOpacityStyle(backgroundColor: string, opacity: nu /** * Generate a style for the background color of the Badge */ -function getBadgeColorStyle(success: boolean, error: boolean, isPressed = false, isAdHoc = false): ViewStyle | CSSProperties { +function getBadgeColorStyle(success: boolean, error: boolean, isPressed = false, isAdHoc = false): ViewStyle { if (success) { if (isAdHoc) { return isPressed ? styles.badgeAdHocSuccessPressed : styles.badgeAdHocSuccess; @@ -455,7 +455,7 @@ function getBadgeColorStyle(success: boolean, error: boolean, isPressed = false, * @param buttonState - One of {'default', 'hovered', 'pressed'} * @param isMenuItem - whether this button is apart of a list */ -function getButtonBackgroundColorStyle(buttonState: ButtonStateName = CONST.BUTTON_STATES.DEFAULT, isMenuItem = false): ViewStyle | CSSProperties { +function getButtonBackgroundColorStyle(buttonState: ButtonStateName = CONST.BUTTON_STATES.DEFAULT, isMenuItem = false): ViewStyle { switch (buttonState) { case CONST.BUTTON_STATES.PRESSED: return {backgroundColor: themeColors.buttonPressedBG}; @@ -498,7 +498,7 @@ function getAnimatedFABStyle(rotate: Animated.Value, backgroundColor: Animated.V }; } -function getWidthAndHeightStyle(width: number, height: number | undefined = undefined): ViewStyle | CSSProperties { +function getWidthAndHeightStyle(width: number, height: number | undefined = undefined): ViewStyle { return { width, height: height ?? width, @@ -519,7 +519,7 @@ function getModalPaddingStyles({ modalContainerStylePaddingTop, modalContainerStylePaddingBottom, insets, -}: ModalPaddingStylesArgs): ViewStyle | CSSProperties { +}: ModalPaddingStylesArgs): ViewStyle { // use fallback value for safeAreaPaddingBottom to keep padding bottom consistent with padding top. // More info: issue #17376 const safeAreaPaddingBottomWithFallback = insets.bottom === 0 ? modalContainerStylePaddingTop || 0 : safeAreaPaddingBottom; @@ -547,7 +547,7 @@ function getFontFamilyMonospace({fontStyle, fontWeight}: TextStyle): string { /** * Gives the width for Emoji picker Widget */ -function getEmojiPickerStyle(isSmallScreenWidth: boolean): ViewStyle | CSSProperties { +function getEmojiPickerStyle(isSmallScreenWidth: boolean): ViewStyle { if (isSmallScreenWidth) { return { width: CONST.SMALL_EMOJI_PICKER_SIZE.WIDTH, @@ -562,7 +562,7 @@ function getEmojiPickerStyle(isSmallScreenWidth: boolean): ViewStyle | CSSProper /** * Get the random promo color and image for Login page */ -function getLoginPagePromoStyle(): ViewStyle | CSSProperties { +function getLoginPagePromoStyle(): ViewStyle { const promos = [ { backgroundColor: colors.green, @@ -592,7 +592,7 @@ function getLoginPagePromoStyle(): ViewStyle | CSSProperties { /** * Generate the styles for the ReportActionItem wrapper view. */ -function getReportActionItemStyle(isHovered = false, isLoading = false): ViewStyle | CSSProperties { +function getReportActionItemStyle(isHovered = false, isLoading = false): ViewStyle { return { display: 'flex', justifyContent: 'space-between', @@ -608,7 +608,7 @@ function getReportActionItemStyle(isHovered = false, isLoading = false): ViewSty /** * Generate the wrapper styles for the mini ReportActionContextMenu. */ -function getMiniReportActionContextMenuWrapperStyle(isReportActionItemGrouped: boolean): ViewStyle | CSSProperties { +function getMiniReportActionContextMenuWrapperStyle(isReportActionItemGrouped: boolean): ViewStyle { return { ...(isReportActionItemGrouped ? positioning.tn8 : positioning.tn4), ...positioning.r4, @@ -618,7 +618,7 @@ function getMiniReportActionContextMenuWrapperStyle(isReportActionItemGrouped: b }; } -function getPaymentMethodMenuWidth(isSmallScreenWidth: boolean): ViewStyle | CSSProperties { +function getPaymentMethodMenuWidth(isSmallScreenWidth: boolean): ViewStyle { const margin = 20; return {width: !isSmallScreenWidth ? variables.sideBarWidth - margin * 2 : undefined}; } @@ -704,14 +704,14 @@ function getThemeBackgroundColor(bgColor: string = themeColors.appBG): string { /** * Parse styleParam and return Styles array */ -function parseStyleAsArray(styleParam: ViewStyle | CSSProperties | Array): Array { +function parseStyleAsArray(styleParam: ViewStyle | ViewStyle[]): ViewStyle[] { return Array.isArray(styleParam) ? styleParam : [styleParam]; } /** * Parse style function and return Styles object */ -function parseStyleFromFunction(style: ParsableStyle, state: PressableStateCallbackType): Array { +function parseStyleFromFunction(style: ParsableStyle, state: PressableStateCallbackType): ViewStyle[] { const functionAppliedStyle = typeof style === 'function' ? style(state) : style; return parseStyleAsArray(functionAppliedStyle); } @@ -719,8 +719,8 @@ function parseStyleFromFunction(style: ParsableStyle, state: PressableStateCallb /** * Receives any number of object or array style objects and returns them all as an array */ -function combineStyles(...allStyles: Array>) { - let finalStyles: Array> = []; +function combineStyles(...allStyles: Array) { + let finalStyles: ViewStyle[][] = []; allStyles.forEach((style) => { finalStyles = finalStyles.concat(parseStyleAsArray(style)); }); @@ -730,7 +730,7 @@ function combineStyles(...allStyles: Array 0 ? -overlapSize : 0, zIndex: index + 2, @@ -826,7 +826,7 @@ function getHorizontalStackedAvatarStyle(index: number, overlapSize: number): Vi /** * Get computed avatar styles of '+1' overlay based on size */ -function getHorizontalStackedOverlayAvatarStyle(oneAvatarSize: AvatarSize, oneAvatarBorderWidth: number): ViewStyle | CSSProperties { +function getHorizontalStackedOverlayAvatarStyle(oneAvatarSize: AvatarSize, oneAvatarBorderWidth: number): ViewStyle { return { borderWidth: oneAvatarBorderWidth, borderRadius: oneAvatarSize.width, @@ -836,7 +836,7 @@ function getHorizontalStackedOverlayAvatarStyle(oneAvatarSize: AvatarSize, oneAv }; } -function getErrorPageContainerStyle(safeAreaPaddingBottom = 0): ViewStyle | CSSProperties { +function getErrorPageContainerStyle(safeAreaPaddingBottom = 0): ViewStyle { return { backgroundColor: themeColors.componentBG, paddingBottom: 40 + safeAreaPaddingBottom, @@ -846,7 +846,7 @@ function getErrorPageContainerStyle(safeAreaPaddingBottom = 0): ViewStyle | CSSP /** * Gets the correct size for the empty state background image based on screen dimensions */ -function getReportWelcomeBackgroundImageStyle(isSmallScreenWidth: boolean): ViewStyle | CSSProperties { +function getReportWelcomeBackgroundImageStyle(isSmallScreenWidth: boolean): ViewStyle { if (isSmallScreenWidth) { return { height: CONST.EMPTY_STATE_BACKGROUND.SMALL_SCREEN.IMAGE_HEIGHT, @@ -865,7 +865,7 @@ function getReportWelcomeBackgroundImageStyle(isSmallScreenWidth: boolean): View /** * Gets the correct top margin size for the chat welcome message based on screen dimensions */ -function getReportWelcomeTopMarginStyle(isSmallScreenWidth: boolean): ViewStyle | CSSProperties { +function getReportWelcomeTopMarginStyle(isSmallScreenWidth: boolean): ViewStyle { if (isSmallScreenWidth) { return { marginTop: CONST.EMPTY_STATE_BACKGROUND.SMALL_SCREEN.VIEW_HEIGHT, @@ -880,7 +880,7 @@ function getReportWelcomeTopMarginStyle(isSmallScreenWidth: boolean): ViewStyle /** * Returns fontSize style */ -function getFontSizeStyle(fontSize: number): TextStyle | CSSProperties { +function getFontSizeStyle(fontSize: number): TextStyle { return { fontSize, }; @@ -889,7 +889,7 @@ function getFontSizeStyle(fontSize: number): TextStyle | CSSProperties { /** * Returns lineHeight style */ -function getLineHeightStyle(lineHeight: number): TextStyle | CSSProperties { +function getLineHeightStyle(lineHeight: number): TextStyle { return { lineHeight, }; @@ -898,7 +898,7 @@ function getLineHeightStyle(lineHeight: number): TextStyle | CSSProperties { /** * Gets the correct size for the empty state container based on screen dimensions */ -function getReportWelcomeContainerStyle(isSmallScreenWidth: boolean): ViewStyle | CSSProperties { +function getReportWelcomeContainerStyle(isSmallScreenWidth: boolean): ViewStyle { if (isSmallScreenWidth) { return { minHeight: CONST.EMPTY_STATE_BACKGROUND.SMALL_SCREEN.CONTAINER_MINHEIGHT, @@ -917,7 +917,7 @@ function getReportWelcomeContainerStyle(isSmallScreenWidth: boolean): ViewStyle /** * Gets styles for AutoCompleteSuggestion row */ -function getAutoCompleteSuggestionItemStyle(highlightedEmojiIndex: number, rowHeight: number, hovered: boolean, currentEmojiIndex: number): Array { +function getAutoCompleteSuggestionItemStyle(highlightedEmojiIndex: number, rowHeight: number, hovered: boolean, currentEmojiIndex: number): ViewStyle[] { let backgroundColor; if (currentEmojiIndex === highlightedEmojiIndex) { @@ -942,14 +942,20 @@ function getAutoCompleteSuggestionItemStyle(highlightedEmojiIndex: number, rowHe /** * Gets the correct position for the base auto complete suggestion container */ -function getBaseAutoCompleteSuggestionContainerStyle({left, bottom, width}: {left: number; bottom: number; width: number}): ViewStyle | CSSProperties { - return {position: 'fixed', bottom, left, width}; +function getBaseAutoCompleteSuggestionContainerStyle({left, bottom, width}: {left: number; bottom: number; width: number}): ViewStyle { + return { + // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". + position: 'fixed' as ViewStyle['position'], + bottom, + left, + width, + }; } /** * Gets the correct position for auto complete suggestion container */ -function getAutoCompleteSuggestionContainerStyle(itemsHeight: number, shouldIncludeReportRecipientLocalTimeHeight: boolean): ViewStyle | CSSProperties { +function getAutoCompleteSuggestionContainerStyle(itemsHeight: number, shouldIncludeReportRecipientLocalTimeHeight: boolean): ViewStyle { 'worklet'; const optionalPadding = shouldIncludeReportRecipientLocalTimeHeight ? CONST.RECIPIENT_LOCAL_TIME_HEIGHT : 0; @@ -969,11 +975,11 @@ function getAutoCompleteSuggestionContainerStyle(itemsHeight: number, shouldIncl /** * Select the correct color for text. */ -function getColoredBackgroundStyle(isColored: boolean): ViewStyle | CSSProperties { +function getColoredBackgroundStyle(isColored: boolean): ViewStyle { return {backgroundColor: isColored ? colors.blueLink : undefined}; } -function getEmojiReactionBubbleStyle(isHovered: boolean, hasUserReacted: boolean, isContextMenu = false): ViewStyle | CSSProperties { +function getEmojiReactionBubbleStyle(isHovered: boolean, hasUserReacted: boolean, isContextMenu = false): ViewStyle { let backgroundColor = themeColors.border; if (isHovered) { @@ -999,7 +1005,7 @@ function getEmojiReactionBubbleStyle(isHovered: boolean, hasUserReacted: boolean }; } -function getEmojiReactionBubbleTextStyle(isContextMenu = false): TextStyle | CSSProperties { +function getEmojiReactionBubbleTextStyle(isContextMenu = false): TextStyle { if (isContextMenu) { return { fontSize: 17, @@ -1013,7 +1019,7 @@ function getEmojiReactionBubbleTextStyle(isContextMenu = false): TextStyle | CSS }; } -function getEmojiReactionCounterTextStyle(hasUserReacted: boolean): TextStyle | CSSProperties { +function getEmojiReactionCounterTextStyle(hasUserReacted: boolean): TextStyle { if (hasUserReacted) { return {color: themeColors.reactionActiveText}; } @@ -1026,7 +1032,7 @@ function getEmojiReactionCounterTextStyle(hasUserReacted: boolean): TextStyle | * * @param direction - The direction of the rotation (CONST.DIRECTION.LEFT or CONST.DIRECTION.RIGHT). */ -function getDirectionStyle(direction: string): ViewStyle | CSSProperties { +function getDirectionStyle(direction: string): ViewStyle { if (direction === CONST.DIRECTION.LEFT) { return {transform: [{rotate: '180deg'}]}; } @@ -1037,11 +1043,11 @@ function getDirectionStyle(direction: string): ViewStyle | CSSProperties { /** * Returns a style object with display flex or none basing on the condition value. */ -function displayIfTrue(condition: boolean): ViewStyle | CSSProperties { +function displayIfTrue(condition: boolean): ViewStyle { return {display: condition ? 'flex' : 'none'}; } -function getGoogleListViewStyle(shouldDisplayBorder: boolean): ViewStyle | CSSProperties { +function getGoogleListViewStyle(shouldDisplayBorder: boolean): ViewStyle { if (shouldDisplayBorder) { return { ...styles.borderTopRounded, @@ -1059,7 +1065,7 @@ function getGoogleListViewStyle(shouldDisplayBorder: boolean): ViewStyle | CSSPr /** * Gets the correct height for emoji picker list based on screen dimensions */ -function getEmojiPickerListHeight(hasAdditionalSpace: boolean, windowHeight: number): ViewStyle | CSSProperties { +function getEmojiPickerListHeight(hasAdditionalSpace: boolean, windowHeight: number): ViewStyle { const style = { ...spacing.ph4, height: hasAdditionalSpace ? CONST.NON_NATIVE_EMOJI_PICKER_LIST_HEIGHT + CONST.CATEGORY_SHORTCUT_BAR_HEIGHT : CONST.NON_NATIVE_EMOJI_PICKER_LIST_HEIGHT, @@ -1079,7 +1085,7 @@ function getEmojiPickerListHeight(hasAdditionalSpace: boolean, windowHeight: num /** * Returns style object for the user mention component based on whether the mention is ours or not. */ -function getMentionStyle(isOurMention: boolean): ViewStyle | CSSProperties { +function getMentionStyle(isOurMention: boolean): ViewStyle { const backgroundColor = isOurMention ? themeColors.ourMentionBG : themeColors.mentionBG; return { backgroundColor, @@ -1098,7 +1104,7 @@ function getMentionTextColor(isOurMention: boolean): string { /** * Returns padding vertical based on number of lines */ -function getComposeTextAreaPadding(numberOfLines: number, isComposerFullSize: boolean): ViewStyle | CSSProperties { +function getComposeTextAreaPadding(numberOfLines: number, isComposerFullSize: boolean): ViewStyle { let paddingValue = 5; // Issue #26222: If isComposerFullSize paddingValue will always be 5 to prevent padding jumps when adding multiple lines. if (!isComposerFullSize) { @@ -1119,14 +1125,14 @@ function getComposeTextAreaPadding(numberOfLines: number, isComposerFullSize: bo /** * Returns style object for the mobile on WEB */ -function getOuterModalStyle(windowHeight: number, viewportOffsetTop: number): ViewStyle | CSSProperties { +function getOuterModalStyle(windowHeight: number, viewportOffsetTop: number): ViewStyle { return Browser.isMobile() ? {maxHeight: windowHeight, marginTop: viewportOffsetTop} : {}; } /** * Returns style object for flexWrap depending on the screen size */ -function getWrappingStyle(isExtraSmallScreenWidth: boolean): ViewStyle | CSSProperties { +function getWrappingStyle(isExtraSmallScreenWidth: boolean): ViewStyle { return { flexWrap: isExtraSmallScreenWidth ? 'wrap' : 'nowrap', }; @@ -1135,7 +1141,7 @@ function getWrappingStyle(isExtraSmallScreenWidth: boolean): ViewStyle | CSSProp /** * Returns the text container styles for menu items depending on if the menu item container a small avatar */ -function getMenuItemTextContainerStyle(isSmallAvatarSubscriptMenu: boolean): ViewStyle | CSSProperties { +function getMenuItemTextContainerStyle(isSmallAvatarSubscriptMenu: boolean): ViewStyle { return { minHeight: isSmallAvatarSubscriptMenu ? variables.avatarSizeSubscript : variables.componentSizeNormal, }; @@ -1144,7 +1150,7 @@ function getMenuItemTextContainerStyle(isSmallAvatarSubscriptMenu: boolean): Vie /** * Returns link styles based on whether the link is disabled or not */ -function getDisabledLinkStyles(isDisabled = false): ViewStyle | CSSProperties { +function getDisabledLinkStyles(isDisabled = false): ViewStyle { const disabledLinkStyles = { color: themeColors.textSupporting, ...cursor.cursorDisabled, @@ -1159,7 +1165,7 @@ function getDisabledLinkStyles(isDisabled = false): ViewStyle | CSSProperties { /** * Returns the checkbox container style */ -function getCheckboxContainerStyle(size: number, borderRadius: number): ViewStyle | CSSProperties { +function getCheckboxContainerStyle(size: number, borderRadius: number): ViewStyle { return { backgroundColor: themeColors.componentBG, height: size, @@ -1176,7 +1182,7 @@ function getCheckboxContainerStyle(size: number, borderRadius: number): ViewStyl /** * Returns style object for the dropbutton height */ -function getDropDownButtonHeight(buttonSize: ButtonSizeValue): ViewStyle | CSSProperties { +function getDropDownButtonHeight(buttonSize: ButtonSizeValue): ViewStyle { if (buttonSize === CONST.DROPDOWN_BUTTON_SIZE.LARGE) { return { height: variables.componentSizeLarge, @@ -1190,7 +1196,7 @@ function getDropDownButtonHeight(buttonSize: ButtonSizeValue): ViewStyle | CSSPr /** * Returns fitting fontSize and lineHeight values in order to prevent large amounts from being cut off on small screen widths. */ -function getAmountFontSizeAndLineHeight(baseFontSize: number, baseLineHeight: number, isSmallScreenWidth: boolean, windowWidth: number): ViewStyle | CSSProperties { +function getAmountFontSizeAndLineHeight(baseFontSize: number, baseLineHeight: number, isSmallScreenWidth: boolean, windowWidth: number): TextStyle { let toSubtract = 0; if (isSmallScreenWidth) { @@ -1224,81 +1230,81 @@ function getTransparentColor(color: string) { } export { + combineStyles, + displayIfTrue, + fade, + getAmountFontSizeAndLineHeight, + getAnimatedFABStyle, + getAutoCompleteSuggestionContainerStyle, + getAutoCompleteSuggestionItemStyle, + getAutoGrowHeightInputStyle, + getAvatarBorderRadius, + getAvatarBorderStyle, + getAvatarBorderWidth, + getAvatarExtraFontSizeStyle, getAvatarSize, - getAvatarWidthStyle, getAvatarStyle, - getAvatarExtraFontSizeStyle, - getAvatarBorderWidth, - getAvatarBorderStyle, - getEmptyAvatarStyle, - getErrorPageContainerStyle, - getSafeAreaPadding, - getSafeAreaMargins, - getZoomCursorStyle, - getZoomSizingStyle, - getWidthStyle, - getAutoGrowHeightInputStyle, + getAvatarWidthStyle, getBackgroundAndBorderStyle, getBackgroundColorStyle, - getTextColorStyle, - getBorderColorStyle, getBackgroundColorWithOpacityStyle, getBadgeColorStyle, - getButtonBackgroundColorStyle, - getIconFillColor, - getAnimatedFABStyle, - getWidthAndHeightStyle, - getModalPaddingStyles, - getFontFamilyMonospace, - getEmojiPickerStyle, - getLoginPagePromoStyle, - getReportActionItemStyle, - getMiniReportActionContextMenuWrapperStyle, - getKeyboardShortcutsModalWidth, - getPaymentMethodMenuWidth, - getThemeBackgroundColor, - parseStyleAsArray, - parseStyleFromFunction, - combineStyles, - getPaddingLeft, - hasSafeAreas, - getHeight, - getMinimumHeight, - getMaximumHeight, - getMaximumWidth, - fade, - getHorizontalStackedAvatarBorderStyle, - getHorizontalStackedAvatarStyle, - getHorizontalStackedOverlayAvatarStyle, - getReportWelcomeBackgroundImageStyle, - getReportWelcomeTopMarginStyle, - getReportWelcomeContainerStyle, getBaseAutoCompleteSuggestionContainerStyle, - getAutoCompleteSuggestionItemStyle, - getAutoCompleteSuggestionContainerStyle, + getBorderColorStyle, + getButtonBackgroundColorStyle, + getCheckboxContainerStyle, getColoredBackgroundStyle, + getComposeTextAreaPadding, getDefaultWorkspaceAvatarColor, - getAvatarBorderRadius, + getDirectionStyle, + getDisabledLinkStyles, + getDropDownButtonHeight, + getEmojiPickerListHeight, + getEmojiPickerStyle, getEmojiReactionBubbleStyle, getEmojiReactionBubbleTextStyle, getEmojiReactionCounterTextStyle, - getDirectionStyle, - displayIfTrue, + getEmptyAvatarStyle, + getErrorPageContainerStyle, + getFontFamilyMonospace, getFontSizeStyle, - getLineHeightStyle, - getSignInWordmarkWidthStyle, getGoogleListViewStyle, - getEmojiPickerListHeight, + getHeight, + getHeightOfMagicCodeInput, + getHorizontalStackedAvatarBorderStyle, + getHorizontalStackedAvatarStyle, + getHorizontalStackedOverlayAvatarStyle, + getIconFillColor, + getKeyboardShortcutsModalWidth, + getLineHeightStyle, + getLoginPagePromoStyle, + getMaximumHeight, + getMaximumWidth, getMentionStyle, getMentionTextColor, - getComposeTextAreaPadding, - getHeightOfMagicCodeInput, - getOuterModalStyle, - getWrappingStyle, getMenuItemTextContainerStyle, - getDisabledLinkStyles, - getCheckboxContainerStyle, - getDropDownButtonHeight, - getAmountFontSizeAndLineHeight, + getMiniReportActionContextMenuWrapperStyle, + getMinimumHeight, + getModalPaddingStyles, + getOuterModalStyle, + getPaddingLeft, + getPaymentMethodMenuWidth, + getReportActionItemStyle, + getReportWelcomeBackgroundImageStyle, + getReportWelcomeContainerStyle, + getReportWelcomeTopMarginStyle, + getSafeAreaMargins, + getSafeAreaPadding, + getSignInWordmarkWidthStyle, + getTextColorStyle, + getThemeBackgroundColor, getTransparentColor, + getWidthAndHeightStyle, + getWidthStyle, + getWrappingStyle, + getZoomCursorStyle, + getZoomSizingStyle, + hasSafeAreas, + parseStyleAsArray, + parseStyleFromFunction, }; diff --git a/src/styles/cardStyles/index.ts b/src/styles/cardStyles/index.ts index 823081b62904..b2ba8d7c24f1 100644 --- a/src/styles/cardStyles/index.ts +++ b/src/styles/cardStyles/index.ts @@ -1,10 +1,12 @@ +import {ViewStyle} from 'react-native'; import GetCardStyles from './types'; /** * Get card style for cardStyleInterpolator */ const getCardStyles: GetCardStyles = (screenWidth) => ({ - position: 'fixed', + // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". + position: 'fixed' as ViewStyle['position'], width: screenWidth, height: '100%', }); diff --git a/src/styles/cardStyles/types.ts b/src/styles/cardStyles/types.ts index e1598b7696ff..517ab76811bc 100644 --- a/src/styles/cardStyles/types.ts +++ b/src/styles/cardStyles/types.ts @@ -1,6 +1,5 @@ -import {CSSProperties} from 'react'; import {ViewStyle} from 'react-native'; -type GetCardStyles = (screenWidth: number) => Partial>; +type GetCardStyles = (screenWidth: number) => Pick; export default GetCardStyles; diff --git a/src/styles/editedLabelStyles/index.ts b/src/styles/editedLabelStyles/index.ts index b3962e507757..5764735d0dea 100644 --- a/src/styles/editedLabelStyles/index.ts +++ b/src/styles/editedLabelStyles/index.ts @@ -1,10 +1,9 @@ -import {TextStyle} from 'react-native'; import display from '../utilities/display'; import flex from '../utilities/flex'; import EditedLabelStyles from './types'; const editedLabelStyles: EditedLabelStyles = { - ...(display.dInlineFlex as TextStyle), + ...display.dInlineFlex, ...flex.alignItemsBaseline, }; diff --git a/src/styles/fontWeight/bold/types.ts b/src/styles/fontWeight/bold/types.ts index 3c9930a63d87..67258eee719c 100644 --- a/src/styles/fontWeight/bold/types.ts +++ b/src/styles/fontWeight/bold/types.ts @@ -1,6 +1,5 @@ -import {CSSProperties} from 'react'; import {TextStyle} from 'react-native'; -type FontWeightBoldStyles = (TextStyle | CSSProperties)['fontWeight']; +type FontWeightBoldStyles = TextStyle['fontWeight']; export default FontWeightBoldStyles; diff --git a/src/styles/getNavigationModalCardStyles/index.website.ts b/src/styles/getNavigationModalCardStyles/index.website.ts index ea76825e5bba..422a17d0a9a8 100644 --- a/src/styles/getNavigationModalCardStyles/index.website.ts +++ b/src/styles/getNavigationModalCardStyles/index.website.ts @@ -10,8 +10,7 @@ const getNavigationModalCardStyles: GetNavigationModalCardStyles = () => ({ width: '100%', height: '100%', - // NOTE: asserting "fixed" TS type to a valid type, because isn't possible - // to augment "position". + // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". position: 'fixed' as ViewStyle['position'], }); diff --git a/src/styles/getReportActionContextMenuStyles.ts b/src/styles/getReportActionContextMenuStyles.ts index 792213f2b54b..33f04b723e25 100644 --- a/src/styles/getReportActionContextMenuStyles.ts +++ b/src/styles/getReportActionContextMenuStyles.ts @@ -17,8 +17,7 @@ const miniWrapperStyle: StylesArray = [ borderWidth: 1, borderColor: themeColors.border, // In Safari, when welcome messages use a code block (triple backticks), they would overlap the context menu below when there is no scrollbar without the transform style. - // NOTE: asserting "transform" TS type to a valid type, because isn't possible - // to augment "transform". + // NOTE: asserting "transform" to a valid type, because isn't possible to augment "transform". transform: 'translateZ(0)' as unknown as ViewStyle['transform'], }, ]; diff --git a/src/styles/getTooltipStyles.ts b/src/styles/getTooltipStyles.ts index 3f9de9c78b97..1e0b41d59aa4 100644 --- a/src/styles/getTooltipStyles.ts +++ b/src/styles/getTooltipStyles.ts @@ -1,12 +1,11 @@ -import {CSSProperties} from 'react'; import {TextStyle, View, ViewStyle} from 'react-native'; -import spacing from './utilities/spacing'; -import styles from './styles'; import colors from './colors'; -import themeColors from './themes/default'; import fontFamily from './fontFamily'; -import variables from './variables'; import roundToNearestMultipleOfFour from './roundToNearestMultipleOfFour'; +import styles from './styles'; +import themeColors from './themes/default'; +import spacing from './utilities/spacing'; +import variables from './variables'; /** This defines the proximity with the edge of the window in which tooltips should not be displayed. * If a tooltip is too close to the edge of the screen, we'll shift it towards the center. */ @@ -96,9 +95,9 @@ function isOverlappingAtTop(tooltip: View | HTMLDivElement, xOffset: number, yOf type TooltipStyles = { animationStyle: ViewStyle; - rootWrapperStyle: ViewStyle | CSSProperties; + rootWrapperStyle: ViewStyle; textStyle: TextStyle; - pointerWrapperStyle: ViewStyle | CSSProperties; + pointerWrapperStyle: ViewStyle; pointerStyle: ViewStyle; }; @@ -238,7 +237,8 @@ export default function getTooltipStyles( transform: [{scale}], }, rootWrapperStyle: { - position: 'fixed', + // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". + position: 'fixed' as ViewStyle['position'], backgroundColor: themeColors.heading, borderRadius: variables.componentBorderRadiusSmall, ...tooltipVerticalPadding, @@ -260,7 +260,8 @@ export default function getTooltipStyles( lineHeight: variables.lineHeightSmall, }, pointerWrapperStyle: { - position: 'fixed', + // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". + position: 'fixed' as ViewStyle['position'], top: pointerWrapperTop, left: pointerWrapperLeft, }, diff --git a/src/styles/utilities/display.ts b/src/styles/utilities/display.ts index 868c2bdb0e3b..e236ad4ea14d 100644 --- a/src/styles/utilities/display.ts +++ b/src/styles/utilities/display.ts @@ -1,4 +1,3 @@ -import {CSSProperties} from 'react'; import {ViewStyle} from 'react-native'; /** @@ -21,14 +20,17 @@ export default { }, dInline: { - display: 'inline', + // NOTE: asserting "display" to a valid type, because isn't possible to augment "display". + display: 'inline' as ViewStyle['display'], }, dInlineFlex: { - display: 'inline-flex', + // NOTE: asserting "display" to a valid type, because isn't possible to augment "display". + display: 'inline-flex' as ViewStyle['display'], }, dBlock: { - display: 'block', + // NOTE: asserting "display" to a valid type, because isn't possible to augment "display". + display: 'block' as ViewStyle['display'], }, -} satisfies Record; +} satisfies Record; diff --git a/src/styles/utilities/overflowAuto/index.ts b/src/styles/utilities/overflowAuto/index.ts index f958d8000c12..d73bee877c50 100644 --- a/src/styles/utilities/overflowAuto/index.ts +++ b/src/styles/utilities/overflowAuto/index.ts @@ -1,9 +1,8 @@ -import { ViewStyle } from 'react-native'; +import {ViewStyle} from 'react-native'; import OverflowAutoStyles from './types'; const overflowAuto: OverflowAutoStyles = { - // NOTE: asserting "auto" TS type to a valid type, because isn't possible - // to augment "overflow". + // NOTE: asserting "overflow" to a valid type, because isn't possible to augment "overflow". overflow: 'auto' as ViewStyle['overflow'], }; From 58d4a25f581df52c7a2077f9f3feabdc90b9ec46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Thu, 14 Sep 2023 01:12:35 +0100 Subject: [PATCH 023/856] Add more props and styles --- src/types/modules/react-native.d.ts | 276 ++++++++++++++++++---------- 1 file changed, 184 insertions(+), 92 deletions(-) diff --git a/src/types/modules/react-native.d.ts b/src/types/modules/react-native.d.ts index c06c09f38fe5..d0a2de964b8d 100644 --- a/src/types/modules/react-native.d.ts +++ b/src/types/modules/react-native.d.ts @@ -1,19 +1,18 @@ /* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/no-empty-interface */ /* eslint-disable @typescript-eslint/consistent-type-definitions */ -import {CSSProperties, FocusEventHandler, KeyboardEventHandler, MouseEventHandler} from 'react'; +import {CSSProperties, FocusEventHandler, KeyboardEventHandler, MouseEventHandler, PointerEventHandler, UIEventHandler, WheelEventHandler} from 'react'; import 'react-native'; declare module 'react-native' { + // <------ REACT NATIVE WEB (0.19.0) ------> // Extracted from react-native-web, packages/react-native-web/src/exports/View/types.js - type NumberOrString = number | string; - type OverscrollBehaviorValue = 'auto' | 'contain' | 'none'; type idRef = string; type idRefList = idRef | idRef[]; // https://necolas.github.io/react-native-web/docs/accessibility/#accessibility-props-api // Extracted from react-native-web, packages/react-native-web/src/exports/View/types.js - type AccessibilityProps = { + interface AccessibilityProps { 'aria-activedescendant'?: idRef; 'aria-atomic'?: boolean; 'aria-autocomplete'?: 'none' | 'list' | 'inline' | 'both'; @@ -108,142 +107,235 @@ declare module 'react-native' { accessibilityValueMin?: number; accessibilityValueNow?: number; accessibilityValueText?: string; - }; + } // https://necolas.github.io/react-native-web/docs/interactions/#pointerevent-props-api - // Extracted from react-native-web, packages/react-native-web/src/exports/View/types.js - // Extracted from @types/react, index.d.ts - type PointerProps = { + // Extracted properties from react-native-web, packages/react-native-web/src/exports/View/types.js and packages/react-native-web/src/modules/forwardedProps/index.js + // Extracted types from @types/react, index.d.ts + interface PointerProps { + onAuxClick?: MouseEventHandler; onClick?: MouseEventHandler; - onClickCapture?: MouseEventHandler; onContextMenu?: MouseEventHandler; + onGotPointerCapture?: PointerEventHandler; + onLostPointerCapture?: PointerEventHandler; + onPointerCancel?: PointerEventHandler; + onPointerDown?: PointerEventHandler; + onPointerEnter?: PointerEventHandler; + onPointerMove?: PointerEventHandler; + onPointerLeave?: PointerEventHandler; + onPointerOut?: PointerEventHandler; + onPointerOver?: PointerEventHandler; + onPointerUp?: PointerEventHandler; + onMouseDown?: MouseEventHandler; + onMouseEnter?: MouseEventHandler; + onMouseLeave?: MouseEventHandler; + onMouseMove?: MouseEventHandler; + onMouseOver?: MouseEventHandler; + onMouseOut?: MouseEventHandler; + onMouseUp?: MouseEventHandler; + onScroll?: UIEventHandler; + onWheel?: WheelEventHandler; + } + + // https://necolas.github.io/react-native-web/docs/interactions/#responderevent-props-api + // Extracted from react-native-web, packages/react-native-web/src/modules/useResponderEvents/ResponderTouchHistoryStore.js + type TouchRecord = { + currentPageX: number; + currentPageY: number; + currentTimeStamp: number; + previousPageX: number; + previousPageY: number; + previousTimeStamp: number; + startPageX: number; + startPageY: number; + startTimeStamp: number; + touchActive: boolean; + }; + + // https://necolas.github.io/react-native-web/docs/interactions/#responderevent-props-api + // Extracted from react-native-web, packages/react-native-web/src/modules/useResponderEvents/ResponderTouchHistoryStore.js + type TouchHistory = Readonly<{ + indexOfSingleActiveTouch: number; + mostRecentTimeStamp: number; + numberActiveTouches: number; + touchBank: TouchRecord[]; + }>; + + // https://necolas.github.io/react-native-web/docs/interactions/#responderevent-props-api + // Extracted from react-native-web, packages/react-native-web/src/modules/useResponderEvents/createResponderEvent.js + type ResponderEvent = { + bubbles: boolean; + cancelable: boolean; + currentTarget?: unknown; // changed from "any" to "unknown" + defaultPrevented?: boolean; + dispatchConfig: { + registrationName?: string; + phasedRegistrationNames?: { + bubbled: string; + captured: string; + }; + }; + eventPhase?: number; + isDefaultPrevented: () => boolean; + isPropagationStopped: () => boolean; + isTrusted?: boolean; + preventDefault: () => void; + stopPropagation: () => void; + nativeEvent: TouchEvent; + persist: () => void; + target?: unknown; // changed from "any" to "unknown" + timeStamp: number; + touchHistory: TouchHistory; }; - // TODO: Confirm - type FocusProps = { + // https://necolas.github.io/react-native-web/docs/interactions/#responderevent-props-api + // Extracted from react-native-web, packages/react-native-web/src/modules/useResponderEvents/ResponderSystem.js + interface ResponderProps { + // Direct responder events dispatched directly to responder. Do not bubble. + onResponderEnd?: (e: ResponderEvent) => void; + onResponderGrant?: (e: ResponderEvent) => void | boolean; + onResponderMove?: (e: ResponderEvent) => void; + onResponderRelease?: (e: ResponderEvent) => void; + onResponderReject?: (e: ResponderEvent) => void; + onResponderStart?: (e: ResponderEvent) => void; + onResponderTerminate?: (e: ResponderEvent) => void; + onResponderTerminationRequest?: (e: ResponderEvent) => boolean; + + // On pointer down, should this element become the responder? + onStartShouldSetResponder?: (e: ResponderEvent) => boolean; + onStartShouldSetResponderCapture?: (e: ResponderEvent) => boolean; + + // On pointer move, should this element become the responder? + onMoveShouldSetResponder?: (e: ResponderEvent) => boolean; + onMoveShouldSetResponderCapture?: (e: ResponderEvent) => boolean; + + // On scroll, should this element become the responder? Do no bubble + onScrollShouldSetResponder?: (e: ResponderEvent) => boolean; + onScrollShouldSetResponderCapture?: (e: ResponderEvent) => boolean; + + // On text selection change, should this element become the responder? + onSelectionChangeShouldSetResponder?: (e: ResponderEvent) => boolean; + onSelectionChangeShouldSetResponderCapture?: (e: ResponderEvent) => boolean; + } + + // https://necolas.github.io/react-native-web/docs/interactions/#focusevent-props-api + // Extracted properties from react-native-web, packages/react-native-web/src/exports/View/types.js and packages/react-native-web/src/modules/forwardedProps/index.js + // Extracted types from @types/react, index.d.ts + interface FocusProps { onBlur?: FocusEventHandler; onFocus?: FocusEventHandler; - }; + } - // TODO: Confirm - type KeyboardProps = { + // https://necolas.github.io/react-native-web/docs/interactions/#keyboardevent-props-api + // Extracted properties from react-native-web, packages/react-native-web/src/exports/View/types.js and packages/react-native-web/src/modules/forwardedProps/index.js + // Extracted types from @types/react, index.d.ts + interface KeyboardProps { onKeyDown?: KeyboardEventHandler; onKeyDownCapture?: KeyboardEventHandler; onKeyUp?: KeyboardEventHandler; onKeyUpCapture?: KeyboardEventHandler; - }; - - // type AnimationDirection = 'alternate' | 'alternate-reverse' | 'normal' | 'reverse'; - // type AnimationFillMode = 'none' | 'forwards' | 'backwards' | 'both'; - // type AnimationIterationCount = number | 'infinite'; - // type AnimationKeyframes = string | object; - // type AnimationPlayState = 'paused' | 'running'; - - // type AnimationStyles = { - // animationDelay?: string | string[]; - // animationDirection?: AnimationDirection | AnimationDirection[]; - // animationDuration?: string | string[]; - // animationFillMode?: AnimationFillMode | AnimationFillMode[]; - // animationIterationCount?: AnimationIterationCount | AnimationIterationCount[]; - // animationKeyframes?: AnimationKeyframes | AnimationKeyframes[]; - // animationPlayState?: AnimationPlayState | AnimationPlayState[]; - // animationTimingFunction?: string | string[]; - // transitionDelay?: string | string[]; - // transitionDuration?: string | string[]; - // transitionProperty?: string | string[]; - // transitionTimingFunction?: string | string[]; - // }; + } /** - * Image + * Shared props + * Extracted from react-native-web, packages/react-native-web/src/exports/View/types.js */ - interface WebImageProps { - draggable?: boolean; + interface WebSharedProps extends AccessibilityProps, PointerProps, ResponderProps, FocusProps, KeyboardProps { + dataSet?: Record; + href?: string; + hrefAttrs?: { + download?: boolean; + rel?: string; + target?: string; + }; + tabIndex?: 0 | -1; + lang?: string; } - interface ImageProps extends WebImageProps {} /** - * Pressable + * View + * Extracted from react-native-web, packages/react-native-web/src/exports/View/types.js */ - interface WebPressableProps { - delayPressIn?: number; - delayPressOut?: number; - onPressMove?: null | ((event: GestureResponderEvent) => void); - onPressEnd?: null | ((event: GestureResponderEvent) => void); - } - interface WebPressableStateCallbackType { - readonly focused: boolean; - readonly hovered: boolean; - readonly pressed: boolean; + interface WebViewProps extends WebSharedProps { + dir?: 'ltr' | 'rtl'; } - interface PressableProps extends WebPressableProps {} - interface PressableStateCallbackType extends WebPressableStateCallbackType {} + interface ViewProps extends WebViewProps {} /** * Text + * Extracted from react-native-web, packages/react-native-web/src/exports/Text/types.js */ - interface WebTextProps { - dataSet?: Record; + interface WebTextProps extends WebSharedProps { dir?: 'auto' | 'ltr' | 'rtl'; - href?: string; - hrefAttrs?: { - download?: boolean; - rel?: string; - target?: string; - }; - lang?: string; } interface TextProps extends WebTextProps {} /** * TextInput + * Extracted from react-native-web, packages/react-native-web/src/exports/TextInput/types.js */ - interface WebTextInputProps { - dataSet?: Record; + interface WebTextInputProps extends WebSharedProps { dir?: 'auto' | 'ltr' | 'rtl'; - lang?: string; disabled?: boolean; + enterKeyHint?: 'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send'; + readOnly?: boolean; } interface TextInputProps extends WebTextInputProps {} /** - * View + * Image + * Extracted from react-native-web, packages/react-native-web/src/exports/Image/types.js */ - interface WebViewProps extends AccessibilityProps, PointerProps, FocusProps, KeyboardProps { - dataSet?: Record; + interface WebImageProps extends WebSharedProps { dir?: 'ltr' | 'rtl'; - href?: string; - hrefAttrs?: { - download?: boolean; - rel?: string; - target?: string; - }; - tabIndex?: 0 | -1; + draggable?: boolean; } - interface ViewProps extends WebViewProps {} + interface ImageProps extends WebImageProps {} + + /** + * ScrollView + * Extracted from react-native-web, packages/react-native-web/src/exports/ScrollView/ScrollViewBase.js + */ + interface WebScrollViewProps extends WebSharedProps {} + interface ScrollViewProps extends WebScrollViewProps {} + + /** + * Pressable + */ + // https://necolas.github.io/react-native-web/docs/pressable/#interactionstate + // Extracted from react-native-web, packages/react-native-web/src/exports/Pressable/index.js + interface WebPressableStateCallbackType { + readonly focused: boolean; + readonly hovered: boolean; + readonly pressed: boolean; + } + interface PressableStateCallbackType extends WebPressableStateCallbackType {} - interface WebStyle { - wordBreak?: CSSProperties['wordBreak']; - whiteSpace?: CSSProperties['whiteSpace']; - visibility?: CSSProperties['visibility']; - userSelect?: CSSProperties['userSelect']; - WebkitUserSelect?: CSSProperties['WebkitUserSelect']; - textUnderlinePosition?: CSSProperties['textUnderlinePosition']; - textDecorationSkipInk?: CSSProperties['textDecorationSkipInk']; - cursor?: CSSProperties['cursor']; - outlineWidth?: CSSProperties['outlineWidth']; - outlineStyle?: CSSProperties['outlineStyle']; - boxShadow?: CSSProperties['boxShadow']; + // Extracted from react-native-web, packages/react-native-web/src/exports/Pressable/index.js + interface WebPressableProps extends WebSharedProps { + delayPressIn?: number; + delayPressOut?: number; + onPressMove?: null | ((event: GestureResponderEvent) => void); + onPressEnd?: null | ((event: GestureResponderEvent) => void); } + interface PressableProps extends WebPressableProps {} - interface WebViewStyle { - overscrollBehaviorX?: CSSProperties['overscrollBehaviorX']; - overflowX?: CSSProperties['overflowX']; + /** + * Styles + */ + // We extend CSSProperties (alias to "csstype" library) which provides all CSS style properties for Web, + // but properties that are already defined on RN won't be overrided / augmented. + interface WebStyle extends CSSProperties { + // https://necolas.github.io/react-native-web/docs/styling/#non-standard-properties + // Exclusive to react-native-web, "pointerEvents" already included on RN + animationKeyframes?: string | Record; + writingDirection?: 'auto' | 'ltr' | 'rtl'; } - interface ViewStyle extends WebStyle, WebViewStyle {} + interface ViewStyle extends WebStyle {} interface TextStyle extends WebStyle {} interface ImageStyle extends WebStyle {} + // <------ REACT NATIVE WEB (0.19.0) ------> interface TextInput { // Typescript type declaration is missing in React Native for setting text selection. From 05d51033203f0316fede6ea0d6f533c6ea2c5fe8 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Wed, 13 Sep 2023 12:30:06 +0530 Subject: [PATCH 024/856] upgrade rn-sdk to 8.3.0 --- ios/Podfile.lock | 10 +++++----- package-lock.json | 11 +++++++---- package.json | 2 +- ...4.0.patch => @onfido+react-native-sdk+8.3.0.patch} | 4 ++-- 4 files changed, 15 insertions(+), 12 deletions(-) rename patches/{@onfido+react-native-sdk+7.4.0.patch => @onfido+react-native-sdk+8.3.0.patch} (90%) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index aeb1887223cd..0884383d2614 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -251,9 +251,9 @@ PODS: - nanopb/encode (= 2.30908.0) - nanopb/decode (2.30908.0) - nanopb/encode (2.30908.0) - - Onfido (27.4.0) - - onfido-react-native-sdk (7.4.0): - - Onfido (= 27.4.0) + - Onfido (28.3.0) + - onfido-react-native-sdk (8.3.0): + - Onfido (~> 28.3.0) - React - OpenSSL-Universal (1.1.1100) - Permission-Camera (3.6.1): @@ -1219,8 +1219,8 @@ SPEC CHECKSUMS: MapboxMaps: af50ec61a7eb3b032c3f7962c6bd671d93d2a209 MapboxMobileEvents: de50b3a4de180dd129c326e09cd12c8adaaa46d6 nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96 - Onfido: e36f284b865adcf99d9c905590a64ac09d4a576b - onfido-react-native-sdk: 4ecde1a97435dcff9f00a878e3f8d1eb14fabbdc + Onfido: c7d010d9793790d44a07799d9be25aa8e3814ee7 + onfido-react-native-sdk: b346a620af5669f9fecb6dc3052314a35a94ad9f OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c Permission-Camera: bf6791b17c7f614b6826019fcfdcc286d3a107f6 Permission-LocationAccuracy: 76df17de5c6b8bc2eee34e61ee92cdd7a864c73d diff --git a/package-lock.json b/package-lock.json index f36d0e88f52b..14152bafb55d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "@kie/act-js": "^2.0.1", "@kie/mock-github": "^1.0.0", "@oguzhnatly/react-native-image-manipulator": "github:Expensify/react-native-image-manipulator#5cdae3d4455b03a04c57f50be3863e2fe6c92c52", - "@onfido/react-native-sdk": "7.4.0", + "@onfido/react-native-sdk": "8.3.0", "@react-native-async-storage/async-storage": "^1.17.10", "@react-native-camera-roll/camera-roll": "5.4.0", "@react-native-community/clipboard": "^1.5.1", @@ -6106,8 +6106,9 @@ } }, "node_modules/@onfido/react-native-sdk": { - "version": "7.4.0", - "license": "MIT", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@onfido/react-native-sdk/-/react-native-sdk-8.3.0.tgz", + "integrity": "sha512-nnhuvezd35v08WXUTQlX+gr4pbnNnwNV5KscC/jJrfjGikNUJnhnAHYxfnfJccTn44qUC6vRaKWq2GfpMUnqNA==", "peerDependencies": { "react": ">=17.0.0", "react-native": ">=0.68.2 <1.0.x" @@ -52195,7 +52196,9 @@ } }, "@onfido/react-native-sdk": { - "version": "7.4.0", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@onfido/react-native-sdk/-/react-native-sdk-8.3.0.tgz", + "integrity": "sha512-nnhuvezd35v08WXUTQlX+gr4pbnNnwNV5KscC/jJrfjGikNUJnhnAHYxfnfJccTn44qUC6vRaKWq2GfpMUnqNA==", "requires": {} }, "@pkgjs/parseargs": { diff --git a/package.json b/package.json index 315d01ea5dc3..c07cf09b7233 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "@oguzhnatly/react-native-image-manipulator": "github:Expensify/react-native-image-manipulator#5cdae3d4455b03a04c57f50be3863e2fe6c92c52", "@kie/act-js": "^2.0.1", "@kie/mock-github": "^1.0.0", - "@onfido/react-native-sdk": "7.4.0", + "@onfido/react-native-sdk": "8.3.0", "@react-native-async-storage/async-storage": "^1.17.10", "@react-native-camera-roll/camera-roll": "5.4.0", "@react-native-community/clipboard": "^1.5.1", diff --git a/patches/@onfido+react-native-sdk+7.4.0.patch b/patches/@onfido+react-native-sdk+8.3.0.patch similarity index 90% rename from patches/@onfido+react-native-sdk+7.4.0.patch rename to patches/@onfido+react-native-sdk+8.3.0.patch index b84225c0f667..12245cb58355 100644 --- a/patches/@onfido+react-native-sdk+7.4.0.patch +++ b/patches/@onfido+react-native-sdk+8.3.0.patch @@ -1,8 +1,8 @@ diff --git a/node_modules/@onfido/react-native-sdk/android/build.gradle b/node_modules/@onfido/react-native-sdk/android/build.gradle -index 781925b..9e16430 100644 +index b4c7106..d5083d3 100644 --- a/node_modules/@onfido/react-native-sdk/android/build.gradle +++ b/node_modules/@onfido/react-native-sdk/android/build.gradle -@@ -134,9 +134,9 @@ afterEvaluate { project -> +@@ -135,9 +135,9 @@ afterEvaluate { project -> group = "Reporting" description = "Generate Jacoco coverage reports after running tests." reports { From 091104ee92a6f1f239849829b6d0ed3cae34b939 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Thu, 14 Sep 2023 17:28:11 +0100 Subject: [PATCH 025/856] Refactor styles and implement missing ones --- src/styles/StyleUtils.ts | 14 +++++++------- src/styles/cardStyles/types.ts | 2 +- .../containerComposeStyles/index.native.ts | 4 ++-- src/styles/containerComposeStyles/index.ts | 4 ++-- src/styles/containerComposeStyles/types.ts | 5 +++++ src/styles/fontFamily/bold/index.android.js | 3 --- src/styles/fontFamily/bold/index.android.ts | 5 +++++ src/styles/fontFamily/bold/index.ios.js | 3 --- src/styles/fontFamily/bold/index.ios.ts | 5 +++++ src/styles/fontFamily/bold/index.js | 3 --- src/styles/fontFamily/bold/index.ts | 5 +++++ src/styles/fontFamily/bold/types.ts | 5 +++++ src/styles/fontFamily/types.ts | 4 +++- src/styles/fontWeight/bold/types.ts | 2 +- .../index.desktop.js | 10 ---------- .../index.desktop.ts | 17 +++++++++++++++++ .../getNavigationModalCardStyles/types.ts | 3 +-- src/styles/getReportActionContextMenuStyles.ts | 8 +++----- src/styles/italic/types.ts | 2 +- .../optionAlternateTextPlatformStyles/types.ts | 2 +- src/styles/optionRowStyles/index.native.ts | 5 ++--- src/styles/optionRowStyles/index.ts | 4 ++-- src/styles/optionRowStyles/types.ts | 4 ++-- src/styles/utilities/cursor/types.ts | 4 ++-- src/styles/utilities/visibility/types.ts | 4 ++-- 25 files changed, 74 insertions(+), 53 deletions(-) create mode 100644 src/styles/containerComposeStyles/types.ts delete mode 100644 src/styles/fontFamily/bold/index.android.js create mode 100644 src/styles/fontFamily/bold/index.android.ts delete mode 100644 src/styles/fontFamily/bold/index.ios.js create mode 100644 src/styles/fontFamily/bold/index.ios.ts delete mode 100644 src/styles/fontFamily/bold/index.js create mode 100644 src/styles/fontFamily/bold/index.ts create mode 100644 src/styles/fontFamily/bold/types.ts delete mode 100644 src/styles/getNavigationModalCardStyles/index.desktop.js create mode 100644 src/styles/getNavigationModalCardStyles/index.desktop.ts diff --git a/src/styles/StyleUtils.ts b/src/styles/StyleUtils.ts index 3daea4555796..1f88829b0e23 100644 --- a/src/styles/StyleUtils.ts +++ b/src/styles/StyleUtils.ts @@ -535,14 +535,14 @@ function getModalPaddingStyles({ }: ModalPaddingStylesArgs): ViewStyle { // use fallback value for safeAreaPaddingBottom to keep padding bottom consistent with padding top. // More info: issue #17376 - const safeAreaPaddingBottomWithFallback = insets.bottom === 0 ? modalContainerStylePaddingTop || 0 : safeAreaPaddingBottom; + const safeAreaPaddingBottomWithFallback = insets.bottom === 0 ? modalContainerStylePaddingTop ?? 0 : safeAreaPaddingBottom; return { - marginTop: (modalContainerStyleMarginTop || 0) + (shouldAddTopSafeAreaMargin ? safeAreaPaddingTop : 0), - marginBottom: (modalContainerStyleMarginBottom || 0) + (shouldAddBottomSafeAreaMargin ? safeAreaPaddingBottomWithFallback : 0), - paddingTop: shouldAddTopSafeAreaPadding ? (modalContainerStylePaddingTop || 0) + safeAreaPaddingTop : modalContainerStylePaddingTop || 0, - paddingBottom: shouldAddBottomSafeAreaPadding ? (modalContainerStylePaddingBottom || 0) + safeAreaPaddingBottomWithFallback : modalContainerStylePaddingBottom || 0, - paddingLeft: safeAreaPaddingLeft || 0, - paddingRight: safeAreaPaddingRight || 0, + marginTop: (modalContainerStyleMarginTop ?? 0) + (shouldAddTopSafeAreaMargin ? safeAreaPaddingTop : 0), + marginBottom: (modalContainerStyleMarginBottom ?? 0) + (shouldAddBottomSafeAreaMargin ? safeAreaPaddingBottomWithFallback : 0), + paddingTop: shouldAddTopSafeAreaPadding ? (modalContainerStylePaddingTop ?? 0) + safeAreaPaddingTop : modalContainerStylePaddingTop ?? 0, + paddingBottom: shouldAddBottomSafeAreaPadding ? (modalContainerStylePaddingBottom ?? 0) + safeAreaPaddingBottomWithFallback : modalContainerStylePaddingBottom ?? 0, + paddingLeft: safeAreaPaddingLeft ?? 0, + paddingRight: safeAreaPaddingRight ?? 0, }; } diff --git a/src/styles/cardStyles/types.ts b/src/styles/cardStyles/types.ts index 517ab76811bc..134b93eae32f 100644 --- a/src/styles/cardStyles/types.ts +++ b/src/styles/cardStyles/types.ts @@ -1,5 +1,5 @@ import {ViewStyle} from 'react-native'; -type GetCardStyles = (screenWidth: number) => Pick; +type GetCardStyles = (screenWidth: number) => ViewStyle; export default GetCardStyles; diff --git a/src/styles/containerComposeStyles/index.native.ts b/src/styles/containerComposeStyles/index.native.ts index 6b6bcf71cfcf..ea525dc652cf 100644 --- a/src/styles/containerComposeStyles/index.native.ts +++ b/src/styles/containerComposeStyles/index.native.ts @@ -1,6 +1,6 @@ -import {StyleProp, ViewStyle} from 'react-native'; import styles from '../styles'; +import ContainerComposeStyles from './types'; -const containerComposeStyles: StyleProp = [styles.textInputComposeSpacing]; +const containerComposeStyles: ContainerComposeStyles = [styles.textInputComposeSpacing]; export default containerComposeStyles; diff --git a/src/styles/containerComposeStyles/index.ts b/src/styles/containerComposeStyles/index.ts index 6968e23a507e..fbbf35a20818 100644 --- a/src/styles/containerComposeStyles/index.ts +++ b/src/styles/containerComposeStyles/index.ts @@ -1,7 +1,7 @@ -import {StyleProp, ViewStyle} from 'react-native'; import styles from '../styles'; +import ContainerComposeStyles from './types'; // We need to set paddingVertical = 0 on web to avoid displaying a normal pointer on some parts of compose box when not in focus -const containerComposeStyles: StyleProp = [styles.textInputComposeSpacing, {paddingVertical: 0}]; +const containerComposeStyles: ContainerComposeStyles = [styles.textInputComposeSpacing, {paddingVertical: 0}]; export default containerComposeStyles; diff --git a/src/styles/containerComposeStyles/types.ts b/src/styles/containerComposeStyles/types.ts new file mode 100644 index 000000000000..278039691b8a --- /dev/null +++ b/src/styles/containerComposeStyles/types.ts @@ -0,0 +1,5 @@ +import {ViewStyle} from 'react-native'; + +type ContainerComposeStyles = ViewStyle[]; + +export default ContainerComposeStyles; diff --git a/src/styles/fontFamily/bold/index.android.js b/src/styles/fontFamily/bold/index.android.js deleted file mode 100644 index 7473e4d7533c..000000000000 --- a/src/styles/fontFamily/bold/index.android.js +++ /dev/null @@ -1,3 +0,0 @@ -const bold = 'ExpensifyNeue-Bold'; - -export default bold; diff --git a/src/styles/fontFamily/bold/index.android.ts b/src/styles/fontFamily/bold/index.android.ts new file mode 100644 index 000000000000..563e063e1bd7 --- /dev/null +++ b/src/styles/fontFamily/bold/index.android.ts @@ -0,0 +1,5 @@ +import FontFamilyBoldStyles from './types'; + +const bold: FontFamilyBoldStyles = 'ExpensifyNeue-Bold'; + +export default bold; diff --git a/src/styles/fontFamily/bold/index.ios.js b/src/styles/fontFamily/bold/index.ios.js deleted file mode 100644 index 3ba35f200d3d..000000000000 --- a/src/styles/fontFamily/bold/index.ios.js +++ /dev/null @@ -1,3 +0,0 @@ -const bold = 'ExpensifyNeue-Regular'; - -export default bold; diff --git a/src/styles/fontFamily/bold/index.ios.ts b/src/styles/fontFamily/bold/index.ios.ts new file mode 100644 index 000000000000..f019dd47fc2a --- /dev/null +++ b/src/styles/fontFamily/bold/index.ios.ts @@ -0,0 +1,5 @@ +import FontFamilyBoldStyles from './types'; + +const bold: FontFamilyBoldStyles = 'ExpensifyNeue-Regular'; + +export default bold; diff --git a/src/styles/fontFamily/bold/index.js b/src/styles/fontFamily/bold/index.js deleted file mode 100644 index 66d3c64f3565..000000000000 --- a/src/styles/fontFamily/bold/index.js +++ /dev/null @@ -1,3 +0,0 @@ -const bold = 'ExpensifyNeue-Regular, Segoe UI Emoji, Noto Color Emoji'; - -export default bold; diff --git a/src/styles/fontFamily/bold/index.ts b/src/styles/fontFamily/bold/index.ts new file mode 100644 index 000000000000..96c9846df985 --- /dev/null +++ b/src/styles/fontFamily/bold/index.ts @@ -0,0 +1,5 @@ +import FontFamilyBoldStyles from './types'; + +const bold: FontFamilyBoldStyles = 'ExpensifyNeue-Regular, Segoe UI Emoji, Noto Color Emoji'; + +export default bold; diff --git a/src/styles/fontFamily/bold/types.ts b/src/styles/fontFamily/bold/types.ts new file mode 100644 index 000000000000..258b23de94a2 --- /dev/null +++ b/src/styles/fontFamily/bold/types.ts @@ -0,0 +1,5 @@ +import {TextStyle} from 'react-native'; + +type FontFamilyBoldStyles = NonNullable; + +export default FontFamilyBoldStyles; diff --git a/src/styles/fontFamily/types.ts b/src/styles/fontFamily/types.ts index 4c9a121e80d7..c688f40927be 100644 --- a/src/styles/fontFamily/types.ts +++ b/src/styles/fontFamily/types.ts @@ -1,3 +1,5 @@ +import {TextStyle} from 'react-native'; + type FontFamilyKeys = | 'EXP_NEUE_ITALIC' | 'EXP_NEUE_BOLD' @@ -10,6 +12,6 @@ type FontFamilyKeys = | 'MONOSPACE_BOLD' | 'MONOSPACE_BOLD_ITALIC'; -type FontFamilyStyles = Record; +type FontFamilyStyles = Record>; export default FontFamilyStyles; diff --git a/src/styles/fontWeight/bold/types.ts b/src/styles/fontWeight/bold/types.ts index 67258eee719c..00e72d0e879c 100644 --- a/src/styles/fontWeight/bold/types.ts +++ b/src/styles/fontWeight/bold/types.ts @@ -1,5 +1,5 @@ import {TextStyle} from 'react-native'; -type FontWeightBoldStyles = TextStyle['fontWeight']; +type FontWeightBoldStyles = NonNullable; export default FontWeightBoldStyles; diff --git a/src/styles/getNavigationModalCardStyles/index.desktop.js b/src/styles/getNavigationModalCardStyles/index.desktop.js deleted file mode 100644 index 54c9790253d6..000000000000 --- a/src/styles/getNavigationModalCardStyles/index.desktop.js +++ /dev/null @@ -1,10 +0,0 @@ -export default () => ({ - // position: fixed is set instead of position absolute to workaround Safari known issues of updating heights in DOM. - // Safari issues: - // https://github.com/Expensify/App/issues/12005 - // https://github.com/Expensify/App/issues/17824 - // https://github.com/Expensify/App/issues/20709 - width: '100%', - height: '100%', - position: 'fixed', -}); diff --git a/src/styles/getNavigationModalCardStyles/index.desktop.ts b/src/styles/getNavigationModalCardStyles/index.desktop.ts new file mode 100644 index 000000000000..422a17d0a9a8 --- /dev/null +++ b/src/styles/getNavigationModalCardStyles/index.desktop.ts @@ -0,0 +1,17 @@ +import {ViewStyle} from 'react-native'; +import GetNavigationModalCardStyles from './types'; + +const getNavigationModalCardStyles: GetNavigationModalCardStyles = () => ({ + // position: fixed is set instead of position absolute to workaround Safari known issues of updating heights in DOM. + // Safari issues: + // https://github.com/Expensify/App/issues/12005 + // https://github.com/Expensify/App/issues/17824 + // https://github.com/Expensify/App/issues/20709 + width: '100%', + height: '100%', + + // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". + position: 'fixed' as ViewStyle['position'], +}); + +export default getNavigationModalCardStyles; diff --git a/src/styles/getNavigationModalCardStyles/types.ts b/src/styles/getNavigationModalCardStyles/types.ts index 68453dc3c3de..877981dd4dd2 100644 --- a/src/styles/getNavigationModalCardStyles/types.ts +++ b/src/styles/getNavigationModalCardStyles/types.ts @@ -1,8 +1,7 @@ import {ViewStyle} from 'react-native'; -import {Merge} from 'type-fest'; type GetNavigationModalCardStylesParams = {isSmallScreenWidth: number}; -type GetNavigationModalCardStyles = (params: GetNavigationModalCardStylesParams) => Merge>; +type GetNavigationModalCardStyles = (params: GetNavigationModalCardStylesParams) => ViewStyle; export default GetNavigationModalCardStyles; diff --git a/src/styles/getReportActionContextMenuStyles.ts b/src/styles/getReportActionContextMenuStyles.ts index 150c9786aaca..6b4ad8807552 100644 --- a/src/styles/getReportActionContextMenuStyles.ts +++ b/src/styles/getReportActionContextMenuStyles.ts @@ -3,13 +3,11 @@ import styles from './styles'; import themeColors from './themes/default'; import variables from './variables'; -type StylesArray = ViewStyle[]; - const defaultWrapperStyle: ViewStyle = { backgroundColor: themeColors.componentBG, }; -const miniWrapperStyle: StylesArray = [ +const miniWrapperStyle: ViewStyle[] = [ styles.flexRow, defaultWrapperStyle, { @@ -22,7 +20,7 @@ const miniWrapperStyle: StylesArray = [ }, ]; -const bigWrapperStyle: StylesArray = [styles.flexColumn, defaultWrapperStyle]; +const bigWrapperStyle: ViewStyle[] = [styles.flexColumn, defaultWrapperStyle]; /** * Generate the wrapper styles for the ReportActionContextMenu. @@ -30,7 +28,7 @@ const bigWrapperStyle: StylesArray = [styles.flexColumn, defaultWrapperStyle]; * @param isMini * @param isSmallScreenWidth */ -function getReportActionContextMenuStyles(isMini: boolean, isSmallScreenWidth: boolean): StylesArray { +function getReportActionContextMenuStyles(isMini: boolean, isSmallScreenWidth: boolean): ViewStyle[] { if (isMini) { return miniWrapperStyle; } diff --git a/src/styles/italic/types.ts b/src/styles/italic/types.ts index 0935c5844bb3..e9feedbdfac5 100644 --- a/src/styles/italic/types.ts +++ b/src/styles/italic/types.ts @@ -1,5 +1,5 @@ import {TextStyle} from 'react-native'; -type ItalicStyles = TextStyle['fontStyle']; +type ItalicStyles = NonNullable; export default ItalicStyles; diff --git a/src/styles/optionAlternateTextPlatformStyles/types.ts b/src/styles/optionAlternateTextPlatformStyles/types.ts index b2e8e4745fff..aacdef7e3501 100644 --- a/src/styles/optionAlternateTextPlatformStyles/types.ts +++ b/src/styles/optionAlternateTextPlatformStyles/types.ts @@ -1,5 +1,5 @@ import {TextStyle} from 'react-native'; -type OptionAlternateTextPlatformStyles = Partial>; +type OptionAlternateTextPlatformStyles = Pick; export default OptionAlternateTextPlatformStyles; diff --git a/src/styles/optionRowStyles/index.native.ts b/src/styles/optionRowStyles/index.native.ts index 11371509ce73..9c13fdd082a4 100644 --- a/src/styles/optionRowStyles/index.native.ts +++ b/src/styles/optionRowStyles/index.native.ts @@ -1,5 +1,5 @@ -import OptionRowStyles from './types'; import styles from '../styles'; +import CompactContentContainerStyles from './types'; /** * On native platforms, alignItemsBaseline does not work correctly @@ -7,8 +7,7 @@ import styles from '../styles'; * keeping compactContentContainerStyles as it is. * https://github.com/Expensify/App/issues/14148 */ - -const compactContentContainerStyles: OptionRowStyles = styles.alignItemsCenter; +const compactContentContainerStyles: CompactContentContainerStyles = styles.alignItemsCenter; export { // eslint-disable-next-line import/prefer-default-export diff --git a/src/styles/optionRowStyles/index.ts b/src/styles/optionRowStyles/index.ts index fbeca3c702d9..975f4243842e 100644 --- a/src/styles/optionRowStyles/index.ts +++ b/src/styles/optionRowStyles/index.ts @@ -1,7 +1,7 @@ -import OptionRowStyles from './types'; +import CompactContentContainerStyles from './types'; import styles from '../styles'; -const compactContentContainerStyles: OptionRowStyles = styles.alignItemsBaseline; +const compactContentContainerStyles: CompactContentContainerStyles = styles.alignItemsBaseline; export { // eslint-disable-next-line import/prefer-default-export diff --git a/src/styles/optionRowStyles/types.ts b/src/styles/optionRowStyles/types.ts index f645c6038397..fcce41f6bc28 100644 --- a/src/styles/optionRowStyles/types.ts +++ b/src/styles/optionRowStyles/types.ts @@ -1,5 +1,5 @@ import {ViewStyle} from 'react-native'; -type OptionRowStyles = ViewStyle; +type CompactContentContainerStyles = ViewStyle; -export default OptionRowStyles; +export default CompactContentContainerStyles; diff --git a/src/styles/utilities/cursor/types.ts b/src/styles/utilities/cursor/types.ts index 98d661491c4b..e9cfc120b161 100644 --- a/src/styles/utilities/cursor/types.ts +++ b/src/styles/utilities/cursor/types.ts @@ -1,4 +1,4 @@ -import {TextStyle, ViewStyle} from 'react-native'; +import {ViewStyle} from 'react-native'; type CursorStylesKeys = | 'cursorDefault' @@ -13,6 +13,6 @@ type CursorStylesKeys = | 'cursorInitial' | 'cursorText'; -type CursorStyles = Record>; +type CursorStyles = Record>; export default CursorStyles; diff --git a/src/styles/utilities/visibility/types.ts b/src/styles/utilities/visibility/types.ts index 872e35195edd..64bdbdd2cca6 100644 --- a/src/styles/utilities/visibility/types.ts +++ b/src/styles/utilities/visibility/types.ts @@ -1,5 +1,5 @@ -import {TextStyle, ViewStyle} from 'react-native'; +import {ViewStyle} from 'react-native'; -type VisibilityStyles = Record<'visible' | 'hidden', Pick>; +type VisibilityStyles = Record<'visible' | 'hidden', Pick>; export default VisibilityStyles; From 023cfdfe48a97f199292d32a4a93f9615c5ad312 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Fri, 15 Sep 2023 06:10:12 +0100 Subject: [PATCH 026/856] migrate withViewportOffsetTop.js class to function component --- src/components/withViewportOffsetTop.js | 58 ++++++++++--------------- 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/src/components/withViewportOffsetTop.js b/src/components/withViewportOffsetTop.js index 113c72ed1e1a..722347a6b307 100644 --- a/src/components/withViewportOffsetTop.js +++ b/src/components/withViewportOffsetTop.js @@ -1,4 +1,4 @@ -import React, {Component} from 'react'; +import React, {useEffect, forwardRef, useState, useCallback} from 'react'; import PropTypes from 'prop-types'; import lodashGet from 'lodash/get'; import getComponentDisplayName from '../libs/getComponentDisplayName'; @@ -13,43 +13,33 @@ const viewportOffsetTopPropTypes = { }; export default function (WrappedComponent) { - class WithViewportOffsetTop extends Component { - constructor(props) { - super(props); - - this.updateDimensions = this.updateDimensions.bind(this); - - this.state = { - viewportOffsetTop: 0, - }; - } - - componentDidMount() { - this.removeViewportResizeListener = addViewportResizeListener(this.updateDimensions); - } - - componentWillUnmount() { - this.removeViewportResizeListener(); - } + function WithViewportOffsetTop(props) { + const [viewportOffsetTop, setViewportOffsetTop] = useState(0); /** * @param {SyntheticEvent} e */ - updateDimensions(e) { - const viewportOffsetTop = lodashGet(e, 'target.offsetTop', 0); - this.setState({viewportOffsetTop}); - } + const updateDimensions = useCallback((e) => { + const viewPortOffsetTop = lodashGet(e, 'target.offsetTop', 0); + setViewportOffsetTop(viewPortOffsetTop); + }, []); + + useEffect(() => { + const removeViewportResizeListener = addViewportResizeListener(updateDimensions); - render() { - return ( - - ); - } + return () => { + removeViewportResizeListener(); + }; + }, [updateDimensions]); + + return ( + + ); } WithViewportOffsetTop.displayName = `WithViewportOffsetTop(${getComponentDisplayName(WrappedComponent)})`; @@ -59,7 +49,7 @@ export default function (WrappedComponent) { WithViewportOffsetTop.defaultProps = { forwardedRef: undefined, }; - return React.forwardRef((props, ref) => ( + return forwardRef((props, ref) => ( Date: Fri, 15 Sep 2023 09:31:06 +0100 Subject: [PATCH 027/856] move updateDimensions to useEffect --- src/components/withViewportOffsetTop.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/components/withViewportOffsetTop.js b/src/components/withViewportOffsetTop.js index 722347a6b307..5dc795ad2750 100644 --- a/src/components/withViewportOffsetTop.js +++ b/src/components/withViewportOffsetTop.js @@ -1,4 +1,4 @@ -import React, {useEffect, forwardRef, useState, useCallback} from 'react'; +import React, {useEffect, forwardRef, useState} from 'react'; import PropTypes from 'prop-types'; import lodashGet from 'lodash/get'; import getComponentDisplayName from '../libs/getComponentDisplayName'; @@ -16,21 +16,21 @@ export default function (WrappedComponent) { function WithViewportOffsetTop(props) { const [viewportOffsetTop, setViewportOffsetTop] = useState(0); - /** - * @param {SyntheticEvent} e - */ - const updateDimensions = useCallback((e) => { - const viewPortOffsetTop = lodashGet(e, 'target.offsetTop', 0); - setViewportOffsetTop(viewPortOffsetTop); - }, []); - useEffect(() => { + /** + * @param {SyntheticEvent} e + */ + const updateDimensions = (e) => { + const targetOffsetTop = lodashGet(e, 'target.offsetTop', 0); + setViewportOffsetTop(targetOffsetTop); + }; + const removeViewportResizeListener = addViewportResizeListener(updateDimensions); return () => { removeViewportResizeListener(); }; - }, [updateDimensions]); + }, []); return ( Date: Fri, 15 Sep 2023 10:12:09 +0100 Subject: [PATCH 028/856] fix lint issue --- src/components/withViewportOffsetTop.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/withViewportOffsetTop.js b/src/components/withViewportOffsetTop.js index 5dc795ad2750..ccf928b3bd13 100644 --- a/src/components/withViewportOffsetTop.js +++ b/src/components/withViewportOffsetTop.js @@ -24,7 +24,7 @@ export default function (WrappedComponent) { const targetOffsetTop = lodashGet(e, 'target.offsetTop', 0); setViewportOffsetTop(targetOffsetTop); }; - + const removeViewportResizeListener = addViewportResizeListener(updateDimensions); return () => { From e4a3313b9cd29ffe80589430c595bcf6b2bea605 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 15 Sep 2023 13:30:09 +0200 Subject: [PATCH 029/856] Migrate styles.ts --- src/styles/{styles.js => styles.ts} | 100 ++++++++----------- src/styles/themes/{default.js => default.ts} | 20 ++-- src/styles/themes/{light.js => light.ts} | 22 ++-- src/styles/themes/types.ts | 8 ++ src/types/utils/DeepRecord.ts | 7 ++ 5 files changed, 79 insertions(+), 78 deletions(-) rename src/styles/{styles.js => styles.ts} (97%) rename src/styles/themes/{default.js => default.ts} (88%) rename src/styles/themes/{light.js => light.ts} (86%) create mode 100644 src/styles/themes/types.ts create mode 100644 src/types/utils/DeepRecord.ts diff --git a/src/styles/styles.js b/src/styles/styles.ts similarity index 97% rename from src/styles/styles.js rename to src/styles/styles.ts index 0fba61f1e8d9..15c1a6b9269f 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.ts @@ -1,4 +1,3 @@ -import {defaultStyles as defaultPickerStyles} from 'react-native-picker-select/src/styles'; import lodashClamp from 'lodash/clamp'; import fontFamily from './fontFamily'; import addOutlineWidth from './addOutlineWidth'; @@ -26,11 +25,16 @@ import * as Browser from '../libs/Browser'; import cursor from './utilities/cursor'; import userSelect from './utilities/userSelect'; import textUnderline from './utilities/textUnderline'; +import {ThemeDefault} from './themes/types'; +import {AnimatableNumericValue, Animated, TransformsStyle} from 'react-native'; +import {ValueOf} from 'type-fest'; + +type Translation = 'perspective' | 'rotate' | 'rotateX' | 'rotateY' | 'rotateZ' | 'scale' | 'scaleX' | 'scaleY' | 'translateX' | 'translateY' | 'skewX' | 'skewY' | 'matrix'; // touchCallout is an iOS safari only property that controls the display of the callout information when you touch and hold a target const touchCalloutNone = Browser.isMobileSafari() ? {WebkitTouchCallout: 'none'} : {}; -const picker = (theme) => ({ +const picker = (theme: ThemeDefault) => ({ backgroundColor: theme.transparent, color: theme.text, fontFamily: fontFamily.EXP_NEUE, @@ -45,13 +49,13 @@ const picker = (theme) => ({ textAlign: 'left', }); -const link = (theme) => ({ +const link = (theme: ThemeDefault) => ({ color: theme.link, textDecorationColor: theme.link, fontFamily: fontFamily.EXP_NEUE, }); -const baseCodeTagStyles = (theme) => ({ +const baseCodeTagStyles = (theme: ThemeDefault) => ({ borderWidth: 1, borderRadius: 5, borderColor: theme.border, @@ -61,9 +65,9 @@ const baseCodeTagStyles = (theme) => ({ const headlineFont = { fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, fontWeight: '500', -}; +} as const; -const webViewStyles = (theme) => ({ +const webViewStyles = (theme: ThemeDefault) => ({ // As of react-native-render-html v6, don't declare distinct styles for // custom renderers, the API for custom renderers has changed. Declare the // styles in the below "tagStyles" instead. If you need to reuse those @@ -156,7 +160,7 @@ const webViewStyles = (theme) => ({ }, }); -const styles = (theme) => ({ +const styles = (theme: ThemeDefault) => ({ // Add all of our utility and helper styles ...spacing, ...sizing, @@ -675,7 +679,7 @@ const styles = (theme) => ({ color: theme.text, }, doneDepressed: { - fontSize: defaultPickerStyles.done.fontSize, + fontSize: 17, }, modalViewMiddle: { backgroundColor: theme.border, @@ -792,7 +796,7 @@ const styles = (theme) => ({ color: theme.textSupporting, }, - uploadReceiptView: (isSmallScreenWidth) => ({ + uploadReceiptView: (isSmallScreenWidth: boolean) => ({ borderRadius: variables.componentBorderRadiusLarge, borderWidth: isSmallScreenWidth ? 0 : 2, borderColor: theme.borderFocus, @@ -922,18 +926,12 @@ const styles = (theme) => ({ backgroundColor: theme.buttonDefaultBG, }, - /** - * @param {number} textInputHeight - * @param {number} minHeight - * @param {number} maxHeight - * @returns {object} - */ - autoGrowHeightInputContainer: (textInputHeight, minHeight, maxHeight) => ({ + autoGrowHeightInputContainer: (textInputHeight: number, minHeight: number, maxHeight: number) => ({ height: lodashClamp(textInputHeight, minHeight, maxHeight), minHeight, }), - autoGrowHeightHiddenInput: (maxWidth, maxHeight) => ({ + autoGrowHeightHiddenInput: (maxWidth: number, maxHeight?: number) => ({ maxWidth, maxHeight: maxHeight && maxHeight + 1, overflow: 'hidden', @@ -971,7 +969,7 @@ const styles = (theme) => ({ transformOrigin: 'left center', }, - textInputLabelTransformation: (translateY, translateX, scale) => ({ + textInputLabelTransformation: (translateY: AnimatableNumericValue, translateX: AnimatableNumericValue, scale: AnimatableNumericValue) => ({ transform: [{translateY}, {translateX}, {scale}], }), @@ -1092,7 +1090,7 @@ const styles = (theme) => ({ color: theme.text, }, doneDepressed: { - fontSize: defaultPickerStyles.done.fontSize, + fontSize: 17, }, modalViewMiddle: { backgroundColor: theme.border, @@ -1358,7 +1356,7 @@ const styles = (theme) => ({ textDecorationLine: 'none', }, - RHPNavigatorContainer: (isSmallScreenWidth) => ({ + RHPNavigatorContainer: (isSmallScreenWidth: boolean) => ({ width: isSmallScreenWidth ? '100%' : variables.sideBarWidth, position: 'absolute', right: 0, @@ -1374,17 +1372,17 @@ const styles = (theme) => ({ lineHeight: variables.fontSizeOnlyEmojisHeight, }, - createMenuPositionSidebar: (windowHeight) => ({ + createMenuPositionSidebar: (windowHeight: number) => ({ horizontal: 18, vertical: windowHeight - 100, }), - createMenuPositionProfile: (windowWidth) => ({ + createMenuPositionProfile: (windowWidth: number) => ({ horizontal: windowWidth - 355, ...getPopOverVerticalOffset(162), }), - createMenuPositionReportActionCompose: (windowHeight) => ({ + createMenuPositionReportActionCompose: (windowHeight: number) => ({ horizontal: 18 + variables.sideBarWidth, vertical: windowHeight - 83, }), @@ -1541,7 +1539,7 @@ const styles = (theme) => ({ height: variables.optionsListSectionHeaderHeight, }, - overlayStyles: (current) => ({ + overlayStyles: (current: {progress: Animated.AnimatedInterpolation}) => ({ position: 'fixed', // We need to stretch the overlay to cover the sidebar and the translate animation distance. @@ -2167,7 +2165,7 @@ const styles = (theme) => ({ outline: 'none', }, - getPDFPasswordFormStyle: (isSmallScreenWidth) => ({ + getPDFPasswordFormStyle: (isSmallScreenWidth: boolean) => ({ width: isSmallScreenWidth ? '100%' : 350, ...(isSmallScreenWidth && flex.flex1), }), @@ -2180,7 +2178,7 @@ const styles = (theme) => ({ backgroundColor: theme.modalBackdrop, }, - centeredModalStyles: (isSmallScreenWidth, isFullScreenWhenSmall) => ({ + centeredModalStyles: (isSmallScreenWidth: boolean, isFullScreenWhenSmall: boolean) => ({ borderWidth: isSmallScreenWidth && !isFullScreenWhenSmall ? 1 : 0, marginHorizontal: isSmallScreenWidth ? 0 : 20, }), @@ -2269,7 +2267,6 @@ const styles = (theme) => ({ }, reportDetailsTitleContainer: { - ...flex.dFlex, ...flex.flexColumn, ...flex.alignItemsCenter, paddingHorizontal: 20, @@ -2278,7 +2275,6 @@ const styles = (theme) => ({ reportDetailsRoomInfo: { ...flex.flex1, - ...flex.dFlex, ...flex.flexColumn, ...flex.alignItemsCenter, }, @@ -2315,15 +2311,15 @@ const styles = (theme) => ({ padding: 0, }, - twoFactorAuthCodesBox: ({isExtraSmallScreenWidth, isSmallScreenWidth}) => { - let paddingHorizontal = styles.ph9; + twoFactorAuthCodesBox: ({isExtraSmallScreenWidth, isSmallScreenWidth}: {isExtraSmallScreenWidth: boolean; isSmallScreenWidth: boolean}) => { + let paddingHorizontal = spacing.ph9; if (isSmallScreenWidth) { - paddingHorizontal = styles.ph4; + paddingHorizontal = spacing.ph4; } if (isExtraSmallScreenWidth) { - paddingHorizontal = styles.ph2; + paddingHorizontal = spacing.ph2; } return { @@ -2373,7 +2369,7 @@ const styles = (theme) => ({ minWidth: 110, }, - anonymousRoomFooter: (isSmallSizeLayout) => ({ + anonymousRoomFooter: (isSmallSizeLayout: boolean) => ({ flexDirection: isSmallSizeLayout ? 'column' : 'row', ...(!isSmallSizeLayout && { alignItems: 'center', @@ -2384,7 +2380,7 @@ const styles = (theme) => ({ borderRadius: variables.componentBorderRadiusLarge, overflow: 'hidden', }), - anonymousRoomFooterWordmarkAndLogoContainer: (isSmallSizeLayout) => ({ + anonymousRoomFooterWordmarkAndLogoContainer: (isSmallSizeLayout: boolean) => ({ flexDirection: 'row', alignItems: 'center', ...(isSmallSizeLayout && { @@ -2433,8 +2429,8 @@ const styles = (theme) => ({ borderRadius: 88, }, - rootNavigatorContainerStyles: (isSmallScreenWidth) => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1}), - RHPNavigatorContainerNavigatorContainerStyles: (isSmallScreenWidth) => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1}), + rootNavigatorContainerStyles: (isSmallScreenWidth: boolean) => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1}), + RHPNavigatorContainerNavigatorContainerStyles: (isSmallScreenWidth: boolean) => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1}), avatarInnerTextChat: { color: theme.textLight, @@ -2619,7 +2615,7 @@ const styles = (theme) => ({ backgroundColor: theme.appBG, }, - switchThumbTransformation: (translateX) => ({ + switchThumbTransformation: (translateX: AnimatableNumericValue) => ({ transform: [{translateX}], }), @@ -2831,11 +2827,11 @@ const styles = (theme) => ({ position: 'fixed', }, - growlNotificationTranslateY: (y) => ({ - transform: [{translateY: y}], + growlNotificationTranslateY: (translateY: AnimatableNumericValue) => ({ + transform: [{translateY}], }), - makeSlideInTranslation: (translationType, fromValue) => ({ + makeSlideInTranslation: (translationType: Translation, fromValue: number) => ({ from: { [translationType]: fromValue, }, @@ -3135,7 +3131,7 @@ const styles = (theme) => ({ ...visibility.visible, }, - floatingMessageCounterTransformation: (translateY) => ({ + floatingMessageCounterTransformation: (translateY: AnimatableNumericValue) => ({ transform: [{translateY}], }), @@ -3162,12 +3158,12 @@ const styles = (theme) => ({ flex: 1, }, - threeDotsPopoverOffset: (windowWidth) => ({ + threeDotsPopoverOffset: (windowWidth: number) => ({ ...getPopOverVerticalOffset(60), horizontal: windowWidth - 60, }), - threeDotsPopoverOffsetNoCloseButton: (windowWidth) => ({ + threeDotsPopoverOffsetNoCloseButton: (windowWidth: number) => ({ ...getPopOverVerticalOffset(60), horizontal: windowWidth - 10, }), @@ -3310,7 +3306,7 @@ const styles = (theme) => ({ zIndex: 2, }, - receiptImageWrapper: (receiptImageTopPosition) => ({ + receiptImageWrapper: (receiptImageTopPosition: number) => ({ position: 'absolute', top: receiptImageTopPosition, }), @@ -3579,8 +3575,6 @@ const styles = (theme) => ({ taskTitleMenuItem: { ...writingDirection.ltr, ...headlineFont, - ...spacing.flexWrap, - ...spacing.flex1, fontSize: variables.fontSizeXLarge, maxWidth: '100%', ...wordBreak.breakWord, @@ -3622,8 +3616,6 @@ const styles = (theme) => ({ marginLeft: 'auto', ...spacing.mt1, ...pointerEventsAuto, - ...spacing.dFlex, - ...spacing.alignItemsCenter, }, shareCodePage: { @@ -3735,19 +3727,14 @@ const styles = (theme) => ({ paddingBottom: 12, }, - tabText: (isSelected) => ({ + tabText: (isSelected: boolean) => ({ marginLeft: 8, fontFamily: isSelected ? fontFamily.EXP_NEUE_BOLD : fontFamily.EXP_NEUE, fontWeight: isSelected ? fontWeightBold : 400, color: isSelected ? theme.textLight : theme.textSupporting, }), - /** - * @param {String} backgroundColor - * @param {Number} height - * @returns {Object} - */ - overscrollSpacer: (backgroundColor, height) => ({ + overscrollSpacer: (backgroundColor: string, height: number) => ({ backgroundColor, height, width: '100%', @@ -3910,7 +3897,7 @@ const styles = (theme) => ({ maxWidth: 400, }, - distanceRequestContainer: (maxHeight) => ({ + distanceRequestContainer: (maxHeight: number) => ({ ...flex.flexShrink2, minHeight: variables.optionRowHeight * 2, maxHeight, @@ -3919,7 +3906,6 @@ const styles = (theme) => ({ mapViewContainer: { ...flex.flex1, ...spacing.p4, - ...spacing.flex1, minHeight: 300, maxHeight: 500, }, diff --git a/src/styles/themes/default.js b/src/styles/themes/default.ts similarity index 88% rename from src/styles/themes/default.js rename to src/styles/themes/default.ts index c101a668666b..9b6cca499ae5 100644 --- a/src/styles/themes/default.js +++ b/src/styles/themes/default.ts @@ -2,6 +2,7 @@ import colors from '../colors'; import SCREENS from '../../SCREENS'; import ROUTES from '../../ROUTES'; +import type {ThemeBase} from './types'; const darkTheme = { // Figma keys @@ -82,15 +83,14 @@ const darkTheme = { skeletonLHNOut: colors.darkDefaultButton, QRLogo: colors.green400, starDefaultBG: 'rgb(254, 228, 94)', -}; - -darkTheme.PAGE_BACKGROUND_COLORS = { - [SCREENS.HOME]: darkTheme.sidebar, - [SCREENS.SETTINGS.PREFERENCES]: colors.blue500, - [SCREENS.SETTINGS.WORKSPACES]: colors.pink800, - [ROUTES.SETTINGS_STATUS]: colors.green700, - [ROUTES.I_KNOW_A_TEACHER]: colors.tangerine800, - [ROUTES.SETTINGS_SECURITY]: colors.ice500, -}; + PAGE_BACKGROUND_COLORS: { + [SCREENS.HOME]: colors.darkHighlightBackground, + [SCREENS.SETTINGS.PREFERENCES]: colors.blue500, + [SCREENS.SETTINGS.WORKSPACES]: colors.pink800, + [ROUTES.SETTINGS_STATUS]: colors.green700, + [ROUTES.I_KNOW_A_TEACHER]: colors.tangerine800, + [ROUTES.SETTINGS_SECURITY]: colors.ice500, + }, +} satisfies ThemeBase; export default darkTheme; diff --git a/src/styles/themes/light.js b/src/styles/themes/light.ts similarity index 86% rename from src/styles/themes/light.js rename to src/styles/themes/light.ts index 1a945cb84913..a14d6b0b89b4 100644 --- a/src/styles/themes/light.js +++ b/src/styles/themes/light.ts @@ -1,6 +1,7 @@ import colors from '../colors'; import SCREENS from '../../SCREENS'; import ROUTES from '../../ROUTES'; +import type {ThemeDefault} from './types'; const lightTheme = { // Figma keys @@ -62,7 +63,7 @@ const lightTheme = { heroCard: colors.blue400, uploadPreviewActivityIndicator: colors.lightHighlightBackground, dropUIBG: 'rgba(252, 251, 249, 0.92)', - dropTransparentOverlay: 'rgba(255,255,255,0)', + receiptDropUIBG: '', // TODO: add color checkBox: colors.green400, pickerOptionsTextColor: colors.lightPrimaryText, imageCropBackgroundColor: colors.lightIcons, @@ -81,15 +82,14 @@ const lightTheme = { skeletonLHNOut: colors.lightDefaultButtonPressed, QRLogo: colors.green400, starDefaultBG: 'rgb(254, 228, 94)', -}; - -lightTheme.PAGE_BACKGROUND_COLORS = { - [SCREENS.HOME]: lightTheme.sidebar, - [SCREENS.SETTINGS.PREFERENCES]: colors.blue500, - [SCREENS.SETTINGS.WORKSPACES]: colors.pink800, - [ROUTES.SETTINGS_STATUS]: colors.green700, - [ROUTES.I_KNOW_A_TEACHER]: colors.tangerine800, - [ROUTES.SETTINGS_SECURITY]: colors.ice500, -}; + PAGE_BACKGROUND_COLORS: { + [SCREENS.HOME]: colors.lightHighlightBackground, + [SCREENS.SETTINGS.PREFERENCES]: colors.blue500, + [SCREENS.SETTINGS.WORKSPACES]: colors.pink800, + [ROUTES.SETTINGS_STATUS]: colors.green700, + [ROUTES.I_KNOW_A_TEACHER]: colors.tangerine800, + [ROUTES.SETTINGS_SECURITY]: colors.ice500, + }, +} satisfies ThemeDefault; export default lightTheme; diff --git a/src/styles/themes/types.ts b/src/styles/themes/types.ts new file mode 100644 index 000000000000..40b8da361654 --- /dev/null +++ b/src/styles/themes/types.ts @@ -0,0 +1,8 @@ +import DeepRecord from '../../types/utils/DeepRecord'; +import defaultTheme from './default'; + +type ThemeBase = DeepRecord; + +type ThemeDefault = typeof defaultTheme; + +export type {ThemeBase, ThemeDefault}; diff --git a/src/types/utils/DeepRecord.ts b/src/types/utils/DeepRecord.ts new file mode 100644 index 000000000000..fba14c75d679 --- /dev/null +++ b/src/types/utils/DeepRecord.ts @@ -0,0 +1,7 @@ +/** + * Represents a deeply nested record. It maps keys to values, + * and those values can either be of type `TValue` or further nested `DeepRecord` instances. + */ +type DeepRecord = {[key: string]: TValue | DeepRecord}; + +export default DeepRecord; From b8d36f774f04737efe9f04b7524beb39f848db1d Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 15 Sep 2023 13:31:10 +0200 Subject: [PATCH 030/856] Remove unused imports --- src/styles/styles.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/styles/styles.ts b/src/styles/styles.ts index 15c1a6b9269f..88b6b161b122 100644 --- a/src/styles/styles.ts +++ b/src/styles/styles.ts @@ -26,8 +26,7 @@ import cursor from './utilities/cursor'; import userSelect from './utilities/userSelect'; import textUnderline from './utilities/textUnderline'; import {ThemeDefault} from './themes/types'; -import {AnimatableNumericValue, Animated, TransformsStyle} from 'react-native'; -import {ValueOf} from 'type-fest'; +import {AnimatableNumericValue, Animated} from 'react-native'; type Translation = 'perspective' | 'rotate' | 'rotateX' | 'rotateY' | 'rotateZ' | 'scale' | 'scaleX' | 'scaleY' | 'translateX' | 'translateY' | 'skewX' | 'skewY' | 'matrix'; From 24f363bcd791a037a077cc358dbdb0fbe57f6fd4 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 15 Sep 2023 14:01:45 +0200 Subject: [PATCH 031/856] Fix return types in styles.ts --- src/styles/styles.ts | 95 ++++++++++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 39 deletions(-) diff --git a/src/styles/styles.ts b/src/styles/styles.ts index 88b6b161b122..927dda71626c 100644 --- a/src/styles/styles.ts +++ b/src/styles/styles.ts @@ -1,4 +1,10 @@ +/* eslint-disable @typescript-eslint/naming-convention */ import lodashClamp from 'lodash/clamp'; +import {AnimatableNumericValue, Animated, TextStyle, ViewStyle} from 'react-native'; +import {MixedStyleDeclaration, MixedStyleRecord} from 'react-native-render-html'; +import {PickerStyle} from 'react-native-picker-select'; +import {CustomAnimation} from 'react-native-animatable'; +import {EmptyObject} from 'type-fest'; import fontFamily from './fontFamily'; import addOutlineWidth from './addOutlineWidth'; import defaultTheme from './themes/default'; @@ -26,14 +32,13 @@ import cursor from './utilities/cursor'; import userSelect from './utilities/userSelect'; import textUnderline from './utilities/textUnderline'; import {ThemeDefault} from './themes/types'; -import {AnimatableNumericValue, Animated} from 'react-native'; type Translation = 'perspective' | 'rotate' | 'rotateX' | 'rotateY' | 'rotateZ' | 'scale' | 'scaleX' | 'scaleY' | 'translateX' | 'translateY' | 'skewX' | 'skewY' | 'matrix'; // touchCallout is an iOS safari only property that controls the display of the callout information when you touch and hold a target -const touchCalloutNone = Browser.isMobileSafari() ? {WebkitTouchCallout: 'none'} : {}; +const touchCalloutNone: {WebkitTouchCallout: 'none'} | EmptyObject = Browser.isMobileSafari() ? {WebkitTouchCallout: 'none'} : {}; -const picker = (theme: ThemeDefault) => ({ +const picker = (theme: ThemeDefault): TextStyle => ({ backgroundColor: theme.transparent, color: theme.text, fontFamily: fontFamily.EXP_NEUE, @@ -48,13 +53,13 @@ const picker = (theme: ThemeDefault) => ({ textAlign: 'left', }); -const link = (theme: ThemeDefault) => ({ +const link = (theme: ThemeDefault): ViewStyle & MixedStyleDeclaration => ({ color: theme.link, textDecorationColor: theme.link, fontFamily: fontFamily.EXP_NEUE, }); -const baseCodeTagStyles = (theme: ThemeDefault) => ({ +const baseCodeTagStyles = (theme: ThemeDefault): ViewStyle & MixedStyleDeclaration => ({ borderWidth: 1, borderRadius: 5, borderColor: theme.border, @@ -66,7 +71,12 @@ const headlineFont = { fontWeight: '500', } as const; -const webViewStyles = (theme: ThemeDefault) => ({ +type WebViewStyles = { + tagStyles: MixedStyleRecord; + baseFontStyle: MixedStyleDeclaration; +}; + +const webViewStyles = (theme: ThemeDefault): WebViewStyles => ({ // As of react-native-render-html v6, don't declare distinct styles for // custom renderers, the API for custom renderers has changed. Declare the // styles in the below "tagStyles" instead. If you need to reuse those @@ -126,7 +136,7 @@ const webViewStyles = (theme: ThemeDefault) => ({ code: { ...baseCodeTagStyles(theme), - ...codeStyles.codeTextStyle, + ...(codeStyles.codeTextStyle as MixedStyleDeclaration), paddingLeft: 5, paddingRight: 5, fontFamily: fontFamily.MONOSPACE, @@ -159,6 +169,11 @@ const webViewStyles = (theme: ThemeDefault) => ({ }, }); +type AnchorPosition = { + horizontal: number; + vertical: number; +}; + const styles = (theme: ThemeDefault) => ({ // Add all of our utility and helper styles ...spacing, @@ -660,7 +675,7 @@ const styles = (theme: ThemeDefault) => ({ height: 140, }, - pickerSmall: (backgroundColor = theme.highlightBG) => ({ + pickerSmall: (backgroundColor = theme.highlightBG): PickerStyle & {icon: ViewStyle} => ({ inputIOS: { fontFamily: fontFamily.EXP_NEUE, fontSize: variables.fontSizeSmall, @@ -795,7 +810,7 @@ const styles = (theme: ThemeDefault) => ({ color: theme.textSupporting, }, - uploadReceiptView: (isSmallScreenWidth: boolean) => ({ + uploadReceiptView: (isSmallScreenWidth: boolean): ViewStyle => ({ borderRadius: variables.componentBorderRadiusLarge, borderWidth: isSmallScreenWidth ? 0 : 2, borderColor: theme.borderFocus, @@ -925,12 +940,12 @@ const styles = (theme: ThemeDefault) => ({ backgroundColor: theme.buttonDefaultBG, }, - autoGrowHeightInputContainer: (textInputHeight: number, minHeight: number, maxHeight: number) => ({ + autoGrowHeightInputContainer: (textInputHeight: number, minHeight: number, maxHeight: number): ViewStyle => ({ height: lodashClamp(textInputHeight, minHeight, maxHeight), minHeight, }), - autoGrowHeightHiddenInput: (maxWidth: number, maxHeight?: number) => ({ + autoGrowHeightHiddenInput: (maxWidth: number, maxHeight?: number): TextStyle => ({ maxWidth, maxHeight: maxHeight && maxHeight + 1, overflow: 'hidden', @@ -968,7 +983,7 @@ const styles = (theme: ThemeDefault) => ({ transformOrigin: 'left center', }, - textInputLabelTransformation: (translateY: AnimatableNumericValue, translateX: AnimatableNumericValue, scale: AnimatableNumericValue) => ({ + textInputLabelTransformation: (translateY: AnimatableNumericValue, translateX: AnimatableNumericValue, scale: AnimatableNumericValue): TextStyle => ({ transform: [{translateY}, {translateX}, {scale}], }), @@ -1068,7 +1083,7 @@ const styles = (theme: ThemeDefault) => ({ zIndex: 1, }, - picker: (disabled = false, backgroundColor = theme.appBG) => ({ + picker: (disabled = false, backgroundColor = theme.appBG): PickerStyle => ({ iconContainer: { top: Math.round(variables.inputHeight * 0.5) - 11, right: 0, @@ -1296,7 +1311,7 @@ const styles = (theme: ThemeDefault) => ({ width: variables.componentSizeNormal, }, - statusIndicator: (backgroundColor = theme.danger) => ({ + statusIndicator: (backgroundColor = theme.danger): ViewStyle => ({ borderColor: theme.sidebar, backgroundColor, borderRadius: 8, @@ -1355,7 +1370,7 @@ const styles = (theme: ThemeDefault) => ({ textDecorationLine: 'none', }, - RHPNavigatorContainer: (isSmallScreenWidth: boolean) => ({ + RHPNavigatorContainer: (isSmallScreenWidth: boolean): ViewStyle => ({ width: isSmallScreenWidth ? '100%' : variables.sideBarWidth, position: 'absolute', right: 0, @@ -1371,17 +1386,17 @@ const styles = (theme: ThemeDefault) => ({ lineHeight: variables.fontSizeOnlyEmojisHeight, }, - createMenuPositionSidebar: (windowHeight: number) => ({ + createMenuPositionSidebar: (windowHeight: number): AnchorPosition => ({ horizontal: 18, vertical: windowHeight - 100, }), - createMenuPositionProfile: (windowWidth: number) => ({ + createMenuPositionProfile: (windowWidth: number): AnchorPosition => ({ horizontal: windowWidth - 355, ...getPopOverVerticalOffset(162), }), - createMenuPositionReportActionCompose: (windowHeight: number) => ({ + createMenuPositionReportActionCompose: (windowHeight: number): AnchorPosition => ({ horizontal: 18 + variables.sideBarWidth, vertical: windowHeight - 83, }), @@ -1538,8 +1553,9 @@ const styles = (theme: ThemeDefault) => ({ height: variables.optionsListSectionHeaderHeight, }, - overlayStyles: (current: {progress: Animated.AnimatedInterpolation}) => ({ - position: 'fixed', + overlayStyles: (current: {progress: Animated.AnimatedInterpolation}): ViewStyle => ({ + // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". + position: 'fixed' as ViewStyle['position'], // We need to stretch the overlay to cover the sidebar and the translate animation distance. left: -2 * variables.sideBarWidth, @@ -2164,7 +2180,7 @@ const styles = (theme: ThemeDefault) => ({ outline: 'none', }, - getPDFPasswordFormStyle: (isSmallScreenWidth: boolean) => ({ + getPDFPasswordFormStyle: (isSmallScreenWidth: boolean): ViewStyle => ({ width: isSmallScreenWidth ? '100%' : 350, ...(isSmallScreenWidth && flex.flex1), }), @@ -2177,7 +2193,7 @@ const styles = (theme: ThemeDefault) => ({ backgroundColor: theme.modalBackdrop, }, - centeredModalStyles: (isSmallScreenWidth: boolean, isFullScreenWhenSmall: boolean) => ({ + centeredModalStyles: (isSmallScreenWidth: boolean, isFullScreenWhenSmall: boolean): ViewStyle => ({ borderWidth: isSmallScreenWidth && !isFullScreenWhenSmall ? 1 : 0, marginHorizontal: isSmallScreenWidth ? 0 : 20, }), @@ -2310,7 +2326,7 @@ const styles = (theme: ThemeDefault) => ({ padding: 0, }, - twoFactorAuthCodesBox: ({isExtraSmallScreenWidth, isSmallScreenWidth}: {isExtraSmallScreenWidth: boolean; isSmallScreenWidth: boolean}) => { + twoFactorAuthCodesBox: ({isExtraSmallScreenWidth, isSmallScreenWidth}: {isExtraSmallScreenWidth: boolean; isSmallScreenWidth: boolean}): ViewStyle => { let paddingHorizontal = spacing.ph9; if (isSmallScreenWidth) { @@ -2368,7 +2384,7 @@ const styles = (theme: ThemeDefault) => ({ minWidth: 110, }, - anonymousRoomFooter: (isSmallSizeLayout: boolean) => ({ + anonymousRoomFooter: (isSmallSizeLayout: boolean): ViewStyle & TextStyle => ({ flexDirection: isSmallSizeLayout ? 'column' : 'row', ...(!isSmallSizeLayout && { alignItems: 'center', @@ -2379,7 +2395,7 @@ const styles = (theme: ThemeDefault) => ({ borderRadius: variables.componentBorderRadiusLarge, overflow: 'hidden', }), - anonymousRoomFooterWordmarkAndLogoContainer: (isSmallSizeLayout: boolean) => ({ + anonymousRoomFooterWordmarkAndLogoContainer: (isSmallSizeLayout: boolean): ViewStyle => ({ flexDirection: 'row', alignItems: 'center', ...(isSmallSizeLayout && { @@ -2428,8 +2444,8 @@ const styles = (theme: ThemeDefault) => ({ borderRadius: 88, }, - rootNavigatorContainerStyles: (isSmallScreenWidth: boolean) => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1}), - RHPNavigatorContainerNavigatorContainerStyles: (isSmallScreenWidth: boolean) => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1}), + rootNavigatorContainerStyles: (isSmallScreenWidth: boolean): ViewStyle => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1}), + RHPNavigatorContainerNavigatorContainerStyles: (isSmallScreenWidth: boolean): ViewStyle => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1}), avatarInnerTextChat: { color: theme.textLight, @@ -2614,7 +2630,7 @@ const styles = (theme: ThemeDefault) => ({ backgroundColor: theme.appBG, }, - switchThumbTransformation: (translateX: AnimatableNumericValue) => ({ + switchThumbTransformation: (translateX: AnimatableNumericValue): ViewStyle => ({ transform: [{translateX}], }), @@ -2823,14 +2839,15 @@ const styles = (theme: ThemeDefault) => ({ growlNotificationDesktopContainer: { maxWidth: variables.sideBarWidth, right: 0, - position: 'fixed', + // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". + position: 'fixed' as ViewStyle['position'], }, - growlNotificationTranslateY: (translateY: AnimatableNumericValue) => ({ + growlNotificationTranslateY: (translateY: AnimatableNumericValue): ViewStyle => ({ transform: [{translateY}], }), - makeSlideInTranslation: (translationType: Translation, fromValue: number) => ({ + makeSlideInTranslation: (translationType: Translation, fromValue: number): CustomAnimation => ({ from: { [translationType]: fromValue, }, @@ -3130,7 +3147,7 @@ const styles = (theme: ThemeDefault) => ({ ...visibility.visible, }, - floatingMessageCounterTransformation: (translateY: AnimatableNumericValue) => ({ + floatingMessageCounterTransformation: (translateY: AnimatableNumericValue): ViewStyle => ({ transform: [{translateY}], }), @@ -3157,12 +3174,12 @@ const styles = (theme: ThemeDefault) => ({ flex: 1, }, - threeDotsPopoverOffset: (windowWidth: number) => ({ + threeDotsPopoverOffset: (windowWidth: number): AnchorPosition => ({ ...getPopOverVerticalOffset(60), horizontal: windowWidth - 60, }), - threeDotsPopoverOffsetNoCloseButton: (windowWidth: number) => ({ + threeDotsPopoverOffsetNoCloseButton: (windowWidth: number): AnchorPosition => ({ ...getPopOverVerticalOffset(60), horizontal: windowWidth - 10, }), @@ -3305,7 +3322,7 @@ const styles = (theme: ThemeDefault) => ({ zIndex: 2, }, - receiptImageWrapper: (receiptImageTopPosition: number) => ({ + receiptImageWrapper: (receiptImageTopPosition: number): ViewStyle => ({ position: 'absolute', top: receiptImageTopPosition, }), @@ -3726,14 +3743,14 @@ const styles = (theme: ThemeDefault) => ({ paddingBottom: 12, }, - tabText: (isSelected: boolean) => ({ + tabText: (isSelected: boolean): TextStyle => ({ marginLeft: 8, fontFamily: isSelected ? fontFamily.EXP_NEUE_BOLD : fontFamily.EXP_NEUE, - fontWeight: isSelected ? fontWeightBold : 400, + fontWeight: isSelected ? fontWeightBold : '400', color: isSelected ? theme.textLight : theme.textSupporting, }), - overscrollSpacer: (backgroundColor: string, height: number) => ({ + overscrollSpacer: (backgroundColor: string, height: number): ViewStyle => ({ backgroundColor, height, width: '100%', @@ -3896,7 +3913,7 @@ const styles = (theme: ThemeDefault) => ({ maxWidth: 400, }, - distanceRequestContainer: (maxHeight: number) => ({ + distanceRequestContainer: (maxHeight: number): ViewStyle => ({ ...flex.flexShrink2, minHeight: variables.optionRowHeight * 2, maxHeight, From 1be0bf59b738c433a78e7fba83d6a5c3b910691e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Fri, 15 Sep 2023 17:16:07 +0100 Subject: [PATCH 032/856] Minor fixes in styles.ts --- src/styles/styles.ts | 56 ++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/src/styles/styles.ts b/src/styles/styles.ts index b55cfd9ad497..93961b20b31f 100644 --- a/src/styles/styles.ts +++ b/src/styles/styles.ts @@ -1,42 +1,45 @@ /* eslint-disable @typescript-eslint/naming-convention */ import lodashClamp from 'lodash/clamp'; import {AnimatableNumericValue, Animated, TextStyle, ViewStyle} from 'react-native'; -import {MixedStyleDeclaration, MixedStyleRecord} from 'react-native-render-html'; -import {PickerStyle} from 'react-native-picker-select'; import {CustomAnimation} from 'react-native-animatable'; -import {EmptyObject} from 'type-fest'; -import fontFamily from './fontFamily'; +import {PickerStyle} from 'react-native-picker-select'; +import {MixedStyleDeclaration, MixedStyleRecord} from 'react-native-render-html'; +import CONST from '../CONST'; +import * as Browser from '../libs/Browser'; import addOutlineWidth from './addOutlineWidth'; -import defaultTheme from './themes/default'; +import codeStyles from './codeStyles'; +import fontFamily from './fontFamily'; import fontWeightBold from './fontWeight/bold'; -import variables from './variables'; -import spacing from './utilities/spacing'; -import sizing from './utilities/sizing'; -import flex from './utilities/flex'; +import getPopOverVerticalOffset from './getPopOverVerticalOffset'; +import optionAlternateTextPlatformStyles from './optionAlternateTextPlatformStyles'; +import overflowXHidden from './overflowXHidden'; +import pointerEventsAuto from './pointerEventsAuto'; +import pointerEventsNone from './pointerEventsNone'; +import defaultTheme from './themes/default'; +import {ThemeDefault} from './themes/types'; +import cursor from './utilities/cursor'; import display from './utilities/display'; +import flex from './utilities/flex'; import overflow from './utilities/overflow'; -import whiteSpace from './utilities/whiteSpace'; -import wordBreak from './utilities/wordBreak'; import positioning from './utilities/positioning'; -import codeStyles from './codeStyles'; +import sizing from './utilities/sizing'; +import spacing from './utilities/spacing'; +import textUnderline from './utilities/textUnderline'; +import userSelect from './utilities/userSelect'; import visibility from './utilities/visibility'; +import whiteSpace from './utilities/whiteSpace'; +import wordBreak from './utilities/wordBreak'; import writingDirection from './utilities/writingDirection'; -import optionAlternateTextPlatformStyles from './optionAlternateTextPlatformStyles'; -import pointerEventsNone from './pointerEventsNone'; -import pointerEventsAuto from './pointerEventsAuto'; -import getPopOverVerticalOffset from './getPopOverVerticalOffset'; -import overflowXHidden from './overflowXHidden'; -import CONST from '../CONST'; -import * as Browser from '../libs/Browser'; -import cursor from './utilities/cursor'; -import userSelect from './utilities/userSelect'; -import textUnderline from './utilities/textUnderline'; -import {ThemeDefault} from './themes/types'; +import variables from './variables'; + +type OverlayStylesParams = {progress: Animated.AnimatedInterpolation}; + +type TwoFactorAuthCodesBoxParams = {isExtraSmallScreenWidth: boolean; isSmallScreenWidth: boolean}; type Translation = 'perspective' | 'rotate' | 'rotateX' | 'rotateY' | 'rotateZ' | 'scale' | 'scaleX' | 'scaleY' | 'translateX' | 'translateY' | 'skewX' | 'skewY' | 'matrix'; // touchCallout is an iOS safari only property that controls the display of the callout information when you touch and hold a target -const touchCalloutNone: {WebkitTouchCallout: 'none'} | EmptyObject = Browser.isMobileSafari() ? {WebkitTouchCallout: 'none'} : {}; +const touchCalloutNone: Pick = Browser.isMobileSafari() ? {WebkitTouchCallout: 'none'} : {}; const picker = (theme: ThemeDefault): TextStyle => ({ backgroundColor: theme.transparent, @@ -693,6 +696,7 @@ const styles = (theme: ThemeDefault) => ({ color: theme.text, }, doneDepressed: { + // Extracted from react-native-picker-select, src/styles.js fontSize: 17, }, modalViewMiddle: { @@ -1553,7 +1557,7 @@ const styles = (theme: ThemeDefault) => ({ height: variables.optionsListSectionHeaderHeight, }, - overlayStyles: (current: {progress: Animated.AnimatedInterpolation}): ViewStyle => ({ + overlayStyles: (current: OverlayStylesParams): ViewStyle => ({ // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". position: 'fixed' as ViewStyle['position'], @@ -2326,7 +2330,7 @@ const styles = (theme: ThemeDefault) => ({ padding: 0, }, - twoFactorAuthCodesBox: ({isExtraSmallScreenWidth, isSmallScreenWidth}: {isExtraSmallScreenWidth: boolean; isSmallScreenWidth: boolean}): ViewStyle => { + twoFactorAuthCodesBox: ({isExtraSmallScreenWidth, isSmallScreenWidth}: TwoFactorAuthCodesBoxParams): ViewStyle => { let paddingHorizontal = spacing.ph9; if (isSmallScreenWidth) { From 1338157f3a87cccde5be0b41aa45a0c60459feca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Fri, 15 Sep 2023 23:47:15 +0100 Subject: [PATCH 033/856] Add stricter types to styles.ts --- src/styles/ThemeStylesProvider.tsx | 2 +- src/styles/getModalStyles.ts | 6 +- src/styles/styles.ts | 7650 ++++++++++++++-------------- src/styles/utilities/display.ts | 5 + 4 files changed, 3861 insertions(+), 3802 deletions(-) diff --git a/src/styles/ThemeStylesProvider.tsx b/src/styles/ThemeStylesProvider.tsx index d0db784ca8ca..4e6f91baf34a 100644 --- a/src/styles/ThemeStylesProvider.tsx +++ b/src/styles/ThemeStylesProvider.tsx @@ -5,7 +5,7 @@ import ThemeStylesContext from './ThemeStylesContext'; // TODO: Rename this to "styles" once the app is migrated to theme switching hooks and HOCs import {stylesGenerator as stylesUntyped} from './styles'; -const styles = stylesUntyped as (theme: Record) => Record; +const styles = stylesUntyped; type ThemeStylesProviderProps = { children: React.ReactNode; diff --git a/src/styles/getModalStyles.ts b/src/styles/getModalStyles.ts index ceea37ddb85b..d52d29568c2d 100644 --- a/src/styles/getModalStyles.ts +++ b/src/styles/getModalStyles.ts @@ -7,9 +7,11 @@ import themeColors from './themes/default'; import variables from './variables'; function getCenteredModalStyles(windowWidth: number, isSmallScreenWidth: boolean, isFullScreenWhenSmall = false): ViewStyle { + const modalStyles = styles.centeredModalStyles(isSmallScreenWidth, isFullScreenWhenSmall); + return { - borderWidth: styles.centeredModalStyles(isSmallScreenWidth, isFullScreenWhenSmall).borderWidth, - width: isSmallScreenWidth ? '100%' : windowWidth - styles.centeredModalStyles(isSmallScreenWidth, isFullScreenWhenSmall).marginHorizontal * 2, + borderWidth: modalStyles.borderWidth, + width: isSmallScreenWidth ? '100%' : windowWidth - modalStyles.marginHorizontal * 2, }; } diff --git a/src/styles/styles.ts b/src/styles/styles.ts index 93961b20b31f..c93e96c8fbee 100644 --- a/src/styles/styles.ts +++ b/src/styles/styles.ts @@ -1,6 +1,8 @@ /* eslint-disable @typescript-eslint/naming-convention */ +import {LineLayerStyleProps} from '@rnmapbox/maps/src/utils/MapboxStyles'; import lodashClamp from 'lodash/clamp'; -import {AnimatableNumericValue, Animated, TextStyle, ViewStyle} from 'react-native'; +import {LineLayer} from 'react-map-gl'; +import {AnimatableNumericValue, Animated, ImageStyle, TextStyle, ViewStyle} from 'react-native'; import {CustomAnimation} from 'react-native-animatable'; import {PickerStyle} from 'react-native-picker-select'; import {MixedStyleDeclaration, MixedStyleRecord} from 'react-native-render-html'; @@ -32,3960 +34,4010 @@ import wordBreak from './utilities/wordBreak'; import writingDirection from './utilities/writingDirection'; import variables from './variables'; +type AnchorPosition = { + horizontal: number; + vertical: number; +}; + +type WebViewStyle = { + tagStyles: MixedStyleRecord; + baseFontStyle: MixedStyleDeclaration; +}; + +type CustomPickerStyle = PickerStyle & {icon?: ViewStyle}; + type OverlayStylesParams = {progress: Animated.AnimatedInterpolation}; type TwoFactorAuthCodesBoxParams = {isExtraSmallScreenWidth: boolean; isSmallScreenWidth: boolean}; type Translation = 'perspective' | 'rotate' | 'rotateX' | 'rotateY' | 'rotateZ' | 'scale' | 'scaleX' | 'scaleY' | 'translateX' | 'translateY' | 'skewX' | 'skewY' | 'matrix'; +type OfflineFeedbackStyle = Record<'deleted' | 'pending' | 'error' | 'container' | 'textContainer' | 'text' | 'errorDot', ViewStyle | TextStyle>; + +type MapDirectionStyle = Pick; + +type MapDirectionLayerStyle = Pick; + +type Styles = Record< + string, + | ViewStyle + | TextStyle + | ImageStyle + | WebViewStyle + | OfflineFeedbackStyle + | MapDirectionStyle + | MapDirectionLayerStyle + | ((...args: any[]) => ViewStyle | TextStyle | ImageStyle | AnchorPosition | CustomAnimation | CustomPickerStyle) +>; + // touchCallout is an iOS safari only property that controls the display of the callout information when you touch and hold a target const touchCalloutNone: Pick = Browser.isMobileSafari() ? {WebkitTouchCallout: 'none'} : {}; -const picker = (theme: ThemeDefault): TextStyle => ({ - backgroundColor: theme.transparent, - color: theme.text, - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeNormal, - lineHeight: variables.fontSizeNormalHeight, - paddingBottom: 8, - paddingTop: 23, - paddingLeft: 0, - paddingRight: 25, - height: variables.inputHeight, - borderWidth: 0, - textAlign: 'left', -}); - -const link = (theme: ThemeDefault): ViewStyle & MixedStyleDeclaration => ({ - color: theme.link, - textDecorationColor: theme.link, - fontFamily: fontFamily.EXP_NEUE, -}); - -const baseCodeTagStyles = (theme: ThemeDefault): ViewStyle & MixedStyleDeclaration => ({ - borderWidth: 1, - borderRadius: 5, - borderColor: theme.border, - backgroundColor: theme.textBackground, -}); +const picker = (theme: ThemeDefault) => + ({ + backgroundColor: theme.transparent, + color: theme.text, + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeNormal, + lineHeight: variables.fontSizeNormalHeight, + paddingBottom: 8, + paddingTop: 23, + paddingLeft: 0, + paddingRight: 25, + height: variables.inputHeight, + borderWidth: 0, + textAlign: 'left', + } satisfies TextStyle); + +const link = (theme: ThemeDefault) => + ({ + color: theme.link, + textDecorationColor: theme.link, + fontFamily: fontFamily.EXP_NEUE, + } satisfies ViewStyle & MixedStyleDeclaration); + +const baseCodeTagStyles = (theme: ThemeDefault) => + ({ + borderWidth: 1, + borderRadius: 5, + borderColor: theme.border, + backgroundColor: theme.textBackground, + } satisfies ViewStyle & MixedStyleDeclaration); const headlineFont = { fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, fontWeight: '500', -} as const; +} satisfies TextStyle; + +const webViewStyles = (theme: ThemeDefault) => + ({ + // As of react-native-render-html v6, don't declare distinct styles for + // custom renderers, the API for custom renderers has changed. Declare the + // styles in the below "tagStyles" instead. If you need to reuse those + // styles from the renderer, just pass the "style" prop to the underlying + // component. + tagStyles: { + em: { + fontFamily: fontFamily.EXP_NEUE, + fontStyle: 'italic', + }, -type WebViewStyles = { - tagStyles: MixedStyleRecord; - baseFontStyle: MixedStyleDeclaration; -}; + del: { + textDecorationLine: 'line-through', + textDecorationStyle: 'solid', + }, + + strong: { + fontFamily: fontFamily.EXP_NEUE, + fontWeight: 'bold', + }, + + a: link(theme), + + ul: { + maxWidth: '100%', + }, + + ol: { + maxWidth: '100%', + }, + + li: { + flexShrink: 1, + }, + + blockquote: { + borderLeftColor: theme.border, + borderLeftWidth: 4, + paddingLeft: 12, + marginTop: 4, + marginBottom: 4, + + // Overwrite default HTML margin for blockquotes + marginLeft: 0, + }, -const webViewStyles = (theme: ThemeDefault): WebViewStyles => ({ - // As of react-native-render-html v6, don't declare distinct styles for - // custom renderers, the API for custom renderers has changed. Declare the - // styles in the below "tagStyles" instead. If you need to reuse those - // styles from the renderer, just pass the "style" prop to the underlying - // component. - tagStyles: { - em: { + pre: { + ...baseCodeTagStyles(theme), + paddingTop: 12, + paddingBottom: 12, + paddingRight: 8, + paddingLeft: 8, + fontFamily: fontFamily.MONOSPACE, + marginTop: 0, + marginBottom: 0, + }, + + code: { + ...baseCodeTagStyles(theme), + ...(codeStyles.codeTextStyle as MixedStyleDeclaration), + paddingLeft: 5, + paddingRight: 5, + fontFamily: fontFamily.MONOSPACE, + fontSize: 13, + }, + + img: { + borderColor: theme.border, + borderRadius: variables.componentBorderRadiusNormal, + borderWidth: 1, + ...touchCalloutNone, + }, + + p: { + marginTop: 0, + marginBottom: 0, + }, + h1: { + fontSize: variables.fontSizeLarge, + marginBottom: 8, + }, + }, + + baseFontStyle: { + color: theme.text, + fontSize: variables.fontSizeNormal, fontFamily: fontFamily.EXP_NEUE, - fontStyle: 'italic', + flex: 1, + lineHeight: variables.fontSizeNormalHeight, + }, + } satisfies WebViewStyle); + +const styles = (theme: ThemeDefault) => + ({ + // Add all of our utility and helper styles + ...spacing, + ...sizing, + ...flex, + ...display, + ...overflow, + ...positioning, + ...wordBreak, + ...whiteSpace, + ...writingDirection, + ...cursor, + ...userSelect, + ...textUnderline, + + rateCol: { + margin: 0, + padding: 0, + flexBasis: '48%', }, - del: { - textDecorationLine: 'line-through', - textDecorationStyle: 'solid', + autoCompleteSuggestionsContainer: { + backgroundColor: theme.appBG, + borderRadius: 8, + borderWidth: 1, + borderColor: theme.border, + justifyContent: 'center', + boxShadow: variables.popoverMenuShadow, + position: 'absolute', + left: 0, + right: 0, + paddingVertical: CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTER_INNER_PADDING, }, - strong: { - fontFamily: fontFamily.EXP_NEUE, - fontWeight: 'bold', + autoCompleteSuggestionContainer: { + flexDirection: 'row', + alignItems: 'center', }, - a: link(theme), + emojiSuggestionsEmoji: { + fontSize: variables.fontSizeMedium, + width: 51, + textAlign: 'center', + }, + emojiSuggestionsText: { + fontSize: variables.fontSizeMedium, + flex: 1, + ...wordBreak.breakWord, + ...spacing.pr4, + }, - ul: { - maxWidth: '100%', + mentionSuggestionsAvatarContainer: { + width: 24, + height: 24, + alignItems: 'center', + justifyContent: 'center', }, - ol: { - maxWidth: '100%', + mentionSuggestionsText: { + fontSize: variables.fontSizeMedium, + ...spacing.ml2, }, - li: { - flexShrink: 1, + mentionSuggestionsDisplayName: { + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontWeight: fontWeightBold, }, - blockquote: { - borderLeftColor: theme.border, - borderLeftWidth: 4, - paddingLeft: 12, - marginTop: 4, - marginBottom: 4, + mentionSuggestionsHandle: { + color: theme.textSupporting, + }, - // Overwrite default HTML margin for blockquotes - marginLeft: 0, + appIconBorderRadius: { + overflow: 'hidden', + borderRadius: 12, }, - pre: { - ...baseCodeTagStyles(theme), - paddingTop: 12, - paddingBottom: 12, - paddingRight: 8, - paddingLeft: 8, - fontFamily: fontFamily.MONOSPACE, - marginTop: 0, - marginBottom: 0, + unitCol: { + margin: 0, + padding: 0, + marginLeft: '4%', + flexBasis: '48%', }, - code: { - ...baseCodeTagStyles(theme), - ...(codeStyles.codeTextStyle as MixedStyleDeclaration), - paddingLeft: 5, - paddingRight: 5, - fontFamily: fontFamily.MONOSPACE, - fontSize: 13, + webViewStyles: webViewStyles(theme), + + link: link(theme), + + linkMuted: { + color: theme.textSupporting, + textDecorationColor: theme.textSupporting, + fontFamily: fontFamily.EXP_NEUE, }, - img: { - borderColor: theme.border, - borderRadius: variables.componentBorderRadiusNormal, - borderWidth: 1, - ...touchCalloutNone, + linkMutedHovered: { + color: theme.textMutedReversed, }, - p: { - marginTop: 0, - marginBottom: 0, + highlightBG: { + backgroundColor: theme.highlightBG, + }, + + appBG: { + backgroundColor: theme.appBG, }, + h1: { - fontSize: variables.fontSizeLarge, - marginBottom: 8, + color: theme.heading, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontSize: variables.fontSizeh1, + fontWeight: fontWeightBold, }, - }, - baseFontStyle: { - color: theme.text, - fontSize: variables.fontSizeNormal, - fontFamily: fontFamily.EXP_NEUE, - flex: 1, - lineHeight: variables.fontSizeNormalHeight, - }, -}); + h3: { + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontSize: variables.fontSizeNormal, + fontWeight: fontWeightBold, + }, -type AnchorPosition = { - horizontal: number; - vertical: number; -}; + h4: { + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontSize: variables.fontSizeLabel, + fontWeight: fontWeightBold, + }, -const styles = (theme: ThemeDefault) => ({ - // Add all of our utility and helper styles - ...spacing, - ...sizing, - ...flex, - ...display, - ...overflow, - ...positioning, - ...wordBreak, - ...whiteSpace, - ...writingDirection, - ...cursor, - ...userSelect, - ...textUnderline, - ...theme, // TODO: Should we do this? - - rateCol: { - margin: 0, - padding: 0, - flexBasis: '48%', - }, - - autoCompleteSuggestionsContainer: { - backgroundColor: theme.appBG, - borderRadius: 8, - borderWidth: 1, - borderColor: theme.border, - justifyContent: 'center', - boxShadow: variables.popoverMenuShadow, - position: 'absolute', - left: 0, - right: 0, - paddingVertical: CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTER_INNER_PADDING, - }, - - autoCompleteSuggestionContainer: { - flexDirection: 'row', - alignItems: 'center', - }, - - emojiSuggestionsEmoji: { - fontSize: variables.fontSizeMedium, - width: 51, - textAlign: 'center', - }, - emojiSuggestionsText: { - fontSize: variables.fontSizeMedium, - flex: 1, - ...wordBreak.breakWord, - ...spacing.pr4, - }, - - mentionSuggestionsAvatarContainer: { - width: 24, - height: 24, - alignItems: 'center', - justifyContent: 'center', - }, - - mentionSuggestionsText: { - fontSize: variables.fontSizeMedium, - ...spacing.ml2, - }, - - mentionSuggestionsDisplayName: { - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - }, - - mentionSuggestionsHandle: { - color: theme.textSupporting, - }, - - appIconBorderRadius: { - overflow: 'hidden', - borderRadius: 12, - }, - - unitCol: { - margin: 0, - padding: 0, - marginLeft: '4%', - flexBasis: '48%', - }, - - webViewStyles: webViewStyles(theme), - - link: link(theme), - - linkMuted: { - color: theme.textSupporting, - textDecorationColor: theme.textSupporting, - fontFamily: fontFamily.EXP_NEUE, - }, + textAlignCenter: { + textAlign: 'center', + }, - linkMutedHovered: { - color: theme.textMutedReversed, - }, + textAlignRight: { + textAlign: 'right', + }, - highlightBG: { - backgroundColor: theme.highlightBG, - }, + textAlignLeft: { + textAlign: 'left', + }, - appBG: { - backgroundColor: theme.appBG, - }, + textUnderline: { + textDecorationLine: 'underline', + }, - h1: { - color: theme.heading, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontSize: variables.fontSizeh1, - fontWeight: fontWeightBold, - }, + label: { + fontSize: variables.fontSizeLabel, + lineHeight: variables.lineHeightLarge, + }, - h3: { - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontSize: variables.fontSizeNormal, - fontWeight: fontWeightBold, - }, + textLabel: { + color: theme.text, + fontSize: variables.fontSizeLabel, + lineHeight: variables.lineHeightLarge, + }, - h4: { - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontSize: variables.fontSizeLabel, - fontWeight: fontWeightBold, - }, + mutedTextLabel: { + color: theme.textSupporting, + fontSize: variables.fontSizeLabel, + lineHeight: variables.lineHeightLarge, + }, - textAlignCenter: { - textAlign: 'center', - }, + textMicro: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeSmall, + lineHeight: variables.lineHeightSmall, + }, - textAlignRight: { - textAlign: 'right', - }, + textMicroBold: { + color: theme.text, + fontWeight: fontWeightBold, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontSize: variables.fontSizeSmall, + lineHeight: variables.lineHeightSmall, + }, - textAlignLeft: { - textAlign: 'left', - }, + textMicroSupporting: { + color: theme.textSupporting, + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeSmall, + lineHeight: variables.lineHeightSmall, + }, - textUnderline: { - textDecorationLine: 'underline', - }, + textExtraSmallSupporting: { + color: theme.textSupporting, + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeExtraSmall, + }, - label: { - fontSize: variables.fontSizeLabel, - lineHeight: variables.lineHeightLarge, - }, + textNormal: { + fontSize: variables.fontSizeNormal, + }, - textLabel: { - color: theme.text, - fontSize: variables.fontSizeLabel, - lineHeight: variables.lineHeightLarge, - }, + textLarge: { + fontSize: variables.fontSizeLarge, + }, - mutedTextLabel: { - color: theme.textSupporting, - fontSize: variables.fontSizeLabel, - lineHeight: variables.lineHeightLarge, - }, + textXLarge: { + fontSize: variables.fontSizeXLarge, + }, - textMicro: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeSmall, - lineHeight: variables.lineHeightSmall, - }, + textXXLarge: { + fontSize: variables.fontSizeXXLarge, + }, - textMicroBold: { - color: theme.text, - fontWeight: fontWeightBold, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontSize: variables.fontSizeSmall, - lineHeight: variables.lineHeightSmall, - }, - - textMicroSupporting: { - color: theme.textSupporting, - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeSmall, - lineHeight: variables.lineHeightSmall, - }, + textXXXLarge: { + fontSize: variables.fontSizeXXXLarge, + }, - textExtraSmallSupporting: { - color: theme.textSupporting, - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeExtraSmall, - }, + textHero: { + fontSize: variables.fontSizeHero, + fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, + lineHeight: variables.lineHeightHero, + }, - textNormal: { - fontSize: variables.fontSizeNormal, - }, - - textLarge: { - fontSize: variables.fontSizeLarge, - }, - - textXLarge: { - fontSize: variables.fontSizeXLarge, - }, - - textXXLarge: { - fontSize: variables.fontSizeXXLarge, - }, - - textXXXLarge: { - fontSize: variables.fontSizeXXXLarge, - }, - - textHero: { - fontSize: variables.fontSizeHero, - fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, - lineHeight: variables.lineHeightHero, - }, - - textStrong: { - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - }, - - textItalic: { - fontFamily: fontFamily.EXP_NEUE_ITALIC, - fontStyle: 'italic', - }, - - textHeadline: { - ...headlineFont, - ...whiteSpace.preWrap, - color: theme.heading, - fontSize: variables.fontSizeXLarge, - lineHeight: variables.lineHeightXXLarge, - }, - - textHeadlineH1: { - ...headlineFont, - ...whiteSpace.preWrap, - color: theme.heading, - fontSize: variables.fontSizeh1, - lineHeight: variables.lineHeightSizeh1, - }, - - textDecorationNoLine: { - textDecorationLine: 'none', - }, - - textWhite: { - color: theme.textLight, - }, - - textBlue: { - color: theme.link, - }, - - textUppercase: { - textTransform: 'uppercase', - }, - - textNoWrap: { - ...whiteSpace.noWrap, - }, - - colorReversed: { - color: theme.textReversed, - }, - - colorMutedReversed: { - color: theme.textMutedReversed, - }, - - colorMuted: { - color: theme.textSupporting, - }, - - colorHeading: { - color: theme.heading, - }, - - bgTransparent: { - backgroundColor: 'transparent', - }, - - bgDark: { - backgroundColor: theme.inverse, - }, - - opacity0: { - opacity: 0, - }, - - opacity1: { - opacity: 1, - }, - - textDanger: { - color: theme.danger, - }, - - borderRadiusNormal: { - borderRadius: variables.buttonBorderRadius, - }, - - button: { - backgroundColor: theme.buttonDefaultBG, - borderRadius: variables.buttonBorderRadius, - minHeight: variables.componentSizeLarge, - justifyContent: 'center', - ...spacing.ph3, - }, - - buttonContainer: { - padding: 1, - borderRadius: variables.buttonBorderRadius, - }, - - buttonText: { - color: theme.text, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontSize: variables.fontSizeNormal, - fontWeight: fontWeightBold, - textAlign: 'center', - flexShrink: 1, - - // It is needed to unset the Lineheight. We don't need it for buttons as button always contains single line of text. - // It allows to vertically center the text. - lineHeight: undefined, - - // Add 1px to the Button text to give optical vertical alignment. - paddingBottom: 1, - }, - - buttonSmall: { - borderRadius: variables.buttonBorderRadius, - minHeight: variables.componentSizeSmall, - paddingTop: 4, - paddingHorizontal: 14, - paddingBottom: 4, - backgroundColor: theme.buttonDefaultBG, - }, - - buttonMedium: { - borderRadius: variables.buttonBorderRadius, - minHeight: variables.componentSizeNormal, - paddingTop: 12, - paddingRight: 16, - paddingBottom: 12, - paddingLeft: 16, - backgroundColor: theme.buttonDefaultBG, - }, - - buttonLarge: { - borderRadius: variables.buttonBorderRadius, - minHeight: variables.componentSizeLarge, - paddingTop: 8, - paddingRight: 10, - paddingBottom: 8, - paddingLeft: 18, - backgroundColor: theme.buttonDefaultBG, - }, - - buttonSmallText: { - fontSize: variables.fontSizeSmall, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - textAlign: 'center', - }, - - buttonMediumText: { - fontSize: variables.fontSizeLabel, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - textAlign: 'center', - }, - - buttonLargeText: { - fontSize: variables.fontSizeNormal, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - textAlign: 'center', - }, + textStrong: { + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontWeight: fontWeightBold, + }, - buttonDefaultHovered: { - backgroundColor: theme.buttonHoveredBG, - borderWidth: 0, - }, + textItalic: { + fontFamily: fontFamily.EXP_NEUE_ITALIC, + fontStyle: 'italic', + }, - buttonSuccess: { - backgroundColor: theme.success, - borderWidth: 0, - }, + textHeadline: { + ...headlineFont, + ...whiteSpace.preWrap, + color: theme.heading, + fontSize: variables.fontSizeXLarge, + lineHeight: variables.lineHeightXXLarge, + }, - buttonOpacityDisabled: { - opacity: 0.5, - }, + textHeadlineH1: { + ...headlineFont, + ...whiteSpace.preWrap, + color: theme.heading, + fontSize: variables.fontSizeh1, + lineHeight: variables.lineHeightSizeh1, + }, - buttonSuccessHovered: { - backgroundColor: theme.successHover, - borderWidth: 0, - }, + textDecorationNoLine: { + textDecorationLine: 'none', + }, - buttonDanger: { - backgroundColor: theme.danger, - borderWidth: 0, - }, + textWhite: { + color: theme.textLight, + }, - buttonDangerHovered: { - backgroundColor: theme.dangerHover, - borderWidth: 0, - }, + textBlue: { + color: theme.link, + }, - buttonDisabled: { - backgroundColor: theme.buttonDefaultBG, - borderWidth: 0, - }, - - buttonDivider: { - height: variables.dropDownButtonDividerHeight, - borderWidth: 0.7, - borderColor: theme.text, - }, - - noBorderRadius: { - borderRadius: 0, - }, - - noRightBorderRadius: { - borderTopRightRadius: 0, - borderBottomRightRadius: 0, - }, - - noLeftBorderRadius: { - borderTopLeftRadius: 0, - borderBottomLeftRadius: 0, - }, - - buttonCTA: { - paddingVertical: 6, - ...spacing.mh4, - }, - - buttonCTAIcon: { - marginRight: 22, - - // Align vertically with the Button text - paddingBottom: 1, - paddingTop: 1, - }, - - buttonConfirm: { - margin: 20, - }, - - attachmentButtonBigScreen: { - minWidth: 300, - alignSelf: 'center', - }, - - buttonConfirmText: { - paddingLeft: 20, - paddingRight: 20, - }, - - buttonSuccessText: { - color: theme.textLight, - }, - - buttonDangerText: { - color: theme.textLight, - }, - - hoveredComponentBG: { - backgroundColor: theme.hoverComponentBG, - }, - - activeComponentBG: { - backgroundColor: theme.activeComponentBG, - }, - - fontWeightBold: { - fontWeight: fontWeightBold, - }, - - touchableButtonImage: { - alignItems: 'center', - height: variables.componentSizeNormal, - justifyContent: 'center', - width: variables.componentSizeNormal, - }, - - visuallyHidden: { - ...visibility.hidden, - overflow: 'hidden', - width: 0, - height: 0, - }, - - visibilityHidden: { - ...visibility.hidden, - }, - - loadingVBAAnimation: { - width: 140, - height: 140, - }, - - pickerSmall: (backgroundColor = theme.highlightBG): PickerStyle & {icon: ViewStyle} => ({ - inputIOS: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeSmall, - paddingLeft: 0, - paddingRight: 17, - paddingTop: 6, - paddingBottom: 6, - borderWidth: 0, - color: theme.text, - height: 26, - opacity: 1, - backgroundColor: 'transparent', + textUppercase: { + textTransform: 'uppercase', }, - done: { - color: theme.text, + + textNoWrap: { + ...whiteSpace.noWrap, }, - doneDepressed: { - // Extracted from react-native-picker-select, src/styles.js - fontSize: 17, + + colorReversed: { + color: theme.textReversed, }, - modalViewMiddle: { - backgroundColor: theme.border, - borderTopWidth: 0, + + colorMutedReversed: { + color: theme.textMutedReversed, }, - modalViewBottom: { - backgroundColor: theme.highlightBG, + + colorMuted: { + color: theme.textSupporting, }, - inputWeb: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeSmall, - paddingLeft: 0, - paddingRight: 17, - paddingTop: 6, - paddingBottom: 6, - borderWidth: 0, - color: theme.text, - appearance: 'none', - height: 26, - opacity: 1, - backgroundColor, - ...cursor.cursorPointer, + + colorHeading: { + color: theme.heading, }, - inputAndroid: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeSmall, - paddingLeft: 0, - paddingRight: 17, - paddingTop: 6, - paddingBottom: 6, - borderWidth: 0, - color: theme.text, - height: 26, - opacity: 1, + + bgTransparent: { backgroundColor: 'transparent', }, - iconContainer: { - top: 7, - ...pointerEventsNone, + + bgDark: { + backgroundColor: theme.inverse, }, - icon: { - width: variables.iconSizeExtraSmall, - height: variables.iconSizeExtraSmall, + + opacity0: { + opacity: 0, }, - }), - badge: { - backgroundColor: theme.border, - borderRadius: 14, - height: variables.iconSizeNormal, - flexDirection: 'row', - paddingHorizontal: 7, - alignItems: 'center', - }, + opacity1: { + opacity: 1, + }, - badgeSuccess: { - backgroundColor: theme.success, - }, + textDanger: { + color: theme.danger, + }, - badgeSuccessPressed: { - backgroundColor: theme.successHover, - }, + borderRadiusNormal: { + borderRadius: variables.buttonBorderRadius, + }, - badgeAdHocSuccess: { - backgroundColor: theme.badgeAdHoc, - }, + button: { + backgroundColor: theme.buttonDefaultBG, + borderRadius: variables.buttonBorderRadius, + minHeight: variables.componentSizeLarge, + justifyContent: 'center', + ...spacing.ph3, + }, - badgeAdHocSuccessPressed: { - backgroundColor: theme.badgeAdHocHover, - }, + buttonContainer: { + padding: 1, + borderRadius: variables.buttonBorderRadius, + }, - badgeDanger: { - backgroundColor: theme.danger, - }, + buttonText: { + color: theme.text, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontSize: variables.fontSizeNormal, + fontWeight: fontWeightBold, + textAlign: 'center', + flexShrink: 1, - badgeDangerPressed: { - backgroundColor: theme.dangerPressed, - }, + // It is needed to unset the Lineheight. We don't need it for buttons as button always contains single line of text. + // It allows to vertically center the text. + lineHeight: undefined, - badgeText: { - color: theme.text, - fontSize: variables.fontSizeSmall, - lineHeight: variables.lineHeightNormal, - ...whiteSpace.noWrap, - }, + // Add 1px to the Button text to give optical vertical alignment. + paddingBottom: 1, + }, - border: { - borderWidth: 1, - borderRadius: variables.componentBorderRadius, - borderColor: theme.border, - }, - - borderColorFocus: { - borderColor: theme.borderFocus, - }, - - borderColorDanger: { - borderColor: theme.danger, - }, - - textInputDisabled: { - // Adding disabled color theme to indicate user that the field is not editable. - backgroundColor: theme.highlightBG, - borderBottomWidth: 2, - borderColor: theme.borderLighter, - // Adding browser specefic style to bring consistency between Safari and other platforms. - // Applying the Webkit styles only to browsers as it is not available in native. - ...(Browser.getBrowser() - ? { - WebkitTextFillColor: theme.textSupporting, - WebkitOpacity: 1, - } - : {}), - color: theme.textSupporting, - }, - - uploadReceiptView: (isSmallScreenWidth: boolean): ViewStyle => ({ - borderRadius: variables.componentBorderRadiusLarge, - borderWidth: isSmallScreenWidth ? 0 : 2, - borderColor: theme.borderFocus, - borderStyle: 'dotted', - marginBottom: 20, - marginLeft: 20, - marginRight: 20, - justifyContent: 'center', - alignItems: 'center', - padding: 40, - gap: 4, - flex: 1, - }), - - cameraView: { - flex: 1, - overflow: 'hidden', - padding: 10, - borderRadius: 28, - borderStyle: 'solid', - borderWidth: 8, - backgroundColor: theme.highlightBG, - borderColor: theme.appBG, - }, - - permissionView: { - paddingVertical: 108, - paddingHorizontal: 61, - alignItems: 'center', - justifyContent: 'center', - }, - - headerAnonymousFooter: { - color: theme.heading, - fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, - fontSize: variables.fontSizeXLarge, - lineHeight: variables.lineHeightXXLarge, - }, - - headerText: { - color: theme.heading, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontSize: variables.fontSizeNormal, - fontWeight: fontWeightBold, - }, + buttonSmall: { + borderRadius: variables.buttonBorderRadius, + minHeight: variables.componentSizeSmall, + paddingTop: 4, + paddingHorizontal: 14, + paddingBottom: 4, + backgroundColor: theme.buttonDefaultBG, + }, - headerGap: { - height: CONST.DESKTOP_HEADER_PADDING, - }, + buttonMedium: { + borderRadius: variables.buttonBorderRadius, + minHeight: variables.componentSizeNormal, + paddingTop: 12, + paddingRight: 16, + paddingBottom: 12, + paddingLeft: 16, + backgroundColor: theme.buttonDefaultBG, + }, - pushTextRight: { - left: 100000, - }, + buttonLarge: { + borderRadius: variables.buttonBorderRadius, + minHeight: variables.componentSizeLarge, + paddingTop: 8, + paddingRight: 10, + paddingBottom: 8, + paddingLeft: 18, + backgroundColor: theme.buttonDefaultBG, + }, - reportOptions: { - marginLeft: 8, - }, + buttonSmallText: { + fontSize: variables.fontSizeSmall, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontWeight: fontWeightBold, + textAlign: 'center', + }, - chatItemComposeSecondaryRow: { - height: 15, - marginBottom: 5, - marginTop: 5, - }, + buttonMediumText: { + fontSize: variables.fontSizeLabel, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontWeight: fontWeightBold, + textAlign: 'center', + }, - chatItemComposeSecondaryRowSubText: { - color: theme.textSupporting, - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeSmall, - lineHeight: variables.lineHeightSmall, - }, - - chatItemComposeSecondaryRowOffset: { - marginLeft: variables.chatInputSpacing, - }, - - offlineIndicator: { - marginLeft: variables.chatInputSpacing, - }, - - offlineIndicatorMobile: { - paddingLeft: 20, - paddingTop: 5, - paddingBottom: 5, - }, - - offlineIndicatorRow: { - height: 25, - }, - - // Actions - actionAvatar: { - borderRadius: 20, - }, - - componentHeightLarge: { - height: variables.inputHeight, - }, - - calendarHeader: { - height: 50, - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - paddingHorizontal: 15, - paddingRight: 5, - ...userSelect.userSelectNone, - }, - - calendarDayRoot: { - flex: 1, - height: 45, - justifyContent: 'center', - alignItems: 'center', - ...userSelect.userSelectNone, - }, - - calendarDayContainer: { - width: 30, - height: 30, - justifyContent: 'center', - alignItems: 'center', - borderRadius: 15, - overflow: 'hidden', - }, - - calendarDayContainerSelected: { - backgroundColor: theme.buttonDefaultBG, - }, - - autoGrowHeightInputContainer: (textInputHeight: number, minHeight: number, maxHeight: number): ViewStyle => ({ - height: lodashClamp(textInputHeight, minHeight, maxHeight), - minHeight, - }), - - autoGrowHeightHiddenInput: (maxWidth: number, maxHeight?: number): TextStyle => ({ - maxWidth, - maxHeight: maxHeight && maxHeight + 1, - overflow: 'hidden', - }), - - textInputContainer: { - flex: 1, - justifyContent: 'center', - height: '100%', - backgroundColor: 'transparent', - borderBottomWidth: 2, - borderColor: theme.border, - overflow: 'hidden', - }, + buttonLargeText: { + fontSize: variables.fontSizeNormal, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontWeight: fontWeightBold, + textAlign: 'center', + }, - textInputLabel: { - position: 'absolute', - left: 0, - top: 0, - fontSize: variables.fontSizeNormal, - color: theme.textSupporting, - fontFamily: fontFamily.EXP_NEUE, - width: '100%', - }, - - textInputLabelBackground: { - position: 'absolute', - top: 0, - width: '100%', - height: 23, - backgroundColor: theme.componentBG, - }, - - textInputLabelDesktop: { - transformOrigin: 'left center', - }, - - textInputLabelTransformation: (translateY: AnimatableNumericValue, translateX: AnimatableNumericValue, scale: AnimatableNumericValue): TextStyle => ({ - transform: [{translateY}, {translateX}, {scale}], - }), - - baseTextInput: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeNormal, - lineHeight: variables.lineHeightXLarge, - color: theme.text, - paddingTop: 23, - paddingBottom: 8, - paddingLeft: 0, - borderWidth: 0, - }, + buttonDefaultHovered: { + backgroundColor: theme.buttonHoveredBG, + borderWidth: 0, + }, - textInputMultiline: { - scrollPadding: '23px 0 0 0', - }, + buttonSuccess: { + backgroundColor: theme.success, + borderWidth: 0, + }, - textInputMultilineContainer: { - paddingTop: 23, - }, - - textInputAndIconContainer: { - flex: 1, - height: '100%', - zIndex: -1, - flexDirection: 'row', - }, - - textInputDesktop: addOutlineWidth({}, 0), - - textInputIconContainer: { - paddingHorizontal: 11, - justifyContent: 'center', - margin: 1, - }, - - secureInput: { - borderTopRightRadius: 0, - borderBottomRightRadius: 0, - }, - - textInput: { - backgroundColor: 'transparent', - borderRadius: variables.componentBorderRadiusNormal, - height: variables.inputComponentSizeNormal, - borderColor: theme.border, - borderWidth: 1, - color: theme.text, - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeNormal, - paddingLeft: 12, - paddingRight: 12, - paddingTop: 10, - paddingBottom: 10, - textAlignVertical: 'center', - }, - - textInputPrefixWrapper: { - position: 'absolute', - left: 0, - top: 0, - height: variables.inputHeight, - display: 'flex', - flexDirection: 'row', - alignItems: 'center', - paddingTop: 23, - paddingBottom: 8, - }, + buttonOpacityDisabled: { + opacity: 0.5, + }, - textInputPrefix: { - color: theme.text, - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeNormal, - textAlignVertical: 'center', - }, + buttonSuccessHovered: { + backgroundColor: theme.successHover, + borderWidth: 0, + }, - pickerContainer: { - borderBottomWidth: 2, - paddingLeft: 0, - borderStyle: 'solid', - borderColor: theme.border, - justifyContent: 'center', - backgroundColor: 'transparent', - height: variables.inputHeight, - overflow: 'hidden', - }, - - pickerContainerSmall: { - height: variables.inputHeightSmall, - }, - - pickerLabel: { - position: 'absolute', - left: 0, - top: 6, - zIndex: 1, - }, - - picker: (disabled = false, backgroundColor = theme.appBG): PickerStyle => ({ - iconContainer: { - top: Math.round(variables.inputHeight * 0.5) - 11, - right: 0, - ...pointerEventsNone, + buttonDanger: { + backgroundColor: theme.danger, + borderWidth: 0, }, - inputWeb: { - appearance: 'none', - ...(disabled ? cursor.cursorDisabled : cursor.cursorPointer), - ...picker(theme), - backgroundColor, + buttonDangerHovered: { + backgroundColor: theme.dangerHover, + borderWidth: 0, }, - inputIOS: { - ...picker(theme), + buttonDisabled: { + backgroundColor: theme.buttonDefaultBG, + borderWidth: 0, }, - done: { - color: theme.text, + + buttonDivider: { + height: variables.dropDownButtonDividerHeight, + borderWidth: 0.7, + borderColor: theme.text, }, - doneDepressed: { - fontSize: 17, + + noBorderRadius: { + borderRadius: 0, }, - modalViewMiddle: { - backgroundColor: theme.border, - borderTopWidth: 0, + + noRightBorderRadius: { + borderTopRightRadius: 0, + borderBottomRightRadius: 0, }, - modalViewBottom: { - backgroundColor: theme.highlightBG, + + noLeftBorderRadius: { + borderTopLeftRadius: 0, + borderBottomLeftRadius: 0, }, - inputAndroid: { - ...picker(theme), + buttonCTA: { + paddingVertical: 6, + ...spacing.mh4, }, - }), - disabledText: { - color: theme.icon, - }, + buttonCTAIcon: { + marginRight: 22, - inputDisabled: { - backgroundColor: theme.highlightBG, - color: theme.icon, - }, + // Align vertically with the Button text + paddingBottom: 1, + paddingTop: 1, + }, - noOutline: addOutlineWidth({}, 0), + buttonConfirm: { + margin: 20, + }, - errorOutline: { - borderColor: theme.danger, - }, + attachmentButtonBigScreen: { + minWidth: 300, + alignSelf: 'center', + }, - textLabelSupporting: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeLabel, - color: theme.textSupporting, - }, + buttonConfirmText: { + paddingLeft: 20, + paddingRight: 20, + }, - textLabelError: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeLabel, - color: theme.textError, - }, - - textReceiptUpload: { - ...headlineFont, - fontSize: variables.fontSizeXLarge, - color: theme.textLight, - textAlign: 'center', - }, - - subTextReceiptUpload: { - fontFamily: fontFamily.EXP_NEUE, - lineHeight: variables.lineHeightLarge, - textAlign: 'center', - color: theme.textLight, - }, + buttonSuccessText: { + color: theme.textLight, + }, - furtherDetailsText: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeSmall, - color: theme.textSupporting, - }, - - lh16: { - lineHeight: 16, - }, - - lh20: { - lineHeight: 20, - }, - - lh140Percent: { - lineHeight: '140%', - }, - - formHelp: { - color: theme.textSupporting, - fontSize: variables.fontSizeLabel, - lineHeight: variables.lineHeightLarge, - marginBottom: 4, - }, - - formError: { - color: theme.textError, - fontSize: variables.fontSizeLabel, - lineHeight: variables.formErrorLineHeight, - marginBottom: 4, - }, - - formSuccess: { - color: theme.success, - fontSize: variables.fontSizeLabel, - lineHeight: 18, - marginBottom: 4, - }, - - desktopRedirectPage: { - backgroundColor: theme.appBG, - minHeight: '100%', - flex: 1, - alignItems: 'center', - }, - - signInPage: { - backgroundColor: theme.highlightBG, - minHeight: '100%', - flex: 1, - }, - - signInPageHeroCenter: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - justifyContent: 'center', - alignItems: 'center', - }, - - signInPageGradient: { - height: '100%', - width: 540, - position: 'absolute', - top: 0, - left: 0, - }, - - signInPageGradientMobile: { - height: 300, - width: 800, - position: 'absolute', - top: 0, - left: 0, - }, - - signInBackground: { - position: 'absolute', - bottom: 0, - left: 0, - minHeight: 700, - }, - - signInPageInner: { - marginLeft: 'auto', - marginRight: 'auto', - height: '100%', - width: '100%', - }, - - signInPageContentTopSpacer: { - maxHeight: 132, - minHeight: 24, - }, - - signInPageContentTopSpacerSmallScreens: { - maxHeight: 132, - minHeight: 45, - }, - - signInPageLeftContainer: { - paddingLeft: 40, - paddingRight: 40, - }, - - signInPageLeftContainerWide: { - maxWidth: variables.sideBarWidth, - }, - - signInPageWelcomeFormContainer: { - maxWidth: CONST.SIGN_IN_FORM_WIDTH, - }, - - signInPageWelcomeTextContainer: { - width: CONST.SIGN_IN_FORM_WIDTH, - }, - - changeExpensifyLoginLinkContainer: { - flexDirection: 'row', - flexWrap: 'wrap', - ...wordBreak.breakWord, - }, - - // Sidebar Styles - sidebar: { - backgroundColor: theme.sidebar, - height: '100%', - }, - - sidebarAnimatedWrapperContainer: { - height: '100%', - position: 'absolute', - }, - - sidebarFooter: { - alignItems: 'center', - display: 'flex', - justifyContent: 'center', - paddingVertical: variables.lineHeightXLarge, - width: '100%', - }, - - sidebarAvatar: { - backgroundColor: theme.icon, - borderRadius: 20, - height: variables.componentSizeNormal, - width: variables.componentSizeNormal, - }, - - statusIndicator: (backgroundColor = theme.danger): ViewStyle => ({ - borderColor: theme.sidebar, - backgroundColor, - borderRadius: 8, - borderWidth: 2, - position: 'absolute', - right: -2, - top: -1, - height: 16, - width: 16, - zIndex: 10, - }), - - floatingActionButtonContainer: { - position: 'absolute', - right: 20, - - // The bottom of the floating action button should align with the bottom of the compose box. - // The value should be equal to the height + marginBottom + marginTop of chatItemComposeSecondaryRow - bottom: 25, - }, - - floatingActionButton: { - backgroundColor: theme.success, - height: variables.componentSizeLarge, - width: variables.componentSizeLarge, - borderRadius: 999, - alignItems: 'center', - justifyContent: 'center', - }, - - sidebarFooterUsername: { - color: theme.heading, - fontSize: variables.fontSizeLabel, - fontWeight: '700', - width: 200, - textOverflow: 'ellipsis', - overflow: 'hidden', - ...whiteSpace.noWrap, - }, - - sidebarFooterLink: { - color: theme.textSupporting, - fontSize: variables.fontSizeSmall, - textDecorationLine: 'none', - fontFamily: fontFamily.EXP_NEUE, - lineHeight: 20, - }, - - sidebarListContainer: { - scrollbarWidth: 'none', - paddingBottom: 4, - }, - - sidebarListItem: { - justifyContent: 'center', - textDecorationLine: 'none', - }, - - RHPNavigatorContainer: (isSmallScreenWidth: boolean): ViewStyle => ({ - width: isSmallScreenWidth ? '100%' : variables.sideBarWidth, - position: 'absolute', - right: 0, - height: '100%', - }), - - onlyEmojisText: { - fontSize: variables.fontSizeOnlyEmojis, - lineHeight: variables.fontSizeOnlyEmojisHeight, - }, - - onlyEmojisTextLineHeight: { - lineHeight: variables.fontSizeOnlyEmojisHeight, - }, - - createMenuPositionSidebar: (windowHeight: number): AnchorPosition => ({ - horizontal: 18, - vertical: windowHeight - 100, - }), - - createMenuPositionProfile: (windowWidth: number): AnchorPosition => ({ - horizontal: windowWidth - 355, - ...getPopOverVerticalOffset(162), - }), - - createMenuPositionReportActionCompose: (windowHeight: number): AnchorPosition => ({ - horizontal: 18 + variables.sideBarWidth, - vertical: windowHeight - 83, - }), - - createMenuPositionRightSidepane: { - right: 18, - bottom: 75, - }, - - createMenuContainer: { - width: variables.sideBarWidth - 40, - paddingVertical: 12, - }, - - createMenuHeaderText: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeLabel, - color: theme.heading, - }, - - popoverMenuItem: { - flexDirection: 'row', - borderRadius: 0, - paddingHorizontal: 20, - paddingVertical: 12, - justifyContent: 'space-between', - width: '100%', - }, - - popoverMenuIcon: { - width: variables.componentSizeNormal, - justifyContent: 'center', - alignItems: 'center', - }, - - popoverMenuText: { - fontSize: variables.fontSizeNormal, - color: theme.heading, - }, - - popoverInnerContainer: { - paddingTop: 0, // adjusting this because the mobile modal adds additional padding that we don't need for our layout - maxHeight: '95%', - }, - - menuItemTextContainer: { - minHeight: variables.componentSizeNormal, - }, - - chatLinkRowPressable: { - minWidth: 0, - textDecorationLine: 'none', - flex: 1, - }, - - sidebarLink: { - textDecorationLine: 'none', - }, - - sidebarLinkInner: { - alignItems: 'center', - flexDirection: 'row', - paddingLeft: 20, - paddingRight: 20, - }, - - sidebarLinkText: { - color: theme.textSupporting, - fontSize: variables.fontSizeNormal, - textDecorationLine: 'none', - overflow: 'hidden', - }, - - sidebarLinkHover: { - backgroundColor: theme.sidebarHover, - }, - - sidebarLinkActive: { - backgroundColor: theme.border, - textDecorationLine: 'none', - }, - - sidebarLinkTextBold: { - fontWeight: '700', - color: theme.heading, - }, - - sidebarLinkActiveText: { - color: theme.textSupporting, - fontSize: variables.fontSizeNormal, - textDecorationLine: 'none', - overflow: 'hidden', - }, + buttonDangerText: { + color: theme.textLight, + }, - optionItemAvatarNameWrapper: { - minWidth: 0, - flex: 1, - }, + hoveredComponentBG: { + backgroundColor: theme.hoverComponentBG, + }, - optionDisplayName: { - fontFamily: fontFamily.EXP_NEUE, - minHeight: variables.alternateTextHeight, - lineHeight: variables.lineHeightXLarge, - ...whiteSpace.noWrap, - }, - - optionDisplayNameCompact: { - minWidth: 'auto', - flexBasis: 'auto', - flexGrow: 0, - flexShrink: 1, - }, - - displayNameTooltipEllipsis: { - position: 'absolute', - opacity: 0, - right: 0, - bottom: 0, - }, - - optionAlternateText: { - minHeight: variables.alternateTextHeight, - lineHeight: variables.lineHeightXLarge, - }, - - optionAlternateTextCompact: { - flexShrink: 1, - flexGrow: 1, - flexBasis: 'auto', - ...optionAlternateTextPlatformStyles, - }, - - optionRow: { - minHeight: variables.optionRowHeight, - paddingTop: 12, - paddingBottom: 12, - }, - - optionRowSelected: { - backgroundColor: theme.activeComponentBG, - }, - - optionRowDisabled: { - color: theme.textSupporting, - }, - - optionRowCompact: { - height: variables.optionRowHeightCompact, - paddingTop: 12, - paddingBottom: 12, - }, - - optionsListSectionHeader: { - height: variables.optionsListSectionHeaderHeight, - }, - - overlayStyles: (current: OverlayStylesParams): ViewStyle => ({ - // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". - position: 'fixed' as ViewStyle['position'], - - // We need to stretch the overlay to cover the sidebar and the translate animation distance. - left: -2 * variables.sideBarWidth, - top: 0, - bottom: 0, - right: 0, - backgroundColor: theme.shadow, - opacity: current.progress.interpolate({ - inputRange: [0, 1], - outputRange: [0, variables.overlayOpacity], - extrapolate: 'clamp', - }), - }), - - appContent: { - backgroundColor: theme.appBG, - overflow: 'hidden', - }, - - appContentHeader: { - height: variables.contentHeaderHeight, - justifyContent: 'center', - display: 'flex', - paddingRight: 20, - }, - - appContentHeaderTitle: { - alignItems: 'center', - flexDirection: 'row', - }, - - LHNToggle: { - alignItems: 'center', - height: variables.contentHeaderHeight, - justifyContent: 'center', - paddingRight: 10, - paddingLeft: 20, - }, - - LHNToggleIcon: { - height: 15, - width: 18, - }, - - chatContent: { - flex: 4, - justifyContent: 'flex-end', - }, - - chatContentScrollView: { - flexGrow: 1, - justifyContent: 'flex-start', - paddingBottom: 16, - }, - - // Chat Item - chatItem: { - display: 'flex', - flexDirection: 'row', - paddingTop: 8, - paddingBottom: 8, - paddingLeft: 20, - paddingRight: 20, - }, - - chatItemRightGrouped: { - flexGrow: 1, - flexShrink: 1, - flexBasis: 0, - position: 'relative', - marginLeft: variables.chatInputSpacing, - }, - - chatItemRight: { - flexGrow: 1, - flexShrink: 1, - flexBasis: 0, - position: 'relative', - }, - - chatItemMessageHeader: { - alignItems: 'center', - display: 'flex', - flexDirection: 'row', - flexWrap: 'nowrap', - }, - - chatItemMessageHeaderSender: { - color: theme.heading, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontSize: variables.fontSizeNormal, - fontWeight: fontWeightBold, - lineHeight: variables.lineHeightXLarge, - ...wordBreak.breakWord, - }, - - chatItemMessageHeaderTimestamp: { - flexShrink: 0, - color: theme.textSupporting, - fontSize: variables.fontSizeSmall, - paddingTop: 2, - }, - - chatItemMessage: { - color: theme.text, - fontSize: variables.fontSizeNormal, - fontFamily: fontFamily.EXP_NEUE, - lineHeight: variables.lineHeightXLarge, - maxWidth: '100%', - ...cursor.cursorAuto, - ...whiteSpace.preWrap, - ...wordBreak.breakWord, - }, - - chatItemComposeWithFirstRow: { - minHeight: 90, - }, - - chatItemFullComposeRow: { - ...sizing.h100, - }, - - chatItemComposeBoxColor: { - borderColor: theme.border, - }, + activeComponentBG: { + backgroundColor: theme.activeComponentBG, + }, - chatItemComposeBoxFocusedColor: { - borderColor: theme.borderFocus, - }, + fontWeightBold: { + fontWeight: fontWeightBold, + }, - chatItemComposeBox: { - backgroundColor: theme.componentBG, - borderWidth: 1, - borderRadius: variables.componentBorderRadiusRounded, - minHeight: variables.componentSizeMedium, - }, - - chatItemFullComposeBox: { - ...flex.flex1, - ...sizing.h100, - }, - - chatFooter: { - paddingLeft: 20, - paddingRight: 20, - display: 'flex', - backgroundColor: theme.appBG, - }, - - chatFooterFullCompose: { - flex: 1, - }, - - chatItemDraft: { - display: 'flex', - flexDirection: 'row', - paddingTop: 8, - paddingBottom: 8, - paddingLeft: 20, - paddingRight: 20, - }, - - chatItemReactionsDraftRight: { - marginLeft: 52, - }, - chatFooterAtTheTop: { - flexGrow: 1, - justifyContent: 'flex-start', - }, - - // Be extremely careful when editing the compose styles, as it is easy to introduce regressions. - // Make sure you run the following tests against any changes: #12669 - textInputCompose: addOutlineWidth( - { - backgroundColor: theme.componentBG, - borderColor: theme.border, - color: theme.text, - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeNormal, - borderWidth: 0, - height: 'auto', - lineHeight: variables.lineHeightXLarge, - ...overflowXHidden, + touchableButtonImage: { + alignItems: 'center', + height: variables.componentSizeNormal, + justifyContent: 'center', + width: variables.componentSizeNormal, + }, - // On Android, multiline TextInput with height: 'auto' will show extra padding unless they are configured with - // paddingVertical: 0, alignSelf: 'center', and textAlignVertical: 'center' + visuallyHidden: { + ...visibility.hidden, + overflow: 'hidden', + width: 0, + height: 0, + }, - paddingHorizontal: variables.avatarChatSpacing, - paddingTop: 0, - paddingBottom: 0, - alignSelf: 'center', - textAlignVertical: 'center', + visibilityHidden: { + ...visibility.hidden, }, - 0, - ), - textInputFullCompose: { - alignSelf: 'stretch', - flex: 1, - maxHeight: '100%', - textAlignVertical: 'top', - }, + loadingVBAAnimation: { + width: 140, + height: 140, + }, - editInputComposeSpacing: { - backgroundColor: theme.transparent, - marginVertical: 8, - }, - - // composer padding should not be modified unless thoroughly tested against the cases in this PR: #12669 - textInputComposeSpacing: { - paddingVertical: 5, - ...flex.flexRow, - flex: 1, - }, - - textInputComposeBorder: { - borderLeftWidth: 1, - borderColor: theme.border, - }, + pickerSmall: (backgroundColor = theme.highlightBG) => + ({ + inputIOS: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeSmall, + paddingLeft: 0, + paddingRight: 17, + paddingTop: 6, + paddingBottom: 6, + borderWidth: 0, + color: theme.text, + height: 26, + opacity: 1, + backgroundColor: 'transparent', + }, + done: { + color: theme.text, + }, + doneDepressed: { + // Extracted from react-native-picker-select, src/styles.js + fontSize: 17, + }, + modalViewMiddle: { + backgroundColor: theme.border, + borderTopWidth: 0, + }, + modalViewBottom: { + backgroundColor: theme.highlightBG, + }, + inputWeb: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeSmall, + paddingLeft: 0, + paddingRight: 17, + paddingTop: 6, + paddingBottom: 6, + borderWidth: 0, + color: theme.text, + appearance: 'none', + height: 26, + opacity: 1, + backgroundColor, + ...cursor.cursorPointer, + }, + inputAndroid: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeSmall, + paddingLeft: 0, + paddingRight: 17, + paddingTop: 6, + paddingBottom: 6, + borderWidth: 0, + color: theme.text, + height: 26, + opacity: 1, + backgroundColor: 'transparent', + }, + iconContainer: { + top: 7, + ...pointerEventsNone, + }, + icon: { + width: variables.iconSizeExtraSmall, + height: variables.iconSizeExtraSmall, + }, + } satisfies CustomPickerStyle), + + badge: { + backgroundColor: theme.border, + borderRadius: 14, + height: variables.iconSizeNormal, + flexDirection: 'row', + paddingHorizontal: 7, + alignItems: 'center', + }, - chatItemSubmitButton: { - alignSelf: 'flex-end', - borderRadius: variables.componentBorderRadiusRounded, - backgroundColor: theme.transparent, - height: 40, - padding: 10, - margin: 3, - justifyContent: 'center', - }, - - emojiPickerContainer: { - backgroundColor: theme.componentBG, - }, - - emojiHeaderContainer: { - backgroundColor: theme.componentBG, - display: 'flex', - height: CONST.EMOJI_PICKER_HEADER_HEIGHT, - justifyContent: 'center', - width: '100%', - }, - - emojiSkinToneTitle: { - width: '100%', - ...spacing.pv1, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - color: theme.heading, - fontSize: variables.fontSizeSmall, - }, - - // Emoji Picker Styles - emojiText: { - textAlign: 'center', - fontSize: variables.emojiSize, - ...spacing.pv0, - ...spacing.ph0, - lineHeight: variables.emojiLineHeight, - }, - - emojiItem: { - width: '12.5%', - textAlign: 'center', - borderRadius: 8, - paddingTop: 2, - paddingBottom: 2, - height: CONST.EMOJI_PICKER_ITEM_HEIGHT, - }, - - emojiItemHighlighted: { - transition: '0.2s ease', - backgroundColor: theme.buttonDefaultBG, - }, - - emojiItemKeyboardHighlighted: { - transition: '0.2s ease', - borderWidth: 1, - borderColor: theme.link, - borderRadius: variables.buttonBorderRadius, - }, - - categoryShortcutButton: { - flex: 1, - borderRadius: 8, - height: CONST.EMOJI_PICKER_ITEM_HEIGHT, - alignItems: 'center', - justifyContent: 'center', - }, - - chatItemEmojiButton: { - alignSelf: 'flex-end', - borderRadius: variables.buttonBorderRadius, - height: 40, - marginVertical: 3, - paddingHorizontal: 10, - justifyContent: 'center', - }, - - editChatItemEmojiWrapper: { - marginRight: 3, - alignSelf: 'flex-end', - }, - - hoveredButton: { - backgroundColor: theme.buttonHoveredBG, - }, - - composerSizeButton: { - alignSelf: 'center', - height: 32, - width: 32, - padding: 6, - margin: 3, - borderRadius: variables.componentBorderRadiusRounded, - backgroundColor: theme.transparent, - justifyContent: 'center', - }, + badgeSuccess: { + backgroundColor: theme.success, + }, - chatItemAttachmentPlaceholder: { - backgroundColor: theme.sidebar, - borderColor: theme.border, - borderWidth: 1, - borderRadius: variables.componentBorderRadiusNormal, - height: 150, - textAlign: 'center', - verticalAlign: 'middle', - width: 200, - }, - - chatSwticherPillWrapper: { - marginTop: 5, - marginRight: 4, - }, - - navigationModalOverlay: { - ...userSelect.userSelectNone, - position: 'absolute', - width: '100%', - height: '100%', - transform: [ - { - translateX: -variables.sideBarWidth, - }, - ], - }, + badgeSuccessPressed: { + backgroundColor: theme.successHover, + }, - sidebarVisible: { - borderRightWidth: 1, - }, + badgeAdHocSuccess: { + backgroundColor: theme.badgeAdHoc, + }, - sidebarHidden: { - width: 0, - borderRightWidth: 0, - }, + badgeAdHocSuccessPressed: { + backgroundColor: theme.badgeAdHocHover, + }, - exampleCheckImage: { - width: '100%', - height: 80, - borderColor: theme.border, - borderWidth: 1, - borderRadius: variables.componentBorderRadiusNormal, - }, - - singleAvatar: { - height: 24, - width: 24, - backgroundColor: theme.icon, - borderRadius: 24, - }, - - singleSubscript: { - height: variables.iconSizeNormal, - width: variables.iconSizeNormal, - backgroundColor: theme.icon, - borderRadius: 20, - zIndex: 1, - }, - - singleAvatarSmall: { - height: 18, - width: 18, - backgroundColor: theme.icon, - borderRadius: 18, - }, - - secondAvatar: { - position: 'absolute', - right: -18, - bottom: -18, - borderWidth: 3, - borderRadius: 30, - borderColor: 'transparent', - }, - - secondAvatarSmall: { - position: 'absolute', - right: -13, - bottom: -13, - borderWidth: 3, - borderRadius: 18, - borderColor: 'transparent', - }, - - secondAvatarSubscript: { - position: 'absolute', - right: -6, - bottom: -6, - }, - - secondAvatarSubscriptCompact: { - position: 'absolute', - bottom: -1, - right: -1, - }, - - secondAvatarSubscriptSmallNormal: { - position: 'absolute', - bottom: 0, - right: 0, - }, - - leftSideLargeAvatar: { - left: 15, - }, - - rightSideLargeAvatar: { - right: 15, - zIndex: 2, - borderWidth: 4, - borderRadius: 100, - }, - - secondAvatarInline: { - bottom: -3, - right: -25, - borderWidth: 3, - borderRadius: 18, - borderColor: theme.cardBorder, - backgroundColor: theme.appBG, - }, - - avatarLarge: { - width: variables.avatarSizeLarge, - height: variables.avatarSizeLarge, - }, - - avatarNormal: { - height: variables.componentSizeNormal, - width: variables.componentSizeNormal, - borderRadius: variables.componentSizeNormal, - }, - - avatarSmall: { - height: variables.avatarSizeSmall, - width: variables.avatarSizeSmall, - borderRadius: variables.avatarSizeSmall, - }, - - avatarInnerText: { - color: theme.textLight, - fontSize: variables.fontSizeSmall, - lineHeight: undefined, - marginLeft: -3, - textAlign: 'center', - }, - - avatarInnerTextSmall: { - color: theme.textLight, - fontSize: variables.fontSizeExtraSmall, - lineHeight: undefined, - marginLeft: -2, - textAlign: 'center', - }, - - avatarSpace: { - top: 3, - left: 3, - }, - - avatar: { - backgroundColor: theme.sidebar, - borderColor: theme.sidebar, - }, - - focusedAvatar: { - backgroundColor: theme.border, - borderColor: theme.border, - }, - - emptyAvatar: { - height: variables.avatarSizeNormal, - width: variables.avatarSizeNormal, - }, - - emptyAvatarSmallNormal: { - height: variables.avatarSizeSmallNormal, - width: variables.avatarSizeSmallNormal, - }, - - emptyAvatarSmall: { - height: variables.avatarSizeSmall, - width: variables.avatarSizeSmall, - }, - - emptyAvatarSmaller: { - height: variables.avatarSizeSmaller, - width: variables.avatarSizeSmaller, - }, - - emptyAvatarMedium: { - height: variables.avatarSizeMedium, - width: variables.avatarSizeMedium, - }, - - emptyAvatarLarge: { - height: variables.avatarSizeLarge, - width: variables.avatarSizeLarge, - }, - - emptyAvatarMargin: { - marginRight: variables.avatarChatSpacing, - }, - - emptyAvatarMarginChat: { - marginRight: variables.avatarChatSpacing - 12, - }, - - emptyAvatarMarginSmall: { - marginRight: variables.avatarChatSpacing - 4, - }, - - emptyAvatarMarginSmaller: { - marginRight: variables.avatarChatSpacing - 4, - }, - - modalViewContainer: { - alignItems: 'center', - flex: 1, - }, - - borderTop: { - borderTopWidth: variables.borderTopWidth, - borderColor: theme.border, - }, + badgeDanger: { + backgroundColor: theme.danger, + }, - borderTopRounded: { - borderTopWidth: 1, - borderColor: theme.border, - borderTopLeftRadius: variables.componentBorderRadiusNormal, - borderTopRightRadius: variables.componentBorderRadiusNormal, - }, + badgeDangerPressed: { + backgroundColor: theme.dangerPressed, + }, - borderBottomRounded: { - borderBottomWidth: 1, - borderColor: theme.border, - borderBottomLeftRadius: variables.componentBorderRadiusNormal, - borderBottomRightRadius: variables.componentBorderRadiusNormal, - }, + badgeText: { + color: theme.text, + fontSize: variables.fontSizeSmall, + lineHeight: variables.lineHeightNormal, + ...whiteSpace.noWrap, + }, - borderBottom: { - borderBottomWidth: 1, - borderColor: theme.border, - }, + border: { + borderWidth: 1, + borderRadius: variables.componentBorderRadius, + borderColor: theme.border, + }, - borderNone: { - borderWidth: 0, - borderBottomWidth: 0, - }, + borderColorFocus: { + borderColor: theme.borderFocus, + }, - borderRight: { - borderRightWidth: 1, - borderColor: theme.border, - }, + borderColorDanger: { + borderColor: theme.danger, + }, - borderLeft: { - borderLeftWidth: 1, - borderColor: theme.border, - }, - - pointerEventsNone, - - pointerEventsAuto, - - headerBar: { - overflow: 'hidden', - justifyContent: 'center', - display: 'flex', - paddingLeft: 20, - height: variables.contentHeaderHeight, - width: '100%', - }, - - imageViewContainer: { - width: '100%', - height: '100%', - alignItems: 'center', - justifyContent: 'center', - }, - - imageModalPDF: { - flex: 1, - backgroundColor: theme.modalBackground, - }, - - PDFView: { - // `display: grid` is not supported in native platforms! - // It's being used on Web/Desktop only to vertically center short PDFs, - // while preventing the overflow of the top of long PDF files. - display: 'grid', - backgroundColor: theme.modalBackground, - width: '100%', - height: '100%', - justifyContent: 'center', - overflow: 'hidden', - alignItems: 'center', - }, - - PDFViewList: { - overflowX: 'hidden', - // There properties disable "focus" effect on list - boxShadow: 'none', - outline: 'none', - }, - - getPDFPasswordFormStyle: (isSmallScreenWidth: boolean): ViewStyle => ({ - width: isSmallScreenWidth ? '100%' : 350, - ...(isSmallScreenWidth && flex.flex1), - }), - - modalCenterContentContainer: { - flex: 1, - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'center', - backgroundColor: theme.modalBackdrop, - }, - - centeredModalStyles: (isSmallScreenWidth: boolean, isFullScreenWhenSmall: boolean): ViewStyle => ({ - borderWidth: isSmallScreenWidth && !isFullScreenWhenSmall ? 1 : 0, - marginHorizontal: isSmallScreenWidth ? 0 : 20, - }), - - imageModalImageCenterContainer: { - alignItems: 'center', - flex: 1, - justifyContent: 'center', - width: '100%', - }, - - defaultAttachmentView: { - backgroundColor: theme.sidebar, - borderRadius: variables.componentBorderRadiusNormal, - borderWidth: 1, - borderColor: theme.border, - flexDirection: 'row', - padding: 20, - alignItems: 'center', - }, - - notFoundSafeArea: { - flex: 1, - backgroundColor: theme.heading, - }, - - notFoundView: { - flex: 1, - alignItems: 'center', - paddingTop: 40, - paddingBottom: 40, - justifyContent: 'space-between', - }, - - notFoundLogo: { - width: 202, - height: 63, - }, - - notFoundContent: { - alignItems: 'center', - }, - - notFoundTextHeader: { - ...headlineFont, - color: theme.heading, - fontSize: variables.fontSizeXLarge, - lineHeight: variables.lineHeightXXLarge, - marginTop: 20, - marginBottom: 8, - textAlign: 'center', - }, - - notFoundTextBody: { - color: theme.componentBG, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - fontSize: 15, - }, - - notFoundButtonText: { - color: theme.link, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - fontSize: 15, - }, - - blockingViewContainer: { - paddingBottom: variables.contentHeaderHeight, - }, - - defaultModalContainer: { - backgroundColor: theme.componentBG, - borderColor: theme.transparent, - }, - - reportActionContextMenuMiniButton: { - ...spacing.p1, - ...spacing.mv1, - ...spacing.mh1, - ...{borderRadius: variables.buttonBorderRadius}, - }, - - reportActionSystemMessageContainer: { - marginLeft: 42, - }, - - reportDetailsTitleContainer: { - ...flex.flexColumn, - ...flex.alignItemsCenter, - paddingHorizontal: 20, - paddingBottom: 20, - }, - - reportDetailsRoomInfo: { - ...flex.flex1, - ...flex.flexColumn, - ...flex.alignItemsCenter, - }, - - reportSettingsVisibilityText: { - textTransform: 'capitalize', - }, - - settingsPageBackground: { - flexDirection: 'column', - width: '100%', - flexGrow: 1, - }, - - settingsPageBody: { - width: '100%', - justifyContent: 'space-around', - }, - - settingsPageColumn: { - width: '100%', - alignItems: 'center', - justifyContent: 'space-around', - }, - - settingsPageContainer: { - justifyContent: 'space-between', - alignItems: 'center', - width: '100%', - }, - - twoFactorAuthSection: { - backgroundColor: theme.appBG, - padding: 0, - }, - - twoFactorAuthCodesBox: ({isExtraSmallScreenWidth, isSmallScreenWidth}: TwoFactorAuthCodesBoxParams): ViewStyle => { - let paddingHorizontal = spacing.ph9; - - if (isSmallScreenWidth) { - paddingHorizontal = spacing.ph4; - } - - if (isExtraSmallScreenWidth) { - paddingHorizontal = spacing.ph2; - } - - return { - alignItems: 'center', - justifyContent: 'center', + textInputDisabled: { + // Adding disabled color theme to indicate user that the field is not editable. backgroundColor: theme.highlightBG, - paddingVertical: 28, - borderRadius: 16, - marginTop: 32, - ...paddingHorizontal, - }; - }, - - twoFactorLoadingContainer: { - alignItems: 'center', - justifyContent: 'center', - height: 210, - }, - - twoFactorAuthCodesContainer: { - alignItems: 'center', - justifyContent: 'center', - flexDirection: 'row', - flexWrap: 'wrap', - gap: 12, - }, - - twoFactorAuthCode: { - fontFamily: fontFamily.MONOSPACE, - width: 112, - textAlign: 'center', - }, - - twoFactorAuthCodesButtonsContainer: { - flexDirection: 'row', - justifyContent: 'center', - gap: 12, - marginTop: 20, - flexWrap: 'wrap', - }, - - twoFactorAuthCodesButton: { - minWidth: 112, - }, - - twoFactorAuthCopyCodeButton: { - minWidth: 110, - }, - - anonymousRoomFooter: (isSmallSizeLayout: boolean): ViewStyle & TextStyle => ({ - flexDirection: isSmallSizeLayout ? 'column' : 'row', - ...(!isSmallSizeLayout && { + borderBottomWidth: 2, + borderColor: theme.borderLighter, + // Adding browser specefic style to bring consistency between Safari and other platforms. + // Applying the Webkit styles only to browsers as it is not available in native. + ...(Browser.getBrowser() + ? { + WebkitTextFillColor: theme.textSupporting, + WebkitOpacity: 1, + } + : {}), + color: theme.textSupporting, + }, + + uploadReceiptView: (isSmallScreenWidth: boolean) => + ({ + borderRadius: variables.componentBorderRadiusLarge, + borderWidth: isSmallScreenWidth ? 0 : 2, + borderColor: theme.borderFocus, + borderStyle: 'dotted', + marginBottom: 20, + marginLeft: 20, + marginRight: 20, + justifyContent: 'center', + alignItems: 'center', + padding: 40, + gap: 4, + flex: 1, + } satisfies ViewStyle), + + cameraView: { + flex: 1, + overflow: 'hidden', + padding: 10, + borderRadius: 28, + borderStyle: 'solid', + borderWidth: 8, + backgroundColor: theme.highlightBG, + borderColor: theme.appBG, + }, + + permissionView: { + paddingVertical: 108, + paddingHorizontal: 61, alignItems: 'center', - justifyContent: 'space-between', - }), - padding: 20, - backgroundColor: theme.sidebar, - borderRadius: variables.componentBorderRadiusLarge, - overflow: 'hidden', - }), - anonymousRoomFooterWordmarkAndLogoContainer: (isSmallSizeLayout: boolean): ViewStyle => ({ - flexDirection: 'row', - alignItems: 'center', - ...(isSmallSizeLayout && { - justifyContent: 'space-between', - marginTop: 16, - }), - }), - anonymousRoomFooterLogo: { - width: 88, - marginLeft: 0, - height: 20, - }, - anonymousRoomFooterLogoTaglineText: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeMedium, - color: theme.textLight, - }, - signInButtonAvatar: { - width: 80, - }, - - anonymousRoomFooterSignInButton: { - width: 110, - }, - - roomHeaderAvatarSize: { - height: variables.componentSizeLarge, - width: variables.componentSizeLarge, - }, - - roomHeaderAvatar: { - backgroundColor: theme.appBG, - borderRadius: 100, - borderColor: theme.componentBG, - borderWidth: 4, - }, - - roomHeaderAvatarOverlay: { - position: 'absolute', - top: 0, - right: 0, - bottom: 0, - left: 0, - backgroundColor: theme.overlay, - opacity: variables.overlayOpacity, - borderRadius: 88, - }, - - rootNavigatorContainerStyles: (isSmallScreenWidth: boolean): ViewStyle => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1}), - RHPNavigatorContainerNavigatorContainerStyles: (isSmallScreenWidth: boolean): ViewStyle => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1}), - - avatarInnerTextChat: { - color: theme.textLight, - fontSize: variables.fontSizeXLarge, - fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, - textAlign: 'center', - fontWeight: 'normal', - position: 'absolute', - width: 88, - left: -16, - }, - - svgAvatarBorder: { - borderRadius: 100, - overflow: 'hidden', - }, - - displayName: { - fontSize: variables.fontSizeLarge, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - color: theme.heading, - }, - - pageWrapper: { - width: '100%', - alignItems: 'center', - padding: 20, - }, - - avatarSectionWrapper: { - width: '100%', - alignItems: 'center', - paddingHorizontal: 20, - paddingBottom: 20, - }, - - avatarSectionWrapperSkeleton: { - width: '100%', - paddingHorizontal: 20, - paddingBottom: 20, - }, - - selectCircle: { - width: variables.componentSizeSmall, - height: variables.componentSizeSmall, - borderColor: theme.border, - borderWidth: 1, - borderRadius: variables.componentSizeSmall / 2, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: theme.componentBG, - marginLeft: 8, - }, - - unreadIndicatorContainer: { - position: 'absolute', - top: -10, - left: 0, - width: '100%', - height: 20, - paddingHorizontal: 20, - flexDirection: 'row', - alignItems: 'center', - zIndex: 1, - ...cursor.cursorDefault, - }, - - unreadIndicatorLine: { - height: 1, - backgroundColor: theme.unreadIndicator, - flexGrow: 1, - marginRight: 8, - opacity: 0.5, - }, - - threadDividerLine: { - height: 1, - backgroundColor: theme.border, - flexGrow: 1, - marginHorizontal: 20, - }, - - unreadIndicatorText: { - color: theme.unreadIndicator, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontSize: variables.fontSizeSmall, - fontWeight: fontWeightBold, - textTransform: 'capitalize', - }, - - flipUpsideDown: { - transform: [{rotate: '180deg'}], - }, - - navigationSceneContainer: { - backgroundColor: theme.appBG, - }, - - navigationScreenCardStyle: { - backgroundColor: theme.appBG, - height: '100%', - }, - - navigationSceneFullScreenWrapper: { - borderRadius: variables.componentBorderRadiusCard, - overflow: 'hidden', - height: '100%', - }, - - invisible: { - position: 'absolute', - opacity: 0, - }, - - containerWithSpaceBetween: { - justifyContent: 'space-between', - width: '100%', - flex: 1, - }, - - detailsPageSectionContainer: { - alignSelf: 'flex-start', - }, - - attachmentCarouselContainer: { - height: '100%', - width: '100%', - display: 'flex', - justifyContent: 'center', - ...cursor.cursorUnset, - }, - - attachmentArrow: { - zIndex: 23, - position: 'absolute', - }, - - attachmentRevealButtonContainer: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - ...spacing.ph4, - }, - - arrowIcon: { - height: 40, - width: 40, - alignItems: 'center', - paddingHorizontal: 0, - paddingTop: 0, - paddingBottom: 0, - }, - - detailsPageSectionVersion: { - alignSelf: 'center', - color: theme.textSupporting, - fontSize: variables.fontSizeSmall, - height: 24, - lineHeight: 20, - }, - - switchTrack: { - width: 50, - height: 28, - justifyContent: 'center', - borderRadius: 20, - padding: 15, - backgroundColor: theme.success, - }, - - switchInactive: { - backgroundColor: theme.border, - }, - - switchThumb: { - width: 22, - height: 22, - borderRadius: 11, - position: 'absolute', - left: 4, - backgroundColor: theme.appBG, - }, - - switchThumbTransformation: (translateX: AnimatableNumericValue): ViewStyle => ({ - transform: [{translateX}], - }), - - radioButtonContainer: { - backgroundColor: theme.componentBG, - borderRadius: 10, - height: 20, - width: 20, - borderColor: theme.icon, - borderWidth: 1, - justifyContent: 'center', - alignItems: 'center', - }, - - checkboxPressable: { - borderRadius: 6, - padding: 2, - justifyContent: 'center', - alignItems: 'center', - }, - - checkedContainer: { - backgroundColor: theme.checkBox, - }, - - magicCodeInputContainer: { - flexDirection: 'row', - justifyContent: 'space-between', - minHeight: variables.inputHeight, - }, - - magicCodeInput: { - fontSize: variables.fontSizeXLarge, - color: theme.heading, - lineHeight: variables.inputHeight, - }, - - // Manually style transparent, in iOS Safari, an input in a container with its opacity set to - // 0 (completely transparent) cannot handle user interaction, hence the Paste option is never shown - inputTransparent: { - color: 'transparent', - // These properties are available in browser only - ...(Browser.getBrowser() - ? { - caretColor: 'transparent', - WebkitTextFillColor: 'transparent', - // After setting the input text color to transparent, it acquires the background-color. - // However, it is not possible to override the background-color directly as explained in this resource: https://developer.mozilla.org/en-US/docs/Web/CSS/:autofill - // Therefore, the transition effect needs to be delayed. - transitionDelay: '99999s', - } - : {}), - }, - - iouAmountText: { - ...headlineFont, - fontSize: variables.iouAmountTextSize, - color: theme.heading, - lineHeight: variables.inputHeight, - }, - - iouAmountTextInput: addOutlineWidth( - { - ...headlineFont, - fontSize: variables.iouAmountTextSize, + justifyContent: 'center', + }, + + headerAnonymousFooter: { color: theme.heading, - padding: 0, - lineHeight: undefined, + fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, + fontSize: variables.fontSizeXLarge, + lineHeight: variables.lineHeightXXLarge, }, - 0, - ), - - moneyRequestConfirmationAmount: { - ...headlineFont, - fontSize: variables.fontSizeh1, - }, - - moneyRequestMenuItem: { - flexDirection: 'row', - borderRadius: 0, - justifyContent: 'space-between', - width: '100%', - paddingHorizontal: 20, - paddingVertical: 12, - }, - - requestPreviewBox: { - marginTop: 12, - maxWidth: variables.sideBarWidth, - }, - - moneyRequestPreviewBox: { - backgroundColor: theme.cardBG, - borderRadius: variables.componentBorderRadiusLarge, - maxWidth: variables.sideBarWidth, - width: '100%', - }, - - moneyRequestPreviewBoxText: { - padding: 16, - }, - - moneyRequestPreviewBoxLoading: { - // When a new IOU request arrives it is very briefly in a loading state, so set the minimum height of the container to 94 to match the rendered height after loading. - // Otherwise, the IOU request pay button will not be fully visible and the user will have to scroll up to reveal the entire IOU request container. - // See https://github.com/Expensify/App/issues/10283. - minHeight: 94, - width: '100%', - }, - - moneyRequestPreviewBoxAvatar: { - marginRight: -10, - marginBottom: 0, - }, - - moneyRequestPreviewAmount: { - ...headlineFont, - ...whiteSpace.preWrap, - color: theme.heading, - }, - - defaultCheckmarkWrapper: { - marginLeft: 8, - alignSelf: 'center', - }, - - iouDetailsContainer: { - flexGrow: 1, - paddingStart: 20, - paddingEnd: 20, - }, - - codeWordWrapper: { - ...codeStyles.codeWordWrapper, - }, - - codeWordStyle: { - borderLeftWidth: 0, - borderRightWidth: 0, - borderTopLeftRadius: 0, - borderBottomLeftRadius: 0, - borderTopRightRadius: 0, - borderBottomRightRadius: 0, - paddingLeft: 0, - paddingRight: 0, - justifyContent: 'center', - ...codeStyles.codeWordStyle, - }, - - codeFirstWordStyle: { - borderLeftWidth: 1, - borderTopLeftRadius: 4, - borderBottomLeftRadius: 4, - paddingLeft: 5, - }, - - codeLastWordStyle: { - borderRightWidth: 1, - borderTopRightRadius: 4, - borderBottomRightRadius: 4, - paddingRight: 5, - }, - - fullScreenLoading: { - backgroundColor: theme.componentBG, - opacity: 0.8, - justifyContent: 'center', - alignItems: 'center', - zIndex: 10, - }, - - navigatorFullScreenLoading: { - backgroundColor: theme.highlightBG, - opacity: 1, - }, - - reimbursementAccountFullScreenLoading: { - backgroundColor: theme.componentBG, - opacity: 0.8, - justifyContent: 'flex-start', - alignItems: 'center', - zIndex: 10, - }, - - hiddenElementOutsideOfWindow: { - position: 'absolute', - top: -10000, - left: 0, - opacity: 0, - }, - - growlNotificationWrapper: { - zIndex: 2, - }, - - growlNotificationContainer: { - flex: 1, - justifyContent: 'flex-start', - position: 'absolute', - width: '100%', - top: 20, - ...spacing.pl5, - ...spacing.pr5, - }, - - growlNotificationDesktopContainer: { - maxWidth: variables.sideBarWidth, - right: 0, - // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". - position: 'fixed' as ViewStyle['position'], - }, - - growlNotificationTranslateY: (translateY: AnimatableNumericValue): ViewStyle => ({ - transform: [{translateY}], - }), - - makeSlideInTranslation: (translationType: Translation, fromValue: number): CustomAnimation => ({ - from: { - [translationType]: fromValue, - }, - to: { - [translationType]: 0, - }, - }), - - growlNotificationBox: { - backgroundColor: theme.inverse, - borderRadius: variables.componentBorderRadiusNormal, - alignItems: 'center', - flexDirection: 'row', - justifyContent: 'space-between', - shadowColor: theme.shadow, - ...spacing.p5, - }, - - growlNotificationText: { - fontSize: variables.fontSizeNormal, - fontFamily: fontFamily.EXP_NEUE, - width: '90%', - lineHeight: variables.fontSizeNormalHeight, - color: theme.textReversed, - ...spacing.ml4, - }, - - blockquote: { - borderLeftColor: theme.border, - borderLeftWidth: 4, - paddingLeft: 12, - marginVertical: 4, - }, - - noSelect: { - boxShadow: 'none', - outline: 'none', - }, - - cardStyleNavigator: { - overflow: 'hidden', - height: '100%', - }, - - fullscreenCard: { - position: 'absolute', - left: 0, - top: 0, - width: '100%', - height: '100%', - }, - - fullscreenCardWeb: { - left: 'auto', - right: '-24%', - top: '-18%', - height: '120%', - }, - - fullscreenCardWebCentered: { - left: '0', - right: '0', - top: '0', - height: '60%', - }, - - fullscreenCardMobile: { - left: '-20%', - top: '-30%', - width: '150%', - }, - - fullscreenCardMediumScreen: { - left: '-15%', - top: '-30%', - width: '145%', - }, - - smallEditIcon: { - alignItems: 'center', - backgroundColor: theme.buttonHoveredBG, - borderColor: theme.textReversed, - borderRadius: 14, - borderWidth: 3, - color: theme.textReversed, - height: 28, - width: 28, - justifyContent: 'center', - }, - - smallAvatarEditIcon: { - position: 'absolute', - right: -4, - bottom: -4, - }, - - workspaceCard: { - width: '100%', - height: 400, - borderRadius: variables.componentBorderRadiusCard, - overflow: 'hidden', - backgroundColor: theme.heroCard, - }, - - workspaceCardMobile: { - height: 475, - }, - - workspaceCardMediumScreen: { - height: 540, - }, - - workspaceCardMainText: { - fontSize: variables.fontSizeXXXLarge, - fontWeight: 'bold', - lineHeight: variables.fontSizeXXXLarge, - }, - - workspaceCardContent: { - zIndex: 1, - padding: 50, - }, - - workspaceCardContentMediumScreen: { - padding: 25, - }, - - workspaceCardCTA: { - width: 250, - }, - - autoGrowHeightMultilineInput: { - maxHeight: 115, - }, - - peopleRow: { - width: '100%', - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - ...spacing.ph5, - }, - - peopleRowBorderBottom: { - borderColor: theme.border, - borderBottomWidth: 1, - ...spacing.pb2, - }, - - peopleBadge: { - backgroundColor: theme.icon, - ...spacing.ph3, - }, - - peopleBadgeText: { - color: theme.textReversed, - fontSize: variables.fontSizeSmall, - lineHeight: variables.lineHeightNormal, - ...whiteSpace.noWrap, - }, - - offlineFeedback: { - deleted: { - textDecorationLine: 'line-through', - textDecorationStyle: 'solid', - }, - pending: { - opacity: 0.5, + + headerText: { + color: theme.heading, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontSize: variables.fontSizeNormal, + fontWeight: fontWeightBold, + }, + + headerGap: { + height: CONST.DESKTOP_HEADER_PADDING, + }, + + pushTextRight: { + left: 100000, + }, + + reportOptions: { + marginLeft: 8, + }, + + chatItemComposeSecondaryRow: { + height: 15, + marginBottom: 5, + marginTop: 5, + }, + + chatItemComposeSecondaryRowSubText: { + color: theme.textSupporting, + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeSmall, + lineHeight: variables.lineHeightSmall, + }, + + chatItemComposeSecondaryRowOffset: { + marginLeft: variables.chatInputSpacing, + }, + + offlineIndicator: { + marginLeft: variables.chatInputSpacing, + }, + + offlineIndicatorMobile: { + paddingLeft: 20, + paddingTop: 5, + paddingBottom: 5, + }, + + offlineIndicatorRow: { + height: 25, + }, + + // Actions + actionAvatar: { + borderRadius: 20, + }, + + componentHeightLarge: { + height: variables.inputHeight, }, - error: { + + calendarHeader: { + height: 50, flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + paddingHorizontal: 15, + paddingRight: 5, + ...userSelect.userSelectNone, + }, + + calendarDayRoot: { + flex: 1, + height: 45, + justifyContent: 'center', alignItems: 'center', + ...userSelect.userSelectNone, }, - container: { - ...spacing.pv2, + + calendarDayContainer: { + width: 30, + height: 30, + justifyContent: 'center', + alignItems: 'center', + borderRadius: 15, + overflow: 'hidden', }, - textContainer: { - flexDirection: 'column', + + calendarDayContainerSelected: { + backgroundColor: theme.buttonDefaultBG, + }, + + autoGrowHeightInputContainer: (textInputHeight: number, minHeight: number, maxHeight: number) => + ({ + height: lodashClamp(textInputHeight, minHeight, maxHeight), + minHeight, + } satisfies ViewStyle), + + autoGrowHeightHiddenInput: (maxWidth: number, maxHeight?: number) => + ({ + maxWidth, + maxHeight: maxHeight && maxHeight + 1, + overflow: 'hidden', + } satisfies TextStyle), + + textInputContainer: { flex: 1, + justifyContent: 'center', + height: '100%', + backgroundColor: 'transparent', + borderBottomWidth: 2, + borderColor: theme.border, + overflow: 'hidden', }, - text: { + + textInputLabel: { + position: 'absolute', + left: 0, + top: 0, + fontSize: variables.fontSizeNormal, color: theme.textSupporting, - textAlignVertical: 'center', - fontSize: variables.fontSizeLabel, + fontFamily: fontFamily.EXP_NEUE, + width: '100%', }, - errorDot: { - marginRight: 12, + + textInputLabelBackground: { + position: 'absolute', + top: 0, + width: '100%', + height: 23, + backgroundColor: theme.componentBG, }, - }, - - dotIndicatorMessage: { - display: 'flex', - flexDirection: 'row', - alignItems: 'center', - }, - - sidebarPopover: { - width: variables.sideBarWidth - 68, - }, - - cardOverlay: { - backgroundColor: theme.overlay, - position: 'absolute', - top: 0, - left: 0, - width: '100%', - height: '100%', - opacity: variables.overlayOpacity, - }, - - communicationsLinkIcon: { - right: -36, - top: 0, - bottom: 0, - }, - - shortTermsBorder: { - borderWidth: 1, - borderColor: theme.border, - }, - shortTermsHorizontalRule: { - borderBottomWidth: 1, - borderColor: theme.border, - ...spacing.mh3, - }, + textInputLabelDesktop: { + transformOrigin: 'left center', + }, - shortTermsLargeHorizontalRule: { - borderWidth: 1, - borderColor: theme.border, - ...spacing.mh3, - }, - - shortTermsRow: { - flexDirection: 'row', - padding: 12, - }, - - termsCenterRight: { - marginTop: 'auto', - marginBottom: 'auto', - }, - - shortTermsBoldHeadingSection: { - paddingRight: 12, - paddingLeft: 12, - marginTop: 12, - }, - - shortTermsHeadline: { - ...headlineFont, - ...whiteSpace.preWrap, - color: theme.heading, - fontSize: variables.fontSizeXXXLarge, - lineHeight: variables.lineHeightXXXLarge, - }, - - longTermsRow: { - flexDirection: 'row', - marginTop: 20, - }, - - collapsibleSectionBorder: { - borderBottomWidth: 2, - borderBottomColor: theme.border, - }, - - communicationsLinkHeight: { - height: variables.communicationsLinkHeight, - }, - - floatingMessageCounterWrapper: { - position: 'absolute', - left: '50%', - top: 0, - zIndex: 100, - ...visibility.hidden, - }, - - floatingMessageCounterWrapperAndroid: { - left: 0, - width: '100%', - alignItems: 'center', - position: 'absolute', - top: 0, - zIndex: 100, - ...visibility.hidden, - }, - - floatingMessageCounterSubWrapperAndroid: { - left: '50%', - width: 'auto', - }, - - floatingMessageCounter: { - left: '-50%', - ...visibility.visible, - }, - - floatingMessageCounterTransformation: (translateY: AnimatableNumericValue): ViewStyle => ({ - transform: [{translateY}], - }), - - confirmationAnimation: { - height: 180, - width: 180, - marginBottom: 20, - }, - - googleSearchTextInputContainer: { - flexDirection: 'column', - }, - - googleSearchSeparator: { - height: 1, - backgroundColor: theme.border, - }, - - googleSearchText: { - color: theme.text, - fontSize: variables.fontSizeNormal, - lineHeight: variables.fontSizeNormalHeight, - fontFamily: fontFamily.EXP_NEUE, - flex: 1, - }, - - threeDotsPopoverOffset: (windowWidth: number): AnchorPosition => ({ - ...getPopOverVerticalOffset(60), - horizontal: windowWidth - 60, - }), - - threeDotsPopoverOffsetNoCloseButton: (windowWidth: number): AnchorPosition => ({ - ...getPopOverVerticalOffset(60), - horizontal: windowWidth - 10, - }), - - invert: { - // It's important to invert the Y AND X axis to prevent a react native issue that can lead to ANRs on android 13 - transform: [{scaleX: -1}, {scaleY: -1}], - }, - - keyboardShortcutModalContainer: { - maxHeight: '100%', - flex: 0, - flexBasis: 'auto', - }, - - keyboardShortcutTableWrapper: { - alignItems: 'center', - flex: 1, - height: 'auto', - maxHeight: '100%', - }, - - keyboardShortcutTableContainer: { - display: 'flex', - width: '100%', - borderColor: theme.border, - height: 'auto', - borderRadius: variables.componentBorderRadius, - borderWidth: 1, - }, + textInputLabelTransformation: (translateY: AnimatableNumericValue, translateX: AnimatableNumericValue, scale: AnimatableNumericValue) => + ({ + transform: [{translateY}, {translateX}, {scale}], + } satisfies TextStyle), - keyboardShortcutTableRow: { - flex: 1, - flexDirection: 'row', - borderColor: theme.border, - flexBasis: 'auto', - alignSelf: 'stretch', - borderTopWidth: 1, - }, - - keyboardShortcutTablePrefix: { - width: '30%', - borderRightWidth: 1, - borderColor: theme.border, - }, + baseTextInput: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeNormal, + lineHeight: variables.lineHeightXLarge, + color: theme.text, + paddingTop: 23, + paddingBottom: 8, + paddingLeft: 0, + borderWidth: 0, + }, - keyboardShortcutTableFirstRow: { - borderTopWidth: 0, - }, + textInputMultiline: { + scrollPadding: '23px 0 0 0', + }, - iPhoneXSafeArea: { - backgroundColor: theme.inverse, - flex: 1, - }, + textInputMultilineContainer: { + paddingTop: 23, + }, - transferBalancePayment: { - borderWidth: 1, - borderRadius: variables.componentBorderRadiusNormal, - borderColor: theme.border, - }, - - transferBalanceSelectedPayment: { - borderColor: theme.iconSuccessFill, - }, - - transferBalanceBalance: { - fontSize: 48, - }, - - closeAccountMessageInput: { - height: 153, - }, - - imageCropContainer: { - overflow: 'hidden', - alignItems: 'center', - justifyContent: 'center', - backgroundColor: theme.imageCropBackgroundColor, - ...cursor.cursorMove, - }, - - sliderKnobTooltipView: { - height: variables.sliderKnobSize, - width: variables.sliderKnobSize, - borderRadius: variables.sliderKnobSize / 2, - }, - - sliderKnob: { - backgroundColor: theme.success, - position: 'absolute', - height: variables.sliderKnobSize, - width: variables.sliderKnobSize, - borderRadius: variables.sliderKnobSize / 2, - left: -(variables.sliderKnobSize / 2), - ...cursor.cursorPointer, - }, - - sliderBar: { - backgroundColor: theme.border, - height: variables.sliderBarHeight, - borderRadius: variables.sliderBarHeight / 2, - alignSelf: 'stretch', - justifyContent: 'center', - }, - - screenCenteredContainer: { - flex: 1, - justifyContent: 'center', - marginBottom: 40, - padding: 16, - }, - - inlineSystemMessage: { - color: theme.textSupporting, - fontSize: variables.fontSizeLabel, - fontFamily: fontFamily.EXP_NEUE, - marginLeft: 6, - }, - - fullScreen: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - }, - - invisibleOverlay: { - backgroundColor: theme.transparent, - zIndex: 1000, - }, - - reportDropOverlay: { - backgroundColor: theme.dropUIBG, - zIndex: 2, - }, - - receiptDropOverlay: { - backgroundColor: theme.receiptDropUIBG, - zIndex: 2, - }, - - receiptImageWrapper: (receiptImageTopPosition: number): ViewStyle => ({ - position: 'absolute', - top: receiptImageTopPosition, - }), - - cardSection: { - backgroundColor: theme.cardBG, - borderRadius: variables.componentBorderRadiusCard, - marginBottom: 20, - marginHorizontal: 16, - padding: 20, - width: 'auto', - textAlign: 'left', - }, - - cardSectionTitle: { - lineHeight: variables.lineHeightXXLarge, - }, - - cardMenuItem: { - paddingLeft: 8, - paddingRight: 0, - borderRadius: variables.buttonBorderRadius, - height: variables.componentSizeLarge, - alignItems: 'center', - }, - - callRequestSection: { - backgroundColor: theme.appBG, - paddingHorizontal: 0, - paddingBottom: 0, - marginHorizontal: 0, - marginBottom: 0, - }, - - archivedReportFooter: { - borderRadius: variables.componentBorderRadius, - ...wordBreak.breakWord, - }, - - saveButtonPadding: { - paddingLeft: 18, - paddingRight: 18, - }, - - deeplinkWrapperContainer: { - padding: 20, - flex: 1, - alignItems: 'center', - justifyContent: 'center', - backgroundColor: theme.appBG, - }, - - deeplinkWrapperMessage: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - }, - - deeplinkWrapperFooter: { - paddingTop: 80, - paddingBottom: 45, - }, - - emojiReactionBubble: { - borderRadius: 28, - alignItems: 'center', - justifyContent: 'center', - flexDirection: 'row', - alignSelf: 'flex-start', - }, - - emojiReactionListHeader: { - marginTop: 8, - paddingBottom: 20, - borderBottomColor: theme.border, - borderBottomWidth: 1, - marginHorizontal: 20, - }, - emojiReactionListHeaderBubble: { - paddingVertical: 2, - paddingHorizontal: 8, - borderRadius: 28, - backgroundColor: theme.border, - alignItems: 'center', - justifyContent: 'center', - flexDirection: 'row', - alignSelf: 'flex-start', - marginRight: 4, - }, - reactionListItem: { - flexDirection: 'row', - paddingVertical: 12, - paddingHorizontal: 20, - }, - reactionListHeaderText: { - color: theme.textSupporting, - marginLeft: 8, - alignSelf: 'center', - }, - - miniQuickEmojiReactionText: { - fontSize: 15, - lineHeight: 20, - textAlignVertical: 'center', - }, - - emojiReactionBubbleText: { - textAlignVertical: 'center', - }, - - reactionCounterText: { - fontSize: 13, - marginLeft: 4, - fontWeight: 'bold', - }, - - fontColorReactionLabel: { - color: theme.tooltipSupportingText, - }, - - reactionEmojiTitle: { - fontSize: variables.iconSizeLarge, - lineHeight: variables.iconSizeXLarge, - }, - - textReactionSenders: { - color: theme.tooltipPrimaryText, - ...wordBreak.breakWord, - }, - - quickReactionsContainer: { - gap: 12, - flexDirection: 'row', - paddingHorizontal: 25, - paddingVertical: 12, - justifyContent: 'space-between', - }, - - reactionListContainer: { - maxHeight: variables.listItemHeightNormal * 5.75, - ...spacing.pv2, - }, - - reactionListContainerFixedWidth: { - maxWidth: variables.popoverWidth, - }, - - validateCodeDigits: { - color: theme.text, - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeXXLarge, - letterSpacing: 4, - }, + textInputAndIconContainer: { + flex: 1, + height: '100%', + zIndex: -1, + flexDirection: 'row', + }, - footerWrapper: { - fontSize: variables.fontSizeNormal, - paddingTop: 64, - maxWidth: 1100, // Match footer across all Expensify platforms - }, - - footerColumnsContainer: { - flex: 1, - flexWrap: 'wrap', - marginBottom: 40, - marginHorizontal: -16, - }, - - footerTitle: { - fontSize: variables.fontSizeLarge, - color: theme.success, - marginBottom: 16, - }, - - footerRow: { - paddingVertical: 4, - marginBottom: 8, - color: theme.textLight, - fontSize: variables.fontSizeMedium, - }, - - footerBottomLogo: { - marginTop: 40, - width: '100%', - }, - - listPickerSeparator: { - height: 1, - backgroundColor: theme.buttonDefaultBG, - }, - - datePickerRoot: { - position: 'relative', - zIndex: 99, - }, - - datePickerPopover: { - backgroundColor: theme.appBG, - width: '100%', - alignSelf: 'center', - zIndex: 100, - marginTop: 8, - }, - - loginHeroHeader: { - fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, - color: theme.success, - fontWeight: '500', - textAlign: 'center', - }, - - newKansasLarge: { - ...headlineFont, - fontSize: variables.fontSizeXLarge, - lineHeight: variables.lineHeightXXLarge, - }, - - loginHeroBody: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeSignInHeroBody, - color: theme.textLight, - textAlign: 'center', - }, - - linkPreviewWrapper: { - marginTop: 16, - borderLeftWidth: 4, - borderLeftColor: theme.border, - paddingLeft: 12, - }, - - linkPreviewImage: { - flex: 1, - resizeMode: 'contain', - borderRadius: 8, - marginTop: 8, - }, - - linkPreviewLogoImage: { - height: 16, - width: 16, - }, - - validateCodeMessage: { - width: variables.modalContentMaxWidth, - textAlign: 'center', - }, - - whisper: { - backgroundColor: theme.cardBG, - }, - - contextMenuItemPopoverMaxWidth: { - maxWidth: 375, - }, - - formSpaceVertical: { - height: 20, - width: 1, - }, - - taskCheckbox: { - height: 16, - width: 16, - }, - - taskTitleMenuItem: { - ...writingDirection.ltr, - ...headlineFont, - fontSize: variables.fontSizeXLarge, - maxWidth: '100%', - ...wordBreak.breakWord, - }, - - taskDescriptionMenuItem: { - maxWidth: '100%', - ...wordBreak.breakWord, - }, - - taskTitleDescription: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeLabel, - color: theme.textSupporting, - lineHeight: variables.lineHeightNormal, - ...spacing.mb1, - }, - - taskMenuItemCheckbox: { - height: 27, - ...spacing.mr3, - }, - - reportHorizontalRule: { - borderBottomWidth: 1, - borderColor: theme.border, - ...spacing.mh5, - ...spacing.mv2, - }, - - assigneeTextStyle: { - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - minHeight: variables.avatarSizeSubscript, - }, - - taskRightIconContainer: { - width: variables.componentSizeNormal, - marginLeft: 'auto', - ...spacing.mt1, - ...pointerEventsAuto, - }, - - shareCodePage: { - paddingHorizontal: 38.5, - }, - - shareCodeContainer: { - width: '100%', - alignItems: 'center', - paddingHorizontal: variables.qrShareHorizontalPadding, - paddingVertical: 20, - borderRadius: 20, - overflow: 'hidden', - borderColor: theme.borderFocus, - borderWidth: 2, - backgroundColor: theme.highlightBG, - }, - - splashScreenHider: { - backgroundColor: theme.splashBG, - alignItems: 'center', - justifyContent: 'center', - }, - - headerEnvBadge: { - marginLeft: 0, - marginBottom: 2, - height: 12, - paddingLeft: 4, - paddingRight: 4, - alignItems: 'center', - }, - - headerEnvBadgeText: { - fontSize: 7, - fontWeight: fontWeightBold, - lineHeight: undefined, - }, - - expensifyQrLogo: { - alignSelf: 'stretch', - height: 27, - marginBottom: 20, - }, - - qrShareTitle: { - marginTop: 15, - textAlign: 'center', - }, - - loginButtonRow: { - justifyContent: 'center', - width: '100%', - ...flex.flexRow, - }, - - loginButtonRowSmallScreen: { - justifyContent: 'center', - width: '100%', - marginBottom: 10, - ...flex.flexRow, - }, - - appleButtonContainer: { - width: 40, - height: 40, - marginRight: 20, - }, - - signInIconButton: { - margin: 10, - marginTop: 0, - padding: 2, - }, - - googleButtonContainer: { - colorScheme: 'light', - width: 40, - height: 40, - marginLeft: 12, - alignItems: 'center', - overflow: 'hidden', - }, - - googlePillButtonContainer: { - colorScheme: 'light', - height: 40, - width: 219, - }, - - thirdPartyLoadingContainer: { - alignItems: 'center', - justifyContent: 'center', - height: 450, - }, - - tabSelectorButton: { - height: variables.tabSelectorButtonHeight, - padding: variables.tabSelectorButtonPadding, - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - borderRadius: variables.buttonBorderRadius, - }, - - tabSelector: { - flexDirection: 'row', - paddingHorizontal: 20, - paddingBottom: 12, - }, - - tabText: (isSelected: boolean): TextStyle => ({ - marginLeft: 8, - fontFamily: isSelected ? fontFamily.EXP_NEUE_BOLD : fontFamily.EXP_NEUE, - fontWeight: isSelected ? fontWeightBold : '400', - color: isSelected ? theme.textLight : theme.textSupporting, - }), - - overscrollSpacer: (backgroundColor: string, height: number): ViewStyle => ({ - backgroundColor, - height, - width: '100%', - position: 'absolute', - top: -height, - left: 0, - right: 0, - }), - - dualColorOverscrollSpacer: { - position: 'absolute', - top: 0, - left: 0, - width: '100%', - height: '100%', - zIndex: -1, - }, - - willChangeTransform: { - willChange: 'transform', - }, - - dropDownButtonCartIconContainerPadding: { - paddingRight: 0, - paddingLeft: 0, - }, - - dropDownButtonArrowContain: { - marginLeft: 12, - marginRight: 14, - }, - - dropDownButtonCartIconView: { - borderTopRightRadius: variables.buttonBorderRadius, - borderBottomRightRadius: variables.buttonBorderRadius, - ...flex.flexRow, - ...flex.alignItemsCenter, - }, - - emojiPickerButtonDropdown: { - justifyContent: 'center', - backgroundColor: theme.activeComponentBG, - width: 86, - height: 52, - borderRadius: 26, - alignItems: 'center', - paddingLeft: 10, - paddingRight: 4, - marginBottom: 32, - alignSelf: 'flex-start', - }, - - emojiPickerButtonDropdownIcon: { - fontSize: 30, - }, - - moneyRequestImage: { - height: 200, - borderRadius: 16, - margin: 20, - }, - - reportPreviewBox: { - backgroundColor: theme.cardBG, - borderRadius: variables.componentBorderRadiusLarge, - maxWidth: variables.sideBarWidth, - width: '100%', - }, - - reportPreviewBoxHoverBorder: { - borderColor: theme.border, - backgroundColor: theme.border, - }, - - reportPreviewBoxBody: { - padding: 16, - }, - - reportActionItemImages: { - flexDirection: 'row', - borderWidth: 4, - borderColor: theme.transparent, - borderTopLeftRadius: variables.componentBorderRadiusLarge, - borderTopRightRadius: variables.componentBorderRadiusLarge, - borderBottomLeftRadius: variables.componentBorderRadiusLarge, - borderBottomRightRadius: variables.componentBorderRadiusLarge, - overflow: 'hidden', - height: 200, - }, - - reportActionItemImage: { - flex: 1, - width: '100%', - height: '100%', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - }, - - reportActionItemImageBorder: { - borderRightWidth: 2, - borderColor: theme.cardBG, - }, - - reportActionItemImagesMore: { - position: 'absolute', - borderRadius: 18, - backgroundColor: theme.cardBG, - width: 36, - height: 36, - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - }, - - moneyRequestHeaderStatusBarBadge: { - paddingHorizontal: 8, - borderRadius: variables.componentBorderRadiusSmall, - height: variables.inputHeightSmall, - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - backgroundColor: theme.border, - marginRight: 12, - }, - - staticHeaderImage: { - minHeight: 240, - }, - - emojiPickerButtonDropdownContainer: { - flexDirection: 'row', - alignItems: 'center', - }, - - rotate90: { - transform: [{rotate: '90deg'}], - }, - - emojiStatusLHN: { - fontSize: 22, - }, - sidebarStatusAvatarContainer: { - height: 44, - width: 84, - backgroundColor: theme.componentBG, - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - borderRadius: 42, - paddingHorizontal: 2, - marginVertical: -2, - marginRight: -2, - }, - sidebarStatusAvatar: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - }, - - moneyRequestViewImage: { - ...spacing.mh5, - ...spacing.mv3, - overflow: 'hidden', - borderWidth: 2, - borderColor: theme.cardBG, - borderRadius: variables.componentBorderRadiusLarge, - height: 200, - maxWidth: 400, - }, - - distanceRequestContainer: (maxHeight: number): ViewStyle => ({ - ...flex.flexShrink2, - minHeight: variables.optionRowHeight * 2, - maxHeight, - }), - - mapViewContainer: { - ...flex.flex1, - ...spacing.p4, - minHeight: 300, - maxHeight: 500, - }, - - mapView: { - flex: 1, - borderRadius: 20, - overflow: 'hidden', - }, - - mapViewOverlay: { - flex: 1, - position: 'absolute', - left: 0, - top: 0, - borderRadius: variables.componentBorderRadiusLarge, - overflow: 'hidden', - backgroundColor: theme.highlightBG, - ...sizing.w100, - ...sizing.h100, - }, - - confirmationListMapItem: { - ...spacing.m5, - height: 200, - }, - - mapDirection: { - lineColor: theme.success, - lineWidth: 7, - }, - - mapDirectionLayer: { - layout: {'line-join': 'round', 'line-cap': 'round'}, - paint: {'line-color': theme.success, 'line-width': 7}, - }, - - mapPendingView: { - backgroundColor: theme.highlightBG, - ...flex.flex1, - borderRadius: variables.componentBorderRadiusLarge, - }, - userReportStatusEmoji: { - fontSize: variables.fontSizeNormal, - marginRight: 4, - }, - draggableTopBar: { - height: 30, - width: '100%', - }, -}); + textInputDesktop: addOutlineWidth({}, 0), + + textInputIconContainer: { + paddingHorizontal: 11, + justifyContent: 'center', + margin: 1, + }, + + secureInput: { + borderTopRightRadius: 0, + borderBottomRightRadius: 0, + }, + + textInput: { + backgroundColor: 'transparent', + borderRadius: variables.componentBorderRadiusNormal, + height: variables.inputComponentSizeNormal, + borderColor: theme.border, + borderWidth: 1, + color: theme.text, + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeNormal, + paddingLeft: 12, + paddingRight: 12, + paddingTop: 10, + paddingBottom: 10, + textAlignVertical: 'center', + }, + + textInputPrefixWrapper: { + position: 'absolute', + left: 0, + top: 0, + height: variables.inputHeight, + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + paddingTop: 23, + paddingBottom: 8, + }, + + textInputPrefix: { + color: theme.text, + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeNormal, + textAlignVertical: 'center', + }, + + pickerContainer: { + borderBottomWidth: 2, + paddingLeft: 0, + borderStyle: 'solid', + borderColor: theme.border, + justifyContent: 'center', + backgroundColor: 'transparent', + height: variables.inputHeight, + overflow: 'hidden', + }, + + pickerContainerSmall: { + height: variables.inputHeightSmall, + }, + + pickerLabel: { + position: 'absolute', + left: 0, + top: 6, + zIndex: 1, + }, + + picker: (disabled = false, backgroundColor = theme.appBG) => + ({ + iconContainer: { + top: Math.round(variables.inputHeight * 0.5) - 11, + right: 0, + ...pointerEventsNone, + }, + + inputWeb: { + appearance: 'none', + ...(disabled ? cursor.cursorDisabled : cursor.cursorPointer), + ...picker(theme), + backgroundColor, + }, + + inputIOS: { + ...picker(theme), + }, + done: { + color: theme.text, + }, + doneDepressed: { + fontSize: 17, + }, + modalViewMiddle: { + backgroundColor: theme.border, + borderTopWidth: 0, + }, + modalViewBottom: { + backgroundColor: theme.highlightBG, + }, + + inputAndroid: { + ...picker(theme), + }, + } satisfies CustomPickerStyle), + + disabledText: { + color: theme.icon, + }, + + inputDisabled: { + backgroundColor: theme.highlightBG, + color: theme.icon, + }, + + noOutline: addOutlineWidth({}, 0), + + errorOutline: { + borderColor: theme.danger, + }, + + textLabelSupporting: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeLabel, + color: theme.textSupporting, + }, + + textLabelError: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeLabel, + color: theme.textError, + }, + + textReceiptUpload: { + ...headlineFont, + fontSize: variables.fontSizeXLarge, + color: theme.textLight, + textAlign: 'center', + }, + + subTextReceiptUpload: { + fontFamily: fontFamily.EXP_NEUE, + lineHeight: variables.lineHeightLarge, + textAlign: 'center', + color: theme.textLight, + }, + + furtherDetailsText: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeSmall, + color: theme.textSupporting, + }, + + lh16: { + lineHeight: 16, + }, + + lh20: { + lineHeight: 20, + }, + + lh140Percent: { + lineHeight: '140%', + }, + + formHelp: { + color: theme.textSupporting, + fontSize: variables.fontSizeLabel, + lineHeight: variables.lineHeightLarge, + marginBottom: 4, + }, + + formError: { + color: theme.textError, + fontSize: variables.fontSizeLabel, + lineHeight: variables.formErrorLineHeight, + marginBottom: 4, + }, + + formSuccess: { + color: theme.success, + fontSize: variables.fontSizeLabel, + lineHeight: 18, + marginBottom: 4, + }, + + desktopRedirectPage: { + backgroundColor: theme.appBG, + minHeight: '100%', + flex: 1, + alignItems: 'center', + }, + + signInPage: { + backgroundColor: theme.highlightBG, + minHeight: '100%', + flex: 1, + }, + + signInPageHeroCenter: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + justifyContent: 'center', + alignItems: 'center', + }, + + signInPageGradient: { + height: '100%', + width: 540, + position: 'absolute', + top: 0, + left: 0, + }, + + signInPageGradientMobile: { + height: 300, + width: 800, + position: 'absolute', + top: 0, + left: 0, + }, + + signInBackground: { + position: 'absolute', + bottom: 0, + left: 0, + minHeight: 700, + }, + + signInPageInner: { + marginLeft: 'auto', + marginRight: 'auto', + height: '100%', + width: '100%', + }, + + signInPageContentTopSpacer: { + maxHeight: 132, + minHeight: 24, + }, + + signInPageContentTopSpacerSmallScreens: { + maxHeight: 132, + minHeight: 45, + }, + + signInPageLeftContainer: { + paddingLeft: 40, + paddingRight: 40, + }, + + signInPageLeftContainerWide: { + maxWidth: variables.sideBarWidth, + }, + + signInPageWelcomeFormContainer: { + maxWidth: CONST.SIGN_IN_FORM_WIDTH, + }, + + signInPageWelcomeTextContainer: { + width: CONST.SIGN_IN_FORM_WIDTH, + }, + + changeExpensifyLoginLinkContainer: { + flexDirection: 'row', + flexWrap: 'wrap', + ...wordBreak.breakWord, + }, + + // Sidebar Styles + sidebar: { + backgroundColor: theme.sidebar, + height: '100%', + }, + + sidebarAnimatedWrapperContainer: { + height: '100%', + position: 'absolute', + }, + + sidebarFooter: { + alignItems: 'center', + display: 'flex', + justifyContent: 'center', + paddingVertical: variables.lineHeightXLarge, + width: '100%', + }, + + sidebarAvatar: { + backgroundColor: theme.icon, + borderRadius: 20, + height: variables.componentSizeNormal, + width: variables.componentSizeNormal, + }, + + statusIndicator: (backgroundColor = theme.danger) => + ({ + borderColor: theme.sidebar, + backgroundColor, + borderRadius: 8, + borderWidth: 2, + position: 'absolute', + right: -2, + top: -1, + height: 16, + width: 16, + zIndex: 10, + } satisfies ViewStyle), + + floatingActionButtonContainer: { + position: 'absolute', + right: 20, + + // The bottom of the floating action button should align with the bottom of the compose box. + // The value should be equal to the height + marginBottom + marginTop of chatItemComposeSecondaryRow + bottom: 25, + }, + + floatingActionButton: { + backgroundColor: theme.success, + height: variables.componentSizeLarge, + width: variables.componentSizeLarge, + borderRadius: 999, + alignItems: 'center', + justifyContent: 'center', + }, + + sidebarFooterUsername: { + color: theme.heading, + fontSize: variables.fontSizeLabel, + fontWeight: '700', + width: 200, + textOverflow: 'ellipsis', + overflow: 'hidden', + ...whiteSpace.noWrap, + }, + + sidebarFooterLink: { + color: theme.textSupporting, + fontSize: variables.fontSizeSmall, + textDecorationLine: 'none', + fontFamily: fontFamily.EXP_NEUE, + lineHeight: 20, + }, + + sidebarListContainer: { + scrollbarWidth: 'none', + paddingBottom: 4, + }, + + sidebarListItem: { + justifyContent: 'center', + textDecorationLine: 'none', + }, + + RHPNavigatorContainer: (isSmallScreenWidth: boolean) => + ({ + width: isSmallScreenWidth ? '100%' : variables.sideBarWidth, + position: 'absolute', + right: 0, + height: '100%', + } satisfies ViewStyle), + + onlyEmojisText: { + fontSize: variables.fontSizeOnlyEmojis, + lineHeight: variables.fontSizeOnlyEmojisHeight, + }, + + onlyEmojisTextLineHeight: { + lineHeight: variables.fontSizeOnlyEmojisHeight, + }, + + createMenuPositionSidebar: (windowHeight: number) => + ({ + horizontal: 18, + vertical: windowHeight - 100, + } satisfies AnchorPosition), + + createMenuPositionProfile: (windowWidth: number) => + ({ + horizontal: windowWidth - 355, + ...getPopOverVerticalOffset(162), + } satisfies AnchorPosition), + + createMenuPositionReportActionCompose: (windowHeight: number) => + ({ + horizontal: 18 + variables.sideBarWidth, + vertical: windowHeight - 83, + } satisfies AnchorPosition), + + createMenuPositionRightSidepane: { + right: 18, + bottom: 75, + }, + + createMenuContainer: { + width: variables.sideBarWidth - 40, + paddingVertical: 12, + }, + + createMenuHeaderText: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeLabel, + color: theme.heading, + }, + + popoverMenuItem: { + flexDirection: 'row', + borderRadius: 0, + paddingHorizontal: 20, + paddingVertical: 12, + justifyContent: 'space-between', + width: '100%', + }, + + popoverMenuIcon: { + width: variables.componentSizeNormal, + justifyContent: 'center', + alignItems: 'center', + }, + + popoverMenuText: { + fontSize: variables.fontSizeNormal, + color: theme.heading, + }, + + popoverInnerContainer: { + paddingTop: 0, // adjusting this because the mobile modal adds additional padding that we don't need for our layout + maxHeight: '95%', + }, + + menuItemTextContainer: { + minHeight: variables.componentSizeNormal, + }, + + chatLinkRowPressable: { + minWidth: 0, + textDecorationLine: 'none', + flex: 1, + }, + + sidebarLink: { + textDecorationLine: 'none', + }, + + sidebarLinkInner: { + alignItems: 'center', + flexDirection: 'row', + paddingLeft: 20, + paddingRight: 20, + }, + + sidebarLinkText: { + color: theme.textSupporting, + fontSize: variables.fontSizeNormal, + textDecorationLine: 'none', + overflow: 'hidden', + }, + + sidebarLinkHover: { + backgroundColor: theme.sidebarHover, + }, + + sidebarLinkActive: { + backgroundColor: theme.border, + textDecorationLine: 'none', + }, + + sidebarLinkTextBold: { + fontWeight: '700', + color: theme.heading, + }, + + sidebarLinkActiveText: { + color: theme.textSupporting, + fontSize: variables.fontSizeNormal, + textDecorationLine: 'none', + overflow: 'hidden', + }, + + optionItemAvatarNameWrapper: { + minWidth: 0, + flex: 1, + }, + + optionDisplayName: { + fontFamily: fontFamily.EXP_NEUE, + minHeight: variables.alternateTextHeight, + lineHeight: variables.lineHeightXLarge, + ...whiteSpace.noWrap, + }, + + optionDisplayNameCompact: { + minWidth: 'auto', + flexBasis: 'auto', + flexGrow: 0, + flexShrink: 1, + }, + + displayNameTooltipEllipsis: { + position: 'absolute', + opacity: 0, + right: 0, + bottom: 0, + }, + + optionAlternateText: { + minHeight: variables.alternateTextHeight, + lineHeight: variables.lineHeightXLarge, + }, + + optionAlternateTextCompact: { + flexShrink: 1, + flexGrow: 1, + flexBasis: 'auto', + ...optionAlternateTextPlatformStyles, + }, + + optionRow: { + minHeight: variables.optionRowHeight, + paddingTop: 12, + paddingBottom: 12, + }, + + optionRowSelected: { + backgroundColor: theme.activeComponentBG, + }, + + optionRowDisabled: { + color: theme.textSupporting, + }, + + optionRowCompact: { + height: variables.optionRowHeightCompact, + paddingTop: 12, + paddingBottom: 12, + }, + + optionsListSectionHeader: { + height: variables.optionsListSectionHeaderHeight, + }, + + overlayStyles: (current: OverlayStylesParams) => + ({ + // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". + position: 'fixed' as ViewStyle['position'], + + // We need to stretch the overlay to cover the sidebar and the translate animation distance. + left: -2 * variables.sideBarWidth, + top: 0, + bottom: 0, + right: 0, + backgroundColor: theme.shadow, + opacity: current.progress.interpolate({ + inputRange: [0, 1], + outputRange: [0, variables.overlayOpacity], + extrapolate: 'clamp', + }), + } satisfies ViewStyle), + + appContent: { + backgroundColor: theme.appBG, + overflow: 'hidden', + }, + + appContentHeader: { + height: variables.contentHeaderHeight, + justifyContent: 'center', + display: 'flex', + paddingRight: 20, + }, + + appContentHeaderTitle: { + alignItems: 'center', + flexDirection: 'row', + }, + + LHNToggle: { + alignItems: 'center', + height: variables.contentHeaderHeight, + justifyContent: 'center', + paddingRight: 10, + paddingLeft: 20, + }, + + LHNToggleIcon: { + height: 15, + width: 18, + }, + + chatContent: { + flex: 4, + justifyContent: 'flex-end', + }, + + chatContentScrollView: { + flexGrow: 1, + justifyContent: 'flex-start', + paddingBottom: 16, + }, + + // Chat Item + chatItem: { + display: 'flex', + flexDirection: 'row', + paddingTop: 8, + paddingBottom: 8, + paddingLeft: 20, + paddingRight: 20, + }, + + chatItemRightGrouped: { + flexGrow: 1, + flexShrink: 1, + flexBasis: 0, + position: 'relative', + marginLeft: variables.chatInputSpacing, + }, + + chatItemRight: { + flexGrow: 1, + flexShrink: 1, + flexBasis: 0, + position: 'relative', + }, + + chatItemMessageHeader: { + alignItems: 'center', + display: 'flex', + flexDirection: 'row', + flexWrap: 'nowrap', + }, + + chatItemMessageHeaderSender: { + color: theme.heading, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontSize: variables.fontSizeNormal, + fontWeight: fontWeightBold, + lineHeight: variables.lineHeightXLarge, + ...wordBreak.breakWord, + }, + + chatItemMessageHeaderTimestamp: { + flexShrink: 0, + color: theme.textSupporting, + fontSize: variables.fontSizeSmall, + paddingTop: 2, + }, + + chatItemMessage: { + color: theme.text, + fontSize: variables.fontSizeNormal, + fontFamily: fontFamily.EXP_NEUE, + lineHeight: variables.lineHeightXLarge, + maxWidth: '100%', + ...cursor.cursorAuto, + ...whiteSpace.preWrap, + ...wordBreak.breakWord, + }, + + chatItemComposeWithFirstRow: { + minHeight: 90, + }, + + chatItemFullComposeRow: { + ...sizing.h100, + }, + + chatItemComposeBoxColor: { + borderColor: theme.border, + }, + + chatItemComposeBoxFocusedColor: { + borderColor: theme.borderFocus, + }, + + chatItemComposeBox: { + backgroundColor: theme.componentBG, + borderWidth: 1, + borderRadius: variables.componentBorderRadiusRounded, + minHeight: variables.componentSizeMedium, + }, + + chatItemFullComposeBox: { + ...flex.flex1, + ...sizing.h100, + }, + + chatFooter: { + paddingLeft: 20, + paddingRight: 20, + display: 'flex', + backgroundColor: theme.appBG, + }, + + chatFooterFullCompose: { + flex: 1, + }, + + chatItemDraft: { + display: 'flex', + flexDirection: 'row', + paddingTop: 8, + paddingBottom: 8, + paddingLeft: 20, + paddingRight: 20, + }, + + chatItemReactionsDraftRight: { + marginLeft: 52, + }, + chatFooterAtTheTop: { + flexGrow: 1, + justifyContent: 'flex-start', + }, + + // Be extremely careful when editing the compose styles, as it is easy to introduce regressions. + // Make sure you run the following tests against any changes: #12669 + textInputCompose: addOutlineWidth( + { + backgroundColor: theme.componentBG, + borderColor: theme.border, + color: theme.text, + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeNormal, + borderWidth: 0, + height: 'auto', + lineHeight: variables.lineHeightXLarge, + ...overflowXHidden, + + // On Android, multiline TextInput with height: 'auto' will show extra padding unless they are configured with + // paddingVertical: 0, alignSelf: 'center', and textAlignVertical: 'center' + + paddingHorizontal: variables.avatarChatSpacing, + paddingTop: 0, + paddingBottom: 0, + alignSelf: 'center', + textAlignVertical: 'center', + }, + 0, + ), + + textInputFullCompose: { + alignSelf: 'stretch', + flex: 1, + maxHeight: '100%', + textAlignVertical: 'top', + }, + + editInputComposeSpacing: { + backgroundColor: theme.transparent, + marginVertical: 8, + }, + + // composer padding should not be modified unless thoroughly tested against the cases in this PR: #12669 + textInputComposeSpacing: { + paddingVertical: 5, + ...flex.flexRow, + flex: 1, + }, + + textInputComposeBorder: { + borderLeftWidth: 1, + borderColor: theme.border, + }, + + chatItemSubmitButton: { + alignSelf: 'flex-end', + borderRadius: variables.componentBorderRadiusRounded, + backgroundColor: theme.transparent, + height: 40, + padding: 10, + margin: 3, + justifyContent: 'center', + }, + + emojiPickerContainer: { + backgroundColor: theme.componentBG, + }, + + emojiHeaderContainer: { + backgroundColor: theme.componentBG, + display: 'flex', + height: CONST.EMOJI_PICKER_HEADER_HEIGHT, + justifyContent: 'center', + width: '100%', + }, + + emojiSkinToneTitle: { + width: '100%', + ...spacing.pv1, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontWeight: fontWeightBold, + color: theme.heading, + fontSize: variables.fontSizeSmall, + }, + + // Emoji Picker Styles + emojiText: { + textAlign: 'center', + fontSize: variables.emojiSize, + ...spacing.pv0, + ...spacing.ph0, + lineHeight: variables.emojiLineHeight, + }, + + emojiItem: { + width: '12.5%', + textAlign: 'center', + borderRadius: 8, + paddingTop: 2, + paddingBottom: 2, + height: CONST.EMOJI_PICKER_ITEM_HEIGHT, + }, + + emojiItemHighlighted: { + transition: '0.2s ease', + backgroundColor: theme.buttonDefaultBG, + }, + + emojiItemKeyboardHighlighted: { + transition: '0.2s ease', + borderWidth: 1, + borderColor: theme.link, + borderRadius: variables.buttonBorderRadius, + }, + + categoryShortcutButton: { + flex: 1, + borderRadius: 8, + height: CONST.EMOJI_PICKER_ITEM_HEIGHT, + alignItems: 'center', + justifyContent: 'center', + }, + + chatItemEmojiButton: { + alignSelf: 'flex-end', + borderRadius: variables.buttonBorderRadius, + height: 40, + marginVertical: 3, + paddingHorizontal: 10, + justifyContent: 'center', + }, + + editChatItemEmojiWrapper: { + marginRight: 3, + alignSelf: 'flex-end', + }, + + hoveredButton: { + backgroundColor: theme.buttonHoveredBG, + }, + + composerSizeButton: { + alignSelf: 'center', + height: 32, + width: 32, + padding: 6, + margin: 3, + borderRadius: variables.componentBorderRadiusRounded, + backgroundColor: theme.transparent, + justifyContent: 'center', + }, + + chatItemAttachmentPlaceholder: { + backgroundColor: theme.sidebar, + borderColor: theme.border, + borderWidth: 1, + borderRadius: variables.componentBorderRadiusNormal, + height: 150, + textAlign: 'center', + verticalAlign: 'middle', + width: 200, + }, + + chatSwticherPillWrapper: { + marginTop: 5, + marginRight: 4, + }, + + navigationModalOverlay: { + ...userSelect.userSelectNone, + position: 'absolute', + width: '100%', + height: '100%', + transform: [ + { + translateX: -variables.sideBarWidth, + }, + ], + }, + + sidebarVisible: { + borderRightWidth: 1, + }, + + sidebarHidden: { + width: 0, + borderRightWidth: 0, + }, + + exampleCheckImage: { + width: '100%', + height: 80, + borderColor: theme.border, + borderWidth: 1, + borderRadius: variables.componentBorderRadiusNormal, + }, + + singleAvatar: { + height: 24, + width: 24, + backgroundColor: theme.icon, + borderRadius: 24, + }, + + singleSubscript: { + height: variables.iconSizeNormal, + width: variables.iconSizeNormal, + backgroundColor: theme.icon, + borderRadius: 20, + zIndex: 1, + }, + + singleAvatarSmall: { + height: 18, + width: 18, + backgroundColor: theme.icon, + borderRadius: 18, + }, + + secondAvatar: { + position: 'absolute', + right: -18, + bottom: -18, + borderWidth: 3, + borderRadius: 30, + borderColor: 'transparent', + }, + + secondAvatarSmall: { + position: 'absolute', + right: -13, + bottom: -13, + borderWidth: 3, + borderRadius: 18, + borderColor: 'transparent', + }, + + secondAvatarSubscript: { + position: 'absolute', + right: -6, + bottom: -6, + }, + + secondAvatarSubscriptCompact: { + position: 'absolute', + bottom: -1, + right: -1, + }, + + secondAvatarSubscriptSmallNormal: { + position: 'absolute', + bottom: 0, + right: 0, + }, + + leftSideLargeAvatar: { + left: 15, + }, + + rightSideLargeAvatar: { + right: 15, + zIndex: 2, + borderWidth: 4, + borderRadius: 100, + }, + + secondAvatarInline: { + bottom: -3, + right: -25, + borderWidth: 3, + borderRadius: 18, + borderColor: theme.cardBorder, + backgroundColor: theme.appBG, + }, + + avatarLarge: { + width: variables.avatarSizeLarge, + height: variables.avatarSizeLarge, + }, + + avatarNormal: { + height: variables.componentSizeNormal, + width: variables.componentSizeNormal, + borderRadius: variables.componentSizeNormal, + }, + + avatarSmall: { + height: variables.avatarSizeSmall, + width: variables.avatarSizeSmall, + borderRadius: variables.avatarSizeSmall, + }, + + avatarInnerText: { + color: theme.textLight, + fontSize: variables.fontSizeSmall, + lineHeight: undefined, + marginLeft: -3, + textAlign: 'center', + }, + + avatarInnerTextSmall: { + color: theme.textLight, + fontSize: variables.fontSizeExtraSmall, + lineHeight: undefined, + marginLeft: -2, + textAlign: 'center', + }, + + avatarSpace: { + top: 3, + left: 3, + }, + + avatar: { + backgroundColor: theme.sidebar, + borderColor: theme.sidebar, + }, + + focusedAvatar: { + backgroundColor: theme.border, + borderColor: theme.border, + }, + + emptyAvatar: { + height: variables.avatarSizeNormal, + width: variables.avatarSizeNormal, + }, + + emptyAvatarSmallNormal: { + height: variables.avatarSizeSmallNormal, + width: variables.avatarSizeSmallNormal, + }, + + emptyAvatarSmall: { + height: variables.avatarSizeSmall, + width: variables.avatarSizeSmall, + }, + + emptyAvatarSmaller: { + height: variables.avatarSizeSmaller, + width: variables.avatarSizeSmaller, + }, + + emptyAvatarMedium: { + height: variables.avatarSizeMedium, + width: variables.avatarSizeMedium, + }, + + emptyAvatarLarge: { + height: variables.avatarSizeLarge, + width: variables.avatarSizeLarge, + }, + + emptyAvatarMargin: { + marginRight: variables.avatarChatSpacing, + }, + + emptyAvatarMarginChat: { + marginRight: variables.avatarChatSpacing - 12, + }, + + emptyAvatarMarginSmall: { + marginRight: variables.avatarChatSpacing - 4, + }, + + emptyAvatarMarginSmaller: { + marginRight: variables.avatarChatSpacing - 4, + }, + + modalViewContainer: { + alignItems: 'center', + flex: 1, + }, + + borderTop: { + borderTopWidth: variables.borderTopWidth, + borderColor: theme.border, + }, + + borderTopRounded: { + borderTopWidth: 1, + borderColor: theme.border, + borderTopLeftRadius: variables.componentBorderRadiusNormal, + borderTopRightRadius: variables.componentBorderRadiusNormal, + }, + + borderBottomRounded: { + borderBottomWidth: 1, + borderColor: theme.border, + borderBottomLeftRadius: variables.componentBorderRadiusNormal, + borderBottomRightRadius: variables.componentBorderRadiusNormal, + }, + + borderBottom: { + borderBottomWidth: 1, + borderColor: theme.border, + }, + + borderNone: { + borderWidth: 0, + borderBottomWidth: 0, + }, + + borderRight: { + borderRightWidth: 1, + borderColor: theme.border, + }, + + borderLeft: { + borderLeftWidth: 1, + borderColor: theme.border, + }, + + pointerEventsNone, + + pointerEventsAuto, + + headerBar: { + overflow: 'hidden', + justifyContent: 'center', + display: 'flex', + paddingLeft: 20, + height: variables.contentHeaderHeight, + width: '100%', + }, + + imageViewContainer: { + width: '100%', + height: '100%', + alignItems: 'center', + justifyContent: 'center', + }, + + imageModalPDF: { + flex: 1, + backgroundColor: theme.modalBackground, + }, + + PDFView: { + // `display: grid` is not supported in native platforms! + // It's being used on Web/Desktop only to vertically center short PDFs, + // while preventing the overflow of the top of long PDF files. + ...display.dGrid, + backgroundColor: theme.modalBackground, + width: '100%', + height: '100%', + justifyContent: 'center', + overflow: 'hidden', + alignItems: 'center', + }, + + PDFViewList: { + overflowX: 'hidden', + // There properties disable "focus" effect on list + boxShadow: 'none', + outline: 'none', + }, + + getPDFPasswordFormStyle: (isSmallScreenWidth: boolean) => + ({ + width: isSmallScreenWidth ? '100%' : 350, + ...(isSmallScreenWidth && flex.flex1), + } satisfies ViewStyle), + + modalCenterContentContainer: { + flex: 1, + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + backgroundColor: theme.modalBackdrop, + }, + + centeredModalStyles: (isSmallScreenWidth: boolean, isFullScreenWhenSmall: boolean) => + ({ + borderWidth: isSmallScreenWidth && !isFullScreenWhenSmall ? 1 : 0, + marginHorizontal: isSmallScreenWidth ? 0 : 20, + } satisfies ViewStyle), + + imageModalImageCenterContainer: { + alignItems: 'center', + flex: 1, + justifyContent: 'center', + width: '100%', + }, + + defaultAttachmentView: { + backgroundColor: theme.sidebar, + borderRadius: variables.componentBorderRadiusNormal, + borderWidth: 1, + borderColor: theme.border, + flexDirection: 'row', + padding: 20, + alignItems: 'center', + }, + + notFoundSafeArea: { + flex: 1, + backgroundColor: theme.heading, + }, + + notFoundView: { + flex: 1, + alignItems: 'center', + paddingTop: 40, + paddingBottom: 40, + justifyContent: 'space-between', + }, + + notFoundLogo: { + width: 202, + height: 63, + }, + + notFoundContent: { + alignItems: 'center', + }, + + notFoundTextHeader: { + ...headlineFont, + color: theme.heading, + fontSize: variables.fontSizeXLarge, + lineHeight: variables.lineHeightXXLarge, + marginTop: 20, + marginBottom: 8, + textAlign: 'center', + }, + + notFoundTextBody: { + color: theme.componentBG, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontWeight: fontWeightBold, + fontSize: 15, + }, + + notFoundButtonText: { + color: theme.link, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontWeight: fontWeightBold, + fontSize: 15, + }, + + blockingViewContainer: { + paddingBottom: variables.contentHeaderHeight, + }, + + defaultModalContainer: { + backgroundColor: theme.componentBG, + borderColor: theme.transparent, + }, + + reportActionContextMenuMiniButton: { + ...spacing.p1, + ...spacing.mv1, + ...spacing.mh1, + ...{borderRadius: variables.buttonBorderRadius}, + }, + + reportActionSystemMessageContainer: { + marginLeft: 42, + }, + + reportDetailsTitleContainer: { + ...flex.flexColumn, + ...flex.alignItemsCenter, + paddingHorizontal: 20, + paddingBottom: 20, + }, + + reportDetailsRoomInfo: { + ...flex.flex1, + ...flex.flexColumn, + ...flex.alignItemsCenter, + }, + + reportSettingsVisibilityText: { + textTransform: 'capitalize', + }, + + settingsPageBackground: { + flexDirection: 'column', + width: '100%', + flexGrow: 1, + }, + + settingsPageBody: { + width: '100%', + justifyContent: 'space-around', + }, + + settingsPageColumn: { + width: '100%', + alignItems: 'center', + justifyContent: 'space-around', + }, + + settingsPageContainer: { + justifyContent: 'space-between', + alignItems: 'center', + width: '100%', + }, + + twoFactorAuthSection: { + backgroundColor: theme.appBG, + padding: 0, + }, + + twoFactorAuthCodesBox: ({isExtraSmallScreenWidth, isSmallScreenWidth}: TwoFactorAuthCodesBoxParams) => { + let paddingHorizontal = spacing.ph9; + + if (isSmallScreenWidth) { + paddingHorizontal = spacing.ph4; + } + + if (isExtraSmallScreenWidth) { + paddingHorizontal = spacing.ph2; + } + + return { + alignItems: 'center', + justifyContent: 'center', + backgroundColor: theme.highlightBG, + paddingVertical: 28, + borderRadius: 16, + marginTop: 32, + ...paddingHorizontal, + } satisfies ViewStyle; + }, + + twoFactorLoadingContainer: { + alignItems: 'center', + justifyContent: 'center', + height: 210, + }, + + twoFactorAuthCodesContainer: { + alignItems: 'center', + justifyContent: 'center', + flexDirection: 'row', + flexWrap: 'wrap', + gap: 12, + }, + + twoFactorAuthCode: { + fontFamily: fontFamily.MONOSPACE, + width: 112, + textAlign: 'center', + }, + + twoFactorAuthCodesButtonsContainer: { + flexDirection: 'row', + justifyContent: 'center', + gap: 12, + marginTop: 20, + flexWrap: 'wrap', + }, + + twoFactorAuthCodesButton: { + minWidth: 112, + }, + + twoFactorAuthCopyCodeButton: { + minWidth: 110, + }, + + anonymousRoomFooter: (isSmallSizeLayout: boolean) => + ({ + flexDirection: isSmallSizeLayout ? 'column' : 'row', + ...(!isSmallSizeLayout && { + alignItems: 'center', + justifyContent: 'space-between', + }), + padding: 20, + backgroundColor: theme.sidebar, + borderRadius: variables.componentBorderRadiusLarge, + overflow: 'hidden', + } satisfies ViewStyle & TextStyle), + anonymousRoomFooterWordmarkAndLogoContainer: (isSmallSizeLayout: boolean) => + ({ + flexDirection: 'row', + alignItems: 'center', + ...(isSmallSizeLayout && { + justifyContent: 'space-between', + marginTop: 16, + }), + } satisfies ViewStyle), + anonymousRoomFooterLogo: { + width: 88, + marginLeft: 0, + height: 20, + }, + anonymousRoomFooterLogoTaglineText: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeMedium, + color: theme.textLight, + }, + signInButtonAvatar: { + width: 80, + }, + + anonymousRoomFooterSignInButton: { + width: 110, + }, + + roomHeaderAvatarSize: { + height: variables.componentSizeLarge, + width: variables.componentSizeLarge, + }, + + roomHeaderAvatar: { + backgroundColor: theme.appBG, + borderRadius: 100, + borderColor: theme.componentBG, + borderWidth: 4, + }, + + roomHeaderAvatarOverlay: { + position: 'absolute', + top: 0, + right: 0, + bottom: 0, + left: 0, + backgroundColor: theme.overlay, + opacity: variables.overlayOpacity, + borderRadius: 88, + }, + + rootNavigatorContainerStyles: (isSmallScreenWidth: boolean) => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1} satisfies ViewStyle), + RHPNavigatorContainerNavigatorContainerStyles: (isSmallScreenWidth: boolean) => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1} satisfies ViewStyle), + + avatarInnerTextChat: { + color: theme.textLight, + fontSize: variables.fontSizeXLarge, + fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, + textAlign: 'center', + fontWeight: 'normal', + position: 'absolute', + width: 88, + left: -16, + }, + + svgAvatarBorder: { + borderRadius: 100, + overflow: 'hidden', + }, + + displayName: { + fontSize: variables.fontSizeLarge, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontWeight: fontWeightBold, + color: theme.heading, + }, + + pageWrapper: { + width: '100%', + alignItems: 'center', + padding: 20, + }, + + avatarSectionWrapper: { + width: '100%', + alignItems: 'center', + paddingHorizontal: 20, + paddingBottom: 20, + }, + + avatarSectionWrapperSkeleton: { + width: '100%', + paddingHorizontal: 20, + paddingBottom: 20, + }, + + selectCircle: { + width: variables.componentSizeSmall, + height: variables.componentSizeSmall, + borderColor: theme.border, + borderWidth: 1, + borderRadius: variables.componentSizeSmall / 2, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: theme.componentBG, + marginLeft: 8, + }, + + unreadIndicatorContainer: { + position: 'absolute', + top: -10, + left: 0, + width: '100%', + height: 20, + paddingHorizontal: 20, + flexDirection: 'row', + alignItems: 'center', + zIndex: 1, + ...cursor.cursorDefault, + }, + + unreadIndicatorLine: { + height: 1, + backgroundColor: theme.unreadIndicator, + flexGrow: 1, + marginRight: 8, + opacity: 0.5, + }, + + threadDividerLine: { + height: 1, + backgroundColor: theme.border, + flexGrow: 1, + marginHorizontal: 20, + }, + + unreadIndicatorText: { + color: theme.unreadIndicator, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontSize: variables.fontSizeSmall, + fontWeight: fontWeightBold, + textTransform: 'capitalize', + }, + + flipUpsideDown: { + transform: [{rotate: '180deg'}], + }, + + navigationSceneContainer: { + backgroundColor: theme.appBG, + }, + + navigationScreenCardStyle: { + backgroundColor: theme.appBG, + height: '100%', + }, + + navigationSceneFullScreenWrapper: { + borderRadius: variables.componentBorderRadiusCard, + overflow: 'hidden', + height: '100%', + }, + + invisible: { + position: 'absolute', + opacity: 0, + }, + + containerWithSpaceBetween: { + justifyContent: 'space-between', + width: '100%', + flex: 1, + }, + + detailsPageSectionContainer: { + alignSelf: 'flex-start', + }, + + attachmentCarouselContainer: { + height: '100%', + width: '100%', + display: 'flex', + justifyContent: 'center', + ...cursor.cursorUnset, + }, + + attachmentArrow: { + zIndex: 23, + position: 'absolute', + }, + + attachmentRevealButtonContainer: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + ...spacing.ph4, + }, + + arrowIcon: { + height: 40, + width: 40, + alignItems: 'center', + paddingHorizontal: 0, + paddingTop: 0, + paddingBottom: 0, + }, + + detailsPageSectionVersion: { + alignSelf: 'center', + color: theme.textSupporting, + fontSize: variables.fontSizeSmall, + height: 24, + lineHeight: 20, + }, + + switchTrack: { + width: 50, + height: 28, + justifyContent: 'center', + borderRadius: 20, + padding: 15, + backgroundColor: theme.success, + }, + + switchInactive: { + backgroundColor: theme.border, + }, + + switchThumb: { + width: 22, + height: 22, + borderRadius: 11, + position: 'absolute', + left: 4, + backgroundColor: theme.appBG, + }, + + switchThumbTransformation: (translateX: AnimatableNumericValue) => + ({ + transform: [{translateX}], + } satisfies ViewStyle), + + radioButtonContainer: { + backgroundColor: theme.componentBG, + borderRadius: 10, + height: 20, + width: 20, + borderColor: theme.icon, + borderWidth: 1, + justifyContent: 'center', + alignItems: 'center', + }, + + checkboxPressable: { + borderRadius: 6, + padding: 2, + justifyContent: 'center', + alignItems: 'center', + }, + + checkedContainer: { + backgroundColor: theme.checkBox, + }, + + magicCodeInputContainer: { + flexDirection: 'row', + justifyContent: 'space-between', + minHeight: variables.inputHeight, + }, + + magicCodeInput: { + fontSize: variables.fontSizeXLarge, + color: theme.heading, + lineHeight: variables.inputHeight, + }, + + // Manually style transparent, in iOS Safari, an input in a container with its opacity set to + // 0 (completely transparent) cannot handle user interaction, hence the Paste option is never shown + inputTransparent: { + color: 'transparent', + // These properties are available in browser only + ...(Browser.getBrowser() + ? { + caretColor: 'transparent', + WebkitTextFillColor: 'transparent', + // After setting the input text color to transparent, it acquires the background-color. + // However, it is not possible to override the background-color directly as explained in this resource: https://developer.mozilla.org/en-US/docs/Web/CSS/:autofill + // Therefore, the transition effect needs to be delayed. + transitionDelay: '99999s', + } + : {}), + }, + + iouAmountText: { + ...headlineFont, + fontSize: variables.iouAmountTextSize, + color: theme.heading, + lineHeight: variables.inputHeight, + }, + + iouAmountTextInput: addOutlineWidth( + { + ...headlineFont, + fontSize: variables.iouAmountTextSize, + color: theme.heading, + padding: 0, + lineHeight: undefined, + }, + 0, + ), + + moneyRequestConfirmationAmount: { + ...headlineFont, + fontSize: variables.fontSizeh1, + }, + + moneyRequestMenuItem: { + flexDirection: 'row', + borderRadius: 0, + justifyContent: 'space-between', + width: '100%', + paddingHorizontal: 20, + paddingVertical: 12, + }, + + requestPreviewBox: { + marginTop: 12, + maxWidth: variables.sideBarWidth, + }, + + moneyRequestPreviewBox: { + backgroundColor: theme.cardBG, + borderRadius: variables.componentBorderRadiusLarge, + maxWidth: variables.sideBarWidth, + width: '100%', + }, + + moneyRequestPreviewBoxText: { + padding: 16, + }, + + moneyRequestPreviewBoxLoading: { + // When a new IOU request arrives it is very briefly in a loading state, so set the minimum height of the container to 94 to match the rendered height after loading. + // Otherwise, the IOU request pay button will not be fully visible and the user will have to scroll up to reveal the entire IOU request container. + // See https://github.com/Expensify/App/issues/10283. + minHeight: 94, + width: '100%', + }, + + moneyRequestPreviewBoxAvatar: { + marginRight: -10, + marginBottom: 0, + }, + + moneyRequestPreviewAmount: { + ...headlineFont, + ...whiteSpace.preWrap, + color: theme.heading, + }, + + defaultCheckmarkWrapper: { + marginLeft: 8, + alignSelf: 'center', + }, + + iouDetailsContainer: { + flexGrow: 1, + paddingStart: 20, + paddingEnd: 20, + }, + + codeWordWrapper: { + ...codeStyles.codeWordWrapper, + }, + + codeWordStyle: { + borderLeftWidth: 0, + borderRightWidth: 0, + borderTopLeftRadius: 0, + borderBottomLeftRadius: 0, + borderTopRightRadius: 0, + borderBottomRightRadius: 0, + paddingLeft: 0, + paddingRight: 0, + justifyContent: 'center', + ...codeStyles.codeWordStyle, + }, + + codeFirstWordStyle: { + borderLeftWidth: 1, + borderTopLeftRadius: 4, + borderBottomLeftRadius: 4, + paddingLeft: 5, + }, + + codeLastWordStyle: { + borderRightWidth: 1, + borderTopRightRadius: 4, + borderBottomRightRadius: 4, + paddingRight: 5, + }, + + fullScreenLoading: { + backgroundColor: theme.componentBG, + opacity: 0.8, + justifyContent: 'center', + alignItems: 'center', + zIndex: 10, + }, + + navigatorFullScreenLoading: { + backgroundColor: theme.highlightBG, + opacity: 1, + }, + + reimbursementAccountFullScreenLoading: { + backgroundColor: theme.componentBG, + opacity: 0.8, + justifyContent: 'flex-start', + alignItems: 'center', + zIndex: 10, + }, + + hiddenElementOutsideOfWindow: { + position: 'absolute', + top: -10000, + left: 0, + opacity: 0, + }, + + growlNotificationWrapper: { + zIndex: 2, + }, + + growlNotificationContainer: { + flex: 1, + justifyContent: 'flex-start', + position: 'absolute', + width: '100%', + top: 20, + ...spacing.pl5, + ...spacing.pr5, + }, + + growlNotificationDesktopContainer: { + maxWidth: variables.sideBarWidth, + right: 0, + // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". + position: 'fixed' as ViewStyle['position'], + }, + + growlNotificationTranslateY: (translateY: AnimatableNumericValue) => + ({ + transform: [{translateY}], + } satisfies ViewStyle), + + makeSlideInTranslation: (translationType: Translation, fromValue: number) => + ({ + from: { + [translationType]: fromValue, + }, + to: { + [translationType]: 0, + }, + } satisfies CustomAnimation), + + growlNotificationBox: { + backgroundColor: theme.inverse, + borderRadius: variables.componentBorderRadiusNormal, + alignItems: 'center', + flexDirection: 'row', + justifyContent: 'space-between', + shadowColor: theme.shadow, + ...spacing.p5, + }, + + growlNotificationText: { + fontSize: variables.fontSizeNormal, + fontFamily: fontFamily.EXP_NEUE, + width: '90%', + lineHeight: variables.fontSizeNormalHeight, + color: theme.textReversed, + ...spacing.ml4, + }, + + blockquote: { + borderLeftColor: theme.border, + borderLeftWidth: 4, + paddingLeft: 12, + marginVertical: 4, + }, + + noSelect: { + boxShadow: 'none', + outline: 'none', + }, + + cardStyleNavigator: { + overflow: 'hidden', + height: '100%', + }, + + fullscreenCard: { + position: 'absolute', + left: 0, + top: 0, + width: '100%', + height: '100%', + }, + + fullscreenCardWeb: { + left: 'auto', + right: '-24%', + top: '-18%', + height: '120%', + }, + + fullscreenCardWebCentered: { + left: 0, + right: 0, + top: 0, + height: '60%', + }, + + fullscreenCardMobile: { + left: '-20%', + top: '-30%', + width: '150%', + }, + + fullscreenCardMediumScreen: { + left: '-15%', + top: '-30%', + width: '145%', + }, + + smallEditIcon: { + alignItems: 'center', + backgroundColor: theme.buttonHoveredBG, + borderColor: theme.textReversed, + borderRadius: 14, + borderWidth: 3, + color: theme.textReversed, + height: 28, + width: 28, + justifyContent: 'center', + }, + + smallAvatarEditIcon: { + position: 'absolute', + right: -4, + bottom: -4, + }, + + workspaceCard: { + width: '100%', + height: 400, + borderRadius: variables.componentBorderRadiusCard, + overflow: 'hidden', + backgroundColor: theme.heroCard, + }, + + workspaceCardMobile: { + height: 475, + }, + + workspaceCardMediumScreen: { + height: 540, + }, + + workspaceCardMainText: { + fontSize: variables.fontSizeXXXLarge, + fontWeight: 'bold', + lineHeight: variables.fontSizeXXXLarge, + }, + + workspaceCardContent: { + zIndex: 1, + padding: 50, + }, + + workspaceCardContentMediumScreen: { + padding: 25, + }, + + workspaceCardCTA: { + width: 250, + }, + + autoGrowHeightMultilineInput: { + maxHeight: 115, + }, + + peopleRow: { + width: '100%', + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + ...spacing.ph5, + }, + + peopleRowBorderBottom: { + borderColor: theme.border, + borderBottomWidth: 1, + ...spacing.pb2, + }, + + peopleBadge: { + backgroundColor: theme.icon, + ...spacing.ph3, + }, + + peopleBadgeText: { + color: theme.textReversed, + fontSize: variables.fontSizeSmall, + lineHeight: variables.lineHeightNormal, + ...whiteSpace.noWrap, + }, + + offlineFeedback: { + deleted: { + textDecorationLine: 'line-through', + textDecorationStyle: 'solid', + }, + pending: { + opacity: 0.5, + }, + error: { + flexDirection: 'row', + alignItems: 'center', + }, + container: { + ...spacing.pv2, + }, + textContainer: { + flexDirection: 'column', + flex: 1, + }, + text: { + color: theme.textSupporting, + textAlignVertical: 'center', + fontSize: variables.fontSizeLabel, + }, + errorDot: { + marginRight: 12, + }, + }, + + dotIndicatorMessage: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + }, + + sidebarPopover: { + width: variables.sideBarWidth - 68, + }, + + cardOverlay: { + backgroundColor: theme.overlay, + position: 'absolute', + top: 0, + left: 0, + width: '100%', + height: '100%', + opacity: variables.overlayOpacity, + }, + + communicationsLinkIcon: { + right: -36, + top: 0, + bottom: 0, + }, + + shortTermsBorder: { + borderWidth: 1, + borderColor: theme.border, + }, + + shortTermsHorizontalRule: { + borderBottomWidth: 1, + borderColor: theme.border, + ...spacing.mh3, + }, + + shortTermsLargeHorizontalRule: { + borderWidth: 1, + borderColor: theme.border, + ...spacing.mh3, + }, + + shortTermsRow: { + flexDirection: 'row', + padding: 12, + }, + + termsCenterRight: { + marginTop: 'auto', + marginBottom: 'auto', + }, + + shortTermsBoldHeadingSection: { + paddingRight: 12, + paddingLeft: 12, + marginTop: 12, + }, + + shortTermsHeadline: { + ...headlineFont, + ...whiteSpace.preWrap, + color: theme.heading, + fontSize: variables.fontSizeXXXLarge, + lineHeight: variables.lineHeightXXXLarge, + }, + + longTermsRow: { + flexDirection: 'row', + marginTop: 20, + }, + + collapsibleSectionBorder: { + borderBottomWidth: 2, + borderBottomColor: theme.border, + }, + + communicationsLinkHeight: { + height: variables.communicationsLinkHeight, + }, + + floatingMessageCounterWrapper: { + position: 'absolute', + left: '50%', + top: 0, + zIndex: 100, + ...visibility.hidden, + }, + + floatingMessageCounterWrapperAndroid: { + left: 0, + width: '100%', + alignItems: 'center', + position: 'absolute', + top: 0, + zIndex: 100, + ...visibility.hidden, + }, + + floatingMessageCounterSubWrapperAndroid: { + left: '50%', + width: 'auto', + }, + + floatingMessageCounter: { + left: '-50%', + ...visibility.visible, + }, + + floatingMessageCounterTransformation: (translateY: AnimatableNumericValue) => + ({ + transform: [{translateY}], + } satisfies ViewStyle), + + confirmationAnimation: { + height: 180, + width: 180, + marginBottom: 20, + }, + + googleSearchTextInputContainer: { + flexDirection: 'column', + }, + + googleSearchSeparator: { + height: 1, + backgroundColor: theme.border, + }, + + googleSearchText: { + color: theme.text, + fontSize: variables.fontSizeNormal, + lineHeight: variables.fontSizeNormalHeight, + fontFamily: fontFamily.EXP_NEUE, + flex: 1, + }, + + threeDotsPopoverOffset: (windowWidth: number) => + ({ + ...getPopOverVerticalOffset(60), + horizontal: windowWidth - 60, + } satisfies AnchorPosition), + + threeDotsPopoverOffsetNoCloseButton: (windowWidth: number) => + ({ + ...getPopOverVerticalOffset(60), + horizontal: windowWidth - 10, + } satisfies AnchorPosition), + + invert: { + // It's important to invert the Y AND X axis to prevent a react native issue that can lead to ANRs on android 13 + transform: [{scaleX: -1}, {scaleY: -1}], + }, + + keyboardShortcutModalContainer: { + maxHeight: '100%', + flex: 0, + flexBasis: 'auto', + }, + + keyboardShortcutTableWrapper: { + alignItems: 'center', + flex: 1, + height: 'auto', + maxHeight: '100%', + }, + + keyboardShortcutTableContainer: { + display: 'flex', + width: '100%', + borderColor: theme.border, + height: 'auto', + borderRadius: variables.componentBorderRadius, + borderWidth: 1, + }, + + keyboardShortcutTableRow: { + flex: 1, + flexDirection: 'row', + borderColor: theme.border, + flexBasis: 'auto', + alignSelf: 'stretch', + borderTopWidth: 1, + }, + + keyboardShortcutTablePrefix: { + width: '30%', + borderRightWidth: 1, + borderColor: theme.border, + }, + + keyboardShortcutTableFirstRow: { + borderTopWidth: 0, + }, + + iPhoneXSafeArea: { + backgroundColor: theme.inverse, + flex: 1, + }, + + transferBalancePayment: { + borderWidth: 1, + borderRadius: variables.componentBorderRadiusNormal, + borderColor: theme.border, + }, + + transferBalanceSelectedPayment: { + borderColor: theme.iconSuccessFill, + }, + + transferBalanceBalance: { + fontSize: 48, + }, + + closeAccountMessageInput: { + height: 153, + }, + + imageCropContainer: { + overflow: 'hidden', + alignItems: 'center', + justifyContent: 'center', + backgroundColor: theme.imageCropBackgroundColor, + ...cursor.cursorMove, + }, + + sliderKnobTooltipView: { + height: variables.sliderKnobSize, + width: variables.sliderKnobSize, + borderRadius: variables.sliderKnobSize / 2, + }, + + sliderKnob: { + backgroundColor: theme.success, + position: 'absolute', + height: variables.sliderKnobSize, + width: variables.sliderKnobSize, + borderRadius: variables.sliderKnobSize / 2, + left: -(variables.sliderKnobSize / 2), + ...cursor.cursorPointer, + }, + + sliderBar: { + backgroundColor: theme.border, + height: variables.sliderBarHeight, + borderRadius: variables.sliderBarHeight / 2, + alignSelf: 'stretch', + justifyContent: 'center', + }, + + screenCenteredContainer: { + flex: 1, + justifyContent: 'center', + marginBottom: 40, + padding: 16, + }, + + inlineSystemMessage: { + color: theme.textSupporting, + fontSize: variables.fontSizeLabel, + fontFamily: fontFamily.EXP_NEUE, + marginLeft: 6, + }, + + fullScreen: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + }, + + invisibleOverlay: { + backgroundColor: theme.transparent, + zIndex: 1000, + }, + + reportDropOverlay: { + backgroundColor: theme.dropUIBG, + zIndex: 2, + }, + + receiptDropOverlay: { + backgroundColor: theme.receiptDropUIBG, + zIndex: 2, + }, + + receiptImageWrapper: (receiptImageTopPosition: number) => + ({ + position: 'absolute', + top: receiptImageTopPosition, + } satisfies ViewStyle), + + cardSection: { + backgroundColor: theme.cardBG, + borderRadius: variables.componentBorderRadiusCard, + marginBottom: 20, + marginHorizontal: 16, + padding: 20, + width: 'auto', + textAlign: 'left', + }, + + cardSectionTitle: { + lineHeight: variables.lineHeightXXLarge, + }, + + cardMenuItem: { + paddingLeft: 8, + paddingRight: 0, + borderRadius: variables.buttonBorderRadius, + height: variables.componentSizeLarge, + alignItems: 'center', + }, + + callRequestSection: { + backgroundColor: theme.appBG, + paddingHorizontal: 0, + paddingBottom: 0, + marginHorizontal: 0, + marginBottom: 0, + }, + + archivedReportFooter: { + borderRadius: variables.componentBorderRadius, + ...wordBreak.breakWord, + }, + + saveButtonPadding: { + paddingLeft: 18, + paddingRight: 18, + }, + + deeplinkWrapperContainer: { + padding: 20, + flex: 1, + alignItems: 'center', + justifyContent: 'center', + backgroundColor: theme.appBG, + }, + + deeplinkWrapperMessage: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, + + deeplinkWrapperFooter: { + paddingTop: 80, + paddingBottom: 45, + }, + + emojiReactionBubble: { + borderRadius: 28, + alignItems: 'center', + justifyContent: 'center', + flexDirection: 'row', + alignSelf: 'flex-start', + }, + + emojiReactionListHeader: { + marginTop: 8, + paddingBottom: 20, + borderBottomColor: theme.border, + borderBottomWidth: 1, + marginHorizontal: 20, + }, + emojiReactionListHeaderBubble: { + paddingVertical: 2, + paddingHorizontal: 8, + borderRadius: 28, + backgroundColor: theme.border, + alignItems: 'center', + justifyContent: 'center', + flexDirection: 'row', + alignSelf: 'flex-start', + marginRight: 4, + }, + reactionListItem: { + flexDirection: 'row', + paddingVertical: 12, + paddingHorizontal: 20, + }, + reactionListHeaderText: { + color: theme.textSupporting, + marginLeft: 8, + alignSelf: 'center', + }, + + miniQuickEmojiReactionText: { + fontSize: 15, + lineHeight: 20, + textAlignVertical: 'center', + }, + + emojiReactionBubbleText: { + textAlignVertical: 'center', + }, + + reactionCounterText: { + fontSize: 13, + marginLeft: 4, + fontWeight: 'bold', + }, + + fontColorReactionLabel: { + color: theme.tooltipSupportingText, + }, + + reactionEmojiTitle: { + fontSize: variables.iconSizeLarge, + lineHeight: variables.iconSizeXLarge, + }, + + textReactionSenders: { + color: theme.tooltipPrimaryText, + ...wordBreak.breakWord, + }, + + quickReactionsContainer: { + gap: 12, + flexDirection: 'row', + paddingHorizontal: 25, + paddingVertical: 12, + justifyContent: 'space-between', + }, + + reactionListContainer: { + maxHeight: variables.listItemHeightNormal * 5.75, + ...spacing.pv2, + }, + + reactionListContainerFixedWidth: { + maxWidth: variables.popoverWidth, + }, + + validateCodeDigits: { + color: theme.text, + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeXXLarge, + letterSpacing: 4, + }, + + footerWrapper: { + fontSize: variables.fontSizeNormal, + paddingTop: 64, + maxWidth: 1100, // Match footer across all Expensify platforms + }, + + footerColumnsContainer: { + flex: 1, + flexWrap: 'wrap', + marginBottom: 40, + marginHorizontal: -16, + }, + + footerTitle: { + fontSize: variables.fontSizeLarge, + color: theme.success, + marginBottom: 16, + }, + + footerRow: { + paddingVertical: 4, + marginBottom: 8, + color: theme.textLight, + fontSize: variables.fontSizeMedium, + }, + + footerBottomLogo: { + marginTop: 40, + width: '100%', + }, + + listPickerSeparator: { + height: 1, + backgroundColor: theme.buttonDefaultBG, + }, + + datePickerRoot: { + position: 'relative', + zIndex: 99, + }, + + datePickerPopover: { + backgroundColor: theme.appBG, + width: '100%', + alignSelf: 'center', + zIndex: 100, + marginTop: 8, + }, + + loginHeroHeader: { + fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, + color: theme.success, + fontWeight: '500', + textAlign: 'center', + }, + + newKansasLarge: { + ...headlineFont, + fontSize: variables.fontSizeXLarge, + lineHeight: variables.lineHeightXXLarge, + }, + + loginHeroBody: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeSignInHeroBody, + color: theme.textLight, + textAlign: 'center', + }, + + linkPreviewWrapper: { + marginTop: 16, + borderLeftWidth: 4, + borderLeftColor: theme.border, + paddingLeft: 12, + }, + + linkPreviewImage: { + flex: 1, + resizeMode: 'contain', + borderRadius: 8, + marginTop: 8, + }, + + linkPreviewLogoImage: { + height: 16, + width: 16, + }, + + validateCodeMessage: { + width: variables.modalContentMaxWidth, + textAlign: 'center', + }, + + whisper: { + backgroundColor: theme.cardBG, + }, + + contextMenuItemPopoverMaxWidth: { + maxWidth: 375, + }, + + formSpaceVertical: { + height: 20, + width: 1, + }, + + taskCheckbox: { + height: 16, + width: 16, + }, + + taskTitleMenuItem: { + ...writingDirection.ltr, + ...headlineFont, + fontSize: variables.fontSizeXLarge, + maxWidth: '100%', + ...wordBreak.breakWord, + }, + + taskDescriptionMenuItem: { + maxWidth: '100%', + ...wordBreak.breakWord, + }, + + taskTitleDescription: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeLabel, + color: theme.textSupporting, + lineHeight: variables.lineHeightNormal, + ...spacing.mb1, + }, + + taskMenuItemCheckbox: { + height: 27, + ...spacing.mr3, + }, + + reportHorizontalRule: { + borderBottomWidth: 1, + borderColor: theme.border, + ...spacing.mh5, + ...spacing.mv2, + }, + + assigneeTextStyle: { + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontWeight: fontWeightBold, + minHeight: variables.avatarSizeSubscript, + }, + + taskRightIconContainer: { + width: variables.componentSizeNormal, + marginLeft: 'auto', + ...spacing.mt1, + ...pointerEventsAuto, + }, + + shareCodePage: { + paddingHorizontal: 38.5, + }, + + shareCodeContainer: { + width: '100%', + alignItems: 'center', + paddingHorizontal: variables.qrShareHorizontalPadding, + paddingVertical: 20, + borderRadius: 20, + overflow: 'hidden', + borderColor: theme.borderFocus, + borderWidth: 2, + backgroundColor: theme.highlightBG, + }, + + splashScreenHider: { + backgroundColor: theme.splashBG, + alignItems: 'center', + justifyContent: 'center', + }, + + headerEnvBadge: { + marginLeft: 0, + marginBottom: 2, + height: 12, + paddingLeft: 4, + paddingRight: 4, + alignItems: 'center', + }, + + headerEnvBadgeText: { + fontSize: 7, + fontWeight: fontWeightBold, + lineHeight: undefined, + }, + + expensifyQrLogo: { + alignSelf: 'stretch', + height: 27, + marginBottom: 20, + }, + + qrShareTitle: { + marginTop: 15, + textAlign: 'center', + }, + + loginButtonRow: { + justifyContent: 'center', + width: '100%', + ...flex.flexRow, + }, + + loginButtonRowSmallScreen: { + justifyContent: 'center', + width: '100%', + marginBottom: 10, + ...flex.flexRow, + }, + + appleButtonContainer: { + width: 40, + height: 40, + marginRight: 20, + }, + + signInIconButton: { + margin: 10, + marginTop: 0, + padding: 2, + }, + + googleButtonContainer: { + colorScheme: 'light', + width: 40, + height: 40, + marginLeft: 12, + alignItems: 'center', + overflow: 'hidden', + }, + + googlePillButtonContainer: { + colorScheme: 'light', + height: 40, + width: 219, + }, + + thirdPartyLoadingContainer: { + alignItems: 'center', + justifyContent: 'center', + height: 450, + }, + + tabSelectorButton: { + height: variables.tabSelectorButtonHeight, + padding: variables.tabSelectorButtonPadding, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + borderRadius: variables.buttonBorderRadius, + }, + + tabSelector: { + flexDirection: 'row', + paddingHorizontal: 20, + paddingBottom: 12, + }, + + tabText: (isSelected: boolean) => + ({ + marginLeft: 8, + fontFamily: isSelected ? fontFamily.EXP_NEUE_BOLD : fontFamily.EXP_NEUE, + fontWeight: isSelected ? fontWeightBold : '400', + color: isSelected ? theme.textLight : theme.textSupporting, + } satisfies TextStyle), + + overscrollSpacer: (backgroundColor: string, height: number) => + ({ + backgroundColor, + height, + width: '100%', + position: 'absolute', + top: -height, + left: 0, + right: 0, + } satisfies ViewStyle), + + dualColorOverscrollSpacer: { + position: 'absolute', + top: 0, + left: 0, + width: '100%', + height: '100%', + zIndex: -1, + }, + + willChangeTransform: { + willChange: 'transform', + }, + + dropDownButtonCartIconContainerPadding: { + paddingRight: 0, + paddingLeft: 0, + }, + + dropDownButtonArrowContain: { + marginLeft: 12, + marginRight: 14, + }, + + dropDownButtonCartIconView: { + borderTopRightRadius: variables.buttonBorderRadius, + borderBottomRightRadius: variables.buttonBorderRadius, + ...flex.flexRow, + ...flex.alignItemsCenter, + }, + + emojiPickerButtonDropdown: { + justifyContent: 'center', + backgroundColor: theme.activeComponentBG, + width: 86, + height: 52, + borderRadius: 26, + alignItems: 'center', + paddingLeft: 10, + paddingRight: 4, + marginBottom: 32, + alignSelf: 'flex-start', + }, + + emojiPickerButtonDropdownIcon: { + fontSize: 30, + }, + + moneyRequestImage: { + height: 200, + borderRadius: 16, + margin: 20, + }, + + reportPreviewBox: { + backgroundColor: theme.cardBG, + borderRadius: variables.componentBorderRadiusLarge, + maxWidth: variables.sideBarWidth, + width: '100%', + }, + + reportPreviewBoxHoverBorder: { + borderColor: theme.border, + backgroundColor: theme.border, + }, + + reportPreviewBoxBody: { + padding: 16, + }, + + reportActionItemImages: { + flexDirection: 'row', + borderWidth: 4, + borderColor: theme.transparent, + borderTopLeftRadius: variables.componentBorderRadiusLarge, + borderTopRightRadius: variables.componentBorderRadiusLarge, + borderBottomLeftRadius: variables.componentBorderRadiusLarge, + borderBottomRightRadius: variables.componentBorderRadiusLarge, + overflow: 'hidden', + height: 200, + }, + + reportActionItemImage: { + flex: 1, + width: '100%', + height: '100%', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + }, + + reportActionItemImageBorder: { + borderRightWidth: 2, + borderColor: theme.cardBG, + }, + + reportActionItemImagesMore: { + position: 'absolute', + borderRadius: 18, + backgroundColor: theme.cardBG, + width: 36, + height: 36, + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + }, + + moneyRequestHeaderStatusBarBadge: { + paddingHorizontal: 8, + borderRadius: variables.componentBorderRadiusSmall, + height: variables.inputHeightSmall, + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + backgroundColor: theme.border, + marginRight: 12, + }, + + staticHeaderImage: { + minHeight: 240, + }, + + emojiPickerButtonDropdownContainer: { + flexDirection: 'row', + alignItems: 'center', + }, + + rotate90: { + transform: [{rotate: '90deg'}], + }, + + emojiStatusLHN: { + fontSize: 22, + }, + sidebarStatusAvatarContainer: { + height: 44, + width: 84, + backgroundColor: theme.componentBG, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + borderRadius: 42, + paddingHorizontal: 2, + marginVertical: -2, + marginRight: -2, + }, + sidebarStatusAvatar: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, + + moneyRequestViewImage: { + ...spacing.mh5, + ...spacing.mv3, + overflow: 'hidden', + borderWidth: 2, + borderColor: theme.cardBG, + borderRadius: variables.componentBorderRadiusLarge, + height: 200, + maxWidth: 400, + }, + + distanceRequestContainer: (maxHeight: number) => + ({ + ...flex.flexShrink2, + minHeight: variables.optionRowHeight * 2, + maxHeight, + } satisfies ViewStyle), + + mapViewContainer: { + ...flex.flex1, + ...spacing.p4, + minHeight: 300, + maxHeight: 500, + }, + + mapView: { + flex: 1, + borderRadius: 20, + overflow: 'hidden', + }, + + mapViewOverlay: { + flex: 1, + position: 'absolute', + left: 0, + top: 0, + borderRadius: variables.componentBorderRadiusLarge, + overflow: 'hidden', + backgroundColor: theme.highlightBG, + ...sizing.w100, + ...sizing.h100, + }, + + confirmationListMapItem: { + ...spacing.m5, + height: 200, + }, + + mapDirection: { + lineColor: theme.success, + lineWidth: 7, + }, + + mapDirectionLayer: { + layout: {'line-join': 'round', 'line-cap': 'round'}, + paint: {'line-color': theme.success, 'line-width': 7}, + }, + + mapPendingView: { + backgroundColor: theme.highlightBG, + ...flex.flex1, + borderRadius: variables.componentBorderRadiusLarge, + }, + userReportStatusEmoji: { + fontSize: variables.fontSizeNormal, + marginRight: 4, + }, + draggableTopBar: { + height: 30, + width: '100%', + }, + } satisfies Styles); // For now we need to export the styles function that takes the theme as an argument // as something named different than "styles", because a lot of files import the "defaultStyles" diff --git a/src/styles/utilities/display.ts b/src/styles/utilities/display.ts index e236ad4ea14d..88b0e0cd5689 100644 --- a/src/styles/utilities/display.ts +++ b/src/styles/utilities/display.ts @@ -33,4 +33,9 @@ export default { // NOTE: asserting "display" to a valid type, because isn't possible to augment "display". display: 'block' as ViewStyle['display'], }, + + dGrid: { + // NOTE: asserting "display" to a valid type, because isn't possible to augment "display". + display: 'grid' as ViewStyle['display'], + }, } satisfies Record; From 5feb1fed6262fdc6f925e63f601acb8de0d6a186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Sat, 16 Sep 2023 00:03:21 +0100 Subject: [PATCH 034/856] Isolate some web-only styles --- src/styles/StyleUtils.ts | 19 ++++++++++++------- src/styles/cardStyles/index.ts | 5 ++--- .../index.desktop.ts | 5 ++--- .../index.website.ts | 5 ++--- src/styles/getTooltipStyles.ts | 7 +++---- src/styles/styles.ts | 6 ++---- src/styles/utilities/display.ts | 12 ++++++++++++ src/styles/utilities/overflowAuto/index.ts | 3 +++ src/styles/utilities/positioning.ts | 8 ++++++++ 9 files changed, 46 insertions(+), 24 deletions(-) diff --git a/src/styles/StyleUtils.ts b/src/styles/StyleUtils.ts index 550ae71365af..9736d79a4a4e 100644 --- a/src/styles/StyleUtils.ts +++ b/src/styles/StyleUtils.ts @@ -40,7 +40,7 @@ type ParsableStyle = ViewStyle | ((state: PressableStateCallbackType) => ViewSty type WorkspaceColorStyle = {backgroundColor: ColorValue; fill: ColorValue}; -type ModalPaddingStylesArgs = { +type ModalPaddingStylesParams = { shouldAddBottomSafeAreaMargin: boolean; shouldAddTopSafeAreaMargin: boolean; shouldAddBottomSafeAreaPadding: boolean; @@ -56,13 +56,19 @@ type ModalPaddingStylesArgs = { insets: EdgeInsets; }; -type AvatarBorderStyleArgs = { +type AvatarBorderStyleParams = { isHovered: boolean; isPressed: boolean; isInReportAction: boolean; shouldUseCardBackground: boolean; }; +type GetBaseAutoCompleteSuggestionContainerStyleParams = { + left: number; + bottom: number; + width: number; +}; + const workspaceColorOptions: WorkspaceColorStyle[] = [ {backgroundColor: colors.blue200, fill: colors.blue700}, {backgroundColor: colors.blue400, fill: colors.blue800}, @@ -531,7 +537,7 @@ function getModalPaddingStyles({ modalContainerStylePaddingTop, modalContainerStylePaddingBottom, insets, -}: ModalPaddingStylesArgs): ViewStyle { +}: ModalPaddingStylesParams): ViewStyle { // use fallback value for safeAreaPaddingBottom to keep padding bottom consistent with padding top. // More info: issue #17376 const safeAreaPaddingBottomWithFallback = insets.bottom === 0 ? modalContainerStylePaddingTop ?? 0 : safeAreaPaddingBottom; @@ -785,7 +791,7 @@ function getKeyboardShortcutsModalWidth(isSmallScreenWidth: boolean): ViewStyle return {maxWidth: 600}; } -function getHorizontalStackedAvatarBorderStyle({isHovered, isPressed, isInReportAction = false, shouldUseCardBackground = false}: AvatarBorderStyleArgs): ViewStyle { +function getHorizontalStackedAvatarBorderStyle({isHovered, isPressed, isInReportAction = false, shouldUseCardBackground = false}: AvatarBorderStyleParams): ViewStyle { let borderColor = shouldUseCardBackground ? themeColors.cardBG : themeColors.appBG; if (isHovered) { @@ -928,10 +934,9 @@ function getAutoCompleteSuggestionItemStyle(highlightedEmojiIndex: number, rowHe /** * Gets the correct position for the base auto complete suggestion container */ -function getBaseAutoCompleteSuggestionContainerStyle({left, bottom, width}: {left: number; bottom: number; width: number}): ViewStyle { +function getBaseAutoCompleteSuggestionContainerStyle({left, bottom, width}: GetBaseAutoCompleteSuggestionContainerStyleParams): ViewStyle { return { - // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". - position: 'fixed' as ViewStyle['position'], + ...positioning.pFixed, bottom, left, width, diff --git a/src/styles/cardStyles/index.ts b/src/styles/cardStyles/index.ts index b2ba8d7c24f1..1f28ca4f4b78 100644 --- a/src/styles/cardStyles/index.ts +++ b/src/styles/cardStyles/index.ts @@ -1,12 +1,11 @@ -import {ViewStyle} from 'react-native'; +import positioning from '../utilities/positioning'; import GetCardStyles from './types'; /** * Get card style for cardStyleInterpolator */ const getCardStyles: GetCardStyles = (screenWidth) => ({ - // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". - position: 'fixed' as ViewStyle['position'], + ...positioning.pFixed, width: screenWidth, height: '100%', }); diff --git a/src/styles/getNavigationModalCardStyles/index.desktop.ts b/src/styles/getNavigationModalCardStyles/index.desktop.ts index 422a17d0a9a8..75b5636f9bd8 100644 --- a/src/styles/getNavigationModalCardStyles/index.desktop.ts +++ b/src/styles/getNavigationModalCardStyles/index.desktop.ts @@ -1,4 +1,4 @@ -import {ViewStyle} from 'react-native'; +import positioning from '../utilities/positioning'; import GetNavigationModalCardStyles from './types'; const getNavigationModalCardStyles: GetNavigationModalCardStyles = () => ({ @@ -10,8 +10,7 @@ const getNavigationModalCardStyles: GetNavigationModalCardStyles = () => ({ width: '100%', height: '100%', - // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". - position: 'fixed' as ViewStyle['position'], + ...positioning.pFixed, }); export default getNavigationModalCardStyles; diff --git a/src/styles/getNavigationModalCardStyles/index.website.ts b/src/styles/getNavigationModalCardStyles/index.website.ts index 422a17d0a9a8..75b5636f9bd8 100644 --- a/src/styles/getNavigationModalCardStyles/index.website.ts +++ b/src/styles/getNavigationModalCardStyles/index.website.ts @@ -1,4 +1,4 @@ -import {ViewStyle} from 'react-native'; +import positioning from '../utilities/positioning'; import GetNavigationModalCardStyles from './types'; const getNavigationModalCardStyles: GetNavigationModalCardStyles = () => ({ @@ -10,8 +10,7 @@ const getNavigationModalCardStyles: GetNavigationModalCardStyles = () => ({ width: '100%', height: '100%', - // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". - position: 'fixed' as ViewStyle['position'], + ...positioning.pFixed, }); export default getNavigationModalCardStyles; diff --git a/src/styles/getTooltipStyles.ts b/src/styles/getTooltipStyles.ts index 7747486a6eb3..f390d658cb5d 100644 --- a/src/styles/getTooltipStyles.ts +++ b/src/styles/getTooltipStyles.ts @@ -3,6 +3,7 @@ import fontFamily from './fontFamily'; import roundToNearestMultipleOfFour from './roundToNearestMultipleOfFour'; import styles from './styles'; import themeColors from './themes/default'; +import positioning from './utilities/positioning'; import spacing from './utilities/spacing'; import variables from './variables'; @@ -236,8 +237,7 @@ export default function getTooltipStyles( transform: [{scale}], }, rootWrapperStyle: { - // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". - position: 'fixed' as ViewStyle['position'], + ...positioning.pFixed, backgroundColor: themeColors.heading, borderRadius: variables.componentBorderRadiusSmall, ...tooltipVerticalPadding, @@ -259,8 +259,7 @@ export default function getTooltipStyles( lineHeight: variables.lineHeightSmall, }, pointerWrapperStyle: { - // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". - position: 'fixed' as ViewStyle['position'], + ...positioning.pFixed, top: pointerWrapperTop, left: pointerWrapperLeft, }, diff --git a/src/styles/styles.ts b/src/styles/styles.ts index c93e96c8fbee..1c663352569a 100644 --- a/src/styles/styles.ts +++ b/src/styles/styles.ts @@ -1596,8 +1596,7 @@ const styles = (theme: ThemeDefault) => overlayStyles: (current: OverlayStylesParams) => ({ - // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". - position: 'fixed' as ViewStyle['position'], + ...positioning.pFixed, // We need to stretch the overlay to cover the sidebar and the translate animation distance. left: -2 * variables.sideBarWidth, @@ -2886,8 +2885,7 @@ const styles = (theme: ThemeDefault) => growlNotificationDesktopContainer: { maxWidth: variables.sideBarWidth, right: 0, - // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". - position: 'fixed' as ViewStyle['position'], + ...positioning.pFixed, }, growlNotificationTranslateY: (translateY: AnimatableNumericValue) => diff --git a/src/styles/utilities/display.ts b/src/styles/utilities/display.ts index 88b0e0cd5689..124cd87bc67a 100644 --- a/src/styles/utilities/display.ts +++ b/src/styles/utilities/display.ts @@ -19,21 +19,33 @@ export default { display: 'none', }, + /** + * Web-only style. + */ dInline: { // NOTE: asserting "display" to a valid type, because isn't possible to augment "display". display: 'inline' as ViewStyle['display'], }, + /** + * Web-only style. + */ dInlineFlex: { // NOTE: asserting "display" to a valid type, because isn't possible to augment "display". display: 'inline-flex' as ViewStyle['display'], }, + /** + * Web-only style. + */ dBlock: { // NOTE: asserting "display" to a valid type, because isn't possible to augment "display". display: 'block' as ViewStyle['display'], }, + /** + * Web-only style. + */ dGrid: { // NOTE: asserting "display" to a valid type, because isn't possible to augment "display". display: 'grid' as ViewStyle['display'], diff --git a/src/styles/utilities/overflowAuto/index.ts b/src/styles/utilities/overflowAuto/index.ts index d73bee877c50..c87ea5437199 100644 --- a/src/styles/utilities/overflowAuto/index.ts +++ b/src/styles/utilities/overflowAuto/index.ts @@ -1,6 +1,9 @@ import {ViewStyle} from 'react-native'; import OverflowAutoStyles from './types'; +/** + * Web-only style. + */ const overflowAuto: OverflowAutoStyles = { // NOTE: asserting "overflow" to a valid type, because isn't possible to augment "overflow". overflow: 'auto' as ViewStyle['overflow'], diff --git a/src/styles/utilities/positioning.ts b/src/styles/utilities/positioning.ts index 651d2a12f2ea..7cd58c52618b 100644 --- a/src/styles/utilities/positioning.ts +++ b/src/styles/utilities/positioning.ts @@ -11,6 +11,14 @@ export default { pAbsolute: { position: 'absolute', }, + /** + * Web-only style. + */ + pFixed: { + // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". + position: 'fixed' as ViewStyle['position'], + }, + t0: { top: 0, }, From d0898636d15a3bd685685a7f5d9a7ac89b2205a0 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Tue, 19 Sep 2023 13:00:46 +0800 Subject: [PATCH 035/856] Fix types --- src/libs/actions/Receipt.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/libs/actions/Receipt.ts b/src/libs/actions/Receipt.ts index 13553e0b0d7d..2d3c347240e4 100644 --- a/src/libs/actions/Receipt.ts +++ b/src/libs/actions/Receipt.ts @@ -5,8 +5,14 @@ import * as API from '../API'; import * as CollectionUtils from '../CollectionUtils'; import Navigation from '../Navigation/Navigation'; import ROUTES from '../../ROUTES'; +import * as OnyxTypes from '../../types/onyx'; +import CONST from '../../CONST'; -const allTransactionData = {}; +type TransactionMap = { + [key: string]: OnyxTypes.Transaction; +}; + +const allTransactionData: TransactionMap = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION, callback: (data, key) => { @@ -42,11 +48,8 @@ function clearUploadReceiptError() { /** * Detaches the receipt from a transaction - * - * @param {String} transactionID - * @param {String} reportID */ -function detachReceipt(transactionID, reportID) { +function detachReceipt(transactionID: string, reportID: string) { API.write( 'DetachReceipt', {transactionID}, @@ -71,7 +74,7 @@ function detachReceipt(transactionID, reportID) { ], }, ); - Navigation.navigate(ROUTES.getReportRoute(reportID)); + Navigation.navigate(ROUTES.getReportRoute(reportID), CONST.NAVIGATION.TYPE.UP); } export default { From 4023821b3f47345840b0bda1a4ad99c7b939793c Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Wed, 20 Sep 2023 10:41:27 +0800 Subject: [PATCH 036/856] Only search when online --- src/ONYXKEYS.ts | 3 + src/components/OptionsListSkeletonView.js | 33 +---- .../OptionsSelector/BaseOptionsSelector.js | 127 ++++++++++-------- src/libs/actions/Report.js | 30 +++++ src/pages/SearchPage.js | 27 ++++ 5 files changed, 137 insertions(+), 83 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index af060ea58901..0274d523094e 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -27,6 +27,9 @@ const ONYXKEYS = { /** Boolean flag set whenever the sidebar has loaded */ IS_SIDEBAR_LOADED: 'isSidebarLoaded', + /** Boolean flag set whenever we are searching for reports in the server */ + IS_SEARCHING_FOR_REPORTS: 'isSearchingForReports', + /** Note: These are Persisted Requests - not all requests in the main queue as the key name might lead one to believe */ PERSISTED_REQUESTS: 'networkRequestQueue', diff --git a/src/components/OptionsListSkeletonView.js b/src/components/OptionsListSkeletonView.js index 15c66affe84d..833cbc0f372e 100644 --- a/src/components/OptionsListSkeletonView.js +++ b/src/components/OptionsListSkeletonView.js @@ -1,11 +1,9 @@ import React from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; -import {Rect, Circle} from 'react-native-svg'; -import SkeletonViewContentLoader from 'react-content-loader/native'; import CONST from '../CONST'; -import themeColors from '../styles/themes/default'; import styles from '../styles/styles'; +import OptionsListSkeletonRow from './OptionsListSkeletonRow'; const propTypes = { /** Whether to animate the skeleton view */ @@ -56,32 +54,11 @@ class OptionsListSkeletonView extends React.Component { lineWidth = '25%'; } skeletonViewItems.push( - - - - - , + shouldAnimate={this.props.shouldAnimate} + lineWidth={lineWidth} + />, ); } diff --git a/src/components/OptionsSelector/BaseOptionsSelector.js b/src/components/OptionsSelector/BaseOptionsSelector.js index bff9f8b6d7d0..0a370cd7b2d5 100755 --- a/src/components/OptionsSelector/BaseOptionsSelector.js +++ b/src/components/OptionsSelector/BaseOptionsSelector.js @@ -17,6 +17,9 @@ import {propTypes as optionsSelectorPropTypes, defaultProps as optionsSelectorDe import setSelection from '../../libs/setSelection'; import compose from '../../libs/compose'; import getPlatform from '../../libs/getPlatform'; +import OptionsListSkeletonRow from '../OptionsListSkeletonRow'; +import Text from '../Text'; +import FormHelpMessage from '../FormHelpMessage'; const propTypes = { /** padding bottom style of safe area */ @@ -344,63 +347,77 @@ class BaseOptionsSelector extends Component { const shouldShowDefaultConfirmButton = !this.props.footerContent && defaultConfirmButtonText; const safeAreaPaddingBottomStyle = shouldShowFooter ? undefined : this.props.safeAreaPaddingBottomStyle; const textInput = ( - (this.textInput = el)} - value={this.props.value} - label={this.props.textInputLabel} - accessibilityLabel={this.props.textInputLabel} - accessibilityRole={CONST.ACCESSIBILITY_ROLE.TEXT} - onChangeText={this.props.onChangeText} - onSubmitEditing={this.selectFocusedOption} - placeholder={this.props.placeholderText} - maxLength={this.props.maxLength} - keyboardType={this.props.keyboardType} - onBlur={(e) => { - if (!this.props.shouldFocusOnSelectRow) { - return; - } - this.relatedTarget = e.relatedTarget; - }} - selectTextOnFocus - blurOnSubmit={Boolean(this.state.allOptions.length)} - spellCheck={false} - /> + <> + (this.textInput = el)} + value={this.props.value} + label={this.props.textInputLabel} + accessibilityLabel={this.props.textInputLabel} + accessibilityRole={CONST.ACCESSIBILITY_ROLE.TEXT} + onChangeText={this.props.onChangeText} + onSubmitEditing={this.selectFocusedOption} + placeholder={this.props.placeholderText} + maxLength={this.props.maxLength} + keyboardType={this.props.keyboardType} + onBlur={(e) => { + if (!this.props.shouldFocusOnSelectRow) { + return; + } + this.relatedTarget = e.relatedTarget; + }} + selectTextOnFocus + blurOnSubmit={Boolean(this.state.allOptions.length)} + spellCheck={false} + /> + {this.props.textInputAlert && ( + + )} + + + ); const optionsList = ( - (this.list = el)} - optionHoveredStyle={this.props.optionHoveredStyle} - onSelectRow={this.props.onSelectRow ? this.selectRow : undefined} - sections={this.props.sections} - focusedIndex={this.state.focusedIndex} - selectedOptions={this.props.selectedOptions} - canSelectMultipleOptions={this.props.canSelectMultipleOptions} - shouldShowMultipleOptionSelectorAsButton={this.props.shouldShowMultipleOptionSelectorAsButton} - multipleOptionSelectorButtonText={this.props.multipleOptionSelectorButtonText} - onAddToSelection={this.props.onAddToSelection} - hideSectionHeaders={this.props.hideSectionHeaders} - headerMessage={this.props.headerMessage} - boldStyle={this.props.boldStyle} - showTitleTooltip={this.props.showTitleTooltip} - isDisabled={this.props.isDisabled} - shouldHaveOptionSeparator={this.props.shouldHaveOptionSeparator} - highlightSelectedOptions={this.props.highlightSelectedOptions} - onLayout={() => { - if (this.props.selectedOptions.length === 0) { - this.scrollToIndex(this.state.focusedIndex, false); - } - - if (this.props.onLayout) { - this.props.onLayout(); - } - }} - contentContainerStyles={[safeAreaPaddingBottomStyle, ...this.props.contentContainerStyles]} - listContainerStyles={this.props.listContainerStyles} - listStyles={this.props.listStyles} - isLoading={!this.props.shouldShowOptions} - showScrollIndicator={this.props.showScrollIndicator} - isRowMultilineSupported={this.props.isRowMultilineSupported} - /> + <> + {this.props.shouldShowLoader && } + (this.list = el)} + optionHoveredStyle={this.props.optionHoveredStyle} + onSelectRow={this.props.onSelectRow ? this.selectRow : undefined} + sections={this.props.sections} + focusedIndex={this.state.focusedIndex} + selectedOptions={this.props.selectedOptions} + canSelectMultipleOptions={this.props.canSelectMultipleOptions} + shouldShowMultipleOptionSelectorAsButton={this.props.shouldShowMultipleOptionSelectorAsButton} + multipleOptionSelectorButtonText={this.props.multipleOptionSelectorButtonText} + onAddToSelection={this.props.onAddToSelection} + hideSectionHeaders={this.props.hideSectionHeaders} + headerMessage={this.props.headerMessage} + boldStyle={this.props.boldStyle} + showTitleTooltip={this.props.showTitleTooltip} + isDisabled={this.props.isDisabled} + shouldHaveOptionSeparator={this.props.shouldHaveOptionSeparator} + highlightSelectedOptions={this.props.highlightSelectedOptions} + onLayout={() => { + if (this.props.selectedOptions.length === 0) { + this.scrollToIndex(this.state.focusedIndex, false); + } + + if (this.props.onLayout) { + this.props.onLayout(); + } + }} + contentContainerStyles={[safeAreaPaddingBottomStyle, ...this.props.contentContainerStyles]} + listContainerStyles={this.props.listContainerStyles} + listStyles={this.props.listStyles} + isLoading={!this.props.shouldShowOptions} + showScrollIndicator={this.props.showScrollIndicator} + isRowMultilineSupported={this.props.isRowMultilineSupported} + /> + ); return ( 0) { + this.throttledSearchInServer(searchValue); + } + + // When the user searches we will this.setState({searchValue}, this.debouncedUpdateOptions); } @@ -116,6 +123,17 @@ class SearchPage extends Component { return sections; } + /** + * @param {string} searchValue + */ + searchInServer(searchValue) { + if (this.props.network.isOffline) { + return; + } + + Report.searchInServer(searchValue); + } + searchRendered() { Timing.end(CONST.TIMING.SEARCH_RENDER); Performance.markEnd(CONST.TIMING.SEARCH_RENDER); @@ -184,9 +202,11 @@ class SearchPage extends Component { showTitleTooltip shouldShowOptions={didScreenTransitionEnd && isOptionsDataReady} textInputLabel={this.props.translate('optionsSelector.nameEmailOrPhoneNumber')} + textInputAlert={this.props.network.isOffline ? 'You appear to be offline. Search results are limited until you come back online.' : ''} onLayout={this.searchRendered} safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle} autoFocus + shouldShowLoader={this.props.isSearchingForReports} /> @@ -212,5 +232,12 @@ export default compose( betas: { key: ONYXKEYS.BETAS, }, + isSearchingForReports: { + key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS, + initWithStoredValues: false, + }, + network: { + key: ONYXKEYS.NETWORK, + } }), )(SearchPage); From 179f9e064fbcac6e3cc371353484dd681ff5165d Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Wed, 20 Sep 2023 11:58:40 +0800 Subject: [PATCH 037/856] Update `react-native-vision-camera` --- package-lock.json | 14 +++++++------- package.json | 2 +- patches/react-native-vision-camera+2.15.4.patch | 13 ------------- 3 files changed, 8 insertions(+), 21 deletions(-) delete mode 100644 patches/react-native-vision-camera+2.15.4.patch diff --git a/package-lock.json b/package-lock.json index 68a2a5c7a064..5c6bf4e268f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -107,7 +107,7 @@ "react-native-tab-view": "^3.5.2", "react-native-url-polyfill": "^2.0.0", "react-native-view-shot": "^3.6.0", - "react-native-vision-camera": "^2.15.4", + "react-native-vision-camera": "^2.16.1", "react-native-web-linear-gradient": "^1.1.2", "react-native-web-lottie": "^1.4.4", "react-native-webview": "^11.17.2", @@ -41479,9 +41479,9 @@ } }, "node_modules/react-native-vision-camera": { - "version": "2.15.4", - "resolved": "https://registry.npmjs.org/react-native-vision-camera/-/react-native-vision-camera-2.15.4.tgz", - "integrity": "sha512-SJXSWH1pu4V3Kj4UuX/vSgOxc9d5wb5+nHqBHd+5iUtVyVLEp0F6Jbbaha7tDoU+kUBwonhlwr2o8oV6NZ7Ibg==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/react-native-vision-camera/-/react-native-vision-camera-2.16.1.tgz", + "integrity": "sha512-2utw/1EkgeaL6CLQX9Bm6HEpkmkbyx/dBJmwjA2nVz3IuXn5ys57NwMa0o/qg8proSGvcs8XKpqtQtnMPA1P+w==", "peerDependencies": { "react": "*", "react-native": "*" @@ -77426,9 +77426,9 @@ "requires": {} }, "react-native-vision-camera": { - "version": "2.15.4", - "resolved": "https://registry.npmjs.org/react-native-vision-camera/-/react-native-vision-camera-2.15.4.tgz", - "integrity": "sha512-SJXSWH1pu4V3Kj4UuX/vSgOxc9d5wb5+nHqBHd+5iUtVyVLEp0F6Jbbaha7tDoU+kUBwonhlwr2o8oV6NZ7Ibg==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/react-native-vision-camera/-/react-native-vision-camera-2.16.1.tgz", + "integrity": "sha512-2utw/1EkgeaL6CLQX9Bm6HEpkmkbyx/dBJmwjA2nVz3IuXn5ys57NwMa0o/qg8proSGvcs8XKpqtQtnMPA1P+w==", "requires": {} }, "react-native-web": { diff --git a/package.json b/package.json index 1b69b7bd8a49..d921a949c6a2 100644 --- a/package.json +++ b/package.json @@ -149,7 +149,7 @@ "react-native-tab-view": "^3.5.2", "react-native-url-polyfill": "^2.0.0", "react-native-view-shot": "^3.6.0", - "react-native-vision-camera": "^2.15.4", + "react-native-vision-camera": "^2.16.1", "react-native-web-linear-gradient": "^1.1.2", "react-native-web-lottie": "^1.4.4", "react-native-webview": "^11.17.2", diff --git a/patches/react-native-vision-camera+2.15.4.patch b/patches/react-native-vision-camera+2.15.4.patch deleted file mode 100644 index 0c80d6a8ce55..000000000000 --- a/patches/react-native-vision-camera+2.15.4.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/node_modules/react-native-vision-camera/ios/Frame Processor/FrameProcessorRuntimeManager.mm b/node_modules/react-native-vision-camera/ios/Frame Processor/FrameProcessorRuntimeManager.mm -index 3841b20..687ea94 100644 ---- a/node_modules/react-native-vision-camera/ios/Frame Processor/FrameProcessorRuntimeManager.mm -+++ b/node_modules/react-native-vision-camera/ios/Frame Processor/FrameProcessorRuntimeManager.mm -@@ -19,6 +19,8 @@ - #import - #import - -+#define VISION_CAMERA_DISABLE_FRAME_PROCESSORS 1 -+ - #ifndef VISION_CAMERA_DISABLE_FRAME_PROCESSORS - #if __has_include() - #if __has_include() From dae32f291bf7793d257cf85874b8752968b77ce7 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Wed, 20 Sep 2023 12:52:44 +0800 Subject: [PATCH 038/856] Remove Text --- src/components/OptionsSelector/BaseOptionsSelector.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/OptionsSelector/BaseOptionsSelector.js b/src/components/OptionsSelector/BaseOptionsSelector.js index 0a370cd7b2d5..022eaab1ba58 100755 --- a/src/components/OptionsSelector/BaseOptionsSelector.js +++ b/src/components/OptionsSelector/BaseOptionsSelector.js @@ -18,7 +18,6 @@ import setSelection from '../../libs/setSelection'; import compose from '../../libs/compose'; import getPlatform from '../../libs/getPlatform'; import OptionsListSkeletonRow from '../OptionsListSkeletonRow'; -import Text from '../Text'; import FormHelpMessage from '../FormHelpMessage'; const propTypes = { From c0e5658b4092bb23282ddb6169bff290d06fcbba Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Wed, 20 Sep 2023 12:53:03 +0800 Subject: [PATCH 039/856] Add missing file --- src/components/OptionsListSkeletonRow.js | 48 ++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/components/OptionsListSkeletonRow.js diff --git a/src/components/OptionsListSkeletonRow.js b/src/components/OptionsListSkeletonRow.js new file mode 100644 index 000000000000..9df9a610bc14 --- /dev/null +++ b/src/components/OptionsListSkeletonRow.js @@ -0,0 +1,48 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import {Rect, Circle} from 'react-native-svg'; +import SkeletonViewContentLoader from 'react-content-loader/native'; +import themeColors from '../styles/themes/default'; +import CONST from '../CONST'; +import styles from '../styles/styles'; + +const propTypes = { + /** Whether to animate the skeleton view */ + shouldAnimate: PropTypes.bool.isRequired, + + /** Line width string */ + lineWidth: PropTypes.string.isRequired, +}; + +function OptionsListSkeletonRow({lineWidth, shouldAnimate}) { + return ( + + + + + + ); +} + +OptionsListSkeletonRow.propTypes = propTypes; +export default OptionsListSkeletonRow; \ No newline at end of file From 61e4199178325c68b07ae16ce567d4340b01fc86 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Wed, 20 Sep 2023 13:01:52 +0800 Subject: [PATCH 040/856] Run prettier --- src/components/OptionsListSkeletonRow.js | 2 +- .../OptionsSelector/BaseOptionsSelector.js | 9 ++-- src/libs/actions/Report.js | 50 ++++++++++--------- src/pages/SearchPage.js | 2 +- 4 files changed, 35 insertions(+), 28 deletions(-) diff --git a/src/components/OptionsListSkeletonRow.js b/src/components/OptionsListSkeletonRow.js index 9df9a610bc14..d7968ef1ba80 100644 --- a/src/components/OptionsListSkeletonRow.js +++ b/src/components/OptionsListSkeletonRow.js @@ -45,4 +45,4 @@ function OptionsListSkeletonRow({lineWidth, shouldAnimate}) { } OptionsListSkeletonRow.propTypes = propTypes; -export default OptionsListSkeletonRow; \ No newline at end of file +export default OptionsListSkeletonRow; diff --git a/src/components/OptionsSelector/BaseOptionsSelector.js b/src/components/OptionsSelector/BaseOptionsSelector.js index 022eaab1ba58..760f86db799a 100755 --- a/src/components/OptionsSelector/BaseOptionsSelector.js +++ b/src/components/OptionsSelector/BaseOptionsSelector.js @@ -375,13 +375,16 @@ class BaseOptionsSelector extends Component { isError={false} /> )} - - ); const optionsList = ( <> - {this.props.shouldShowLoader && } + {this.props.shouldShowLoader && ( + + )} (this.list = el)} optionHoveredStyle={this.props.optionHoveredStyle} diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 51fe69acb6fc..016b5928a8f6 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -2063,29 +2063,33 @@ function clearPrivateNotesError(reportID, accountID) { * @param {string} searchInput */ function searchInServer(searchInput) { - API.read('SearchForReports', {searchInput}, { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS, - value: true, - } - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS, - value: false, - } - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS, - value: false, - } - ] - }); + API.read( + 'SearchForReports', + {searchInput}, + { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS, + value: true, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS, + value: false, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS, + value: false, + }, + ], + }, + ); } export { diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index fb95f3d24f7e..432c64d51be5 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -238,6 +238,6 @@ export default compose( }, network: { key: ONYXKEYS.NETWORK, - } + }, }), )(SearchPage); From f3087eeb74b2c727a229f85d713c5f199880682f Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Wed, 20 Sep 2023 16:51:39 +0800 Subject: [PATCH 041/856] Remove old version of custom header --- src/components/AttachmentModal.js | 1 - src/components/ReceiptAttachmentHeader.js | 85 ----------------------- src/styles/styles.js | 4 -- 3 files changed, 90 deletions(-) delete mode 100644 src/components/ReceiptAttachmentHeader.js diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index eacf8780b6e2..946b5e2ddec9 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -18,7 +18,6 @@ import compose from '../libs/compose'; import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions'; import Button from './Button'; import HeaderWithBackButton from './HeaderWithBackButton'; -import ReceiptAttachmentHeader from './ReceiptAttachmentHeader'; import fileDownload from '../libs/fileDownload'; import withLocalize, {withLocalizePropTypes} from './withLocalize'; import ConfirmModal from './ConfirmModal'; diff --git a/src/components/ReceiptAttachmentHeader.js b/src/components/ReceiptAttachmentHeader.js deleted file mode 100644 index 4927c3fe458e..000000000000 --- a/src/components/ReceiptAttachmentHeader.js +++ /dev/null @@ -1,85 +0,0 @@ -import React, {useState, useCallback} from 'react'; -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; -import {View} from 'react-native'; -import HeaderWithBackButton from './HeaderWithBackButton'; -import * as Expensicons from './Icon/Expensicons'; -import styles from '../styles/styles'; -import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions'; -import ConfirmModal from './ConfirmModal'; -import useLocalize from '../hooks/useLocalize'; -import ReceiptActions from '../libs/actions/Receipt'; -import * as ReportActionsUtils from '../libs/ReportActionsUtils'; - -const propTypes = { - /** The report currently being looked at */ - report: PropTypes.shape({ - reportID: PropTypes.string.isRequired, - parentReportID: PropTypes.string.isRequired, - parentReportActionID: PropTypes.string.isRequired, - }).isRequired, - - /** Method to trigger when pressing back button of the header */ - onBackButtonPress: PropTypes.func.isRequired, - - /** Method to trigger when pressing close button of the header */ - onCloseButtonPress: PropTypes.func.isRequired, - - ...windowDimensionsPropTypes, -}; - -function ReceiptAttachmentHeader(props) { - const {translate} = useLocalize(); - const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false); - - const deleteReceipt = useCallback(() => { - // Get receipt attachment transaction ID - const parentReportAction = ReportActionsUtils.getParentReportAction(props.report); - const transactionID = lodashGet(parentReportAction, ['originalMessage', 'IOUTransactionID'], 0); - - // Detatch receipt & clear modal open state - ReceiptActions.detachReceipt(transactionID, props.report.reportID); - setIsDeleteModalVisible(false); - }, [props.report, setIsDeleteModalVisible]); - - return ( - <> - - setIsDeleteModalVisible(true), - }, - ]} - threeDotsAnchorPosition={styles.threeDotsPopoverOffset(props.windowWidth, true)} - outerThreeDotsMenuStyle={styles.receiptAttachmentHeaderThreeDotsMenuStyles} - onBackButtonPress={props.onBackButtonPress} - onCloseButtonPress={props.onCloseButtonPress} - /> - - setIsDeleteModalVisible(false)} - prompt={translate('receipt.deleteConfirmation')} - confirmText={translate('common.delete')} - cancelText={translate('common.cancel')} - danger - /> - - ); -} - -ReceiptAttachmentHeader.displayName = 'ReceiptAttachmentHeader'; -ReceiptAttachmentHeader.propTypes = propTypes; - -export default withWindowDimensions(ReceiptAttachmentHeader); diff --git a/src/styles/styles.js b/src/styles/styles.js index fac4ce864cba..8b5d2b052cc7 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -2197,10 +2197,6 @@ const styles = (theme) => ({ width: '100%', }, - receiptAttachmentHeaderThreeDotsMenuStyles: { - zIndex: variables.popoverModalzIndex, - }, - defaultAttachmentView: { backgroundColor: theme.sidebar, borderRadius: variables.componentBorderRadiusNormal, From 7c83e8c7d59cdba6e60542949dbd427d995ead9b Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Wed, 20 Sep 2023 16:53:57 +0800 Subject: [PATCH 042/856] Revert a few style updates --- .../HeaderWithBackButton/headerWithBackButtonPropTypes.js | 3 --- src/components/HeaderWithBackButton/index.js | 1 - src/components/PopoverMenu/index.js | 5 ----- src/components/ThreeDotsMenu/index.js | 7 +------ src/styles/styles.js | 4 ++-- 5 files changed, 3 insertions(+), 17 deletions(-) diff --git a/src/components/HeaderWithBackButton/headerWithBackButtonPropTypes.js b/src/components/HeaderWithBackButton/headerWithBackButtonPropTypes.js index b3248f2447eb..d2cdc5b29898 100644 --- a/src/components/HeaderWithBackButton/headerWithBackButtonPropTypes.js +++ b/src/components/HeaderWithBackButton/headerWithBackButtonPropTypes.js @@ -48,9 +48,6 @@ const propTypes = { left: PropTypes.number, }), - /** Outer style that gets passed down to Modal */ - outerThreeDotsMenuStyle: PropTypes.oneOfType([PropTypes.array, PropTypes.object]), - /** Whether we should show a close button */ shouldShowCloseButton: PropTypes.bool, diff --git a/src/components/HeaderWithBackButton/index.js b/src/components/HeaderWithBackButton/index.js index 27b4a9f2408b..cf61a4cf4eb7 100755 --- a/src/components/HeaderWithBackButton/index.js +++ b/src/components/HeaderWithBackButton/index.js @@ -46,7 +46,6 @@ function HeaderWithBackButton({ horizontal: 0, }, threeDotsMenuItems = [], - outerThreeDotsMenuStyle = {}, children = null, shouldOverlay = false, }) { diff --git a/src/components/PopoverMenu/index.js b/src/components/PopoverMenu/index.js index bd913d3f0a9b..5fabf73547ea 100644 --- a/src/components/PopoverMenu/index.js +++ b/src/components/PopoverMenu/index.js @@ -27,9 +27,6 @@ const propTypes = { /** Ref of the anchor */ anchorRef: refPropTypes, - /** Outer style of popover that passes down to Modal */ - outerStyle: PropTypes.object, - /** Where the popover should be positioned relative to the anchor points. */ anchorAlignment: PropTypes.shape({ horizontal: PropTypes.oneOf(_.values(CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL)), @@ -45,7 +42,6 @@ const defaultProps = { horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM, }, - outerStyle: {}, anchorRef: () => {}, withoutOverlay: false, }; @@ -93,7 +89,6 @@ function PopoverMenu(props) { disableAnimation={props.disableAnimation} fromSidebarMediumScreen={props.fromSidebarMediumScreen} withoutOverlay={props.withoutOverlay} - outerStyle={props.outerStyle} > {!_.isEmpty(props.headerText) && {props.headerText}} diff --git a/src/components/ThreeDotsMenu/index.js b/src/components/ThreeDotsMenu/index.js index efef3bb6a8c8..f0cee6fdea2f 100644 --- a/src/components/ThreeDotsMenu/index.js +++ b/src/components/ThreeDotsMenu/index.js @@ -29,9 +29,6 @@ const propTypes = { /** Function to call on icon press */ onIconPress: PropTypes.func, - /** Outer styles that get passed down to Modal */ - outerStyle: PropTypes.object, - /** menuItems that'll show up on toggle of the popup menu */ menuItems: ThreeDotsMenuItemPropTypes.isRequired, @@ -59,7 +56,6 @@ const defaultProps = { iconStyles: [], icon: Expensicons.ThreeDots, onIconPress: () => {}, - outerStyle: {}, anchorAlignment: { horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP, // we assume that popover menu opens below the button, anchor is at TOP @@ -67,7 +63,7 @@ const defaultProps = { shouldOverlay: false, }; -function ThreeDotsMenu({iconTooltip, icon, iconFill, iconStyles, onIconPress, outerStyle, menuItems, anchorPosition, anchorAlignment, shouldOverlay}) { +function ThreeDotsMenu({iconTooltip, icon, iconFill, iconStyles, onIconPress, menuItems, anchorPosition, anchorAlignment, shouldOverlay}) { const [isPopupMenuVisible, setPopupMenuVisible] = useState(false); const buttonRef = useRef(null); const {translate} = useLocalize(); @@ -116,7 +112,6 @@ function ThreeDotsMenu({iconTooltip, icon, iconFill, iconStyles, onIconPress, ou menuItems={menuItems} withoutOverlay={!shouldOverlay} anchorRef={buttonRef} - outerStyle={outerStyle} /> ); diff --git a/src/styles/styles.js b/src/styles/styles.js index 8b5d2b052cc7..01689a43952a 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -3177,8 +3177,8 @@ const styles = (theme) => ({ flex: 1, }, - threeDotsPopoverOffset: (windowWidth, isWithinFullScreenModal) => ({ - ...getPopOverVerticalOffset(60 + (isWithinFullScreenModal ? 20 : 0)), + threeDotsPopoverOffset: (windowWidth) => ({ + ...getPopOverVerticalOffset(60), horizontal: windowWidth - 60, }), From eaa6671829478be268f9820185241417806588ae Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Wed, 20 Sep 2023 16:54:34 +0800 Subject: [PATCH 043/856] Revert unused var --- src/styles/variables.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/styles/variables.ts b/src/styles/variables.ts index 0c1183e00768..6291323cef0b 100644 --- a/src/styles/variables.ts +++ b/src/styles/variables.ts @@ -83,7 +83,6 @@ export default { sideBarWidth: 375, pdfPageMaxWidth: 992, tooltipzIndex: 10050, - popoverModalzIndex: 9999, gutterWidth: 12, popoverMenuShadow: '0px 4px 12px 0px rgba(0, 0, 0, 0.06)', optionRowHeight: 64, From ca054cfb9ec65b07f49e8656cc916ee873b35ca4 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Wed, 20 Sep 2023 18:04:52 +0800 Subject: [PATCH 044/856] Connect detachReceipt with transactionID --- src/components/AttachmentModal.js | 35 ++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index 946b5e2ddec9..68cee61bbc9b 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -1,6 +1,7 @@ import React, {useState, useCallback, useRef} from 'react'; import PropTypes from 'prop-types'; import {View, Animated, Keyboard} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; import Str from 'expensify-common/lib/str'; import lodashGet from 'lodash/get'; import lodashExtend from 'lodash/extend'; @@ -30,6 +31,8 @@ import useWindowDimensions from '../hooks/useWindowDimensions'; import Navigation from '../libs/Navigation/Navigation'; import ROUTES from '../ROUTES'; import useNativeDriver from '../libs/useNativeDriver'; +import Receipt from '../libs/actions/Receipt'; +import ONYXKEYS from '../ONYXKEYS'; /** * Modal render prop component that exposes modal launching triggers that can be used @@ -79,6 +82,13 @@ const propTypes = { /** Denotes whether it is a workspace avatar or not */ isWorkspaceAvatar: PropTypes.bool, + + /* Onyx Props */ + /** The parent report */ + parentReport: reportPropTypes, + + /** The report action this report is tied to from the parent report */ + parentReportAction: PropTypes.shape(reportActionPropTypes), }; const defaultProps = { @@ -95,6 +105,8 @@ const defaultProps = { onModalHide: () => {}, onCarouselAttachmentChange: () => {}, isWorkspaceAvatar: false, + parentReport: {}, + parentReportAction: {}, }; function AttachmentModal(props) { @@ -372,6 +384,14 @@ function AttachmentModal(props) { text: props.translate('common.download'), onSelected: () => downloadAttachment(source), }, + { + icon: Expensicons.Trashcan, + text: props.translate('receipt.deleteReceipt'), + onSelected: () => { + const transactionID = lodashGet(props.parentReportAction, 'originalMessage.IOUTransactionID', ''); + Receipt.detachReceipt(transactionID, props.report.reportID) + }, + }, ]} shouldOverlay /> @@ -441,4 +461,17 @@ function AttachmentModal(props) { AttachmentModal.propTypes = propTypes; AttachmentModal.defaultProps = defaultProps; AttachmentModal.displayName = 'AttachmentModal'; -export default compose(withWindowDimensions, withLocalize)(AttachmentModal); +export default compose( + withWindowDimensions, + withLocalize, + withOnyx({ + parentReport: { + key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`, + }, + parentReportAction: { + key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${(report.parentReportID, report.parentReportActionID)}`, + selector: (reportActions, props) => props && props.parentReport && reportActions && reportActions[props.parentReport.parentReportActionID], + canEvict: false, + }, + }), +)(AttachmentModal); From 88aff92a40ed443aaf7dac1881febecfe7d851f1 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 20 Sep 2023 13:09:29 +0100 Subject: [PATCH 045/856] ref: moved local file download to TS --- .../{index.android.js => index.android.ts} | 27 ++++++--------- .../{index.ios.js => index.ios.ts} | 33 +++++++------------ src/libs/fileDownload/{index.js => index.ts} | 9 ++--- 3 files changed, 24 insertions(+), 45 deletions(-) rename src/libs/fileDownload/{index.android.js => index.android.ts} (83%) rename src/libs/fileDownload/{index.ios.js => index.ios.ts} (78%) rename src/libs/fileDownload/{index.js => index.ts} (85%) diff --git a/src/libs/fileDownload/index.android.js b/src/libs/fileDownload/index.android.ts similarity index 83% rename from src/libs/fileDownload/index.android.js rename to src/libs/fileDownload/index.android.ts index c19301cbde49..a57a4bed71fd 100644 --- a/src/libs/fileDownload/index.android.js +++ b/src/libs/fileDownload/index.android.ts @@ -1,15 +1,14 @@ import {PermissionsAndroid, Platform} from 'react-native'; -import RNFetchBlob from 'react-native-blob-util'; +import RNFetchBlob, {FetchBlobResponse} from 'react-native-blob-util'; import * as FileUtils from './FileUtils'; /** * Android permission check to store images - * @returns {Promise} */ function hasAndroidPermission() { // On Android API Level 33 and above, these permissions do nothing and always return 'never_ask_again' // More info here: https://stackoverflow.com/a/74296799 - if (Platform.Version >= 33) { + if (Number(Platform.Version) >= 33) { return Promise.resolve(true); } @@ -31,12 +30,9 @@ function hasAndroidPermission() { /** * Handling the download - * @param {String} url - * @param {String} fileName - * @returns {Promise} */ -function handleDownload(url, fileName) { - return new Promise((resolve) => { +function handleDownload(url: string, fileName: string) { + return new Promise((resolve) => { const dirs = RNFetchBlob.fs.dirs; // Android files will download to Download directory @@ -46,7 +42,7 @@ function handleDownload(url, fileName) { const isLocalFile = url.startsWith('file://'); let attachmentPath = isLocalFile ? url : undefined; - let fetchedAttachment = Promise.resolve(); + let fetchedAttachment: Promise = Promise.resolve(); if (!isLocalFile) { // Fetching the attachment @@ -68,7 +64,7 @@ function handleDownload(url, fileName) { return Promise.reject(); } - if (!isLocalFile) attachmentPath = attachment.path(); + if (!isLocalFile) attachmentPath = attachment?.path() ?? ''; return RNFetchBlob.MediaCollection.copyToMediaStore( { @@ -77,11 +73,11 @@ function handleDownload(url, fileName) { mimeType: null, }, 'Download', - attachmentPath, + String(attachmentPath), ); }) .then(() => { - RNFetchBlob.fs.unlink(attachmentPath); + RNFetchBlob.fs.unlink(String(attachmentPath)); FileUtils.showSuccessAlert(); }) .catch(() => { @@ -93,12 +89,9 @@ function handleDownload(url, fileName) { /** * Checks permission and downloads the file for Android - * @param {String} url - * @param {String} fileName - * @returns {Promise} */ -export default function fileDownload(url, fileName) { - return new Promise((resolve) => { +export default function fileDownload(url: string, fileName: string) { + return new Promise((resolve) => { hasAndroidPermission() .then((hasPermission) => { if (hasPermission) { diff --git a/src/libs/fileDownload/index.ios.js b/src/libs/fileDownload/index.ios.ts similarity index 78% rename from src/libs/fileDownload/index.ios.js rename to src/libs/fileDownload/index.ios.ts index 95d92f1e3103..b0540da95a33 100644 --- a/src/libs/fileDownload/index.ios.js +++ b/src/libs/fileDownload/index.ios.ts @@ -1,16 +1,12 @@ import RNFetchBlob from 'react-native-blob-util'; import {CameraRoll} from '@react-native-camera-roll/camera-roll'; -import lodashGet from 'lodash/get'; import * as FileUtils from './FileUtils'; import CONST from '../../CONST'; /** * Downloads the file to Documents section in iOS - * @param {String} fileUrl - * @param {String} fileName - * @returns {Promise} */ -function downloadFile(fileUrl, fileName) { +function downloadFile(fileUrl: string, fileName: string) { const dirs = RNFetchBlob.fs.dirs; // The iOS files will download to documents directory @@ -31,29 +27,25 @@ function downloadFile(fileUrl, fileName) { /** * Download the image to photo lib in iOS - * @param {String} fileUrl - * @param {String} fileName - * @returns {String} URI + * @returns URI */ -function downloadImage(fileUrl) { +function downloadImage(fileUrl: string) { return CameraRoll.save(fileUrl); } /** * Download the video to photo lib in iOS - * @param {String} fileUrl - * @param {String} fileName - * @returns {String} URI + * @returns URI */ -function downloadVideo(fileUrl, fileName) { +function downloadVideo(fileUrl: string, fileName: string): Promise { return new Promise((resolve, reject) => { - let documentPathUri = null; - let cameraRollUri = null; + let documentPathUri: string; + let cameraRollUri: string; // Because CameraRoll doesn't allow direct downloads of video with remote URIs, we first download as documents, then copy to photo lib and unlink the original file. downloadFile(fileUrl, fileName) .then((attachment) => { - documentPathUri = lodashGet(attachment, 'data'); + documentPathUri = attachment?.data; return CameraRoll.save(documentPathUri); }) .then((attachment) => { @@ -67,19 +59,16 @@ function downloadVideo(fileUrl, fileName) { /** * Download the file based on type(image, video, other file types)for iOS - * @param {String} fileUrl - * @param {String} fileName - * @returns {Promise} */ -export default function fileDownload(fileUrl, fileName) { - return new Promise((resolve) => { +export default function fileDownload(fileUrl: string, fileName: string) { + return new Promise((resolve) => { let fileDownloadPromise = null; const fileType = FileUtils.getFileType(fileUrl); const attachmentName = FileUtils.appendTimeToFileName(fileName) || FileUtils.getAttachmentName(fileUrl); switch (fileType) { case CONST.ATTACHMENT_FILE_TYPE.IMAGE: - fileDownloadPromise = downloadImage(fileUrl, attachmentName); + fileDownloadPromise = downloadImage(fileUrl); break; case CONST.ATTACHMENT_FILE_TYPE.VIDEO: fileDownloadPromise = downloadVideo(fileUrl, attachmentName); diff --git a/src/libs/fileDownload/index.js b/src/libs/fileDownload/index.ts similarity index 85% rename from src/libs/fileDownload/index.js rename to src/libs/fileDownload/index.ts index a775576eb365..81e78f6baa9a 100644 --- a/src/libs/fileDownload/index.js +++ b/src/libs/fileDownload/index.ts @@ -3,12 +3,9 @@ import * as Link from '../actions/Link'; /** * Downloading attachment in web, desktop - * @param {String} url - * @param {String} fileName - * @returns {Promise} */ -export default function fileDownload(url, fileName) { - return new Promise((resolve) => { +export default function fileDownload(url: string, fileName: string) { + return new Promise((resolve) => { fetch(url) .then((response) => response.blob()) .then((blob) => { @@ -34,7 +31,7 @@ export default function fileDownload(url, fileName) { // Clean up and remove the link URL.revokeObjectURL(link.href); - link.parentNode.removeChild(link); + link.parentNode?.removeChild(link); return resolve(); }) .catch(() => { From 61b1e6ef63a84c91c9e208f2d58ac856c7953680 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 20 Sep 2023 13:47:15 +0100 Subject: [PATCH 046/856] fix: undo wrongly migrated file download file to TS --- .../{index.android.ts => index.android.js} | 27 +++++++++------ .../{index.ios.ts => index.ios.js} | 33 ++++++++++++------- src/libs/fileDownload/{index.ts => index.js} | 9 +++-- 3 files changed, 45 insertions(+), 24 deletions(-) rename src/libs/fileDownload/{index.android.ts => index.android.js} (83%) rename src/libs/fileDownload/{index.ios.ts => index.ios.js} (78%) rename src/libs/fileDownload/{index.ts => index.js} (85%) diff --git a/src/libs/fileDownload/index.android.ts b/src/libs/fileDownload/index.android.js similarity index 83% rename from src/libs/fileDownload/index.android.ts rename to src/libs/fileDownload/index.android.js index a57a4bed71fd..c19301cbde49 100644 --- a/src/libs/fileDownload/index.android.ts +++ b/src/libs/fileDownload/index.android.js @@ -1,14 +1,15 @@ import {PermissionsAndroid, Platform} from 'react-native'; -import RNFetchBlob, {FetchBlobResponse} from 'react-native-blob-util'; +import RNFetchBlob from 'react-native-blob-util'; import * as FileUtils from './FileUtils'; /** * Android permission check to store images + * @returns {Promise} */ function hasAndroidPermission() { // On Android API Level 33 and above, these permissions do nothing and always return 'never_ask_again' // More info here: https://stackoverflow.com/a/74296799 - if (Number(Platform.Version) >= 33) { + if (Platform.Version >= 33) { return Promise.resolve(true); } @@ -30,9 +31,12 @@ function hasAndroidPermission() { /** * Handling the download + * @param {String} url + * @param {String} fileName + * @returns {Promise} */ -function handleDownload(url: string, fileName: string) { - return new Promise((resolve) => { +function handleDownload(url, fileName) { + return new Promise((resolve) => { const dirs = RNFetchBlob.fs.dirs; // Android files will download to Download directory @@ -42,7 +46,7 @@ function handleDownload(url: string, fileName: string) { const isLocalFile = url.startsWith('file://'); let attachmentPath = isLocalFile ? url : undefined; - let fetchedAttachment: Promise = Promise.resolve(); + let fetchedAttachment = Promise.resolve(); if (!isLocalFile) { // Fetching the attachment @@ -64,7 +68,7 @@ function handleDownload(url: string, fileName: string) { return Promise.reject(); } - if (!isLocalFile) attachmentPath = attachment?.path() ?? ''; + if (!isLocalFile) attachmentPath = attachment.path(); return RNFetchBlob.MediaCollection.copyToMediaStore( { @@ -73,11 +77,11 @@ function handleDownload(url: string, fileName: string) { mimeType: null, }, 'Download', - String(attachmentPath), + attachmentPath, ); }) .then(() => { - RNFetchBlob.fs.unlink(String(attachmentPath)); + RNFetchBlob.fs.unlink(attachmentPath); FileUtils.showSuccessAlert(); }) .catch(() => { @@ -89,9 +93,12 @@ function handleDownload(url: string, fileName: string) { /** * Checks permission and downloads the file for Android + * @param {String} url + * @param {String} fileName + * @returns {Promise} */ -export default function fileDownload(url: string, fileName: string) { - return new Promise((resolve) => { +export default function fileDownload(url, fileName) { + return new Promise((resolve) => { hasAndroidPermission() .then((hasPermission) => { if (hasPermission) { diff --git a/src/libs/fileDownload/index.ios.ts b/src/libs/fileDownload/index.ios.js similarity index 78% rename from src/libs/fileDownload/index.ios.ts rename to src/libs/fileDownload/index.ios.js index b0540da95a33..95d92f1e3103 100644 --- a/src/libs/fileDownload/index.ios.ts +++ b/src/libs/fileDownload/index.ios.js @@ -1,12 +1,16 @@ import RNFetchBlob from 'react-native-blob-util'; import {CameraRoll} from '@react-native-camera-roll/camera-roll'; +import lodashGet from 'lodash/get'; import * as FileUtils from './FileUtils'; import CONST from '../../CONST'; /** * Downloads the file to Documents section in iOS + * @param {String} fileUrl + * @param {String} fileName + * @returns {Promise} */ -function downloadFile(fileUrl: string, fileName: string) { +function downloadFile(fileUrl, fileName) { const dirs = RNFetchBlob.fs.dirs; // The iOS files will download to documents directory @@ -27,25 +31,29 @@ function downloadFile(fileUrl: string, fileName: string) { /** * Download the image to photo lib in iOS - * @returns URI + * @param {String} fileUrl + * @param {String} fileName + * @returns {String} URI */ -function downloadImage(fileUrl: string) { +function downloadImage(fileUrl) { return CameraRoll.save(fileUrl); } /** * Download the video to photo lib in iOS - * @returns URI + * @param {String} fileUrl + * @param {String} fileName + * @returns {String} URI */ -function downloadVideo(fileUrl: string, fileName: string): Promise { +function downloadVideo(fileUrl, fileName) { return new Promise((resolve, reject) => { - let documentPathUri: string; - let cameraRollUri: string; + let documentPathUri = null; + let cameraRollUri = null; // Because CameraRoll doesn't allow direct downloads of video with remote URIs, we first download as documents, then copy to photo lib and unlink the original file. downloadFile(fileUrl, fileName) .then((attachment) => { - documentPathUri = attachment?.data; + documentPathUri = lodashGet(attachment, 'data'); return CameraRoll.save(documentPathUri); }) .then((attachment) => { @@ -59,16 +67,19 @@ function downloadVideo(fileUrl: string, fileName: string): Promise} */ -export default function fileDownload(fileUrl: string, fileName: string) { - return new Promise((resolve) => { +export default function fileDownload(fileUrl, fileName) { + return new Promise((resolve) => { let fileDownloadPromise = null; const fileType = FileUtils.getFileType(fileUrl); const attachmentName = FileUtils.appendTimeToFileName(fileName) || FileUtils.getAttachmentName(fileUrl); switch (fileType) { case CONST.ATTACHMENT_FILE_TYPE.IMAGE: - fileDownloadPromise = downloadImage(fileUrl); + fileDownloadPromise = downloadImage(fileUrl, attachmentName); break; case CONST.ATTACHMENT_FILE_TYPE.VIDEO: fileDownloadPromise = downloadVideo(fileUrl, attachmentName); diff --git a/src/libs/fileDownload/index.ts b/src/libs/fileDownload/index.js similarity index 85% rename from src/libs/fileDownload/index.ts rename to src/libs/fileDownload/index.js index 81e78f6baa9a..a775576eb365 100644 --- a/src/libs/fileDownload/index.ts +++ b/src/libs/fileDownload/index.js @@ -3,9 +3,12 @@ import * as Link from '../actions/Link'; /** * Downloading attachment in web, desktop + * @param {String} url + * @param {String} fileName + * @returns {Promise} */ -export default function fileDownload(url: string, fileName: string) { - return new Promise((resolve) => { +export default function fileDownload(url, fileName) { + return new Promise((resolve) => { fetch(url) .then((response) => response.blob()) .then((blob) => { @@ -31,7 +34,7 @@ export default function fileDownload(url: string, fileName: string) { // Clean up and remove the link URL.revokeObjectURL(link.href); - link.parentNode?.removeChild(link); + link.parentNode.removeChild(link); return resolve(); }) .catch(() => { From 7bd5be7df8da7107dabcab98f6043ac572a8626f Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 20 Sep 2023 13:47:58 +0100 Subject: [PATCH 047/856] ref: move local file download to TS --- .../localFileDownload/{index.android.js => index.android.ts} | 5 +---- src/libs/localFileDownload/{index.ios.js => index.ios.ts} | 5 +---- src/libs/localFileDownload/{index.js => index.ts} | 5 +---- 3 files changed, 3 insertions(+), 12 deletions(-) rename src/libs/localFileDownload/{index.android.js => index.android.ts} (89%) rename src/libs/localFileDownload/{index.ios.js => index.ios.ts} (84%) rename src/libs/localFileDownload/{index.js => index.ts} (80%) diff --git a/src/libs/localFileDownload/index.android.js b/src/libs/localFileDownload/index.android.ts similarity index 89% rename from src/libs/localFileDownload/index.android.js rename to src/libs/localFileDownload/index.android.ts index b3e39e7a7a53..10cb8c2e35dc 100644 --- a/src/libs/localFileDownload/index.android.js +++ b/src/libs/localFileDownload/index.android.ts @@ -5,11 +5,8 @@ import * as FileUtils from '../fileDownload/FileUtils'; * Writes a local file to the app's internal directory with the given fileName * and textContent, so we're able to copy it to the Android public download dir. * After the file is copied, it is removed from the internal dir. - * - * @param {String} fileName - * @param {String} textContent */ -export default function localFileDownload(fileName, textContent) { +export default function localFileDownload(fileName: string, textContent: string) { const newFileName = FileUtils.appendTimeToFileName(fileName); const dir = RNFetchBlob.fs.dirs.DocumentDir; const path = `${dir}/${newFileName}.txt`; diff --git a/src/libs/localFileDownload/index.ios.js b/src/libs/localFileDownload/index.ios.ts similarity index 84% rename from src/libs/localFileDownload/index.ios.js rename to src/libs/localFileDownload/index.ios.ts index 1241f5a535db..2d0d54b411ae 100644 --- a/src/libs/localFileDownload/index.ios.js +++ b/src/libs/localFileDownload/index.ios.ts @@ -6,11 +6,8 @@ import * as FileUtils from '../fileDownload/FileUtils'; * Writes a local file to the app's internal directory with the given fileName * and textContent, so we're able to share it using iOS' share API. * After the file is shared, it is removed from the internal dir. - * - * @param {String} fileName - * @param {String} textContent */ -export default function localFileDownload(fileName, textContent) { +export default function localFileDownload(fileName: string, textContent: string) { const newFileName = FileUtils.appendTimeToFileName(fileName); const dir = RNFetchBlob.fs.dirs.DocumentDir; const path = `${dir}/${newFileName}.txt`; diff --git a/src/libs/localFileDownload/index.js b/src/libs/localFileDownload/index.ts similarity index 80% rename from src/libs/localFileDownload/index.js rename to src/libs/localFileDownload/index.ts index 427928834c9c..42074cc9146f 100644 --- a/src/libs/localFileDownload/index.js +++ b/src/libs/localFileDownload/index.ts @@ -4,11 +4,8 @@ import * as FileUtils from '../fileDownload/FileUtils'; * Creates a Blob with the given fileName and textContent, then dynamically * creates a temporary anchor, just to programmatically click it, so the file * is downloaded by the browser. - * - * @param {String} fileName - * @param {String} textContent */ -export default function localFileDownload(fileName, textContent) { +export default function localFileDownload(fileName: string, textContent: string) { const blob = new Blob([textContent], {type: 'text/plain'}); const url = URL.createObjectURL(blob); const link = document.createElement('a'); From 9b4818eeb00c3cf99d18e6e97fa8b0d8d743b8b4 Mon Sep 17 00:00:00 2001 From: Thiago Brezinski Date: Wed, 20 Sep 2023 13:51:32 +0100 Subject: [PATCH 048/856] feat(workspace-settings): currency selector push to page --- src/ROUTES.ts | 2 + .../AppNavigator/ModalStackNavigators.js | 7 ++ src/libs/Navigation/linkingConfig.js | 3 + .../WorkspaceSettingsCurrencyPage.js | 106 ++++++++++++++++++ src/pages/workspace/WorkspaceSettingsPage.js | 40 +++---- 5 files changed, 136 insertions(+), 22 deletions(-) create mode 100644 src/pages/workspace/WorkspaceSettingsCurrencyPage.js diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 2c37116db395..8c3f60802ec3 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -190,6 +190,7 @@ export default { WORKSPACE_INVITE: 'workspace/:policyID/invite', WORKSPACE_INVITE_MESSAGE: 'workspace/:policyID/invite-message', WORKSPACE_SETTINGS: 'workspace/:policyID/settings', + WORKSPACE_SETTINGS_CURRENCY: 'workspace/:policyID/settings/currency', WORKSPACE_CARD: 'workspace/:policyID/card', WORKSPACE_REIMBURSE: 'workspace/:policyID/reimburse', WORKSPACE_RATE_AND_UNIT: 'workspace/:policyID/rateandunit', @@ -202,6 +203,7 @@ export default { getWorkspaceInviteRoute: (policyID: string) => `workspace/${policyID}/invite`, getWorkspaceInviteMessageRoute: (policyID: string) => `workspace/${policyID}/invite-message`, getWorkspaceSettingsRoute: (policyID: string) => `workspace/${policyID}/settings`, + getWorkspaceSettingsCurrencyRoute: (policyID: string) => `workspace/${policyID}/settings/currency`, getWorkspaceCardRoute: (policyID: string) => `workspace/${policyID}/card`, getWorkspaceReimburseRoute: (policyID: string) => `workspace/${policyID}/reimburse`, getWorkspaceRateAndUnitRoute: (policyID: string) => `workspace/${policyID}/rateandunit`, diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index 392781a777db..eabce0686e14 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -599,6 +599,13 @@ const SettingsModalStackNavigator = createModalStackNavigator([ }, name: 'Workspace_Settings', }, + { + getComponent: () => { + const WorkspaceSettingsCurrencyPage = require('../../../pages/workspace/WorkspaceSettingsCurrencyPage').default; + return WorkspaceSettingsCurrencyPage; + }, + name: 'Workspace_Settings_Currency', + }, { getComponent: () => { const WorkspaceCardPage = require('../../../pages/workspace/card/WorkspaceCardPage').default; diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index 11d21d6d005c..925d786cc6b4 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -173,6 +173,9 @@ export default { Workspace_Settings: { path: ROUTES.WORKSPACE_SETTINGS, }, + Workspace_Settings_Currency: { + path: ROUTES.WORKSPACE_SETTINGS_CURRENCY, + }, Workspace_Card: { path: ROUTES.WORKSPACE_CARD, }, diff --git a/src/pages/workspace/WorkspaceSettingsCurrencyPage.js b/src/pages/workspace/WorkspaceSettingsCurrencyPage.js new file mode 100644 index 000000000000..de554d95860a --- /dev/null +++ b/src/pages/workspace/WorkspaceSettingsCurrencyPage.js @@ -0,0 +1,106 @@ +import React, {useState, useMemo, useCallback} from 'react'; +import _ from 'underscore'; +import {withOnyx} from 'react-native-onyx'; +import PropTypes from 'prop-types'; +import useLocalize from '../../hooks/useLocalize'; +import ScreenWrapper from '../../components/ScreenWrapper'; +import HeaderWithBackButton from '../../components/HeaderWithBackButton'; +import SelectionList from '../../components/SelectionList'; +import Navigation from '../../libs/Navigation/Navigation'; +import ROUTES from '../../ROUTES'; +import compose from '../../libs/compose'; +import ONYXKEYS from '../../ONYXKEYS'; +import withPolicy, {policyDefaultProps, policyPropTypes} from './withPolicy'; +import * as Policy from '../../libs/actions/Policy'; + +const propTypes = { + // List of available currencies + currencyList: PropTypes.objectOf( + PropTypes.shape({ + // Symbol for the currency + symbol: PropTypes.string, + }), + ), + ...policyPropTypes, +}; + +const defaultProps = { + currencyList: {}, + ...policyDefaultProps, +}; + +function WorkspaceSettingsCurrencyPage({currencyList, policy}) { + const {translate} = useLocalize(); + const [searchText, setSearchText] = useState(''); + + const getDisplayText = useCallback((currencyCode, currencySymbol) => `${currencyCode} - ${currencySymbol}`, []); + + const {sections, initiallyFocusedOptionKey} = useMemo(() => { + const trimmedText = searchText.trim().toLowerCase(); + const currencyListKeys = _.keys(currencyList); + + const filteredItems = _.filter(currencyListKeys, (currencyCode) => { + const currency = currencyList[currencyCode]; + return getDisplayText(currencyCode, currency.symbol).toLowerCase().includes(trimmedText); + }); + + let selectedCurrencyCode; + + const currencyItems = _.map(filteredItems, (currencyCode) => { + const currency = currencyList[currencyCode]; + const isSelected = policy.outputCurrency === currencyCode; + + if (isSelected) { + selectedCurrencyCode = currencyCode; + } + + return { + text: getDisplayText(currencyCode, currency.symbol), + keyForList: currencyCode, + isSelected, + }; + }); + + return { + sections: [{data: currencyItems, indexOffset: 0}], + initiallyFocusedOptionKey: selectedCurrencyCode, + }; + }, [getDisplayText, currencyList, policy.outputCurrency, searchText]); + + const headerMessage = Boolean(searchText.trim()) && !sections[0].data.length ? translate('common.noResultsFound') : ''; + + const onSelectCurrency = (item) => { + Policy.updateGeneralSettings(policy.id, policy.name, item.keyForList); + Navigation.goBack(ROUTES.getWorkspaceSettingsRoute(policy.id)); + }; + + return ( + + Navigation.goBack(ROUTES.getWorkspaceSettingsRoute(policy.id))} + /> + + + + ); +} + +WorkspaceSettingsCurrencyPage.propTypes = propTypes; +WorkspaceSettingsCurrencyPage.defaultProps = defaultProps; + +export default compose( + withPolicy, + withOnyx({ + currencyList: {key: ONYXKEYS.CURRENCY_LIST}, + }), +)(WorkspaceSettingsCurrencyPage); diff --git a/src/pages/workspace/WorkspaceSettingsPage.js b/src/pages/workspace/WorkspaceSettingsPage.js index 2a9576d5d8d3..00dcb99a92dc 100644 --- a/src/pages/workspace/WorkspaceSettingsPage.js +++ b/src/pages/workspace/WorkspaceSettingsPage.js @@ -1,8 +1,7 @@ -import React, {useCallback, useMemo} from 'react'; +import React, {useCallback} from 'react'; import {Keyboard, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import PropTypes from 'prop-types'; -import _ from 'underscore'; import lodashGet from 'lodash/get'; import ONYXKEYS from '../../ONYXKEYS'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; @@ -12,7 +11,6 @@ import * as Policy from '../../libs/actions/Policy'; import * as Expensicons from '../../components/Icon/Expensicons'; import AvatarWithImagePicker from '../../components/AvatarWithImagePicker'; import CONST from '../../CONST'; -import Picker from '../../components/Picker'; import TextInput from '../../components/TextInput'; import WorkspacePageWithSections from './WorkspacePageWithSections'; import withPolicy, {policyPropTypes, policyDefaultProps} from './withPolicy'; @@ -25,6 +23,8 @@ import Avatar from '../../components/Avatar'; import Navigation from '../../libs/Navigation/Navigation'; import ROUTES from '../../ROUTES'; import withWindowDimensions, {windowDimensionsPropTypes} from '../../components/withWindowDimensions'; +import MenuItemWithTopDescription from '../../components/MenuItemWithTopDescription'; +import Text from '../../components/Text'; const propTypes = { // The currency list constant object from Onyx @@ -45,25 +45,19 @@ const defaultProps = { }; function WorkspaceSettingsPage(props) { - const currencyItems = useMemo(() => { - const currencyListKeys = _.keys(props.currencyList); - return _.map(currencyListKeys, (currencyCode) => ({ - value: currencyCode, - label: `${currencyCode} - ${props.currencyList[currencyCode].symbol}`, - })); - }, [props.currencyList]); + const formattedCurrency = `${props.policy.outputCurrency} - ${props.currencyList[props.policy.outputCurrency].symbol}`; const submit = useCallback( (values) => { if (props.policy.isPolicyUpdating) { return; } - const outputCurrency = values.currency; - Policy.updateGeneralSettings(props.policy.id, values.name.trim(), outputCurrency); + + Policy.updateGeneralSettings(props.policy.id, values.name.trim(), props.policy.outputCurrency); Keyboard.dismiss(); Navigation.goBack(ROUTES.getWorkspaceInitialRoute(props.policy.id)); }, - [props.policy.id, props.policy.isPolicyUpdating], + [props.policy.id, props.policy.isPolicyUpdating, props.policy.outputCurrency], ); const validate = useCallback((values) => { @@ -93,7 +87,7 @@ function WorkspaceSettingsPage(props) {
- Navigation.navigate(ROUTES.getWorkspaceSettingsCurrencyRoute(props.policy.id))} /> + + {hasVBA ? props.translate('workspace.editor.currencyInputDisabledText') : props.translate('workspace.editor.currencyInputHelpText')} +
From 4847ec46bb5c67000fd09ad98b3de4f7820e117a Mon Sep 17 00:00:00 2001 From: Thiago Brezinski Date: Wed, 20 Sep 2023 14:05:21 +0100 Subject: [PATCH 049/856] chore: cleanup --- src/pages/workspace/WorkspaceSettingsCurrencyPage.js | 6 +++--- src/pages/workspace/WorkspaceSettingsPage.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pages/workspace/WorkspaceSettingsCurrencyPage.js b/src/pages/workspace/WorkspaceSettingsCurrencyPage.js index de554d95860a..aa5e1701f8ce 100644 --- a/src/pages/workspace/WorkspaceSettingsCurrencyPage.js +++ b/src/pages/workspace/WorkspaceSettingsCurrencyPage.js @@ -14,10 +14,10 @@ import withPolicy, {policyDefaultProps, policyPropTypes} from './withPolicy'; import * as Policy from '../../libs/actions/Policy'; const propTypes = { - // List of available currencies + /** Constant, list of available currencies */ currencyList: PropTypes.objectOf( PropTypes.shape({ - // Symbol for the currency + /** Symbol of the currency */ symbol: PropTypes.string, }), ), @@ -88,8 +88,8 @@ function WorkspaceSettingsCurrencyPage({currencyList, policy}) { onChangeText={setSearchText} onSelectRow={onSelectCurrency} headerMessage={headerMessage} - showScrollIndicator initiallyFocusedOptionKey={initiallyFocusedOptionKey} + showScrollIndicator /> ); diff --git a/src/pages/workspace/WorkspaceSettingsPage.js b/src/pages/workspace/WorkspaceSettingsPage.js index 00dcb99a92dc..d680d6f38625 100644 --- a/src/pages/workspace/WorkspaceSettingsPage.js +++ b/src/pages/workspace/WorkspaceSettingsPage.js @@ -27,10 +27,10 @@ import MenuItemWithTopDescription from '../../components/MenuItemWithTopDescript import Text from '../../components/Text'; const propTypes = { - // The currency list constant object from Onyx + /** Constant, list of available currencies */ currencyList: PropTypes.objectOf( PropTypes.shape({ - // Symbol for the currency + /** Symbol of the currency */ symbol: PropTypes.string, }), ), From 1eed015711e5e810cf6f5a6abbe7655e70972857 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 20 Sep 2023 15:01:20 +0100 Subject: [PATCH 050/856] group platform-specific types --- src/libs/localFileDownload/index.android.ts | 3 ++- src/libs/localFileDownload/index.ios.ts | 3 ++- src/libs/localFileDownload/index.ts | 3 ++- src/libs/localFileDownload/types.ts | 5 +++++ 4 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 src/libs/localFileDownload/types.ts diff --git a/src/libs/localFileDownload/index.android.ts b/src/libs/localFileDownload/index.android.ts index 10cb8c2e35dc..0b91b3a10d0d 100644 --- a/src/libs/localFileDownload/index.android.ts +++ b/src/libs/localFileDownload/index.android.ts @@ -1,12 +1,13 @@ import RNFetchBlob from 'react-native-blob-util'; import * as FileUtils from '../fileDownload/FileUtils'; +import LocalFileDownload from './types'; /** * Writes a local file to the app's internal directory with the given fileName * and textContent, so we're able to copy it to the Android public download dir. * After the file is copied, it is removed from the internal dir. */ -export default function localFileDownload(fileName: string, textContent: string) { +export default function localFileDownload({fileName, textContent}: LocalFileDownload) { const newFileName = FileUtils.appendTimeToFileName(fileName); const dir = RNFetchBlob.fs.dirs.DocumentDir; const path = `${dir}/${newFileName}.txt`; diff --git a/src/libs/localFileDownload/index.ios.ts b/src/libs/localFileDownload/index.ios.ts index 2d0d54b411ae..427f6c3463ed 100644 --- a/src/libs/localFileDownload/index.ios.ts +++ b/src/libs/localFileDownload/index.ios.ts @@ -1,13 +1,14 @@ import {Share} from 'react-native'; import RNFetchBlob from 'react-native-blob-util'; import * as FileUtils from '../fileDownload/FileUtils'; +import LocalFileDownload from './types'; /** * Writes a local file to the app's internal directory with the given fileName * and textContent, so we're able to share it using iOS' share API. * After the file is shared, it is removed from the internal dir. */ -export default function localFileDownload(fileName: string, textContent: string) { +export default function localFileDownload({fileName, textContent}: LocalFileDownload) { const newFileName = FileUtils.appendTimeToFileName(fileName); const dir = RNFetchBlob.fs.dirs.DocumentDir; const path = `${dir}/${newFileName}.txt`; diff --git a/src/libs/localFileDownload/index.ts b/src/libs/localFileDownload/index.ts index 42074cc9146f..8c2e4e4baf38 100644 --- a/src/libs/localFileDownload/index.ts +++ b/src/libs/localFileDownload/index.ts @@ -1,11 +1,12 @@ import * as FileUtils from '../fileDownload/FileUtils'; +import LocalFileDownload from './types'; /** * Creates a Blob with the given fileName and textContent, then dynamically * creates a temporary anchor, just to programmatically click it, so the file * is downloaded by the browser. */ -export default function localFileDownload(fileName: string, textContent: string) { +export default function localFileDownload({fileName, textContent}: LocalFileDownload) { const blob = new Blob([textContent], {type: 'text/plain'}); const url = URL.createObjectURL(blob); const link = document.createElement('a'); diff --git a/src/libs/localFileDownload/types.ts b/src/libs/localFileDownload/types.ts new file mode 100644 index 000000000000..ddc9f8a40246 --- /dev/null +++ b/src/libs/localFileDownload/types.ts @@ -0,0 +1,5 @@ +type LocalFileDownload = { + fileName: string; + textContent: string; +}; +export default LocalFileDownload; From 2f2286d0573f280deca2ccee06d2c7612a5fd684 Mon Sep 17 00:00:00 2001 From: Thiago Brezinski Date: Wed, 20 Sep 2023 15:28:09 +0100 Subject: [PATCH 051/856] chore: address pr comments --- .../WorkspaceSettingsCurrencyPage.js | 56 +++++++++---------- src/pages/workspace/WorkspaceSettingsPage.js | 6 +- 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/src/pages/workspace/WorkspaceSettingsCurrencyPage.js b/src/pages/workspace/WorkspaceSettingsCurrencyPage.js index aa5e1701f8ce..649943cdbf56 100644 --- a/src/pages/workspace/WorkspaceSettingsCurrencyPage.js +++ b/src/pages/workspace/WorkspaceSettingsCurrencyPage.js @@ -18,7 +18,7 @@ const propTypes = { currencyList: PropTypes.objectOf( PropTypes.shape({ /** Symbol of the currency */ - symbol: PropTypes.string, + symbol: PropTypes.string.isRequired, }), ), ...policyPropTypes, @@ -29,45 +29,41 @@ const defaultProps = { ...policyDefaultProps, }; +const getDisplayText = (currencyCode, currencySymbol) => `${currencyCode} - ${currencySymbol}`; + function WorkspaceSettingsCurrencyPage({currencyList, policy}) { const {translate} = useLocalize(); const [searchText, setSearchText] = useState(''); + const trimmedText = searchText.trim().toLowerCase(); + const currencyListKeys = _.keys(currencyList); - const getDisplayText = useCallback((currencyCode, currencySymbol) => `${currencyCode} - ${currencySymbol}`, []); - - const {sections, initiallyFocusedOptionKey} = useMemo(() => { - const trimmedText = searchText.trim().toLowerCase(); - const currencyListKeys = _.keys(currencyList); - - const filteredItems = _.filter(currencyListKeys, (currencyCode) => { - const currency = currencyList[currencyCode]; - return getDisplayText(currencyCode, currency.symbol).toLowerCase().includes(trimmedText); - }); + const filteredItems = _.filter(currencyListKeys, (currencyCode) => { + const currency = currencyList[currencyCode]; + return getDisplayText(currencyCode, currency.symbol).toLowerCase().includes(trimmedText); + }); - let selectedCurrencyCode; + let initiallyFocusedOptionKey; - const currencyItems = _.map(filteredItems, (currencyCode) => { - const currency = currencyList[currencyCode]; - const isSelected = policy.outputCurrency === currencyCode; + const currencyItems = _.map(filteredItems, (currencyCode) => { + const currency = currencyList[currencyCode]; + const isSelected = policy.outputCurrency === currencyCode; - if (isSelected) { - selectedCurrencyCode = currencyCode; - } - - return { - text: getDisplayText(currencyCode, currency.symbol), - keyForList: currencyCode, - isSelected, - }; - }); + if (isSelected) { + initiallyFocusedOptionKey = currencyCode; + } return { - sections: [{data: currencyItems, indexOffset: 0}], - initiallyFocusedOptionKey: selectedCurrencyCode, + text: getDisplayText(currencyCode, currency.symbol), + keyForList: currencyCode, + isSelected, }; - }, [getDisplayText, currencyList, policy.outputCurrency, searchText]); + }); + + const sections = [{data: currencyItems, indexOffset: 0}]; + + const headerMessage = searchText.trim() && !sections[0].data.length ? translate('common.noResultsFound') : ''; - const headerMessage = Boolean(searchText.trim()) && !sections[0].data.length ? translate('common.noResultsFound') : ''; + const onBackButtonPress = useCallback(() => Navigation.goBack(ROUTES.getWorkspaceSettingsRoute(policy.id)), [policy.id]); const onSelectCurrency = (item) => { Policy.updateGeneralSettings(policy.id, policy.name, item.keyForList); @@ -78,7 +74,7 @@ function WorkspaceSettingsCurrencyPage({currencyList, policy}) { Navigation.goBack(ROUTES.getWorkspaceSettingsRoute(policy.id))} + onBackButtonPress={onBackButtonPress} /> { @@ -87,7 +87,7 @@ function WorkspaceSettingsPage(props) {
Date: Wed, 20 Sep 2023 15:46:56 +0100 Subject: [PATCH 052/856] chore: cleanup --- src/pages/workspace/WorkspaceSettingsCurrencyPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/WorkspaceSettingsCurrencyPage.js b/src/pages/workspace/WorkspaceSettingsCurrencyPage.js index 649943cdbf56..63457f491951 100644 --- a/src/pages/workspace/WorkspaceSettingsCurrencyPage.js +++ b/src/pages/workspace/WorkspaceSettingsCurrencyPage.js @@ -1,4 +1,4 @@ -import React, {useState, useMemo, useCallback} from 'react'; +import React, {useState, useCallback} from 'react'; import _ from 'underscore'; import {withOnyx} from 'react-native-onyx'; import PropTypes from 'prop-types'; From 05a58d8ecbbea76e1b21a9f989d72e0bc278906f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Wed, 20 Sep 2023 17:07:13 +0100 Subject: [PATCH 053/856] Address review comments --- src/styles/StyleUtils.ts | 42 +++++++++---------- src/styles/addOutlineWidth/index.ts | 4 +- src/styles/addOutlineWidth/types.ts | 2 +- .../getReportActionContextMenuStyles.ts | 2 +- src/styles/utilities/display.ts | 8 ++-- src/styles/utilities/overflowAuto/index.ts | 2 +- src/styles/utilities/positioning.ts | 2 +- 7 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/styles/StyleUtils.ts b/src/styles/StyleUtils.ts index 9736d79a4a4e..3c131b9645f2 100644 --- a/src/styles/StyleUtils.ts +++ b/src/styles/StyleUtils.ts @@ -1,4 +1,4 @@ -import {Animated, FlexStyle, PressableStateCallbackType, TextStyle, ViewStyle} from 'react-native'; +import {Animated, DimensionValue, PressableStateCallbackType, TextStyle, ViewStyle} from 'react-native'; import {EdgeInsets} from 'react-native-safe-area-context'; import {ValueOf} from 'type-fest'; import CONST from '../CONST'; @@ -272,7 +272,7 @@ function getZoomCursorStyle(isZoomed: boolean, isDragging: boolean): ViewStyle { return isDragging ? styles.cursorGrabbing : styles.cursorZoomOut; } -// NOTE: asserting some web style properties to a valid type, because isn't possible to augment them. +// NOTE: asserting some web style properties to a valid type, because it isn't possible to augment them. function getZoomSizingStyle( isZoomed: boolean, imgWidth: number, @@ -286,23 +286,23 @@ function getZoomSizingStyle( if (isLoading || imgWidth === 0 || imgHeight === 0) { return undefined; } - const top = `${Math.max((containerHeight - imgHeight) / 2, 0)}px` as ViewStyle['top']; - const left = `${Math.max((containerWidth - imgWidth) / 2, 0)}px` as ViewStyle['left']; + const top = `${Math.max((containerHeight - imgHeight) / 2, 0)}px` as DimensionValue; + const left = `${Math.max((containerWidth - imgWidth) / 2, 0)}px` as DimensionValue; // Return different size and offset style based on zoomScale and isZoom. if (isZoomed) { // When both width and height are smaller than container(modal) size, set the height by multiplying zoomScale if it is zoomed in. if (zoomScale >= 1) { return { - height: `${imgHeight * zoomScale}px` as FlexStyle['height'], - width: `${imgWidth * zoomScale}px` as FlexStyle['width'], + height: `${imgHeight * zoomScale}px` as DimensionValue, + width: `${imgWidth * zoomScale}px` as DimensionValue, }; } // If image height and width are bigger than container size, display image with original size because original size is bigger and position absolute. return { - height: `${imgHeight}px` as FlexStyle['height'], - width: `${imgWidth}px` as FlexStyle['width'], + height: `${imgHeight}px` as DimensionValue, + width: `${imgWidth}px` as DimensionValue, top, left, }; @@ -311,8 +311,8 @@ function getZoomSizingStyle( // If image is not zoomed in and image size is smaller than container size, display with original size based on offset and position absolute. if (zoomScale > 1) { return { - height: `${imgHeight}px` as FlexStyle['height'], - width: `${imgWidth}px` as FlexStyle['width'], + height: `${imgHeight}px` as DimensionValue, + width: `${imgWidth}px` as DimensionValue, top, left, }; @@ -320,11 +320,11 @@ function getZoomSizingStyle( // If image is bigger than container size, display full image in the screen with scaled size (fit by container size) and position absolute. // top, left offset should be different when displaying long or wide image. - const scaledTop = `${Math.max((containerHeight - imgHeight * zoomScale) / 2, 0)}px` as ViewStyle['top']; - const scaledLeft = `${Math.max((containerWidth - imgWidth * zoomScale) / 2, 0)}px` as ViewStyle['left']; + const scaledTop = `${Math.max((containerHeight - imgHeight * zoomScale) / 2, 0)}px` as DimensionValue; + const scaledLeft = `${Math.max((containerWidth - imgWidth * zoomScale) / 2, 0)}px` as DimensionValue; return { - height: `${imgHeight * zoomScale}px` as FlexStyle['height'], - width: `${imgWidth * zoomScale}px` as FlexStyle['width'], + height: `${imgHeight * zoomScale}px` as DimensionValue, + width: `${imgWidth * zoomScale}px` as DimensionValue, top: scaledTop, left: scaledLeft, }; @@ -448,8 +448,8 @@ function getBackgroundColorWithOpacityStyle(backgroundColor: string, opacity: nu /** * Generate a style for the background color of the Badge */ -function getBadgeColorStyle(success: boolean, error: boolean, isPressed = false, isAdHoc = false): ViewStyle { - if (success) { +function getBadgeColorStyle(isSuccess: boolean, isError: boolean, isPressed = false, isAdHoc = false): ViewStyle { + if (isSuccess) { if (isAdHoc) { // TODO: Remove this "eslint-disable-next" once the theme switching migration is done and styles are fully typed (GH Issue: https://github.com/Expensify/App/issues/27337) // eslint-disable-next-line @typescript-eslint/no-unsafe-return @@ -459,7 +459,7 @@ function getBadgeColorStyle(success: boolean, error: boolean, isPressed = false, // eslint-disable-next-line @typescript-eslint/no-unsafe-return return isPressed ? styles.badgeSuccessPressed : styles.badgeSuccess; } - if (error) { + if (isError) { // TODO: Remove this "eslint-disable-next" once the theme switching migration is done and styles are fully typed (GH Issue: https://github.com/Expensify/App/issues/27337) // eslint-disable-next-line @typescript-eslint/no-unsafe-return return isPressed ? styles.badgeDangerPressed : styles.badgeDanger; @@ -516,7 +516,7 @@ function getAnimatedFABStyle(rotate: Animated.Value, backgroundColor: Animated.V }; } -function getWidthAndHeightStyle(width: number, height: number | undefined = undefined): ViewStyle { +function getWidthAndHeightStyle(width: number, height?: number): ViewStyle { return { width, height: height ?? width, @@ -909,12 +909,12 @@ function getReportWelcomeContainerStyle(isSmallScreenWidth: boolean): ViewStyle /** * Gets styles for AutoCompleteSuggestion row */ -function getAutoCompleteSuggestionItemStyle(highlightedEmojiIndex: number, rowHeight: number, hovered: boolean, currentEmojiIndex: number): ViewStyle[] { +function getAutoCompleteSuggestionItemStyle(highlightedEmojiIndex: number, rowHeight: number, isHovered: boolean, currentEmojiIndex: number): ViewStyle[] { let backgroundColor; if (currentEmojiIndex === highlightedEmojiIndex) { backgroundColor = themeColors.activeComponentBG; - } else if (hovered) { + } else if (isHovered) { backgroundColor = themeColors.hoverComponentBG; } @@ -1023,7 +1023,7 @@ function getEmojiReactionCounterTextStyle(hasUserReacted: boolean): TextStyle { * * @param direction - The direction of the rotation (CONST.DIRECTION.LEFT or CONST.DIRECTION.RIGHT). */ -function getDirectionStyle(direction: string): ViewStyle { +function getDirectionStyle(direction: ValueOf): ViewStyle { if (direction === CONST.DIRECTION.LEFT) { return {transform: [{rotate: '180deg'}]}; } diff --git a/src/styles/addOutlineWidth/index.ts b/src/styles/addOutlineWidth/index.ts index 257a3de2b6cd..6fe2ecf85978 100644 --- a/src/styles/addOutlineWidth/index.ts +++ b/src/styles/addOutlineWidth/index.ts @@ -9,11 +9,11 @@ import AddOutlineWidth from './types'; /** * Adds the addOutlineWidth property to an object to be used when styling */ -const addOutlineWidth: AddOutlineWidth = (obj, val, error = false) => ({ +const addOutlineWidth: AddOutlineWidth = (obj, val, hasError = false) => ({ ...obj, outlineWidth: val, outlineStyle: val ? 'auto' : 'none', - boxShadow: val !== 0 ? `0px 0px 0px ${val}px ${error ? themeDefault.danger : themeDefault.borderFocus}` : 'none', + boxShadow: val !== 0 ? `0px 0px 0px ${val}px ${hasError ? themeDefault.danger : themeDefault.borderFocus}` : 'none', }); export default addOutlineWidth; diff --git a/src/styles/addOutlineWidth/types.ts b/src/styles/addOutlineWidth/types.ts index fe5d1f5719b2..7a3a86506959 100644 --- a/src/styles/addOutlineWidth/types.ts +++ b/src/styles/addOutlineWidth/types.ts @@ -1,5 +1,5 @@ import {TextStyle} from 'react-native'; -type AddOutlineWidth = (obj: TextStyle, val?: number, error?: boolean) => TextStyle; +type AddOutlineWidth = (obj: TextStyle, val?: number, hasError?: boolean) => TextStyle; export default AddOutlineWidth; diff --git a/src/styles/getReportActionContextMenuStyles.ts b/src/styles/getReportActionContextMenuStyles.ts index 6b4ad8807552..cd3843169a43 100644 --- a/src/styles/getReportActionContextMenuStyles.ts +++ b/src/styles/getReportActionContextMenuStyles.ts @@ -15,7 +15,7 @@ const miniWrapperStyle: ViewStyle[] = [ borderWidth: 1, borderColor: themeColors.border, // In Safari, when welcome messages use a code block (triple backticks), they would overlap the context menu below when there is no scrollbar without the transform style. - // NOTE: asserting "transform" to a valid type, because isn't possible to augment "transform". + // NOTE: asserting "transform" to a valid type, because it isn't possible to augment "transform". transform: 'translateZ(0)' as unknown as ViewStyle['transform'], }, ]; diff --git a/src/styles/utilities/display.ts b/src/styles/utilities/display.ts index 124cd87bc67a..f14a25d641b1 100644 --- a/src/styles/utilities/display.ts +++ b/src/styles/utilities/display.ts @@ -23,7 +23,7 @@ export default { * Web-only style. */ dInline: { - // NOTE: asserting "display" to a valid type, because isn't possible to augment "display". + // NOTE: asserting "display" to a valid type, because it isn't possible to augment "display". display: 'inline' as ViewStyle['display'], }, @@ -31,7 +31,7 @@ export default { * Web-only style. */ dInlineFlex: { - // NOTE: asserting "display" to a valid type, because isn't possible to augment "display". + // NOTE: asserting "display" to a valid type, because it isn't possible to augment "display". display: 'inline-flex' as ViewStyle['display'], }, @@ -39,7 +39,7 @@ export default { * Web-only style. */ dBlock: { - // NOTE: asserting "display" to a valid type, because isn't possible to augment "display". + // NOTE: asserting "display" to a valid type, because it isn't possible to augment "display". display: 'block' as ViewStyle['display'], }, @@ -47,7 +47,7 @@ export default { * Web-only style. */ dGrid: { - // NOTE: asserting "display" to a valid type, because isn't possible to augment "display". + // NOTE: asserting "display" to a valid type, because it isn't possible to augment "display". display: 'grid' as ViewStyle['display'], }, } satisfies Record; diff --git a/src/styles/utilities/overflowAuto/index.ts b/src/styles/utilities/overflowAuto/index.ts index c87ea5437199..1e7ac8ed8246 100644 --- a/src/styles/utilities/overflowAuto/index.ts +++ b/src/styles/utilities/overflowAuto/index.ts @@ -5,7 +5,7 @@ import OverflowAutoStyles from './types'; * Web-only style. */ const overflowAuto: OverflowAutoStyles = { - // NOTE: asserting "overflow" to a valid type, because isn't possible to augment "overflow". + // NOTE: asserting "overflow" to a valid type, because it isn't possible to augment "overflow". overflow: 'auto' as ViewStyle['overflow'], }; diff --git a/src/styles/utilities/positioning.ts b/src/styles/utilities/positioning.ts index 7cd58c52618b..26e6198a5827 100644 --- a/src/styles/utilities/positioning.ts +++ b/src/styles/utilities/positioning.ts @@ -15,7 +15,7 @@ export default { * Web-only style. */ pFixed: { - // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". + // NOTE: asserting "position" to a valid type, because it isn't possible to augment "position". position: 'fixed' as ViewStyle['position'], }, From 55392425d06d0b9edcc2fc61e7878d9a0705a2af Mon Sep 17 00:00:00 2001 From: Thiago Brezinski Date: Wed, 20 Sep 2023 18:16:59 +0100 Subject: [PATCH 054/856] chore: address pr comments --- .../WorkspaceSettingsCurrencyPage.js | 2 +- src/pages/workspace/WorkspaceSettingsPage.js | 77 +++++++++++-------- 2 files changed, 45 insertions(+), 34 deletions(-) diff --git a/src/pages/workspace/WorkspaceSettingsCurrencyPage.js b/src/pages/workspace/WorkspaceSettingsCurrencyPage.js index 63457f491951..a7b3739edfb2 100644 --- a/src/pages/workspace/WorkspaceSettingsCurrencyPage.js +++ b/src/pages/workspace/WorkspaceSettingsCurrencyPage.js @@ -61,7 +61,7 @@ function WorkspaceSettingsCurrencyPage({currencyList, policy}) { const sections = [{data: currencyItems, indexOffset: 0}]; - const headerMessage = searchText.trim() && !sections[0].data.length ? translate('common.noResultsFound') : ''; + const headerMessage = searchText.trim() && !currencyItems.length ? translate('common.noResultsFound') : ''; const onBackButtonPress = useCallback(() => Navigation.goBack(ROUTES.getWorkspaceSettingsRoute(policy.id)), [policy.id]); diff --git a/src/pages/workspace/WorkspaceSettingsPage.js b/src/pages/workspace/WorkspaceSettingsPage.js index d6dc5150d0cb..c472bfbfd7a6 100644 --- a/src/pages/workspace/WorkspaceSettingsPage.js +++ b/src/pages/workspace/WorkspaceSettingsPage.js @@ -4,7 +4,6 @@ import {withOnyx} from 'react-native-onyx'; import PropTypes from 'prop-types'; import lodashGet from 'lodash/get'; import ONYXKEYS from '../../ONYXKEYS'; -import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; import styles from '../../styles/styles'; import compose from '../../libs/compose'; import * as Policy from '../../libs/actions/Policy'; @@ -25,6 +24,7 @@ import ROUTES from '../../ROUTES'; import withWindowDimensions, {windowDimensionsPropTypes} from '../../components/withWindowDimensions'; import MenuItemWithTopDescription from '../../components/MenuItemWithTopDescription'; import Text from '../../components/Text'; +import useLocalize from '../../hooks/useLocalize'; const propTypes = { /** Constant, list of available currencies */ @@ -34,8 +34,17 @@ const propTypes = { symbol: PropTypes.string.isRequired, }), ), + + /** The route object passed to this page from the navigator */ + route: PropTypes.shape({ + /** Each parameter passed via the URL */ + params: PropTypes.shape({ + /** The policyID that is being configured */ + policyID: PropTypes.string.isRequired, + }).isRequired, + }).isRequired, + ...policyPropTypes, - ...withLocalizePropTypes, ...windowDimensionsPropTypes, }; @@ -44,20 +53,21 @@ const defaultProps = { ...policyDefaultProps, }; -function WorkspaceSettingsPage(props) { - const formattedCurrency = props.policy ? `${props.policy.outputCurrency} - ${props.currencyList[props.policy.outputCurrency].symbol}` : ''; +function WorkspaceSettingsPage({policy, currencyList, windowWidth, route}) { + const {translate} = useLocalize(); + const formattedCurrency = policy ? `${policy.outputCurrency} - ${currencyList[policy.outputCurrency].symbol}` : ''; const submit = useCallback( (values) => { - if (props.policy.isPolicyUpdating) { + if (policy.isPolicyUpdating) { return; } - Policy.updateGeneralSettings(props.policy.id, values.name.trim(), props.policy.outputCurrency); + Policy.updateGeneralSettings(policy.id, values.name.trim(), policy.outputCurrency); Keyboard.dismiss(); - Navigation.goBack(ROUTES.getWorkspaceInitialRoute(props.policy.id)); + Navigation.goBack(ROUTES.getWorkspaceInitialRoute(policy.id)); }, - [props.policy.id, props.policy.isPolicyUpdating, props.policy.outputCurrency], + [policy.id, policy.isPolicyUpdating, policy.outputCurrency], ); const validate = useCallback((values) => { @@ -75,18 +85,20 @@ function WorkspaceSettingsPage(props) { return errors; }, []); - const policyName = lodashGet(props.policy, 'name', ''); + const onPressCurrency = useCallback(() => Navigation.navigate(ROUTES.getWorkspaceSettingsCurrencyRoute(policy.id)), [policy.id]); + + const policyName = lodashGet(policy, 'name', ''); return ( {(hasVBA) => ( ( Policy.updateWorkspaceAvatar(lodashGet(props.policy, 'id', ''), file)} - onImageRemoved={() => Policy.deleteWorkspaceAvatar(lodashGet(props.policy, 'id', ''))} + isUsingDefaultAvatar={!lodashGet(policy, 'avatar', null)} + onImageSelected={(file) => Policy.updateWorkspaceAvatar(lodashGet(policy, 'id', ''), file)} + onImageRemoved={() => Policy.deleteWorkspaceAvatar(lodashGet(policy, 'id', ''))} editorMaskImage={Expensicons.ImageCropSquareMask} - pendingAction={lodashGet(props.policy, 'pendingFields.avatar', null)} - errors={lodashGet(props.policy, 'errorFields.avatar', null)} - onErrorClose={() => Policy.clearAvatarErrors(props.policy.id)} - previewSource={UserUtils.getFullSizeAvatar(props.policy.avatar, '')} - headerTitle={props.translate('workspace.common.workspaceAvatar')} - originalFileName={props.policy.originalFileName} + pendingAction={lodashGet(policy, 'pendingFields.avatar', null)} + errors={lodashGet(policy, 'errorFields.avatar', null)} + onErrorClose={() => Policy.clearAvatarErrors(policy.id)} + previewSource={UserUtils.getFullSizeAvatar(policy.avatar, '')} + headerTitle={translate('workspace.common.workspaceAvatar')} + originalFileName={policy.originalFileName} /> - + Navigation.navigate(ROUTES.getWorkspaceSettingsCurrencyRoute(props.policy.id))} + onPress={onPressCurrency} /> - {hasVBA ? props.translate('workspace.editor.currencyInputDisabledText') : props.translate('workspace.editor.currencyInputHelpText')} + {hasVBA ? translate('workspace.editor.currencyInputDisabledText') : translate('workspace.editor.currencyInputHelpText')} @@ -164,6 +176,5 @@ export default compose( withOnyx({ currencyList: {key: ONYXKEYS.CURRENCY_LIST}, }), - withLocalize, withNetwork(), )(WorkspaceSettingsPage); From 0d9a313de199df2554deb4f6ade28be73144e09f Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Thu, 21 Sep 2023 10:24:53 +0800 Subject: [PATCH 055/856] Slow down server search --- src/pages/SearchPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index 432c64d51be5..9fae2fe2cd4a 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -59,7 +59,7 @@ class SearchPage extends Component { const {recentReports, personalDetails, userToInvite} = OptionsListUtils.getSearchOptions(props.reports, props.personalDetails, '', props.betas); - this.throttledSearchInServer = _.throttle(this.searchInServer.bind(this), 300, {leading: false}); + this.throttledSearchInServer = _.throttle(this.searchInServer.bind(this), 1000, {leading: false}); this.state = { searchValue: '', From 71689bd7edd91cd94c4d871f7d5546d2c2bfb3b1 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 21 Sep 2023 15:10:42 +0800 Subject: [PATCH 056/856] Add amount and thumbnail image --- src/components/EReceipt.js | 19 +++++++++++++------ src/styles/styles.js | 7 +++++++ src/styles/themes/default.js | 1 + 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/components/EReceipt.js b/src/components/EReceipt.js index 2068a5e50f12..1d4057c6e437 100644 --- a/src/components/EReceipt.js +++ b/src/components/EReceipt.js @@ -1,10 +1,13 @@ import React from 'react'; import {View} from 'react-native'; -import _ from 'underscore'; +import Text from './Text'; import styles from '../styles/styles'; import transactionPropTypes from './transactionPropTypes'; import * as ReceiptUtils from '../libs/ReceiptUtils'; -import Image from './Image'; +import * as ReportUtils from '../libs/ReportUtils'; +import * as CurrencyUtils from '../libs/CurrencyUtils'; +import tryResolveUrlFromApiRoot from '../libs/tryResolveUrlFromApiRoot'; +import ThumbnailImage from './ThumbnailImage'; const propTypes = { /** The transaction for the eReceipt */ @@ -17,16 +20,20 @@ const defaultProps = { function EReceipt({transaction}) { const {thumbnail} = ReceiptUtils.getThumbnailAndImageURIs(transaction.receipt.source, transaction.filename); + const {amount: transactionAmount, currency: transactionCurrency} = ReportUtils.getTransactionDetails(transaction); + const formattedTransactionAmount = CurrencyUtils.convertToDisplayString(transactionAmount, transactionCurrency); + const thumbnailSource = tryResolveUrlFromApiRoot(thumbnail || ''); return ( <> - - + + {formattedTransactionAmount} ); } diff --git a/src/styles/styles.js b/src/styles/styles.js index 4a2472913fd2..58a6ada7de0c 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -3553,6 +3553,13 @@ const styles = (theme) => ({ lineHeight: variables.lineHeightXXLarge, }, + eReceiptAmount: { + ...headlineFont, + fontSize: variables.fontSizeXXXLarge, + lineHeight: variables.lineHeightXXXLarge, + color: theme.eReceiptAmount, + }, + loginHeroBody: { fontFamily: fontFamily.EXP_NEUE, fontSize: variables.fontSizeSignInHeroBody, diff --git a/src/styles/themes/default.js b/src/styles/themes/default.js index 5ff997684304..75d3ded6b545 100644 --- a/src/styles/themes/default.js +++ b/src/styles/themes/default.js @@ -83,6 +83,7 @@ const darkTheme = { QRLogo: colors.green400, starDefaultBG: 'rgb(254, 228, 94)', loungeAccessOverlay: colors.blue800, + eReceiptAmount: colors.green400, }; darkTheme.PAGE_BACKGROUND_COLORS = { From 8095338a5e17b95d7aaea97d38b5831ffe29d0e2 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 21 Sep 2023 15:29:28 +0800 Subject: [PATCH 057/856] Add merchant --- src/components/EReceipt.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/EReceipt.js b/src/components/EReceipt.js index 1d4057c6e437..bca23ae83e24 100644 --- a/src/components/EReceipt.js +++ b/src/components/EReceipt.js @@ -20,7 +20,7 @@ const defaultProps = { function EReceipt({transaction}) { const {thumbnail} = ReceiptUtils.getThumbnailAndImageURIs(transaction.receipt.source, transaction.filename); - const {amount: transactionAmount, currency: transactionCurrency} = ReportUtils.getTransactionDetails(transaction); + const {amount: transactionAmount, currency: transactionCurrency, merchant: transactionMerchant} = ReportUtils.getTransactionDetails(transaction); const formattedTransactionAmount = CurrencyUtils.convertToDisplayString(transactionAmount, transactionCurrency); const thumbnailSource = tryResolveUrlFromApiRoot(thumbnail || ''); return ( @@ -34,6 +34,7 @@ function EReceipt({transaction}) { /> {formattedTransactionAmount} + {transactionMerchant} ); } From d9ddb6f3def934ab57c0a2803855212f8b9acedf Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 21 Sep 2023 15:40:40 +0800 Subject: [PATCH 058/856] Display waypoints --- src/components/EReceipt.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/components/EReceipt.js b/src/components/EReceipt.js index bca23ae83e24..58d49f5a54be 100644 --- a/src/components/EReceipt.js +++ b/src/components/EReceipt.js @@ -1,13 +1,17 @@ import React from 'react'; import {View} from 'react-native'; +import lodashGet from 'lodash/get'; +import _ from 'underscore'; import Text from './Text'; import styles from '../styles/styles'; import transactionPropTypes from './transactionPropTypes'; import * as ReceiptUtils from '../libs/ReceiptUtils'; import * as ReportUtils from '../libs/ReportUtils'; import * as CurrencyUtils from '../libs/CurrencyUtils'; +import * as TransactionUtils from '../libs/TransactionUtils'; import tryResolveUrlFromApiRoot from '../libs/tryResolveUrlFromApiRoot'; import ThumbnailImage from './ThumbnailImage'; +import useLocalize from '../hooks/useLocalize'; const propTypes = { /** The transaction for the eReceipt */ @@ -19,10 +23,12 @@ const defaultProps = { }; function EReceipt({transaction}) { + const {translate} = useLocalize(); const {thumbnail} = ReceiptUtils.getThumbnailAndImageURIs(transaction.receipt.source, transaction.filename); const {amount: transactionAmount, currency: transactionCurrency, merchant: transactionMerchant} = ReportUtils.getTransactionDetails(transaction); const formattedTransactionAmount = CurrencyUtils.convertToDisplayString(transactionAmount, transactionCurrency); const thumbnailSource = tryResolveUrlFromApiRoot(thumbnail || ''); + const waypoints = lodashGet(transaction, 'comment.waypoints', {}); return ( <> @@ -35,6 +41,25 @@ function EReceipt({transaction}) { {formattedTransactionAmount} {transactionMerchant} + <> + {_.map(waypoints, (waypoint, key) => { + const index = TransactionUtils.getWaypointIndex(key); + let descriptionKey = 'distance.waypointDescription.'; + if (index === 0) { + descriptionKey += 'start'; + } else if (index === _.size(waypoints) - 1) { + descriptionKey += 'finish'; + } else { + descriptionKey += 'stop'; + } + return ( + + {translate(descriptionKey)} + {waypoint.address || ''} + + ); + })} + ); } From 71ff545f73fac187bf1db127243daf1a2c53794f Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 21 Sep 2023 15:50:53 +0800 Subject: [PATCH 059/856] Remove unused import --- src/styles/themes/default.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/styles/themes/default.js b/src/styles/themes/default.js index 75d3ded6b545..561a1bafb532 100644 --- a/src/styles/themes/default.js +++ b/src/styles/themes/default.js @@ -1,7 +1,6 @@ /* eslint-disable no-unused-vars */ import colors from '../colors'; import SCREENS from '../../SCREENS'; -import ROUTES from '../../ROUTES'; const darkTheme = { // Figma keys From 215c03eda0b4bbe33f0b9e8c891418342936da4c Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 21 Sep 2023 15:51:13 +0800 Subject: [PATCH 060/856] Style e receipt merchant text --- src/components/EReceipt.js | 2 +- src/styles/styles.js | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/components/EReceipt.js b/src/components/EReceipt.js index 58d49f5a54be..3094ef59bf9c 100644 --- a/src/components/EReceipt.js +++ b/src/components/EReceipt.js @@ -40,7 +40,7 @@ function EReceipt({transaction}) { /> {formattedTransactionAmount} - {transactionMerchant} + {transactionMerchant} <> {_.map(waypoints, (waypoint, key) => { const index = TransactionUtils.getWaypointIndex(key); diff --git a/src/styles/styles.js b/src/styles/styles.js index 58a6ada7de0c..0b7aff4037fe 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -3560,6 +3560,13 @@ const styles = (theme) => ({ color: theme.eReceiptAmount, }, + eReceiptMerchant: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeXLarge, + lineHeight: variables.lineHeightXLarge, + color: theme.text, + }, + loginHeroBody: { fontFamily: fontFamily.EXP_NEUE, fontSize: variables.fontSizeSignInHeroBody, From 90ad2292efab5db14689ba5f0aa11fe6964b381b Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 21 Sep 2023 15:57:30 +0800 Subject: [PATCH 061/856] Style eReceipt waypoint titles --- src/components/EReceipt.js | 2 +- src/styles/styles.js | 9 ++++++++- src/styles/themes/default.js | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/components/EReceipt.js b/src/components/EReceipt.js index 3094ef59bf9c..ba9dd13ae276 100644 --- a/src/components/EReceipt.js +++ b/src/components/EReceipt.js @@ -54,7 +54,7 @@ function EReceipt({transaction}) { } return ( - {translate(descriptionKey)} + {translate(descriptionKey)} {waypoint.address || ''} ); diff --git a/src/styles/styles.js b/src/styles/styles.js index 0b7aff4037fe..d17547341df0 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -3557,7 +3557,7 @@ const styles = (theme) => ({ ...headlineFont, fontSize: variables.fontSizeXXXLarge, lineHeight: variables.lineHeightXXXLarge, - color: theme.eReceiptAmount, + color: theme.textBrand, }, eReceiptMerchant: { @@ -3567,6 +3567,13 @@ const styles = (theme) => ({ color: theme.text, }, + eReceiptWaypointTitle: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeSmall, + lineHeight: variables.lineHeightSmall, + color: theme.textBrand, + }, + loginHeroBody: { fontFamily: fontFamily.EXP_NEUE, fontSize: variables.fontSizeSignInHeroBody, diff --git a/src/styles/themes/default.js b/src/styles/themes/default.js index 561a1bafb532..eac8b087ead0 100644 --- a/src/styles/themes/default.js +++ b/src/styles/themes/default.js @@ -82,7 +82,7 @@ const darkTheme = { QRLogo: colors.green400, starDefaultBG: 'rgb(254, 228, 94)', loungeAccessOverlay: colors.blue800, - eReceiptAmount: colors.green400, + textBrand: colors.green400, }; darkTheme.PAGE_BACKGROUND_COLORS = { From c1aa7b37a9a84eeb6fffbab0bb5a72e09d8f1ef0 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 21 Sep 2023 16:05:51 +0800 Subject: [PATCH 062/856] Style eReceipt waypoint address --- src/components/EReceipt.js | 2 +- src/styles/styles.js | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/components/EReceipt.js b/src/components/EReceipt.js index ba9dd13ae276..bd26b0a5b4b0 100644 --- a/src/components/EReceipt.js +++ b/src/components/EReceipt.js @@ -55,7 +55,7 @@ function EReceipt({transaction}) { return ( {translate(descriptionKey)} - {waypoint.address || ''} + {waypoint.address || ''} ); })} diff --git a/src/styles/styles.js b/src/styles/styles.js index d17547341df0..c15b0f8b9dcf 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -3574,6 +3574,13 @@ const styles = (theme) => ({ color: theme.textBrand, }, + eReceiptWaypointAddress: { + fontFamily: fontFamily.MONOSPACE, + fontSize: variables.fontSizeNormal, + lineHeight: variables.lineHeightNormal, + color: theme.textColorfulBackground, + }, + loginHeroBody: { fontFamily: fontFamily.EXP_NEUE, fontSize: variables.fontSizeSignInHeroBody, From 5bb7b9eb6661903993300ded689d3f19c4e06ef2 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 21 Sep 2023 16:10:18 +0800 Subject: [PATCH 063/856] Add date text --- src/components/EReceipt.js | 40 +++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/components/EReceipt.js b/src/components/EReceipt.js index bd26b0a5b4b0..b84be5dc8be7 100644 --- a/src/components/EReceipt.js +++ b/src/components/EReceipt.js @@ -25,7 +25,7 @@ const defaultProps = { function EReceipt({transaction}) { const {translate} = useLocalize(); const {thumbnail} = ReceiptUtils.getThumbnailAndImageURIs(transaction.receipt.source, transaction.filename); - const {amount: transactionAmount, currency: transactionCurrency, merchant: transactionMerchant} = ReportUtils.getTransactionDetails(transaction); + const {amount: transactionAmount, currency: transactionCurrency, merchant: transactionMerchant, created: transactionDate} = ReportUtils.getTransactionDetails(transaction); const formattedTransactionAmount = CurrencyUtils.convertToDisplayString(transactionAmount, transactionCurrency); const thumbnailSource = tryResolveUrlFromApiRoot(thumbnail || ''); const waypoints = lodashGet(transaction, 'comment.waypoints', {}); @@ -41,25 +41,25 @@ function EReceipt({transaction}) { {formattedTransactionAmount} {transactionMerchant} - <> - {_.map(waypoints, (waypoint, key) => { - const index = TransactionUtils.getWaypointIndex(key); - let descriptionKey = 'distance.waypointDescription.'; - if (index === 0) { - descriptionKey += 'start'; - } else if (index === _.size(waypoints) - 1) { - descriptionKey += 'finish'; - } else { - descriptionKey += 'stop'; - } - return ( - - {translate(descriptionKey)} - {waypoint.address || ''} - - ); - })} - + {_.map(waypoints, (waypoint, key) => { + const index = TransactionUtils.getWaypointIndex(key); + let descriptionKey = 'distance.waypointDescription.'; + if (index === 0) { + descriptionKey += 'start'; + } else if (index === _.size(waypoints) - 1) { + descriptionKey += 'finish'; + } else { + descriptionKey += 'stop'; + } + return ( + + {translate(descriptionKey)} + {waypoint.address || ''} + + ); + })} + {translate('common.date')} + {transactionDate} ); } From dd0fe7ebe01a713d0ba42004d9fc07c9f249160b Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 21 Sep 2023 16:18:38 +0800 Subject: [PATCH 064/856] WIP add logo --- src/components/EReceipt.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/components/EReceipt.js b/src/components/EReceipt.js index b84be5dc8be7..cd5959b1a41d 100644 --- a/src/components/EReceipt.js +++ b/src/components/EReceipt.js @@ -12,6 +12,7 @@ import * as TransactionUtils from '../libs/TransactionUtils'; import tryResolveUrlFromApiRoot from '../libs/tryResolveUrlFromApiRoot'; import ThumbnailImage from './ThumbnailImage'; import useLocalize from '../hooks/useLocalize'; +import fontFamily from '../styles/fontFamily'; const propTypes = { /** The transaction for the eReceipt */ @@ -60,6 +61,14 @@ function EReceipt({transaction}) { })} {translate('common.date')} {transactionDate} + + + ); } From cf3979a2e6bd9f848a862a6cfb932513a7f45209 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Thu, 21 Sep 2023 18:30:52 +0800 Subject: [PATCH 065/856] Fix up logic --- src/components/AttachmentModal.js | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index 68cee61bbc9b..ebfcf5f0f3a3 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -1,7 +1,6 @@ import React, {useState, useCallback, useRef} from 'react'; import PropTypes from 'prop-types'; import {View, Animated, Keyboard} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; import Str from 'expensify-common/lib/str'; import lodashGet from 'lodash/get'; import lodashExtend from 'lodash/extend'; @@ -32,7 +31,7 @@ import Navigation from '../libs/Navigation/Navigation'; import ROUTES from '../ROUTES'; import useNativeDriver from '../libs/useNativeDriver'; import Receipt from '../libs/actions/Receipt'; -import ONYXKEYS from '../ONYXKEYS'; +import * as ReportActionsUtils from '../libs/ReportActionsUtils'; /** * Modal render prop component that exposes modal launching triggers that can be used @@ -82,13 +81,6 @@ const propTypes = { /** Denotes whether it is a workspace avatar or not */ isWorkspaceAvatar: PropTypes.bool, - - /* Onyx Props */ - /** The parent report */ - parentReport: reportPropTypes, - - /** The report action this report is tied to from the parent report */ - parentReportAction: PropTypes.shape(reportActionPropTypes), }; const defaultProps = { @@ -388,7 +380,8 @@ function AttachmentModal(props) { icon: Expensicons.Trashcan, text: props.translate('receipt.deleteReceipt'), onSelected: () => { - const transactionID = lodashGet(props.parentReportAction, 'originalMessage.IOUTransactionID', ''); + const parentReportAction = ReportActionsUtils.getReportAction(props.report.parentReportID, props.report.parentReportActionID); + const transactionID = lodashGet(parentReportAction, 'originalMessage.IOUTransactionID', ''); Receipt.detachReceipt(transactionID, props.report.reportID) }, }, @@ -464,14 +457,4 @@ AttachmentModal.displayName = 'AttachmentModal'; export default compose( withWindowDimensions, withLocalize, - withOnyx({ - parentReport: { - key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`, - }, - parentReportAction: { - key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${(report.parentReportID, report.parentReportActionID)}`, - selector: (reportActions, props) => props && props.parentReport && reportActions && reportActions[props.parentReport.parentReportActionID], - canEvict: false, - }, - }), )(AttachmentModal); From 64a4f68e5f9ab3ebe098d2a1d9a1f1657cc8c9f4 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Thu, 21 Sep 2023 18:35:49 +0800 Subject: [PATCH 066/856] revert a few old changes --- src/components/AttachmentModal.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index ebfcf5f0f3a3..4fbe0d49d7b8 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -97,8 +97,6 @@ const defaultProps = { onModalHide: () => {}, onCarouselAttachmentChange: () => {}, isWorkspaceAvatar: false, - parentReport: {}, - parentReportAction: {}, }; function AttachmentModal(props) { @@ -454,7 +452,4 @@ function AttachmentModal(props) { AttachmentModal.propTypes = propTypes; AttachmentModal.defaultProps = defaultProps; AttachmentModal.displayName = 'AttachmentModal'; -export default compose( - withWindowDimensions, - withLocalize, -)(AttachmentModal); +export default compose(withWindowDimensions, withLocalize)(AttachmentModal); From 43140ea100b2c7737db7fc2f487913a2fa37d3b6 Mon Sep 17 00:00:00 2001 From: Thiago Brezinski Date: Thu, 21 Sep 2023 12:07:58 +0100 Subject: [PATCH 067/856] chore: fix submit button style --- src/components/Form.js | 7 ++++++- src/pages/workspace/WorkspaceSettingsPage.js | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/Form.js b/src/components/Form.js index ef6c3ea10474..be73f448548b 100644 --- a/src/components/Form.js +++ b/src/components/Form.js @@ -76,6 +76,10 @@ const propTypes = { /** Container styles */ style: stylePropTypes, + /** Submit button container styles */ + // eslint-disable-next-line react/forbid-prop-types + submitButtonStyles: PropTypes.arrayOf(PropTypes.object), + /** Custom content to display in the footer after submit button */ footerContent: PropTypes.oneOfType([PropTypes.func, PropTypes.node]), @@ -98,6 +102,7 @@ const defaultProps = { shouldValidateOnBlur: true, footerContent: null, style: [], + submitButtonStyles: [], validate: () => ({}), }; @@ -447,7 +452,7 @@ function Form(props) { focusInput.focus(); } }} - containerStyles={[styles.mh0, styles.mt5, styles.flex1]} + containerStyles={[styles.mh0, styles.mt5, styles.flex1, ...props.submitButtonStyles]} enabledWhenOffline={props.enabledWhenOffline} isSubmitActionDangerous={props.isSubmitActionDangerous} disablePressOnEnter diff --git a/src/pages/workspace/WorkspaceSettingsPage.js b/src/pages/workspace/WorkspaceSettingsPage.js index c472bfbfd7a6..2ff1491c7ed9 100644 --- a/src/pages/workspace/WorkspaceSettingsPage.js +++ b/src/pages/workspace/WorkspaceSettingsPage.js @@ -100,6 +100,7 @@ function WorkspaceSettingsPage({policy, currencyList, windowWidth, route}) { formID={ONYXKEYS.FORMS.WORKSPACE_SETTINGS_FORM} submitButtonText={translate('workspace.editor.save')} style={styles.flexGrow1} + submitButtonStyles={[styles.mh5]} scrollContextEnabled validate={validate} onSubmit={submit} From 86b650f4aabc0223a436b4d760909a9636f32980 Mon Sep 17 00:00:00 2001 From: Thiago Brezinski Date: Thu, 21 Sep 2023 12:14:07 +0100 Subject: [PATCH 068/856] chore: cleanup --- src/components/Form.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Form.js b/src/components/Form.js index be73f448548b..7d62d936c159 100644 --- a/src/components/Form.js +++ b/src/components/Form.js @@ -477,6 +477,7 @@ function Form(props) { props.isSubmitActionDangerous, props.isSubmitButtonVisible, props.submitButtonText, + props.submitButtonStyles, ], ); From f69bf9936eb47e7bb9d2a671b06020ab5fc91efa Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Fri, 22 Sep 2023 08:37:03 +0800 Subject: [PATCH 069/856] Add logo and guaranteed eReceipt --- src/components/EReceipt.js | 7 +++++-- src/languages/en.ts | 3 +++ src/styles/styles.js | 7 +++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/components/EReceipt.js b/src/components/EReceipt.js index cd5959b1a41d..1f24144031d4 100644 --- a/src/components/EReceipt.js +++ b/src/components/EReceipt.js @@ -12,7 +12,9 @@ import * as TransactionUtils from '../libs/TransactionUtils'; import tryResolveUrlFromApiRoot from '../libs/tryResolveUrlFromApiRoot'; import ThumbnailImage from './ThumbnailImage'; import useLocalize from '../hooks/useLocalize'; -import fontFamily from '../styles/fontFamily'; +import Icon from './Icon'; +import themeColors from '../styles/themes/default'; +import * as Expensicons from './Icon/Expensicons'; const propTypes = { /** The transaction for the eReceipt */ @@ -65,9 +67,10 @@ function EReceipt({transaction}) { + {translate('eReceipt.guaranteed')} ); diff --git a/src/languages/en.ts b/src/languages/en.ts index f7c028d2a106..495e7b0f7105 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1778,4 +1778,7 @@ export default { selectSuggestedAddress: 'Please select a suggested address', }, }, + eReceipt: { + guaranteed: 'Guaranteed eReceipt', + }, } satisfies TranslationBase; diff --git a/src/styles/styles.js b/src/styles/styles.js index c15b0f8b9dcf..de7cfec48ef7 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -3581,6 +3581,13 @@ const styles = (theme) => ({ color: theme.textColorfulBackground, }, + eReceiptGuaranteed: { + fontFamily: fontFamily.MONOSPACE, + fontSize: variables.fontSizeSmall, + lineHeight: variables.lineHeightSmall, + color: theme.textColorfulBackground, + }, + loginHeroBody: { fontFamily: fontFamily.EXP_NEUE, fontSize: variables.fontSizeSignInHeroBody, From 3a67ed468d4721a7d819c92bb9c082683b416a83 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Fri, 22 Sep 2023 08:57:16 +0800 Subject: [PATCH 070/856] Set up wrapping views with padding --- src/components/EReceipt.js | 80 +++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 39 deletions(-) diff --git a/src/components/EReceipt.js b/src/components/EReceipt.js index 1f24144031d4..d89439e32326 100644 --- a/src/components/EReceipt.js +++ b/src/components/EReceipt.js @@ -33,46 +33,48 @@ function EReceipt({transaction}) { const thumbnailSource = tryResolveUrlFromApiRoot(thumbnail || ''); const waypoints = lodashGet(transaction, 'comment.waypoints', {}); return ( - <> - - + + + + + + {formattedTransactionAmount} + {transactionMerchant} + {_.map(waypoints, (waypoint, key) => { + const index = TransactionUtils.getWaypointIndex(key); + let descriptionKey = 'distance.waypointDescription.'; + if (index === 0) { + descriptionKey += 'start'; + } else if (index === _.size(waypoints) - 1) { + descriptionKey += 'finish'; + } else { + descriptionKey += 'stop'; + } + return ( + + {translate(descriptionKey)} + {waypoint.address || ''} + + ); + })} + {translate('common.date')} + {transactionDate} + + + {translate('eReceipt.guaranteed')} + - {formattedTransactionAmount} - {transactionMerchant} - {_.map(waypoints, (waypoint, key) => { - const index = TransactionUtils.getWaypointIndex(key); - let descriptionKey = 'distance.waypointDescription.'; - if (index === 0) { - descriptionKey += 'start'; - } else if (index === _.size(waypoints) - 1) { - descriptionKey += 'finish'; - } else { - descriptionKey += 'stop'; - } - return ( - - {translate(descriptionKey)} - {waypoint.address || ''} - - ); - })} - {translate('common.date')} - {transactionDate} - - - {translate('eReceipt.guaranteed')} - - + ); } From 74664ceab6450c5411d8bdab9cc533cf11d61dd4 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Fri, 22 Sep 2023 10:12:10 +0800 Subject: [PATCH 071/856] Fix lint issues / clean up --- src/components/Attachments/AttachmentView/index.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index e7d1dd5abb6b..b0e22c237eaf 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -69,6 +69,7 @@ function AttachmentView({ isWorkspaceAvatar, }) { const [loadComplete, setLoadComplete] = useState(false); + const currentRoute = useRoute(); // Handles case where source is a component (ex: SVG) if (_.isFunction(source)) { @@ -112,7 +113,6 @@ function AttachmentView({ ); } - const currentRoute = useRoute(); const reportID = _.get(currentRoute, ['params', 'reportID']); const report = ReportUtils.getReport(reportID); @@ -120,8 +120,6 @@ function AttachmentView({ const parentReportAction = ReportActionsUtils.getParentReportAction(report); const transactionID = _.get(parentReportAction, ['originalMessage', 'IOUTransactionID'], 0); const transaction = TransactionUtils.getTransaction(transactionID); - console.log('transaction', transaction); - const shouldShowEReceipt = TransactionUtils.isDistanceRequest(transaction); if (shouldShowEReceipt) { return ; From 6e1440edff178f5be01b29cef62756565971c6c6 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Fri, 22 Sep 2023 10:12:35 +0800 Subject: [PATCH 072/856] Add the eReceipt background --- assets/images/eReceipt_background.svg | 1635 +++++++++++++++++++++++++ src/components/EReceipt.js | 25 +- src/styles/styles.js | 28 + src/styles/themes/default.js | 1 + src/styles/utilities/spacing.ts | 4 + 5 files changed, 1686 insertions(+), 7 deletions(-) create mode 100644 assets/images/eReceipt_background.svg diff --git a/assets/images/eReceipt_background.svg b/assets/images/eReceipt_background.svg new file mode 100644 index 000000000000..257489bd6fdd --- /dev/null +++ b/assets/images/eReceipt_background.svg @@ -0,0 +1,1635 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/EReceipt.js b/src/components/EReceipt.js index d89439e32326..5f5619cb22e4 100644 --- a/src/components/EReceipt.js +++ b/src/components/EReceipt.js @@ -15,6 +15,7 @@ import useLocalize from '../hooks/useLocalize'; import Icon from './Icon'; import themeColors from '../styles/themes/default'; import * as Expensicons from './Icon/Expensicons'; +import EReceiptBackground from '../../assets/images/eReceipt_background.svg'; const propTypes = { /** The transaction for the eReceipt */ @@ -33,8 +34,14 @@ function EReceipt({transaction}) { const thumbnailSource = tryResolveUrlFromApiRoot(thumbnail || ''); const waypoints = lodashGet(transaction, 'comment.waypoints', {}); return ( - - + + + + + - {formattedTransactionAmount} - {transactionMerchant} + + {formattedTransactionAmount} + {transactionMerchant} + {_.map(waypoints, (waypoint, key) => { const index = TransactionUtils.getWaypointIndex(key); let descriptionKey = 'distance.waypointDescription.'; @@ -56,14 +65,16 @@ function EReceipt({transaction}) { descriptionKey += 'stop'; } return ( - + {translate(descriptionKey)} {waypoint.address || ''} ); })} - {translate('common.date')} - {transactionDate} + + {translate('common.date')} + {transactionDate} + ({ color: theme.textColorfulBackground, }, + eReceiptBackgroundContainer: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + justifyContent: 'center', + alignItems: 'center', + }, + + eReceiptBackground: { + ...sizing.w100, + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + minHeight: 540, + }, + + eReceiptPanel: { + ...spacing.p5, + ...spacing.pb8, + ...flex.flex1, + backgroundColor: theme.panelBackground, + borderRadius: 20, + }, + loginHeroBody: { fontFamily: fontFamily.EXP_NEUE, fontSize: variables.fontSizeSignInHeroBody, diff --git a/src/styles/themes/default.js b/src/styles/themes/default.js index eac8b087ead0..5e06753d719a 100644 --- a/src/styles/themes/default.js +++ b/src/styles/themes/default.js @@ -83,6 +83,7 @@ const darkTheme = { starDefaultBG: 'rgb(254, 228, 94)', loungeAccessOverlay: colors.blue800, textBrand: colors.green400, + panelBackground: colors.green800, }; darkTheme.PAGE_BACKGROUND_COLORS = { diff --git a/src/styles/utilities/spacing.ts b/src/styles/utilities/spacing.ts index a3667f05ac06..a47efe504326 100644 --- a/src/styles/utilities/spacing.ts +++ b/src/styles/utilities/spacing.ts @@ -55,6 +55,10 @@ export default { marginHorizontal: -20, }, + mv0: { + marginVertical: 0, + }, + mv1: { marginVertical: 4, }, From f8ef14475d4abb35300cea6a4898138c3611d713 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Fri, 22 Sep 2023 15:20:08 +0800 Subject: [PATCH 073/856] The eReceipt is specific to distance requests --- src/components/Attachments/AttachmentView/index.js | 6 +++--- src/components/{EReceipt.js => DistanceEReceipt.js} | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) rename src/components/{EReceipt.js => DistanceEReceipt.js} (95%) diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index b0e22c237eaf..aca92bf7ba5d 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -3,6 +3,7 @@ import {View, ActivityIndicator} from 'react-native'; import _ from 'underscore'; import PropTypes from 'prop-types'; import Str from 'expensify-common/lib/str'; +import {useRoute} from '@react-navigation/native'; import styles from '../../../styles/styles'; import Icon from '../../Icon'; import * as Expensicons from '../../Icon/Expensicons'; @@ -17,10 +18,9 @@ import AttachmentViewPdf from './AttachmentViewPdf'; import addEncryptedAuthTokenToURL from '../../../libs/addEncryptedAuthTokenToURL'; import * as StyleUtils from '../../../styles/StyleUtils'; import {attachmentViewPropTypes, attachmentViewDefaultProps} from './propTypes'; -import {useRoute} from '@react-navigation/native'; import * as ReportUtils from '../../../libs/ReportUtils'; import * as TransactionUtils from '../../../libs/TransactionUtils'; -import EReceipt from '../../EReceipt'; +import DistanceEReceipt from '../../DistanceEReceipt'; import * as ReportActionsUtils from '../../../libs/ReportActionsUtils'; const propTypes = { @@ -122,7 +122,7 @@ function AttachmentView({ const transaction = TransactionUtils.getTransaction(transactionID); const shouldShowEReceipt = TransactionUtils.isDistanceRequest(transaction); if (shouldShowEReceipt) { - return ; + return ; } // For this check we use both source and file.name since temporary file source is a blob diff --git a/src/components/EReceipt.js b/src/components/DistanceEReceipt.js similarity index 95% rename from src/components/EReceipt.js rename to src/components/DistanceEReceipt.js index 5f5619cb22e4..c8d428dde000 100644 --- a/src/components/EReceipt.js +++ b/src/components/DistanceEReceipt.js @@ -26,7 +26,7 @@ const defaultProps = { transaction: {}, }; -function EReceipt({transaction}) { +function DistanceEReceipt({transaction}) { const {translate} = useLocalize(); const {thumbnail} = ReceiptUtils.getThumbnailAndImageURIs(transaction.receipt.source, transaction.filename); const {amount: transactionAmount, currency: transactionCurrency, merchant: transactionMerchant, created: transactionDate} = ReportUtils.getTransactionDetails(transaction); @@ -89,7 +89,7 @@ function EReceipt({transaction}) { ); } -export default EReceipt; -EReceipt.displayName = 'EReceipt'; -EReceipt.propTypes = propTypes; -EReceipt.defaultProps = defaultProps; +export default DistanceEReceipt; +DistanceEReceipt.displayName = 'DistanceEReceipt'; +DistanceEReceipt.propTypes = propTypes; +DistanceEReceipt.defaultProps = defaultProps; From ab4db9793693bd7e57b254a3fd025657f61ee81d Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Fri, 22 Sep 2023 15:38:36 +0800 Subject: [PATCH 074/856] Set up gap between main sections --- src/components/DistanceEReceipt.js | 46 ++++++++++++++++-------------- src/styles/utilities/spacing.ts | 4 +++ 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/src/components/DistanceEReceipt.js b/src/components/DistanceEReceipt.js index c8d428dde000..0543cbc00794 100644 --- a/src/components/DistanceEReceipt.js +++ b/src/components/DistanceEReceipt.js @@ -42,7 +42,7 @@ function DistanceEReceipt({transaction}) { pointerEvents="none" /> - + - + {formattedTransactionAmount} {transactionMerchant} - {_.map(waypoints, (waypoint, key) => { - const index = TransactionUtils.getWaypointIndex(key); - let descriptionKey = 'distance.waypointDescription.'; - if (index === 0) { - descriptionKey += 'start'; - } else if (index === _.size(waypoints) - 1) { - descriptionKey += 'finish'; - } else { - descriptionKey += 'stop'; - } - return ( - - {translate(descriptionKey)} - {waypoint.address || ''} - - ); - })} - - {translate('common.date')} - {transactionDate} + + {_.map(waypoints, (waypoint, key) => { + const index = TransactionUtils.getWaypointIndex(key); + let descriptionKey = 'distance.waypointDescription.'; + if (index === 0) { + descriptionKey += 'start'; + } else if (index === _.size(waypoints) - 1) { + descriptionKey += 'finish'; + } else { + descriptionKey += 'stop'; + } + return ( + + {translate(descriptionKey)} + {waypoint.address || ''} + + ); + })} + + {translate('common.date')} + {transactionDate} + Date: Fri, 22 Sep 2023 16:19:11 +0800 Subject: [PATCH 075/856] Spacing and layout within sections --- src/components/DistanceEReceipt.js | 17 ++++++++++------- src/styles/utilities/spacing.ts | 4 ++++ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/components/DistanceEReceipt.js b/src/components/DistanceEReceipt.js index 0543cbc00794..0f83adc86ef2 100644 --- a/src/components/DistanceEReceipt.js +++ b/src/components/DistanceEReceipt.js @@ -50,11 +50,11 @@ function DistanceEReceipt({transaction}) { shouldDynamicallyResize={false} /> - + {formattedTransactionAmount} {transactionMerchant} - + {_.map(waypoints, (waypoint, key) => { const index = TransactionUtils.getWaypointIndex(key); let descriptionKey = 'distance.waypointDescription.'; @@ -66,21 +66,24 @@ function DistanceEReceipt({transaction}) { descriptionKey += 'stop'; } return ( - + {translate(descriptionKey)} {waypoint.address || ''} ); })} - + {translate('common.date')} {transactionDate} - + diff --git a/src/styles/utilities/spacing.ts b/src/styles/utilities/spacing.ts index e87d8cf85b31..a998b3302b3a 100644 --- a/src/styles/utilities/spacing.ts +++ b/src/styles/utilities/spacing.ts @@ -171,6 +171,10 @@ export default { marginLeft: -32, }, + mt0: { + marginTop: 0, + }, + mt1: { marginTop: 4, }, From 24638882894a9077d6d9ca7af75a6dc0cbca4177 Mon Sep 17 00:00:00 2001 From: Alberto Date: Fri, 22 Sep 2023 16:30:53 +0800 Subject: [PATCH 076/856] Use the new property --- src/libs/ReportUtils.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 2f9bd39d03c7..f7286465546a 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -2274,7 +2274,7 @@ function buildOptimisticReportPreview(chatReport, iouReport, comment = '', trans actorAccountID: hasReceipt ? currentUserAccountID : iouReport.managerID || 0, childMoneyRequestCount: 1, childLastMoneyRequestComment: comment, - childLastReceiptTransactionIDs: hasReceipt ? transaction.transactionID : '', + childRecentReceiptTransactionIDs: hasReceipt ? {transaction.transactionID = transaction.transactionID} : [], whisperedToAccountIDs: hasReceipt ? [currentUserAccountID] : [], }; } @@ -2333,7 +2333,7 @@ function buildOptimisticModifiedExpenseReportAction(transactionThread, oldTransa */ function updateReportPreview(iouReport, reportPreviewAction, isPayRequest = false, comment = '', transaction = undefined) { const hasReceipt = TransactionUtils.hasReceipt(transaction); - const lastReceiptTransactionIDs = lodashGet(reportPreviewAction, 'childLastReceiptTransactionIDs', ''); + const recentReceiptTransactionIDs = lodashGet(reportPreviewAction, 'childRecentReceiptTransactionIDs', {}); const previousTransactionIDs = lastReceiptTransactionIDs.split(',').slice(0, 2); const message = getReportPreviewMessage(iouReport, reportPreviewAction); @@ -2350,7 +2350,10 @@ function updateReportPreview(iouReport, reportPreviewAction, isPayRequest = fals ], childLastMoneyRequestComment: comment || reportPreviewAction.childLastMoneyRequestComment, childMoneyRequestCount: reportPreviewAction.childMoneyRequestCount + (isPayRequest ? 0 : 1), - childLastReceiptTransactionIDs: hasReceipt ? [transaction.transactionID, ...previousTransactionIDs].join(',') : lastReceiptTransactionIDs, + childRecentReceiptTransactionIDs: hasReceipt ? { + [transaction.transactionID]: transaction.transactionID, + ...previousTransactionIDs + } : recentReceiptTransactionIDs, // As soon as we add a transaction without a receipt to the report, it will have ready money requests, // so we remove the whisper whisperedToAccountIDs: hasReceipt ? reportPreviewAction.whisperedToAccountIDs : [], @@ -3547,7 +3550,7 @@ function getTaskAssigneeChatOnyxData(accountID, assigneeEmail, assigneeAccountID * @returns {Object} */ function getReportPreviewDisplayTransactions(reportPreviewAction) { - const transactionIDs = lodashGet(reportPreviewAction, ['childLastReceiptTransactionIDs'], '').split(','); + const transactionIDs = lodashGet(reportPreviewAction, ['childRecentReceiptTransactionIDs']); return _.reduce( transactionIDs, (transactions, transactionID) => { From 8b8afe9c1aea0cd85ba57e50bed19bc49ca805ad Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Fri, 22 Sep 2023 16:35:58 +0800 Subject: [PATCH 077/856] Set width on eReceipt for large screens --- src/components/DistanceEReceipt.js | 2 +- src/styles/styles.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/DistanceEReceipt.js b/src/components/DistanceEReceipt.js index 0f83adc86ef2..ab9065a8c49f 100644 --- a/src/components/DistanceEReceipt.js +++ b/src/components/DistanceEReceipt.js @@ -34,7 +34,7 @@ function DistanceEReceipt({transaction}) { const thumbnailSource = tryResolveUrlFromApiRoot(thumbnail || ''); const waypoints = lodashGet(transaction, 'comment.waypoints', {}); return ( - + ({ eReceiptPanel: { ...spacing.p5, ...spacing.pb8, - ...flex.flex1, backgroundColor: theme.panelBackground, borderRadius: 20, + width: 335, }, loginHeroBody: { From b319defb7906e9e86cdcbd1bd458566af53b52ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Fri, 22 Sep 2023 17:33:58 +0200 Subject: [PATCH 078/856] initial commit --- package-lock.json | 1 + package.json | 7 +- src/libs/fileDownload/FileUtils.js | 52 +++++- .../ReceiptSelector/NavigationAwareCamera.js | 65 ++----- .../NavigationAwareCamera.native.js | 77 ++++++++ src/pages/iou/ReceiptSelector/index.js | 166 +++++++++++++++++- 6 files changed, 307 insertions(+), 61 deletions(-) create mode 100644 src/pages/iou/ReceiptSelector/NavigationAwareCamera.native.js diff --git a/package-lock.json b/package-lock.json index ff0500eb385b..85a2d05fd26a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -114,6 +114,7 @@ "react-pdf": "^6.2.2", "react-plaid-link": "3.3.2", "react-web-config": "^1.0.0", + "react-webcam": "^7.1.1", "react-window": "^1.8.9", "save": "^2.4.0", "semver": "^7.5.2", diff --git a/package.json b/package.json index 44b936c8c588..6a9f852ca3ec 100644 --- a/package.json +++ b/package.json @@ -61,9 +61,9 @@ "@formatjs/intl-pluralrules": "^5.2.2", "@gorhom/portal": "^1.0.14", "@invertase/react-native-apple-authentication": "^2.2.2", - "@oguzhnatly/react-native-image-manipulator": "github:Expensify/react-native-image-manipulator#5cdae3d4455b03a04c57f50be3863e2fe6c92c52", "@kie/act-js": "^2.0.1", "@kie/mock-github": "^1.0.0", + "@oguzhnatly/react-native-image-manipulator": "github:Expensify/react-native-image-manipulator#5cdae3d4455b03a04c57f50be3863e2fe6c92c52", "@onfido/react-native-sdk": "7.4.0", "@react-native-async-storage/async-storage": "^1.17.10", "@react-native-camera-roll/camera-roll": "5.4.0", @@ -81,9 +81,9 @@ "@react-navigation/stack": "6.3.16", "@react-ng/bounds-observer": "^0.2.1", "@rnmapbox/maps": "^10.0.11", + "@types/node": "^18.14.0", "@ua/react-native-airship": "^15.2.6", "awesome-phonenumber": "^5.4.0", - "@types/node": "^18.14.0", "babel-plugin-transform-remove-console": "^6.9.4", "babel-polyfill": "^6.26.0", "canvas-size": "^1.2.6", @@ -111,8 +111,8 @@ "react-collapse": "^5.1.0", "react-content-loader": "^6.1.0", "react-dom": "18.1.0", - "react-map-gl": "^7.1.3", "react-error-boundary": "^4.0.11", + "react-map-gl": "^7.1.3", "react-native": "0.72.4", "react-native-blob-util": "^0.17.3", "react-native-collapsible": "^1.6.0", @@ -156,6 +156,7 @@ "react-pdf": "^6.2.2", "react-plaid-link": "3.3.2", "react-web-config": "^1.0.0", + "react-webcam": "^7.1.1", "react-window": "^1.8.9", "save": "^2.4.0", "semver": "^7.5.2", diff --git a/src/libs/fileDownload/FileUtils.js b/src/libs/fileDownload/FileUtils.js index cee2b8877ef6..ca784ff95b4e 100644 --- a/src/libs/fileDownload/FileUtils.js +++ b/src/libs/fileDownload/FileUtils.js @@ -170,4 +170,54 @@ const readFileAsync = (path, fileName) => }); }); -export {showGeneralErrorAlert, showSuccessAlert, showPermissionErrorAlert, splitExtensionFromFileName, getAttachmentName, getFileType, cleanFileName, appendTimeToFileName, readFileAsync}; +/** + * Converts a base64 encoded image string to a File instance. + * Adds a `uri` property to the File instance for accessing the blob as a URI. + * + * @param {string} base64 - The base64 encoded image string. + * @param {string} filename - Desired filename for the File instance. + * @returns {File} The File instance created from the base64 string with an additional `uri` property. + * + * @example + * const base64Image = "data:image/png;base64,..."; // your base64 encoded image + * const imageFile = base64ToFile(base64Image, "example.png"); + * console.log(imageFile.uri); // Blob URI + */ +function base64ToFile(base64, filename) { + // Decode the base64 string + const byteString = atob(base64.split(',')[1]); + + // Get the mime type from the base64 string + const mimeString = base64.split(',')[0].split(':')[1].split(';')[0]; + + // Convert byte string to Uint8Array + const arrayBuffer = new ArrayBuffer(byteString.length); + const uint8Array = new Uint8Array(arrayBuffer); + for (let i = 0; i < byteString.length; i++) { + uint8Array[i] = byteString.charCodeAt(i); + } + + // Create a blob from the Uint8Array + const blob = new Blob([uint8Array], {type: mimeString}); + + // Create a File instance from the Blob + const file = new File([blob], filename, {type: mimeString, lastModified: Date.now()}); + + // Add a uri property to the File instance for accessing the blob as a URI + file.uri = URL.createObjectURL(blob); + + return file; +} + +export { + showGeneralErrorAlert, + showSuccessAlert, + showPermissionErrorAlert, + splitExtensionFromFileName, + getAttachmentName, + getFileType, + cleanFileName, + appendTimeToFileName, + readFileAsync, + base64ToFile, +}; diff --git a/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js b/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js index 8fe00c7a65b3..95ed1f6c046b 100644 --- a/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js +++ b/src/pages/iou/ReceiptSelector/NavigationAwareCamera.js @@ -1,7 +1,6 @@ import React, {useEffect, useState} from 'react'; -import {Camera} from 'react-native-vision-camera'; -import {useTabAnimation} from '@react-navigation/material-top-tabs'; -import {useNavigation} from '@react-navigation/native'; +import Webcam from 'react-webcam'; +import {useIsFocused} from '@react-navigation/native'; import PropTypes from 'prop-types'; import refPropTypes from '../../../components/refPropTypes'; @@ -14,53 +13,19 @@ const propTypes = { }; // Wraps a camera that will only be active when the tab is focused or as soon as it starts to become focused. -function NavigationAwareCamera({cameraTabIndex, forwardedRef, ...props}) { - // Get navigation to get initial isFocused value (only needed once during init!) - const navigation = useNavigation(); - const [isCameraActive, setIsCameraActive] = useState(navigation.isFocused()); - - // Get the animation value from the tab navigator. Its a value between 0 and the - // number of pages we render in the tab navigator. When we even just slightly start to scroll to the camera page, - // (value is e.g. 0.001 on animation start) we want to activate the camera, so its as fast as possible active. - const tabPositionAnimation = useTabAnimation(); - - useEffect(() => { - const listenerId = tabPositionAnimation.addListener(({value}) => { - // Activate camera as soon the index is animating towards the `cameraTabIndex` - setIsCameraActive(value > cameraTabIndex - 1 && value < cameraTabIndex + 1); - }); - - return () => { - tabPositionAnimation.removeListener(listenerId); - }; - }, [cameraTabIndex, tabPositionAnimation]); - - // Note: The useEffect can be removed once VisionCamera V3 is used. - // Its only needed for android, because there is a native cameraX android bug. With out this flow would break the camera: - // 1. Open camera tab - // 2. Take a picture - // 3. Go back from the opened screen - // 4. The camera is not working anymore - useEffect(() => { - const removeBlurListener = navigation.addListener('blur', () => { - setIsCameraActive(false); - }); - const removeFocusListener = navigation.addListener('focus', () => { - setIsCameraActive(true); - }); - - return () => { - removeBlurListener(); - removeFocusListener(); - }; - }, [navigation]); +function NavigationAwareCamera({cameraTabIndex, ...props}, ref) { + const isCameraActive = useIsFocused(); + if (!isCameraActive) { + return null; + } return ( - ); } @@ -68,10 +33,4 @@ function NavigationAwareCamera({cameraTabIndex, forwardedRef, ...props}) { NavigationAwareCamera.propTypes = propTypes; NavigationAwareCamera.displayName = 'NavigationAwareCamera'; -export default React.forwardRef((props, ref) => ( - -)); +export default React.forwardRef(NavigationAwareCamera); diff --git a/src/pages/iou/ReceiptSelector/NavigationAwareCamera.native.js b/src/pages/iou/ReceiptSelector/NavigationAwareCamera.native.js new file mode 100644 index 000000000000..8fe00c7a65b3 --- /dev/null +++ b/src/pages/iou/ReceiptSelector/NavigationAwareCamera.native.js @@ -0,0 +1,77 @@ +import React, {useEffect, useState} from 'react'; +import {Camera} from 'react-native-vision-camera'; +import {useTabAnimation} from '@react-navigation/material-top-tabs'; +import {useNavigation} from '@react-navigation/native'; +import PropTypes from 'prop-types'; +import refPropTypes from '../../../components/refPropTypes'; + +const propTypes = { + /* The index of the tab that contains this camera */ + cameraTabIndex: PropTypes.number.isRequired, + + /* Forwarded ref */ + forwardedRef: refPropTypes.isRequired, +}; + +// Wraps a camera that will only be active when the tab is focused or as soon as it starts to become focused. +function NavigationAwareCamera({cameraTabIndex, forwardedRef, ...props}) { + // Get navigation to get initial isFocused value (only needed once during init!) + const navigation = useNavigation(); + const [isCameraActive, setIsCameraActive] = useState(navigation.isFocused()); + + // Get the animation value from the tab navigator. Its a value between 0 and the + // number of pages we render in the tab navigator. When we even just slightly start to scroll to the camera page, + // (value is e.g. 0.001 on animation start) we want to activate the camera, so its as fast as possible active. + const tabPositionAnimation = useTabAnimation(); + + useEffect(() => { + const listenerId = tabPositionAnimation.addListener(({value}) => { + // Activate camera as soon the index is animating towards the `cameraTabIndex` + setIsCameraActive(value > cameraTabIndex - 1 && value < cameraTabIndex + 1); + }); + + return () => { + tabPositionAnimation.removeListener(listenerId); + }; + }, [cameraTabIndex, tabPositionAnimation]); + + // Note: The useEffect can be removed once VisionCamera V3 is used. + // Its only needed for android, because there is a native cameraX android bug. With out this flow would break the camera: + // 1. Open camera tab + // 2. Take a picture + // 3. Go back from the opened screen + // 4. The camera is not working anymore + useEffect(() => { + const removeBlurListener = navigation.addListener('blur', () => { + setIsCameraActive(false); + }); + const removeFocusListener = navigation.addListener('focus', () => { + setIsCameraActive(true); + }); + + return () => { + removeBlurListener(); + removeFocusListener(); + }; + }, [navigation]); + + return ( + + ); +} + +NavigationAwareCamera.propTypes = propTypes; +NavigationAwareCamera.displayName = 'NavigationAwareCamera'; + +export default React.forwardRef((props, ref) => ( + +)); diff --git a/src/pages/iou/ReceiptSelector/index.js b/src/pages/iou/ReceiptSelector/index.js index eb6e2328afd2..246fb52c455f 100644 --- a/src/pages/iou/ReceiptSelector/index.js +++ b/src/pages/iou/ReceiptSelector/index.js @@ -1,5 +1,5 @@ import {View, Text, PixelRatio} from 'react-native'; -import React, {useContext, useState} from 'react'; +import React, {useCallback, useContext, useRef, useState} from 'react'; import lodashGet from 'lodash/get'; import _ from 'underscore'; import PropTypes from 'prop-types'; @@ -22,6 +22,12 @@ import {DragAndDropContext} from '../../../components/DragAndDrop/Provider'; import {iouPropTypes, iouDefaultProps} from '../propTypes'; import * as FileUtils from '../../../libs/fileDownload/FileUtils'; import Navigation from '../../../libs/Navigation/Navigation'; +import * as Expensicons from '../../../components/Icon/Expensicons'; +import Icon from '../../../components/Icon'; +import themeColors from '../../../styles/themes/default'; +import Shutter from '../../../../assets/images/shutter.svg'; +import NavigationAwareCamera from './NavigationAwareCamera'; +import * as Browser from '../../../libs/Browser'; const propTypes = { /** The report on which the request is initiated on */ @@ -62,6 +68,8 @@ function ReceiptSelector(props) { const {isSmallScreenWidth} = useWindowDimensions(); const {translate} = useLocalize(); const {isDraggingOver} = useContext(DragAndDropContext); + const [cameraPermissionState, setCameraPermissionState] = useState('prompt'); + const cameraRef = useRef(null); const hideReciptModal = () => { setIsAttachmentInvalid(false); @@ -122,9 +130,93 @@ function ReceiptSelector(props) { IOU.navigateToNextPage(iou, iouType, reportID, report); }; + const capturePhoto = useCallback(() => { + if (!cameraRef.current.getScreenshot) { + return; + } + const imageB64 = cameraRef.current.getScreenshot(); + const filename = `receipt_${Date.now()}.png`; + const imageFile = FileUtils.base64ToFile(imageB64, filename); + const filePath = URL.createObjectURL(imageFile); + IOU.setMoneyRequestReceipt(filePath, imageFile.name); + + if (props.transactionID) { + IOU.replaceReceipt(props.transactionID, imageFile, filePath); + Navigation.dismissModal(); + return; + } + + IOU.navigateToNextPage(props.iou, iouType, reportID, props.report); + }, [cameraRef, props.iou, props.report, reportID, iouType, props.transactionID]); + return ( - - {!isDraggingOver ? ( + + {!isDraggingOver && Browser.isMobile() && ( + <> + + {(cameraPermissionState === 'prompt' || cameraPermissionState === 'unknown') && Give permissions} + {cameraPermissionState === 'denied' && Turn on permissions or else} + setCameraPermissionState('granted')} + onUserMediaError={() => setCameraPermissionState('denied')} + style={{objectFit: 'cover', width: '100%', height: '100%'}} + ref={cameraRef} + screenshotFormat="image/png" + /> + + + + + {({openPicker}) => ( + { + openPicker({ + onPicked: (file) => { + setReceiptAndNavigate(file, props.iou, props.report); + }, + }); + }} + > + + + )} + + + + + alert('set flash')} + > + + + + + )} + {!isDraggingOver && !Browser.isMobile() && ( <> { @@ -168,7 +260,7 @@ function ReceiptSelector(props) { )} - ) : null} + )} { const file = lodashGet(e, ['dataTransfer', 'files', 0]); @@ -187,6 +279,72 @@ function ReceiptSelector(props) { /> ); + + // return ( + // + // {!isDraggingOver ? ( + // <> + // { + // setReceiptImageTopPosition(PixelRatio.roundToNearestPixel(nativeEvent.layout.top)); + // }} + // > + // + // + // {translate('receipt.upload')} + // + // {isSmallScreenWidth ? translate('receipt.chooseReceipt') : translate('receipt.dragReceiptBeforeEmail')} + // + // {isSmallScreenWidth ? null : translate('receipt.dragReceiptAfterEmail')} + // + // + // {({openPicker}) => ( + // + //