diff --git a/android/app/build.gradle b/android/app/build.gradle index e11f938a9a8e..c7dd40eaeae3 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -91,8 +91,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001040306 - versionName "1.4.3-6" + versionCode 1001040307 + versionName "1.4.3-7" } flavorDimensions "default" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 2d89178b271b..ce615bf345c0 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.3.6 + 1.4.3.7 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 2f053575ff3b..5721e8644981 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.3.6 + 1.4.3.7 diff --git a/package-lock.json b/package-lock.json index 9dd6974b12c5..a60d1d129895 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.3-6", + "version": "1.4.3-7", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.3-6", + "version": "1.4.3-7", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index eb1b725eea10..ab8e6da87446 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.3-6", + "version": "1.4.3-7", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index 4ab81ae462c9..d346f271b36d 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -128,6 +128,8 @@ function AttachmentModal(props) { const [isDownloadButtonReadyToBeShown, setIsDownloadButtonReadyToBeShown] = React.useState(true); const {windowWidth} = useWindowDimensions(); + const isOverlayModalVisible = (isAttachmentReceipt && isDeleteReceiptConfirmModalVisible) || (!isAttachmentReceipt && isAttachmentInvalid); + const [file, setFile] = useState( props.originalFileName ? { @@ -406,7 +408,7 @@ function AttachmentModal(props) { { diff --git a/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.js b/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx similarity index 77% rename from src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.js rename to src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx index ec53507d4d8e..3e5e7b4fdd9a 100644 --- a/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.js +++ b/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx @@ -1,5 +1,6 @@ import {FlashList} from '@shopify/flash-list'; -import React, {useCallback, useEffect, useRef} from 'react'; +import React, {ForwardedRef, forwardRef, ReactElement, useCallback, useEffect, useRef} from 'react'; +import {View} from 'react-native'; // We take ScrollView from this package to properly handle the scrolling of AutoCompleteSuggestions in chats since one scroll is nested inside another import {ScrollView} from 'react-native-gesture-handler'; import Animated, {Easing, FadeOutDown, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated'; @@ -7,14 +8,10 @@ import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; import * as StyleUtils from '@styles/StyleUtils'; import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; -import {propTypes} from './autoCompleteSuggestionsPropTypes'; +import viewForwardedRef from '@src/types/utils/viewForwardedRef'; +import type {AutoCompleteSuggestionsProps, RenderSuggestionMenuItemProps} from './types'; -/** - * @param {Number} numRows - * @param {Boolean} isSuggestionPickerLarge - * @returns {Number} - */ -const measureHeightOfSuggestionRows = (numRows, isSuggestionPickerLarge) => { +const measureHeightOfSuggestionRows = (numRows: number, isSuggestionPickerLarge: boolean): number => { if (isSuggestionPickerLarge) { if (numRows > CONST.AUTO_COMPLETE_SUGGESTER.MAX_AMOUNT_OF_VISIBLE_SUGGESTIONS_IN_CONTAINER) { // On large screens, if there are more than 5 suggestions, we display a scrollable window with a height of 5 items, indicating that there are more items available @@ -29,28 +26,26 @@ const measureHeightOfSuggestionRows = (numRows, isSuggestionPickerLarge) => { return numRows * CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT; }; -function BaseAutoCompleteSuggestions({ - highlightedSuggestionIndex, - onSelect, - renderSuggestionMenuItem, - suggestions, - accessibilityLabelExtractor, - keyExtractor, - isSuggestionPickerLarge, - forwardedRef, -}) { +function BaseAutoCompleteSuggestions( + { + highlightedSuggestionIndex, + onSelect, + accessibilityLabelExtractor, + renderSuggestionMenuItem, + suggestions, + isSuggestionPickerLarge, + keyExtractor, + }: AutoCompleteSuggestionsProps, + ref: ForwardedRef, +) { const styles = useThemeStyles(); const rowHeight = useSharedValue(0); - const scrollRef = useRef(null); + const scrollRef = useRef>(null); /** * Render a suggestion menu item component. - * @param {Object} params - * @param {Object} params.item - * @param {Number} params.index - * @returns {JSX.Element} */ const renderItem = useCallback( - ({item, index}) => ( + ({item, index}: RenderSuggestionMenuItemProps): ReactElement => ( StyleUtils.getAutoCompleteSuggestionItemStyle(highlightedSuggestionIndex, CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT, hovered, index)} hoverDimmingValue={1} @@ -84,7 +79,7 @@ function BaseAutoCompleteSuggestions({ return ( @@ -104,17 +99,6 @@ function BaseAutoCompleteSuggestions({ ); } -BaseAutoCompleteSuggestions.propTypes = propTypes; BaseAutoCompleteSuggestions.displayName = 'BaseAutoCompleteSuggestions'; -const BaseAutoCompleteSuggestionsWithRef = React.forwardRef((props, ref) => ( - -)); - -BaseAutoCompleteSuggestionsWithRef.displayName = 'BaseAutoCompleteSuggestionsWithRef'; - -export default BaseAutoCompleteSuggestionsWithRef; +export default forwardRef(BaseAutoCompleteSuggestions); diff --git a/src/components/AutoCompleteSuggestions/autoCompleteSuggestionsPropTypes.js b/src/components/AutoCompleteSuggestions/autoCompleteSuggestionsPropTypes.js deleted file mode 100644 index 8c6dca1902c5..000000000000 --- a/src/components/AutoCompleteSuggestions/autoCompleteSuggestionsPropTypes.js +++ /dev/null @@ -1,36 +0,0 @@ -import PropTypes from 'prop-types'; - -const propTypes = { - /** Array of suggestions */ - // eslint-disable-next-line react/forbid-prop-types - suggestions: PropTypes.arrayOf(PropTypes.object).isRequired, - - /** Function used to render each suggestion, returned JSX will be enclosed inside a Pressable component */ - renderSuggestionMenuItem: PropTypes.func.isRequired, - - /** Create unique keys for each suggestion item */ - keyExtractor: PropTypes.func.isRequired, - - /** The index of the highlighted suggestion */ - highlightedSuggestionIndex: PropTypes.number.isRequired, - - /** Fired when the user selects a suggestion */ - onSelect: PropTypes.func.isRequired, - - /** Show that we can use large auto-complete suggestion picker. - * Depending on available space and whether the input is expanded, we can have a small or large mention suggester. - * When this value is false, the suggester will have a height of 2.5 items. When this value is true, the height can be up to 5 items. */ - isSuggestionPickerLarge: PropTypes.bool.isRequired, - - /** create accessibility label for each item */ - accessibilityLabelExtractor: PropTypes.func.isRequired, - - /** Meaures the parent container's position and dimensions. */ - measureParentContainer: PropTypes.func, -}; - -const defaultProps = { - measureParentContainer: () => {}, -}; - -export {propTypes, defaultProps}; diff --git a/src/components/AutoCompleteSuggestions/index.native.js b/src/components/AutoCompleteSuggestions/index.native.tsx similarity index 61% rename from src/components/AutoCompleteSuggestions/index.native.js rename to src/components/AutoCompleteSuggestions/index.native.tsx index 439fa45eae78..fbfa7d953581 100644 --- a/src/components/AutoCompleteSuggestions/index.native.js +++ b/src/components/AutoCompleteSuggestions/index.native.tsx @@ -1,18 +1,17 @@ import {Portal} from '@gorhom/portal'; import React from 'react'; -import {propTypes} from './autoCompleteSuggestionsPropTypes'; import BaseAutoCompleteSuggestions from './BaseAutoCompleteSuggestions'; +import type {AutoCompleteSuggestionsProps} from './types'; -function AutoCompleteSuggestions({measureParentContainer, ...props}) { +function AutoCompleteSuggestions({measureParentContainer, ...props}: AutoCompleteSuggestionsProps) { return ( {/* eslint-disable-next-line react/jsx-props-no-spreading */} - + {...props} /> ); } -AutoCompleteSuggestions.propTypes = propTypes; AutoCompleteSuggestions.displayName = 'AutoCompleteSuggestions'; export default AutoCompleteSuggestions; diff --git a/src/components/AutoCompleteSuggestions/index.js b/src/components/AutoCompleteSuggestions/index.tsx similarity index 76% rename from src/components/AutoCompleteSuggestions/index.js rename to src/components/AutoCompleteSuggestions/index.tsx index 30654caf5708..24b846c265a9 100644 --- a/src/components/AutoCompleteSuggestions/index.js +++ b/src/components/AutoCompleteSuggestions/index.tsx @@ -4,8 +4,8 @@ import {View} from 'react-native'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import * as StyleUtils from '@styles/StyleUtils'; -import {propTypes} from './autoCompleteSuggestionsPropTypes'; import BaseAutoCompleteSuggestions from './BaseAutoCompleteSuggestions'; +import type {AutoCompleteSuggestionsProps} from './types'; /** * On the mobile-web platform, when long-pressing on auto-complete suggestions, @@ -14,8 +14,8 @@ import BaseAutoCompleteSuggestions from './BaseAutoCompleteSuggestions'; * On the native platform, tapping on auto-complete suggestions will not blur the main input. */ -function AutoCompleteSuggestions({measureParentContainer, ...props}) { - const containerRef = React.useRef(null); +function AutoCompleteSuggestions({measureParentContainer = () => {}, ...props}: AutoCompleteSuggestionsProps) { + const containerRef = React.useRef(null); const {windowHeight, windowWidth} = useWindowDimensions(); const [{width, left, bottom}, setContainerState] = React.useState({ width: 0, @@ -25,7 +25,7 @@ function AutoCompleteSuggestions({measureParentContainer, ...props}) { React.useEffect(() => { const container = containerRef.current; if (!container) { - return; + return () => {}; } container.onpointerdown = (e) => { if (DeviceCapabilities.hasHoverSupport()) { @@ -44,20 +44,20 @@ function AutoCompleteSuggestions({measureParentContainer, ...props}) { }, [measureParentContainer, windowHeight, windowWidth]); const componentToRender = ( - // eslint-disable-next-line react/jsx-props-no-spreading {...props} ref={containerRef} /> ); + const bodyElement = document.querySelector('body'); + return ( - Boolean(width) && - ReactDOM.createPortal({componentToRender}, document.querySelector('body')) + !!width && bodyElement && ReactDOM.createPortal({componentToRender}, bodyElement) ); } -AutoCompleteSuggestions.propTypes = propTypes; AutoCompleteSuggestions.displayName = 'AutoCompleteSuggestions'; export default AutoCompleteSuggestions; diff --git a/src/components/AutoCompleteSuggestions/types.ts b/src/components/AutoCompleteSuggestions/types.ts new file mode 100644 index 000000000000..9130f5139d71 --- /dev/null +++ b/src/components/AutoCompleteSuggestions/types.ts @@ -0,0 +1,38 @@ +import {ReactElement} from 'react'; + +type MeasureParentContainerCallback = (x: number, y: number, width: number) => void; + +type RenderSuggestionMenuItemProps = { + item: TSuggestion; + index: number; +}; + +type AutoCompleteSuggestionsProps = { + /** Array of suggestions */ + suggestions: TSuggestion[]; + + /** Function used to render each suggestion, returned JSX will be enclosed inside a Pressable component */ + renderSuggestionMenuItem: (item: TSuggestion, index: number) => ReactElement; + + /** Create unique keys for each suggestion item */ + keyExtractor: (item: TSuggestion, index: number) => string; + + /** The index of the highlighted suggestion */ + highlightedSuggestionIndex: number; + + /** Fired when the user selects a suggestion */ + onSelect: (index: number) => void; + + /** Show that we can use large auto-complete suggestion picker. + * Depending on available space and whether the input is expanded, we can have a small or large mention suggester. + * When this value is false, the suggester will have a height of 2.5 items. When this value is true, the height can be up to 5 items. */ + isSuggestionPickerLarge: boolean; + + /** create accessibility label for each item */ + accessibilityLabelExtractor: (item: TSuggestion, index: number) => string; + + /** Meaures the parent container's position and dimensions. */ + measureParentContainer?: (callback: MeasureParentContainerCallback) => void; +}; + +export type {AutoCompleteSuggestionsProps, RenderSuggestionMenuItemProps}; diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx index 02f743b6a1b6..71bce9777174 100644 --- a/src/components/Button/index.tsx +++ b/src/components/Button/index.tsx @@ -1,3 +1,4 @@ +import {useIsFocused} from '@react-navigation/native'; import React, {ForwardedRef, useCallback} from 'react'; import {ActivityIndicator, GestureResponderEvent, StyleProp, TextStyle, View, ViewStyle} from 'react-native'; import {SvgProps} from 'react-native-svg'; @@ -110,7 +111,6 @@ type ButtonProps = (ButtonWithText | ChildrenProps) & { /** Accessibility label for the component */ accessibilityLabel?: string; - isFocused: boolean; }; function Button( @@ -149,7 +149,6 @@ function Button( shouldRemoveRightBorderRadius = false, shouldRemoveLeftBorderRadius = false, shouldEnableHapticFeedback = false, - isFocused, id = '', accessibilityLabel = '', @@ -159,6 +158,7 @@ function Button( ) { const theme = useTheme(); const styles = useThemeStyles(); + const isFocused = useIsFocused(); const keyboardShortcutCallback = useCallback( (event?: GestureResponderEvent | KeyboardEvent) => { diff --git a/src/components/ReportActionItem/MoneyRequestPreview.js b/src/components/ReportActionItem/MoneyRequestPreview.js index 07aba132be0e..466a5a6eec51 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview.js +++ b/src/components/ReportActionItem/MoneyRequestPreview.js @@ -358,15 +358,17 @@ function MoneyRequestPreview(props) { return childContainer; } + const shouldDisableOnPress = props.isBillSplit && _.isEmpty(props.transaction); + return ( DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()} onPressOut={() => ControlSelection.unblock()} onLongPress={showContextMenu} accessibilityLabel={props.isBillSplit ? props.translate('iou.split') : props.translate('iou.cash')} accessibilityHint={CurrencyUtils.convertToDisplayString(requestAmount, requestCurrency)} - style={[styles.moneyRequestPreviewBox, ...props.containerStyles]} + style={[styles.moneyRequestPreviewBox, ...props.containerStyles, shouldDisableOnPress && styles.cursorDefault]} > {childContainer} diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 9ebd2ff0a089..d93661778b83 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2762,7 +2762,7 @@ function buildOptimisticApprovedReportAction(amount: number, currency: string, e * Builds an optimistic MOVED report action with a randomly generated reportActionID. * This action is used when we move reports across workspaces. */ -function buildOptimisticMovedReportAction(fromPolicyID: string, toPolicyID: string, newParentReportID: string, movedReportID: string): ReportAction { +function buildOptimisticMovedReportAction(fromPolicyID: string, toPolicyID: string, newParentReportID: string, movedReportID: string, policyName: string): ReportAction { const originalMessage = { fromPolicyID, toPolicyID, @@ -2770,7 +2770,6 @@ function buildOptimisticMovedReportAction(fromPolicyID: string, toPolicyID: stri movedReportID, }; - const policyName = getPolicyName(allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${newParentReportID}`]); const movedActionMessage = [ { html: `moved the report to the ${policyName} workspace`, diff --git a/src/libs/actions/Policy.js b/src/libs/actions/Policy.js index ebc1cdf9a2e1..b993b4a5dd8b 100644 --- a/src/libs/actions/Policy.js +++ b/src/libs/actions/Policy.js @@ -1812,7 +1812,7 @@ function createWorkspaceFromIOUPayment(iouReport) { }); // Create the MOVED report action and add it to the DM chat which indicates to the user where the report has been moved - const movedReportAction = ReportUtils.buildOptimisticMovedReportAction(oldPersonalPolicyID, policyID, memberData.workspaceChatReportID, iouReportID); + const movedReportAction = ReportUtils.buildOptimisticMovedReportAction(oldPersonalPolicyID, policyID, memberData.workspaceChatReportID, iouReportID, workspaceName); optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldChatReportID}`, diff --git a/src/types/utils/viewForwardedRef.ts b/src/types/utils/viewForwardedRef.ts new file mode 100644 index 000000000000..3bc18495b153 --- /dev/null +++ b/src/types/utils/viewForwardedRef.ts @@ -0,0 +1,6 @@ +import {ForwardedRef} from 'react'; +import {View} from 'react-native'; + +const viewForwardedRef = (ref: ForwardedRef) => ref as ForwardedRef; + +export default viewForwardedRef;