From 613aaaffb473b0f6140ed402f7fb3ee4a1067d66 Mon Sep 17 00:00:00 2001 From: Wiktor Gut Date: Wed, 16 Apr 2025 17:20:07 +0200 Subject: [PATCH 01/11] slight delay for more than 3 --- .../MoneyRequestReportPreviewContent.tsx | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx index 6f11b89f10a6..4d8a63ad5a0a 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx @@ -378,7 +378,9 @@ function MoneyRequestReportPreviewContent({ }, [isApproved, isApprovedAnimationRunning, thumbsUpScale]); const [currentIndex, setCurrentIndex] = useState(0); - const [lastVisibleIndex, setLastVisibleIndex] = useState(0); + const [visibleItemsCount, setVisibleItemsCount] = useState(0); + const [optimisticLast, setOptimisticLast] = useState(transactions.length === 1); + const [optimisticFirst, setOptimisticFirst] = useState(true); const carouselRef = useRef | null>(null); const viewabilityConfig = useMemo(() => { return {itemVisiblePercentThreshold: 90}; @@ -387,12 +389,15 @@ function MoneyRequestReportPreviewContent({ // eslint-disable-next-line react-compiler/react-compiler const onViewableItemsChanged = useRef(({viewableItems}: {viewableItems: ViewToken[]; changed: ViewToken[]}) => { const newIndex = viewableItems.at(0)?.index; - const lastIndex = viewableItems.at(viewableItems.length - 1)?.index; + setVisibleItemsCount(viewableItems.length); if (typeof newIndex === 'number') { setCurrentIndex(newIndex); - } - if (typeof lastIndex === 'number') { - setLastVisibleIndex(lastIndex); + if (newIndex + viewableItems.length === transactions.length) { + setOptimisticLast(false); + } + if (newIndex === 0) { + setOptimisticFirst(false); + } } }).current; @@ -400,6 +405,13 @@ function MoneyRequestReportPreviewContent({ if (index >= transactions.length || index < 0) { return; } + if (index === 0) { + setOptimisticFirst(true); + } + + if (index + visibleItemsCount === transactions.length) { + setOptimisticLast(true); + } carouselRef.current?.scrollToIndex({index, animated: true, viewOffset: 2 * styles.gap2.gap}); }; @@ -504,7 +516,7 @@ function MoneyRequestReportPreviewContent({ accessibilityLabel="button" style={[styles.reportPreviewArrowButton, {backgroundColor: theme.buttonDefaultBG}]} onPress={() => handleChange(currentIndex - 1)} - disabled={currentIndex === 0} + disabled={(currentIndex === 0 || optimisticFirst) && !optimisticLast} disabledStyle={[styles.cursorDefault, styles.buttonOpacityDisabled]} > handleChange(currentIndex + 1)} - disabled={lastVisibleIndex === Math.min(transactions.length - 1, 10)} + disabled={(currentIndex + visibleItemsCount === transactions.length || optimisticLast) && !optimisticFirst} disabledStyle={[styles.cursorDefault, styles.buttonOpacityDisabled]} > Date: Wed, 16 Apr 2025 17:20:41 +0200 Subject: [PATCH 02/11] better solution but flicker at end --- .../MoneyRequestReportPreviewContent.tsx | 58 ++++++++++++------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx index 4d8a63ad5a0a..8cfe0c40c7d4 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx @@ -378,9 +378,10 @@ function MoneyRequestReportPreviewContent({ }, [isApproved, isApprovedAnimationRunning, thumbsUpScale]); const [currentIndex, setCurrentIndex] = useState(0); - const [visibleItemsCount, setVisibleItemsCount] = useState(0); - const [optimisticLast, setOptimisticLast] = useState(transactions.length === 1); - const [optimisticFirst, setOptimisticFirst] = useState(true); + const [visibleItemsCount, setVisibleItemsCount] = useState(1); + // const [optimisticLast, setOptimisticLast] = useState(transactions.length === 1); + // const [optimisticFirst, setOptimisticFirst] = useState(true); + const [optimistic, setOptimistic] = useState(0); const carouselRef = useRef | null>(null); const viewabilityConfig = useMemo(() => { return {itemVisiblePercentThreshold: 90}; @@ -389,15 +390,24 @@ function MoneyRequestReportPreviewContent({ // eslint-disable-next-line react-compiler/react-compiler const onViewableItemsChanged = useRef(({viewableItems}: {viewableItems: ViewToken[]; changed: ViewToken[]}) => { const newIndex = viewableItems.at(0)?.index; - setVisibleItemsCount(viewableItems.length); + // setVisibleItemsCount(viewableItems.length); if (typeof newIndex === 'number') { setCurrentIndex(newIndex); - if (newIndex + viewableItems.length === transactions.length) { - setOptimisticLast(false); - } - if (newIndex === 0) { - setOptimisticFirst(false); - } + // if (newIndex + viewableItems.length === transactions.length) { + // setOptimisticLast(false); + // // setOptimistic(-1); + // } + // if (newIndex === 0) { + // setOptimisticFirst(false); + // // setOptimistic(-1); + // } + // if (optimistic === -5){ + // setOp + // } + // if (newIndex === optimistic) { + // setOptimistic(-5); + // } + setOptimistic(newIndex); } }).current; @@ -405,13 +415,14 @@ function MoneyRequestReportPreviewContent({ if (index >= transactions.length || index < 0) { return; } - if (index === 0) { - setOptimisticFirst(true); - } - - if (index + visibleItemsCount === transactions.length) { - setOptimisticLast(true); - } + // if (index === 0) { + // setOptimisticFirst(true); + // } + + // if (index + visibleItemsCount === transactions.length) { + // setOptimisticLast(true); + // } + setOptimistic(index); carouselRef.current?.scrollToIndex({index, animated: true, viewOffset: 2 * styles.gap2.gap}); }; @@ -459,7 +470,10 @@ function MoneyRequestReportPreviewContent({ > { + getCurrentWidth(e); + setVisibleItemsCount(Math.floor(e.nativeEvent.layout.width / reportPreviewStyles.transactionPreviewStyle.width)); + }} > {}} @@ -516,7 +530,7 @@ function MoneyRequestReportPreviewContent({ accessibilityLabel="button" style={[styles.reportPreviewArrowButton, {backgroundColor: theme.buttonDefaultBG}]} onPress={() => handleChange(currentIndex - 1)} - disabled={(currentIndex === 0 || optimisticFirst) && !optimisticLast} + disabled={optimistic === 0} disabledStyle={[styles.cursorDefault, styles.buttonOpacityDisabled]} > handleChange(currentIndex + 1)} - disabled={(currentIndex + visibleItemsCount === transactions.length || optimisticLast) && !optimisticFirst} + // disabled={ + // (currentIndex + visibleItemsCount === transactions.length || optimistic + visibleItemsCount === transactions.length) && + // optimistic !== 0 + // } + disabled={optimistic + visibleItemsCount === transactions.length} disabledStyle={[styles.cursorDefault, styles.buttonOpacityDisabled]} > Date: Tue, 22 Apr 2025 16:43:00 +0200 Subject: [PATCH 03/11] Carousel navigation by arrows working flawlessly, not when narrower than 1 expense --- .../MoneyRequestReportPreviewContent.tsx | 81 ++++++++++--------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx index fa813e66612a..cb77a9c66cb0 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx @@ -375,50 +375,41 @@ function MoneyRequestReportPreviewContent({ }, [isApproved, isApprovedAnimationRunning, thumbsUpScale]); const [currentIndex, setCurrentIndex] = useState(0); - const [visibleItemsCount, setVisibleItemsCount] = useState(1); - // const [optimisticLast, setOptimisticLast] = useState(transactions.length === 1); - // const [optimisticFirst, setOptimisticFirst] = useState(true); - const [optimistic, setOptimistic] = useState(0); + const [currentVisible, setCurrentVisible] = useState([0]); + const [wrapperWidth, setWrapperWidth] = useState(0); + const [footerWidth, setFooterWidth] = useState(0); + const [optimistic, setOptimistic] = useState(undefined); const carouselRef = useRef | null>(null); + const visibleEndItemsCount = useMemo(() => { + const lastItemWidth = transactions.length > 10 ? footerWidth : reportPreviewStyles.transactionPreviewStyle.width; + return Math.floor((wrapperWidth - lastItemWidth - 24) / (reportPreviewStyles.transactionPreviewStyle.width + 8)) + 1; + }, [footerWidth, reportPreviewStyles.transactionPreviewStyle.width, transactions.length, wrapperWidth]); const viewabilityConfig = useMemo(() => { - return {itemVisiblePercentThreshold: 90}; + return {itemVisiblePercentThreshold: 100}; }, []); // eslint-disable-next-line react-compiler/react-compiler const onViewableItemsChanged = useRef(({viewableItems}: {viewableItems: ViewToken[]; changed: ViewToken[]}) => { const newIndex = viewableItems.at(0)?.index; - // setVisibleItemsCount(viewableItems.length); if (typeof newIndex === 'number') { setCurrentIndex(newIndex); - // if (newIndex + viewableItems.length === transactions.length) { - // setOptimisticLast(false); - // // setOptimistic(-1); - // } - // if (newIndex === 0) { - // setOptimisticFirst(false); - // // setOptimistic(-1); - // } - // if (optimistic === -5){ - // setOp - // } - // if (newIndex === optimistic) { - // setOptimistic(-5); - // } - setOptimistic(newIndex); } + setCurrentVisible(viewableItems.map((item) => item.index).filter((item) => item != null)); }).current; + const carouselTransactionsCount = transactions.slice(0, 11).length; + const handleChange = (index: number) => { - if (index >= transactions.length || index < 0) { + if (index > carouselTransactionsCount - visibleEndItemsCount) { + setOptimistic(carouselTransactionsCount - visibleEndItemsCount); + carouselRef.current?.scrollToIndex({index: carouselTransactionsCount - visibleEndItemsCount, animated: true, viewOffset: 2 * styles.gap2.gap}); + return; + } + if (index < 0) { + setOptimistic(0); + carouselRef.current?.scrollToIndex({index: 0, animated: true, viewOffset: 2 * styles.gap2.gap}); return; } - // if (index === 0) { - // setOptimisticFirst(true); - // } - - // if (index + visibleItemsCount === transactions.length) { - // setOptimisticLast(true); - // } setOptimistic(index); carouselRef.current?.scrollToIndex({index, animated: true, viewOffset: 2 * styles.gap2.gap}); }; @@ -433,7 +424,10 @@ function MoneyRequestReportPreviewContent({ const renderFlatlistItem = (itemInfo: ListRenderItemInfo) => { if (itemInfo.index > 9) { return ( - + setFooterWidth(e.nativeEvent.layout.width)} + > +{transactions.length - 10} {translate('common.more').toLowerCase()} @@ -457,6 +451,19 @@ function MoneyRequestReportPreviewContent({ /> ); + useEffect(() => { + if ( + optimistic === undefined || + optimistic !== currentIndex || + // currentIndex is still the same as target (f.ex. 0), but not yet scrolled to the far left + (currentVisible.at(0) !== optimistic && optimistic !== undefined) || + (optimistic === carouselTransactionsCount - visibleEndItemsCount && currentVisible.length !== visibleEndItemsCount) + ) { + return; + } + setOptimistic(undefined); + }, [carouselTransactionsCount, currentIndex, currentVisible, currentVisible.length, optimistic, visibleEndItemsCount]); + const getPreviewName = () => { if (isInvoice && isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW)) { const originalMessage = getOriginalMessage(action); @@ -477,7 +484,7 @@ function MoneyRequestReportPreviewContent({ style={[styles.chatItemMessage, containerStyles]} onLayout={(e: LayoutChangeEvent) => { getCurrentWidth(e); - setVisibleItemsCount(Math.floor(e.nativeEvent.layout.width / reportPreviewStyles.transactionPreviewStyle.width)); + setWrapperWidth(e.nativeEvent.layout.width); }} > handleChange(currentIndex - 1)} - disabled={optimistic === 0} + disabled={optimistic !== undefined ? optimistic === 0 : currentIndex === 0 && currentVisible.at(0) === 0} disabledStyle={[styles.cursorDefault, styles.buttonOpacityDisabled]} > handleChange(currentIndex + 1)} - // disabled={ - // (currentIndex + visibleItemsCount === transactions.length || optimistic + visibleItemsCount === transactions.length) && - // optimistic !== 0 - // } - disabled={optimistic + visibleItemsCount === transactions.length} + disabled={ + optimistic + ? optimistic + visibleEndItemsCount >= carouselTransactionsCount + : currentVisible.at(-1) === carouselTransactionsCount - 1 + } disabledStyle={[styles.cursorDefault, styles.buttonOpacityDisabled]} > Date: Tue, 22 Apr 2025 17:40:32 +0200 Subject: [PATCH 04/11] ts fix --- .../MoneyRequestReportPreviewContent.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx index cb77a9c66cb0..dc60dca4e72e 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx @@ -394,7 +394,8 @@ function MoneyRequestReportPreviewContent({ if (typeof newIndex === 'number') { setCurrentIndex(newIndex); } - setCurrentVisible(viewableItems.map((item) => item.index).filter((item) => item != null)); + const viewableItemsIndexes = viewableItems.map((item) => item.index).filter((item) => item !== null); + setCurrentVisible(viewableItemsIndexes); }).current; const carouselTransactionsCount = transactions.slice(0, 11).length; From b0e84ed9f257a53615276b93e53d54148d77cb5a Mon Sep 17 00:00:00 2001 From: Wiktor Gut Date: Tue, 22 Apr 2025 17:51:04 +0200 Subject: [PATCH 05/11] ts fix 2 --- .../MoneyRequestReportPreviewContent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx index dc60dca4e72e..75648bd8dbe0 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx @@ -394,7 +394,7 @@ function MoneyRequestReportPreviewContent({ if (typeof newIndex === 'number') { setCurrentIndex(newIndex); } - const viewableItemsIndexes = viewableItems.map((item) => item.index).filter((item) => item !== null); + const viewableItemsIndexes = viewableItems.map((item) => item.index).filter((item): item is number => item !== null); setCurrentVisible(viewableItemsIndexes); }).current; From 53ffeae7525187df4acb67eb717f135eaf576d53 Mon Sep 17 00:00:00 2001 From: Wiktor Gut Date: Tue, 22 Apr 2025 18:05:28 +0200 Subject: [PATCH 06/11] refactor, ready for review --- .../MoneyRequestReportPreviewContent.tsx | 56 ++++++++++--------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx index 75648bd8dbe0..32a5bf1e9c19 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx @@ -374,16 +374,19 @@ function MoneyRequestReportPreviewContent({ thumbsUpScale.set(isApprovedAnimationRunning ? withDelay(CONST.ANIMATION_THUMBSUP_DELAY, withSpring(1, {duration: CONST.ANIMATION_THUMBSUP_DURATION})) : 1); }, [isApproved, isApprovedAnimationRunning, thumbsUpScale]); + const carouselTransactions = transactions.slice(0, 11); const [currentIndex, setCurrentIndex] = useState(0); - const [currentVisible, setCurrentVisible] = useState([0]); - const [wrapperWidth, setWrapperWidth] = useState(0); + const [currentVisibleItems, setCurrentVisibleItems] = useState([0]); + const [carouselWrapperWidth, setCarouselWrapperWidth] = useState(0); const [footerWidth, setFooterWidth] = useState(0); - const [optimistic, setOptimistic] = useState(undefined); + const [optimisticIndex, setOptimisticIndex] = useState(undefined); const carouselRef = useRef | null>(null); - const visibleEndItemsCount = useMemo(() => { + const visibleItemsOnEndCount = useMemo(() => { const lastItemWidth = transactions.length > 10 ? footerWidth : reportPreviewStyles.transactionPreviewStyle.width; - return Math.floor((wrapperWidth - lastItemWidth - 24) / (reportPreviewStyles.transactionPreviewStyle.width + 8)) + 1; - }, [footerWidth, reportPreviewStyles.transactionPreviewStyle.width, transactions.length, wrapperWidth]); + const lastItemWithGap = lastItemWidth + styles.gap2.gap; + const itemWithGap = reportPreviewStyles.transactionPreviewStyle.width + styles.gap2.gap; + return Math.floor((carouselWrapperWidth - 2 * styles.pl2.paddingLeft - lastItemWithGap) / itemWithGap) + 1; + }, [transactions.length, footerWidth, reportPreviewStyles.transactionPreviewStyle.width, carouselWrapperWidth, styles.pl2.paddingLeft, styles.gap2.gap]); const viewabilityConfig = useMemo(() => { return {itemVisiblePercentThreshold: 100}; }, []); @@ -395,23 +398,21 @@ function MoneyRequestReportPreviewContent({ setCurrentIndex(newIndex); } const viewableItemsIndexes = viewableItems.map((item) => item.index).filter((item): item is number => item !== null); - setCurrentVisible(viewableItemsIndexes); + setCurrentVisibleItems(viewableItemsIndexes); }).current; - const carouselTransactionsCount = transactions.slice(0, 11).length; - const handleChange = (index: number) => { - if (index > carouselTransactionsCount - visibleEndItemsCount) { - setOptimistic(carouselTransactionsCount - visibleEndItemsCount); - carouselRef.current?.scrollToIndex({index: carouselTransactionsCount - visibleEndItemsCount, animated: true, viewOffset: 2 * styles.gap2.gap}); + if (index > carouselTransactions.length - visibleItemsOnEndCount) { + setOptimisticIndex(carouselTransactions.length - visibleItemsOnEndCount); + carouselRef.current?.scrollToIndex({index: carouselTransactions.length - visibleItemsOnEndCount, animated: true, viewOffset: 2 * styles.gap2.gap}); return; } if (index < 0) { - setOptimistic(0); + setOptimisticIndex(0); carouselRef.current?.scrollToIndex({index: 0, animated: true, viewOffset: 2 * styles.gap2.gap}); return; } - setOptimistic(index); + setOptimisticIndex(index); carouselRef.current?.scrollToIndex({index, animated: true, viewOffset: 2 * styles.gap2.gap}); }; @@ -454,16 +455,17 @@ function MoneyRequestReportPreviewContent({ useEffect(() => { if ( - optimistic === undefined || - optimistic !== currentIndex || + optimisticIndex === undefined || + optimisticIndex !== currentIndex || // currentIndex is still the same as target (f.ex. 0), but not yet scrolled to the far left - (currentVisible.at(0) !== optimistic && optimistic !== undefined) || - (optimistic === carouselTransactionsCount - visibleEndItemsCount && currentVisible.length !== visibleEndItemsCount) + (currentVisibleItems.at(0) !== optimisticIndex && optimisticIndex !== undefined) || + // currentIndex reached, but not scrolled to the end + (optimisticIndex === carouselTransactions.length - visibleItemsOnEndCount && currentVisibleItems.length !== visibleItemsOnEndCount) ) { return; } - setOptimistic(undefined); - }, [carouselTransactionsCount, currentIndex, currentVisible, currentVisible.length, optimistic, visibleEndItemsCount]); + setOptimisticIndex(undefined); + }, [carouselTransactions.length, currentIndex, currentVisibleItems, currentVisibleItems.length, optimisticIndex, visibleItemsOnEndCount]); const getPreviewName = () => { if (isInvoice && isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW)) { @@ -485,7 +487,7 @@ function MoneyRequestReportPreviewContent({ style={[styles.chatItemMessage, containerStyles]} onLayout={(e: LayoutChangeEvent) => { getCurrentWidth(e); - setWrapperWidth(e.nativeEvent.layout.width); + setCarouselWrapperWidth(e.nativeEvent.layout.width); }} > handleChange(currentIndex - 1)} - disabled={optimistic !== undefined ? optimistic === 0 : currentIndex === 0 && currentVisible.at(0) === 0} + disabled={optimisticIndex !== undefined ? optimisticIndex === 0 : currentIndex === 0 && currentVisibleItems.at(0) === 0} disabledStyle={[styles.cursorDefault, styles.buttonOpacityDisabled]} > handleChange(currentIndex + 1)} disabled={ - optimistic - ? optimistic + visibleEndItemsCount >= carouselTransactionsCount - : currentVisible.at(-1) === carouselTransactionsCount - 1 + optimisticIndex + ? optimisticIndex + visibleItemsOnEndCount >= carouselTransactions.length + : currentVisibleItems.at(-1) === carouselTransactions.length - 1 } disabledStyle={[styles.cursorDefault, styles.buttonOpacityDisabled]} > @@ -593,7 +595,7 @@ function MoneyRequestReportPreviewContent({ decelerationRate="fast" snapToInterval={reportPreviewStyles.transactionPreviewStyle.width + styles.gap2.gap} horizontal - data={transactions.slice(0, 11)} + data={carouselTransactions} ref={carouselRef} nestedScrollEnabled bounces={false} @@ -610,7 +612,7 @@ function MoneyRequestReportPreviewContent({ {shouldUseNarrowLayout && transactions.length > 1 && ( - {transactions.slice(0, 11).map((item, index) => ( + {carouselTransactions.map((item, index) => ( Date: Wed, 23 Apr 2025 08:27:18 +0200 Subject: [PATCH 07/11] optimisticIndex explanation added --- .../MoneyRequestReportPreviewContent.tsx | 79 ++++++------------- 1 file changed, 26 insertions(+), 53 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx index 32a5bf1e9c19..420f9dd39dc9 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx @@ -1,22 +1,22 @@ -import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; -import {FlatList, View} from 'react-native'; -import type {LayoutChangeEvent, ListRenderItemInfo, ViewToken} from 'react-native'; -import Animated, {useAnimatedStyle, useSharedValue, withDelay, withSpring, withTiming} from 'react-native-reanimated'; -import type {LayoutRectangle} from 'react-native/Libraries/Types/CoreEventTypes'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { FlatList, View } from 'react-native'; +import type { LayoutChangeEvent, ListRenderItemInfo, ViewToken } from 'react-native'; +import Animated, { useAnimatedStyle, useSharedValue, withDelay, withSpring, withTiming } from 'react-native-reanimated'; +import type { LayoutRectangle } from 'react-native/Libraries/Types/CoreEventTypes'; import Button from '@components/Button'; -import {getButtonRole} from '@components/Button/utils'; +import { getButtonRole } from '@components/Button/utils'; import DelegateNoAccessModal from '@components/DelegateNoAccessModal'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import ImageSVG from '@components/ImageSVG'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; -import {PressableWithFeedback} from '@components/Pressable'; +import { PressableWithFeedback } from '@components/Pressable'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; import ProcessMoneyReportHoldMenu from '@components/ProcessMoneyReportHoldMenu'; -import type {ActionHandledType} from '@components/ProcessMoneyReportHoldMenu'; +import type { ActionHandledType } from '@components/ProcessMoneyReportHoldMenu'; import ExportWithDropdownMenu from '@components/ReportActionItem/ExportWithDropdownMenu'; import AnimatedSettlementButton from '@components/SettlementButton/AnimatedSettlementButton'; -import {showContextMenuForReport} from '@components/ShowContextMenuContext'; +import { showContextMenuForReport } from '@components/ShowContextMenuContext'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -26,55 +26,25 @@ import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import ControlSelection from '@libs/ControlSelection'; -import {convertToDisplayString} from '@libs/CurrencyUtils'; -import {canUseTouchScreen} from '@libs/DeviceCapabilities'; -import type {RootNavigatorParamList, State} from '@libs/Navigation/types'; -import {getConnectedIntegration} from '@libs/PolicyUtils'; -import {getOriginalMessage, isActionOfType} from '@libs/ReportActionsUtils'; -import { - areAllRequestsBeingSmartScanned as areAllRequestsBeingSmartScannedReportUtils, - canBeExported, - getBankAccountRoute, - getDisplayNameForParticipant, - getInvoicePayerName, - getMoneyRequestSpendBreakdown, - getNonHeldAndFullAmount, - getPolicyName, - getTransactionsWithReceipts, - hasActionsWithErrors, - hasHeldExpenses as hasHeldExpensesReportUtils, - hasMissingSmartscanFields as hasMissingSmartscanFieldsReportUtils, - hasNonReimbursableTransactions as hasNonReimbursableTransactionsReportUtils, - hasNoticeTypeViolations, - hasOnlyHeldExpenses as hasOnlyHeldExpensesReportUtils, - hasOnlyTransactionsWithPendingRoutes as hasOnlyTransactionsWithPendingRoutesReportUtils, - hasReportViolations, - hasUpdatedTotal, - hasViolations, - hasWarningTypeViolations, - isAllowedToApproveExpenseReport, - isAllowedToSubmitDraftExpenseReport, - isInvoiceReport as isInvoiceReportUtils, - isInvoiceRoom as isInvoiceRoomReportUtils, - isPolicyExpenseChat as isPolicyExpenseChatReportUtils, - isReportApproved, - isReportOwner, - isSettled, - isTripRoom as isTripRoomReportUtils, - isWaitingForSubmissionFromCurrentUser as isWaitingForSubmissionFromCurrentUserReportUtils, -} from '@libs/ReportUtils'; -import {getMerchant, hasPendingUI, isCardTransaction, isPartialMerchant, isPending, shouldShowBrokenConnectionViolationForMultipleTransactions} from '@libs/TransactionUtils'; +import { convertToDisplayString } from '@libs/CurrencyUtils'; +import { canUseTouchScreen } from '@libs/DeviceCapabilities'; +import type { RootNavigatorParamList, State } from '@libs/Navigation/types'; +import { getConnectedIntegration } from '@libs/PolicyUtils'; +import { getOriginalMessage, isActionOfType } from '@libs/ReportActionsUtils'; +import { areAllRequestsBeingSmartScanned as areAllRequestsBeingSmartScannedReportUtils, canBeExported, getBankAccountRoute, getDisplayNameForParticipant, getInvoicePayerName, getMoneyRequestSpendBreakdown, getNonHeldAndFullAmount, getPolicyName, getTransactionsWithReceipts, hasActionsWithErrors, hasHeldExpenses as hasHeldExpensesReportUtils, hasMissingSmartscanFields as hasMissingSmartscanFieldsReportUtils, hasNonReimbursableTransactions as hasNonReimbursableTransactionsReportUtils, hasNoticeTypeViolations, hasOnlyHeldExpenses as hasOnlyHeldExpensesReportUtils, hasOnlyTransactionsWithPendingRoutes as hasOnlyTransactionsWithPendingRoutesReportUtils, hasReportViolations, hasUpdatedTotal, hasViolations, hasWarningTypeViolations, isAllowedToApproveExpenseReport, isAllowedToSubmitDraftExpenseReport, isInvoiceReport as isInvoiceReportUtils, isInvoiceRoom as isInvoiceRoomReportUtils, isPolicyExpenseChat as isPolicyExpenseChatReportUtils, isReportApproved, isReportOwner, isSettled, isTripRoom as isTripRoomReportUtils, isWaitingForSubmissionFromCurrentUser as isWaitingForSubmissionFromCurrentUserReportUtils } from '@libs/ReportUtils'; +import { getMerchant, hasPendingUI, isCardTransaction, isPartialMerchant, isPending, shouldShowBrokenConnectionViolationForMultipleTransactions } from '@libs/TransactionUtils'; import navigationRef from '@navigation/navigationRef'; import colors from '@styles/theme/colors'; import variables from '@styles/variables'; -import {approveMoneyRequest, canApproveIOU, canIOUBePaid as canIOUBePaidIOUActions, canSubmitReport, payInvoice, payMoneyRequest, submitReport} from '@userActions/IOU'; +import { approveMoneyRequest, canApproveIOU, canIOUBePaid as canIOUBePaidIOUActions, canSubmitReport, payInvoice, payMoneyRequest, submitReport } from '@userActions/IOU'; import CONST from '@src/CONST'; -import type {TranslationPaths} from '@src/languages/types'; +import type { TranslationPaths } from '@src/languages/types'; import NAVIGATORS from '@src/NAVIGATORS'; import ROUTES from '@src/ROUTES'; -import type {Transaction} from '@src/types/onyx'; -import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; -import type {MoneyRequestReportPreviewContentProps} from './types'; +import type { Transaction } from '@src/types/onyx'; +import type { PaymentMethodType } from '@src/types/onyx/OriginalMessage'; +import type { MoneyRequestReportPreviewContentProps } from './types'; + type WebLayoutNativeEvent = { layout: LayoutRectangle; @@ -379,6 +349,9 @@ function MoneyRequestReportPreviewContent({ const [currentVisibleItems, setCurrentVisibleItems] = useState([0]); const [carouselWrapperWidth, setCarouselWrapperWidth] = useState(0); const [footerWidth, setFooterWidth] = useState(0); + // optimisticIndex - value for index we are scrolling to with an arrow button or undefined after scroll is completed + // value ensures that disabled state is applied instantly and not overriden by onViewableItemsChanged when scrolling + // undefined makes arrow buttons react on currentIndex changes when scrolling manually const [optimisticIndex, setOptimisticIndex] = useState(undefined); const carouselRef = useRef | null>(null); const visibleItemsOnEndCount = useMemo(() => { @@ -725,4 +698,4 @@ function MoneyRequestReportPreviewContent({ MoneyRequestReportPreviewContent.displayName = 'MoneyRequestReportPreviewContent'; -export default MoneyRequestReportPreviewContent; +export default MoneyRequestReportPreviewContent; \ No newline at end of file From ca798f1e1fbbe966ea0c5d57283117298e42cf49 Mon Sep 17 00:00:00 2001 From: Wiktor Gut Date: Wed, 23 Apr 2025 08:29:01 +0200 Subject: [PATCH 08/11] prettier --- .../MoneyRequestReportPreviewContent.tsx | 76 +++++++++++++------ 1 file changed, 53 insertions(+), 23 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx index 420f9dd39dc9..93846dffdf2e 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx @@ -1,22 +1,22 @@ -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { FlatList, View } from 'react-native'; -import type { LayoutChangeEvent, ListRenderItemInfo, ViewToken } from 'react-native'; -import Animated, { useAnimatedStyle, useSharedValue, withDelay, withSpring, withTiming } from 'react-native-reanimated'; -import type { LayoutRectangle } from 'react-native/Libraries/Types/CoreEventTypes'; +import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; +import {FlatList, View} from 'react-native'; +import type {LayoutChangeEvent, ListRenderItemInfo, ViewToken} from 'react-native'; +import Animated, {useAnimatedStyle, useSharedValue, withDelay, withSpring, withTiming} from 'react-native-reanimated'; +import type {LayoutRectangle} from 'react-native/Libraries/Types/CoreEventTypes'; import Button from '@components/Button'; -import { getButtonRole } from '@components/Button/utils'; +import {getButtonRole} from '@components/Button/utils'; import DelegateNoAccessModal from '@components/DelegateNoAccessModal'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import ImageSVG from '@components/ImageSVG'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; -import { PressableWithFeedback } from '@components/Pressable'; +import {PressableWithFeedback} from '@components/Pressable'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; import ProcessMoneyReportHoldMenu from '@components/ProcessMoneyReportHoldMenu'; -import type { ActionHandledType } from '@components/ProcessMoneyReportHoldMenu'; +import type {ActionHandledType} from '@components/ProcessMoneyReportHoldMenu'; import ExportWithDropdownMenu from '@components/ReportActionItem/ExportWithDropdownMenu'; import AnimatedSettlementButton from '@components/SettlementButton/AnimatedSettlementButton'; -import { showContextMenuForReport } from '@components/ShowContextMenuContext'; +import {showContextMenuForReport} from '@components/ShowContextMenuContext'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -26,25 +26,55 @@ import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import ControlSelection from '@libs/ControlSelection'; -import { convertToDisplayString } from '@libs/CurrencyUtils'; -import { canUseTouchScreen } from '@libs/DeviceCapabilities'; -import type { RootNavigatorParamList, State } from '@libs/Navigation/types'; -import { getConnectedIntegration } from '@libs/PolicyUtils'; -import { getOriginalMessage, isActionOfType } from '@libs/ReportActionsUtils'; -import { areAllRequestsBeingSmartScanned as areAllRequestsBeingSmartScannedReportUtils, canBeExported, getBankAccountRoute, getDisplayNameForParticipant, getInvoicePayerName, getMoneyRequestSpendBreakdown, getNonHeldAndFullAmount, getPolicyName, getTransactionsWithReceipts, hasActionsWithErrors, hasHeldExpenses as hasHeldExpensesReportUtils, hasMissingSmartscanFields as hasMissingSmartscanFieldsReportUtils, hasNonReimbursableTransactions as hasNonReimbursableTransactionsReportUtils, hasNoticeTypeViolations, hasOnlyHeldExpenses as hasOnlyHeldExpensesReportUtils, hasOnlyTransactionsWithPendingRoutes as hasOnlyTransactionsWithPendingRoutesReportUtils, hasReportViolations, hasUpdatedTotal, hasViolations, hasWarningTypeViolations, isAllowedToApproveExpenseReport, isAllowedToSubmitDraftExpenseReport, isInvoiceReport as isInvoiceReportUtils, isInvoiceRoom as isInvoiceRoomReportUtils, isPolicyExpenseChat as isPolicyExpenseChatReportUtils, isReportApproved, isReportOwner, isSettled, isTripRoom as isTripRoomReportUtils, isWaitingForSubmissionFromCurrentUser as isWaitingForSubmissionFromCurrentUserReportUtils } from '@libs/ReportUtils'; -import { getMerchant, hasPendingUI, isCardTransaction, isPartialMerchant, isPending, shouldShowBrokenConnectionViolationForMultipleTransactions } from '@libs/TransactionUtils'; +import {convertToDisplayString} from '@libs/CurrencyUtils'; +import {canUseTouchScreen} from '@libs/DeviceCapabilities'; +import type {RootNavigatorParamList, State} from '@libs/Navigation/types'; +import {getConnectedIntegration} from '@libs/PolicyUtils'; +import {getOriginalMessage, isActionOfType} from '@libs/ReportActionsUtils'; +import { + areAllRequestsBeingSmartScanned as areAllRequestsBeingSmartScannedReportUtils, + canBeExported, + getBankAccountRoute, + getDisplayNameForParticipant, + getInvoicePayerName, + getMoneyRequestSpendBreakdown, + getNonHeldAndFullAmount, + getPolicyName, + getTransactionsWithReceipts, + hasActionsWithErrors, + hasHeldExpenses as hasHeldExpensesReportUtils, + hasMissingSmartscanFields as hasMissingSmartscanFieldsReportUtils, + hasNonReimbursableTransactions as hasNonReimbursableTransactionsReportUtils, + hasNoticeTypeViolations, + hasOnlyHeldExpenses as hasOnlyHeldExpensesReportUtils, + hasOnlyTransactionsWithPendingRoutes as hasOnlyTransactionsWithPendingRoutesReportUtils, + hasReportViolations, + hasUpdatedTotal, + hasViolations, + hasWarningTypeViolations, + isAllowedToApproveExpenseReport, + isAllowedToSubmitDraftExpenseReport, + isInvoiceReport as isInvoiceReportUtils, + isInvoiceRoom as isInvoiceRoomReportUtils, + isPolicyExpenseChat as isPolicyExpenseChatReportUtils, + isReportApproved, + isReportOwner, + isSettled, + isTripRoom as isTripRoomReportUtils, + isWaitingForSubmissionFromCurrentUser as isWaitingForSubmissionFromCurrentUserReportUtils, +} from '@libs/ReportUtils'; +import {getMerchant, hasPendingUI, isCardTransaction, isPartialMerchant, isPending, shouldShowBrokenConnectionViolationForMultipleTransactions} from '@libs/TransactionUtils'; import navigationRef from '@navigation/navigationRef'; import colors from '@styles/theme/colors'; import variables from '@styles/variables'; -import { approveMoneyRequest, canApproveIOU, canIOUBePaid as canIOUBePaidIOUActions, canSubmitReport, payInvoice, payMoneyRequest, submitReport } from '@userActions/IOU'; +import {approveMoneyRequest, canApproveIOU, canIOUBePaid as canIOUBePaidIOUActions, canSubmitReport, payInvoice, payMoneyRequest, submitReport} from '@userActions/IOU'; import CONST from '@src/CONST'; -import type { TranslationPaths } from '@src/languages/types'; +import type {TranslationPaths} from '@src/languages/types'; import NAVIGATORS from '@src/NAVIGATORS'; import ROUTES from '@src/ROUTES'; -import type { Transaction } from '@src/types/onyx'; -import type { PaymentMethodType } from '@src/types/onyx/OriginalMessage'; -import type { MoneyRequestReportPreviewContentProps } from './types'; - +import type {Transaction} from '@src/types/onyx'; +import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; +import type {MoneyRequestReportPreviewContentProps} from './types'; type WebLayoutNativeEvent = { layout: LayoutRectangle; @@ -698,4 +728,4 @@ function MoneyRequestReportPreviewContent({ MoneyRequestReportPreviewContent.displayName = 'MoneyRequestReportPreviewContent'; -export default MoneyRequestReportPreviewContent; \ No newline at end of file +export default MoneyRequestReportPreviewContent; From 61d1e4cb034f9f0309d9763a5a9c08e24d3fb04f Mon Sep 17 00:00:00 2001 From: Wiktor Gut Date: Wed, 23 Apr 2025 20:29:46 +0200 Subject: [PATCH 09/11] merge Mobile-Expensify --- Mobile-Expensify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index f294ca85d22c..b79cebb96598 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit f294ca85d22c2b02ffac7f7b6900861f5c070f23 +Subproject commit b79cebb9659848dea62b42bb23d94d89c931c112 From d3c11eb015486708b508ae0943f1fac9ea5a75e6 Mon Sep 17 00:00:00 2001 From: Wiktor Gut Date: Wed, 23 Apr 2025 20:31:28 +0200 Subject: [PATCH 10/11] dont push MobileExp --- Mobile-Expensify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index b79cebb96598..feeac2b78ced 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit b79cebb9659848dea62b42bb23d94d89c931c112 +Subproject commit feeac2b78ced7f16d9beb0f71346ef706cca8942 From f90273e15536f90aa81cb8e7e5e3351139146198 Mon Sep 17 00:00:00 2001 From: Wiktor Gut Date: Wed, 23 Apr 2025 20:45:34 +0200 Subject: [PATCH 11/11] prettier --- .../MoneyRequestReportPreview/types.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/types.ts b/src/components/ReportActionItem/MoneyRequestReportPreview/types.ts index 1228a019b940..030ff001907f 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/types.ts +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/types.ts @@ -1,9 +1,8 @@ -import type { LayoutChangeEvent, ListRenderItem, StyleProp, ViewStyle } from 'react-native'; -import type { OnyxCollection, OnyxEntry } from 'react-native-onyx'; -import type { TransactionPreviewStyleType } from '@components/ReportActionItem/TransactionPreview/types'; -import type { ContextMenuAnchor } from '@pages/home/report/ContextMenu/ReportActionContextMenu'; -import type { PersonalDetails, Policy, Report, ReportAction, Transaction, TransactionViolation, TransactionViolations } from '@src/types/onyx'; - +import type {LayoutChangeEvent, ListRenderItem, StyleProp, ViewStyle} from 'react-native'; +import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import type {TransactionPreviewStyleType} from '@components/ReportActionItem/TransactionPreview/types'; +import type {ContextMenuAnchor} from '@pages/home/report/ContextMenu/ReportActionContextMenu'; +import type {PersonalDetails, Policy, Report, ReportAction, Transaction, TransactionViolation, TransactionViolations} from '@src/types/onyx'; type TransactionPreviewStyle = { [key in keyof TransactionPreviewStyleType]: number; @@ -91,4 +90,4 @@ type MoneyRequestReportPreviewContentProps = MoneyRequestReportPreviewContentOny onPress: () => void; }; -export type {MoneyRequestReportPreviewContentProps, MoneyRequestReportPreviewProps, MoneyRequestReportPreviewStyleType}; \ No newline at end of file +export type {MoneyRequestReportPreviewContentProps, MoneyRequestReportPreviewProps, MoneyRequestReportPreviewStyleType};