From ac0f5b90eda70154b6612310766f1cc394678458 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Wed, 2 Oct 2024 05:55:42 +0530 Subject: [PATCH 1/7] =?UTF-8?q?feat:=20Implement=20to=20use=20a=20?= =?UTF-8?q?=F0=9F=91=8Dicon=20next=20to=20approved=20report=20preview.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: krishna2323 --- .../ReportActionItem/ReportPreview.tsx | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 87f06f43d82a..94755ebb6944 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -145,12 +145,18 @@ function ReportPreview({ transform: [{scale: checkMarkScale.value}], })); + const isApproved = ReportUtils.isReportApproved(iouReport, action); + const thumbsUpScale = useSharedValue(isApproved ? 1 : 0.25); + const thumbsUpStyle = useAnimatedStyle(() => ({ + ...styles.defaultCheckmarkWrapper, + transform: [{scale: thumbsUpScale.value}], + })); + const moneyRequestComment = action?.childLastMoneyRequestComment ?? ''; const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(chatReport); const isInvoiceRoom = ReportUtils.isInvoiceRoom(chatReport); const isOpenExpenseReport = isPolicyExpenseChat && ReportUtils.isOpenExpenseReport(iouReport); - const isApproved = ReportUtils.isReportApproved(iouReport, action); const canAllowSettlement = ReportUtils.hasUpdatedTotal(iouReport, policy); const numberOfRequests = allTransactions.length; const transactionsWithReceipts = ReportUtils.getTransactionsWithReceipts(iouReportID); @@ -433,6 +439,14 @@ function ReportPreview({ } }, [isPaidAnimationRunning, iouSettled, checkMarkScale]); + useEffect(() => { + if (!isApproved) { + return; + } + + thumbsUpScale.value = withSpring(1, {duration: 200}); + }, [isApproved, thumbsUpScale]); + return ( )} + {isApproved && ( + + + + )} {shouldShowSubtitle && supportText && ( From 51842c47fb01dc9373f19ef7537aec5c20d88a35 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Sun, 20 Oct 2024 18:03:09 +0530 Subject: [PATCH 2/7] minor updates. Signed-off-by: krishna2323 --- src/components/ReportActionItem/ReportPreview.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 6c87d9f3d559..5097d34111c7 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -139,7 +139,7 @@ function ReportPreview({ const checkMarkScale = useSharedValue(iouSettled ? 1 : 0); const isApproved = ReportUtils.isReportApproved(iouReport, action); - const thumbsUpScale = useSharedValue(isApproved ? 1 : 0.25); + const thumbsUpScale = useSharedValue(isApproved ? 1 : 0); const thumbsUpStyle = useAnimatedStyle(() => ({ ...styles.defaultCheckmarkWrapper, transform: [{scale: thumbsUpScale.value}], @@ -483,7 +483,7 @@ function ReportPreview({ - {previewMessage} + {previewMessage} {shouldShowRBR && ( Date: Sun, 20 Oct 2024 18:07:14 +0530 Subject: [PATCH 3/7] make animation subtle. Signed-off-by: krishna2323 --- src/components/ReportActionItem/ReportPreview.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 5097d34111c7..9ebcf792ed5e 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -139,7 +139,7 @@ function ReportPreview({ const checkMarkScale = useSharedValue(iouSettled ? 1 : 0); const isApproved = ReportUtils.isReportApproved(iouReport, action); - const thumbsUpScale = useSharedValue(isApproved ? 1 : 0); + const thumbsUpScale = useSharedValue(isApproved ? 1 : 0.25); const thumbsUpStyle = useAnimatedStyle(() => ({ ...styles.defaultCheckmarkWrapper, transform: [{scale: thumbsUpScale.value}], From 3ff8ba053cc4f2c732f93617e7f26d2f462b955e Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Sat, 2 Nov 2024 01:13:20 +0530 Subject: [PATCH 4/7] feat: animation after approving an expense. Signed-off-by: krishna2323 --- src/components/ProcessMoneyReportHoldMenu.tsx | 3 +++ .../ReportActionItem/ReportPreview.tsx | 23 +++++++++++++--- .../AnimatedSettlementButton.tsx | 27 +++++++++++++++---- 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/components/ProcessMoneyReportHoldMenu.tsx b/src/components/ProcessMoneyReportHoldMenu.tsx index 3d6ad9006dc5..ba320a594135 100644 --- a/src/components/ProcessMoneyReportHoldMenu.tsx +++ b/src/components/ProcessMoneyReportHoldMenu.tsx @@ -66,6 +66,9 @@ function ProcessMoneyReportHoldMenu({ const onSubmit = (full: boolean) => { if (isApprove) { + if (startAnimation) { + startAnimation(); + } IOU.approveMoneyRequest(moneyRequestReport, full); if (!full && isLinkedTransactionHeld(Navigation.getTopmostReportActionId() ?? '-1', moneyRequestReport?.reportID ?? '')) { Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(moneyRequestReport?.reportID ?? '')); diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index dc4e396ee75e..c2be6db0a4aa 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -120,6 +120,7 @@ function ReportPreview({ ); const [isPaidAnimationRunning, setIsPaidAnimationRunning] = useState(false); + const [isApprovedAnimationRunning, setIsApprovedAnimationRunning] = useState(false); const [isHoldMenuVisible, setIsHoldMenuVisible] = useState(false); const [requestType, setRequestType] = useState(); const [nonHeldAmount, fullAmount] = ReportUtils.getNonHeldAndFullAmount(iouReport, policy); @@ -200,11 +201,18 @@ function ReportPreview({ const {isDelegateAccessRestricted, delegatorEmail} = useDelegateUserDetails(); const [isNoDelegateAccessMenuVisible, setIsNoDelegateAccessMenuVisible] = useState(false); - const stopAnimation = useCallback(() => setIsPaidAnimationRunning(false), []); + const stopAnimation = useCallback(() => { + setIsPaidAnimationRunning(false); + setIsApprovedAnimationRunning(false); + }, []); const startAnimation = useCallback(() => { setIsPaidAnimationRunning(true); HapticFeedback.longPress(); }, []); + const startApprovedAnimation = useCallback(() => { + setIsApprovedAnimationRunning(true); + HapticFeedback.longPress(); + }, []); const confirmPayment = useCallback( (type: PaymentMethodType | undefined, payAsBusiness?: boolean) => { if (!type) { @@ -236,6 +244,8 @@ function ReportPreview({ } else if (ReportUtils.hasHeldExpenses(iouReport?.reportID)) { setIsHoldMenuVisible(true); } else { + setIsApprovedAnimationRunning(true); + HapticFeedback.longPress(); IOU.approveMoneyRequest(iouReport, true); } }; @@ -427,7 +437,7 @@ function ReportPreview({ const shouldShowExportIntegrationButton = !shouldShowPayButton && !shouldShowSubmitButton && connectedIntegration && isAdmin && ReportUtils.canBeExported(iouReport); useEffect(() => { - if (!isPaidAnimationRunning) { + if (!isPaidAnimationRunning || isApprovedAnimationRunning) { return; } @@ -556,6 +566,7 @@ function ReportPreview({ { + if (requestType === 'approve') { + startApprovedAnimation(); + } else { + startAnimation(); + } + }} /> )} diff --git a/src/components/SettlementButton/AnimatedSettlementButton.tsx b/src/components/SettlementButton/AnimatedSettlementButton.tsx index 5de528d741a2..375e76a33582 100644 --- a/src/components/SettlementButton/AnimatedSettlementButton.tsx +++ b/src/components/SettlementButton/AnimatedSettlementButton.tsx @@ -11,9 +11,10 @@ import type SettlementButtonProps from './types'; type AnimatedSettlementButtonProps = SettlementButtonProps & { isPaidAnimationRunning: boolean; onAnimationFinish: () => void; + isApprovedAnimationRunning: boolean; }; -function AnimatedSettlementButton({isPaidAnimationRunning, onAnimationFinish, isDisabled, ...settlementButtonProps}: AnimatedSettlementButtonProps) { +function AnimatedSettlementButton({isPaidAnimationRunning, onAnimationFinish, isApprovedAnimationRunning, isDisabled, ...settlementButtonProps}: AnimatedSettlementButtonProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const buttonScale = useSharedValue(1); @@ -38,7 +39,7 @@ function AnimatedSettlementButton({isPaidAnimationRunning, onAnimationFinish, is overflow: 'hidden', marginTop: buttonMarginTop.value, })); - const buttonDisabledStyle = isPaidAnimationRunning + const buttonDisabledStyle = isApprovedAnimationRunning ? { opacity: 1, ...styles.cursorDefault, @@ -56,7 +57,7 @@ function AnimatedSettlementButton({isPaidAnimationRunning, onAnimationFinish, is }, [buttonScale, buttonOpacity, paymentCompleteTextScale, paymentCompleteTextOpacity, height, buttonMarginTop, styles.expenseAndReportPreviewTextButtonContainer.gap]); useEffect(() => { - if (!isPaidAnimationRunning) { + if (!isApprovedAnimationRunning && !isPaidAnimationRunning) { resetAnimation(); return; } @@ -73,7 +74,18 @@ function AnimatedSettlementButton({isPaidAnimationRunning, onAnimationFinish, is ); buttonMarginTop.value = withDelay(totalDelay, withTiming(0, {duration: CONST.ANIMATION_PAID_DURATION})); paymentCompleteTextOpacity.value = withDelay(totalDelay, withTiming(0, {duration: CONST.ANIMATION_PAID_DURATION})); - }, [isPaidAnimationRunning, onAnimationFinish, buttonOpacity, buttonScale, height, paymentCompleteTextOpacity, paymentCompleteTextScale, buttonMarginTop, resetAnimation]); + }, [ + isPaidAnimationRunning, + isApprovedAnimationRunning, + onAnimationFinish, + buttonOpacity, + buttonScale, + height, + paymentCompleteTextOpacity, + paymentCompleteTextScale, + buttonMarginTop, + resetAnimation, + ]); return ( @@ -82,11 +94,16 @@ function AnimatedSettlementButton({isPaidAnimationRunning, onAnimationFinish, is {translate('iou.paymentComplete')} )} + {isApprovedAnimationRunning && ( + + {translate('iou.approved')} + + )} From 31ddb55edabc6b07126cd908af2631fc63633e8e Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Sat, 2 Nov 2024 01:38:27 +0530 Subject: [PATCH 5/7] minor fixes. Signed-off-by: krishna2323 --- .../ReportActionItem/ReportPreview.tsx | 6 +++- .../AnimatedSettlementButton.tsx | 34 ++++++++++++++----- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index c2be6db0a4aa..27068ff2f80f 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -203,6 +203,9 @@ function ReportPreview({ const stopAnimation = useCallback(() => { setIsPaidAnimationRunning(false); + }, []); + + const stopApprovedAnimation = useCallback(() => { setIsApprovedAnimationRunning(false); }, []); const startAnimation = useCallback(() => { @@ -567,6 +570,7 @@ function ReportPreview({ onlyShowPayElsewhere={onlyShowPayElsewhere} isPaidAnimationRunning={isPaidAnimationRunning} isApprovedAnimationRunning={isApprovedAnimationRunning} + onApprovedAnimationFinish={stopApprovedAnimation} onAnimationFinish={stopAnimation} formattedAmount={getSettlementAmount() ?? ''} currency={iouReport?.currency} @@ -636,7 +640,7 @@ function ReportPreview({ moneyRequestReport={iouReport} transactionCount={numberOfRequests} startAnimation={() => { - if (requestType === 'approve') { + if (requestType === CONST.IOU.REPORT_ACTION_TYPE.APPROVE) { startApprovedAnimation(); } else { startAnimation(); diff --git a/src/components/SettlementButton/AnimatedSettlementButton.tsx b/src/components/SettlementButton/AnimatedSettlementButton.tsx index 375e76a33582..f8205a1b1ab0 100644 --- a/src/components/SettlementButton/AnimatedSettlementButton.tsx +++ b/src/components/SettlementButton/AnimatedSettlementButton.tsx @@ -12,9 +12,17 @@ type AnimatedSettlementButtonProps = SettlementButtonProps & { isPaidAnimationRunning: boolean; onAnimationFinish: () => void; isApprovedAnimationRunning: boolean; + onApprovedAnimationFinish: () => void; }; -function AnimatedSettlementButton({isPaidAnimationRunning, onAnimationFinish, isApprovedAnimationRunning, isDisabled, ...settlementButtonProps}: AnimatedSettlementButtonProps) { +function AnimatedSettlementButton({ + isPaidAnimationRunning, + onAnimationFinish, + isApprovedAnimationRunning, + onApprovedAnimationFinish, + isDisabled, + ...settlementButtonProps +}: AnimatedSettlementButtonProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const buttonScale = useSharedValue(1); @@ -39,12 +47,13 @@ function AnimatedSettlementButton({isPaidAnimationRunning, onAnimationFinish, is overflow: 'hidden', marginTop: buttonMarginTop.value, })); - const buttonDisabledStyle = isApprovedAnimationRunning - ? { - opacity: 1, - ...styles.cursorDefault, - } - : undefined; + const buttonDisabledStyle = + isPaidAnimationRunning || isApprovedAnimationRunning + ? { + opacity: 1, + ...styles.cursorDefault, + } + : undefined; const resetAnimation = useCallback(() => { // eslint-disable-next-line react-compiler/react-compiler @@ -70,7 +79,15 @@ function AnimatedSettlementButton({isPaidAnimationRunning, onAnimationFinish, is const totalDelay = CONST.ANIMATION_PAID_DURATION + CONST.ANIMATION_PAID_BUTTON_HIDE_DELAY; height.value = withDelay( totalDelay, - withTiming(0, {duration: CONST.ANIMATION_PAID_DURATION}, () => runOnJS(onAnimationFinish)()), + withTiming(0, {duration: CONST.ANIMATION_PAID_DURATION}, () => + runOnJS(() => { + if (isApprovedAnimationRunning) { + onApprovedAnimationFinish(); + } else { + onAnimationFinish(); + } + })(), + ), ); buttonMarginTop.value = withDelay(totalDelay, withTiming(0, {duration: CONST.ANIMATION_PAID_DURATION})); paymentCompleteTextOpacity.value = withDelay(totalDelay, withTiming(0, {duration: CONST.ANIMATION_PAID_DURATION})); @@ -85,6 +102,7 @@ function AnimatedSettlementButton({isPaidAnimationRunning, onAnimationFinish, is paymentCompleteTextScale, buttonMarginTop, resetAnimation, + onApprovedAnimationFinish, ]); return ( From 88cd7752bfb26bf72e61ab75e639d7650534b042 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Wed, 6 Nov 2024 12:16:50 +0530 Subject: [PATCH 6/7] fix height when approve button changes to pay. Signed-off-by: krishna2323 --- .../ReportActionItem/ReportPreview.tsx | 11 ++++----- .../AnimatedSettlementButton.tsx | 23 ++++++++----------- src/libs/actions/IOU.ts | 3 ++- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 27068ff2f80f..cbb50243dfe5 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -203,11 +203,9 @@ function ReportPreview({ const stopAnimation = useCallback(() => { setIsPaidAnimationRunning(false); - }, []); - - const stopApprovedAnimation = useCallback(() => { setIsApprovedAnimationRunning(false); }, []); + const startAnimation = useCallback(() => { setIsPaidAnimationRunning(true); HapticFeedback.longPress(); @@ -346,14 +344,15 @@ function ReportPreview({ const bankAccountRoute = ReportUtils.getBankAccountRoute(chatReport); const getCanIOUBePaid = useCallback( - (onlyShowPayElsewhere = false) => IOU.canIOUBePaid(iouReport, chatReport, policy, allTransactions, onlyShowPayElsewhere), + (onlyShowPayElsewhere = false, shouldCheckApprovedState = true) => IOU.canIOUBePaid(iouReport, chatReport, policy, allTransactions, onlyShowPayElsewhere, shouldCheckApprovedState), [iouReport, chatReport, policy, allTransactions], ); const canIOUBePaid = useMemo(() => getCanIOUBePaid(), [getCanIOUBePaid]); + const canIOUBePaidAndApproved = useMemo(() => getCanIOUBePaid(false, false), [getCanIOUBePaid]); const onlyShowPayElsewhere = useMemo(() => !canIOUBePaid && getCanIOUBePaid(true), [canIOUBePaid, getCanIOUBePaid]); const shouldShowPayButton = isPaidAnimationRunning || canIOUBePaid || onlyShowPayElsewhere; - const shouldShowApproveButton = useMemo(() => IOU.canApproveIOU(iouReport, policy), [iouReport, policy]); + const shouldShowApproveButton = useMemo(() => IOU.canApproveIOU(iouReport, policy), [iouReport, policy]) || isApprovedAnimationRunning; const shouldDisableApproveButton = shouldShowApproveButton && !ReportUtils.isAllowedToApproveExpenseReport(iouReport); @@ -570,7 +569,7 @@ function ReportPreview({ onlyShowPayElsewhere={onlyShowPayElsewhere} isPaidAnimationRunning={isPaidAnimationRunning} isApprovedAnimationRunning={isApprovedAnimationRunning} - onApprovedAnimationFinish={stopApprovedAnimation} + canIOUBePaid={canIOUBePaidAndApproved || isPaidAnimationRunning} onAnimationFinish={stopAnimation} formattedAmount={getSettlementAmount() ?? ''} currency={iouReport?.currency} diff --git a/src/components/SettlementButton/AnimatedSettlementButton.tsx b/src/components/SettlementButton/AnimatedSettlementButton.tsx index f8205a1b1ab0..7e42c8cdc45c 100644 --- a/src/components/SettlementButton/AnimatedSettlementButton.tsx +++ b/src/components/SettlementButton/AnimatedSettlementButton.tsx @@ -12,15 +12,15 @@ type AnimatedSettlementButtonProps = SettlementButtonProps & { isPaidAnimationRunning: boolean; onAnimationFinish: () => void; isApprovedAnimationRunning: boolean; - onApprovedAnimationFinish: () => void; + canIOUBePaid: boolean; }; function AnimatedSettlementButton({ isPaidAnimationRunning, onAnimationFinish, isApprovedAnimationRunning, - onApprovedAnimationFinish, isDisabled, + canIOUBePaid, ...settlementButtonProps }: AnimatedSettlementButtonProps) { const styles = useThemeStyles(); @@ -77,19 +77,15 @@ function AnimatedSettlementButton({ // Wait for the above animation + 1s delay before hiding the component const totalDelay = CONST.ANIMATION_PAID_DURATION + CONST.ANIMATION_PAID_BUTTON_HIDE_DELAY; + const willShowPaymentButton = canIOUBePaid && isApprovedAnimationRunning; height.value = withDelay( totalDelay, - withTiming(0, {duration: CONST.ANIMATION_PAID_DURATION}, () => - runOnJS(() => { - if (isApprovedAnimationRunning) { - onApprovedAnimationFinish(); - } else { - onAnimationFinish(); - } - })(), - ), + withTiming(willShowPaymentButton ? variables.componentSizeNormal : 0, {duration: CONST.ANIMATION_PAID_DURATION}, () => runOnJS(onAnimationFinish)()), + ); + buttonMarginTop.value = withDelay( + totalDelay, + withTiming(willShowPaymentButton ? styles.expenseAndReportPreviewTextButtonContainer.gap : 0, {duration: CONST.ANIMATION_PAID_DURATION}), ); - buttonMarginTop.value = withDelay(totalDelay, withTiming(0, {duration: CONST.ANIMATION_PAID_DURATION})); paymentCompleteTextOpacity.value = withDelay(totalDelay, withTiming(0, {duration: CONST.ANIMATION_PAID_DURATION})); }, [ isPaidAnimationRunning, @@ -102,7 +98,8 @@ function AnimatedSettlementButton({ paymentCompleteTextScale, buttonMarginTop, resetAnimation, - onApprovedAnimationFinish, + canIOUBePaid, + styles.expenseAndReportPreviewTextButtonContainer.gap, ]); return ( diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 7a72df9f1d87..6fafe3c59a4f 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -7060,6 +7060,7 @@ function canIOUBePaid( policy: OnyxTypes.OnyxInputOrEntry, transactions?: OnyxTypes.Transaction[], onlyShowPayElsewhere = false, + shouldCheckApprovedState = true, ) { const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(chatReport); const reportNameValuePairs = ReportUtils.getReportNameValuePairs(chatReport?.reportID); @@ -7113,7 +7114,7 @@ function canIOUBePaid( reimbursableSpend !== 0 && !isChatReportArchived && !isAutoReimbursable && - !shouldBeApproved && + (!shouldBeApproved || !shouldCheckApprovedState) && !isPayAtEndExpenseReport ); } From 351284498ea5bc40979ce06db3b3667af1ee8fe0 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Tue, 12 Nov 2024 16:07:32 +0530 Subject: [PATCH 7/7] minor fix. Signed-off-by: krishna2323 --- src/libs/SearchUIUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index 29537b9c8368..5ed7a55e2e6a 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -288,7 +288,7 @@ function getAction(data: OnyxTypes.SearchResults['data'], key: string): SearchTr : [] ) as SearchTransaction[]; - if (IOU.canIOUBePaid(report, chatReport, policy, allReportTransactions, false, chatReportRNVP, invoiceReceiverPolicy)) { + if (IOU.canIOUBePaid(report, chatReport, policy, allReportTransactions, false, true, chatReportRNVP, invoiceReceiverPolicy)) { return CONST.SEARCH.ACTION_TYPES.PAY; }