Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Animate payment icon in money request preview #54490

Open
wants to merge 44 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
43857d6
Animate payment icons in money request preview
abzokhattab Dec 3, 2024
8d8bf05
Merge remote-tracking branch 'origin/main' into animate-payment-icon-…
abzokhattab Dec 4, 2024
380f2a3
animating pay button inside the money report header
abzokhattab Dec 4, 2024
16a8901
refactoring
abzokhattab Dec 4, 2024
2b24c3d
reverting member.ts changes
abzokhattab Dec 6, 2024
bd7843d
change payment complete from text to animated button component
abzokhattab Dec 6, 2024
fd94665
Creating a hook for animation configurations
abzokhattab Dec 13, 2024
587600c
Merge remote-tracking branch 'origin/main' into animate-payment-icon-…
abzokhattab Dec 24, 2024
b46eb8a
fade out animation
abzokhattab Dec 24, 2024
ccc5bc8
revert Member.ts
abzokhattab Dec 24, 2024
eaac9e5
Revert Member.ts
abzokhattab Dec 24, 2024
d96bd06
Remove canIOUBePaid prop from the AnimatedSettlementButton
abzokhattab Dec 24, 2024
21ddf32
removing the onLayout in AnimatedSettlementButton
abzokhattab Dec 24, 2024
b2a08e7
Merge remote-tracking branch 'origin/main' into animate-payment-icon-…
abzokhattab Jan 12, 2025
ac5b588
Merge remote-tracking branch 'origin/main' into animate-payment-icon-…
abzokhattab Jan 12, 2025
8acf3a1
Refactoring
abzokhattab Jan 14, 2025
5dd1afe
Merge remote-tracking branch 'origin/main' into animate-payment-icon-…
abzokhattab Jan 14, 2025
f006f53
eslint fix
abzokhattab Jan 14, 2025
478bb7b
Merge remote-tracking branch 'origin/main' into animate-payment-icon-…
abzokhattab Jan 18, 2025
402bb43
fixing approved/paid race condition
abzokhattab Jan 18, 2025
91ce21f
eslint fix
abzokhattab Jan 18, 2025
d7aa16b
fixing eslint
abzokhattab Jan 18, 2025
f216d5f
Merge remote-tracking branch 'origin/main' into animate-payment-icon-…
abzokhattab Jan 23, 2025
3919768
fixing eslint
abzokhattab Jan 23, 2025
1752bf9
Making button size large
abzokhattab Jan 24, 2025
0c91530
Fixing eslint errs
abzokhattab Jan 24, 2025
1559e45
Merge remote-tracking branch 'origin/main' into animate-payment-icon-…
abzokhattab Jan 28, 2025
073ff30
Tweaking the pay/approve animations
abzokhattab Jan 29, 2025
c76819f
Merge remote-tracking branch 'origin/main' into animate-payment-icon-…
abzokhattab Jan 29, 2025
351a978
removing shouldUseSuccessStyle from AnimatedSettlementButton
abzokhattab Jan 29, 2025
c7b018a
Merge remote-tracking branch 'origin/main' into animate-payment-icon-…
abzokhattab Jan 29, 2025
ba537b3
Using useLayoutEffect instead of useEffect to avoid the brief text ch…
abzokhattab Jan 30, 2025
19f123d
Merge remote-tracking branch 'origin/main' into animate-payment-icon-…
abzokhattab Jan 31, 2025
8de1027
Merge remote-tracking branch 'origin/main' into animate-payment-icon-…
abzokhattab Jan 31, 2025
7c2135b
Reducing vertical margin
abzokhattab Feb 3, 2025
6c3ca0a
Merge remote-tracking branch 'origin/main' into animate-payment-icon-…
abzokhattab Feb 10, 2025
a2370b9
Use keyframe animation
abzokhattab Feb 11, 2025
49c3d2d
Prevent the container height shrinking on approval
abzokhattab Feb 12, 2025
f396e51
reverting the upper and lower margin for descision modal
abzokhattab Feb 12, 2025
6d464e5
using useAnimatedStyle for the container styles
abzokhattab Feb 12, 2025
1517d2b
fixing eslint
abzokhattab Feb 13, 2025
7bac9a8
Animated button refactoring
abzokhattab Feb 13, 2025
1cbb226
Merge remote-tracking branch 'origin/main' into animate-payment-icon-…
abzokhattab Feb 14, 2025
949a1ba
Cleaning unused ANIMATION_HIDE_DELAY
abzokhattab Feb 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ const CONST = {
ANIMATION_PAID_CHECKMARK_DELAY: 300,
ANIMATION_THUMBSUP_DURATION: 250,
ANIMATION_THUMBSUP_DELAY: 200,
ANIMATION_PAID_BUTTON_HIDE_DELAY: 1000,
ANIMATION_PAID_BUTTON_HIDE_DELAY: 300,
BACKGROUND_IMAGE_TRANSITION_DURATION: 1000,
SCREEN_TRANSITION_END_TIMEOUT: 1000,
ARROW_HIDE_DELAY: 3000,
Expand Down
8 changes: 5 additions & 3 deletions src/components/DecisionModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ function DecisionModal({title, prompt = '', firstOptionText, secondOptionText, o
>
<View style={[styles.m5]}>
abzokhattab marked this conversation as resolved.
Show resolved Hide resolved
<View>
<View style={[styles.flexRow, styles.mb4]}>
<View style={[styles.flexRow, styles.mb5]}>
<Header
title={title}
containerStyles={[styles.alignItemsCenter]}
Expand All @@ -58,16 +58,18 @@ function DecisionModal({title, prompt = '', firstOptionText, secondOptionText, o
{!!firstOptionText && (
<Button
success
style={[styles.mt4]}
style={[styles.mt5]}
onPress={onFirstOptionSubmit}
pressOnEnter
text={firstOptionText}
large
/>
)}
<Button
style={[styles.mt3, styles.noSelect]}
style={[styles.mt5, styles.noSelect]}
onPress={onSecondOptionSubmit}
text={secondOptionText}
large
/>
</View>
</Modal>
Expand Down
44 changes: 35 additions & 9 deletions src/components/MoneyReportHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {OnyxEntry} from 'react-native-onyx';
import {useOnyx} from 'react-native-onyx';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import usePaymentAnimations from '@hooks/usePaymentAnimations';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
Expand All @@ -29,6 +30,7 @@ import {
isClosedExpenseReportWithNoExpenses,
isCurrentUserSubmitter,
isInvoiceReport,
isReportApproved,
navigateBackOnDeleteTransaction,
reportTransactionsSelector,
} from '@libs/ReportUtils';
Expand Down Expand Up @@ -79,7 +81,7 @@ import MoneyRequestHeaderStatusBar from './MoneyRequestHeaderStatusBar';
import type {ActionHandledType} from './ProcessMoneyReportHoldMenu';
import ProcessMoneyReportHoldMenu from './ProcessMoneyReportHoldMenu';
import ExportWithDropdownMenu from './ReportActionItem/ExportWithDropdownMenu';
import SettlementButton from './SettlementButton';
import AnimatedSettlementButton from './SettlementButton/AnimatedSettlementButton';

type MoneyReportHeaderProps = {
/** The report currently being looked at */
Expand Down Expand Up @@ -132,7 +134,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
const {reimbursableSpend} = getMoneyRequestSpendBreakdown(moneyRequestReport);
const isOnHold = isOnHoldTransactionUtils(transaction);
const isDeletedParentAction = !!requestParentReportAction && isDeletedAction(requestParentReportAction);
const isDuplicate = isDuplicateTransactionUtils(transaction?.transactionID);
const isDuplicate = isDuplicateTransactionUtils(transaction?.transactionID) && !isReportApproved({report: moneyRequestReport});

// Only the requestor can delete the request, admins can only edit it.
const isActionOwner =
Expand All @@ -156,21 +158,27 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
const isPayAtEndExpense = isPayAtEndExpenseTransactionUtils(transaction);
const isArchivedReport = isArchivedReportWithID(moneyRequestReport?.reportID);
const [archiveReason] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${moneyRequestReport?.reportID}`, {selector: getArchiveReason});
const {isPaidAnimationRunning, isApprovedAnimationRunning, stopAnimation, startAnimation, startApprovedAnimation} = usePaymentAnimations();

const getCanIOUBePaid = useCallback(
(onlyShowPayElsewhere = false) => canIOUBePaidAction(moneyRequestReport, chatReport, policy, transaction ? [transaction] : undefined, onlyShowPayElsewhere),
(onlyShowPayElsewhere = false, shouldCheckApprovedState = true) =>
canIOUBePaidAction(moneyRequestReport, chatReport, policy, transaction ? [transaction] : undefined, onlyShowPayElsewhere, undefined, undefined, shouldCheckApprovedState),
[moneyRequestReport, chatReport, policy, transaction],
);
const canIOUBePaid = useMemo(() => getCanIOUBePaid(), [getCanIOUBePaid]);

const canIOUBePaid = useMemo(() => getCanIOUBePaid(), [getCanIOUBePaid]);
const canIOUBePaidAndApproved = useMemo(() => getCanIOUBePaid(false, false), [getCanIOUBePaid]);
const onlyShowPayElsewhere = useMemo(() => !canIOUBePaid && getCanIOUBePaid(true), [canIOUBePaid, getCanIOUBePaid]);

const shouldShowMarkAsCashButton =
hasAllPendingRTERViolations || (shouldShowBrokenConnectionViolation && (!isPolicyAdmin(policy) || isCurrentUserSubmitter(moneyRequestReport?.reportID)));

const shouldShowPayButton = canIOUBePaid || onlyShowPayElsewhere;
const shouldShowPayButton = isPaidAnimationRunning || canIOUBePaid || onlyShowPayElsewhere;

const shouldShowApproveButton = useMemo(() => canApproveIOU(moneyRequestReport, policy) && !hasOnlyPendingTransactions, [moneyRequestReport, policy, hasOnlyPendingTransactions]);
const shouldShowApproveButton = useMemo(
() => (canApproveIOU(moneyRequestReport, policy) && !hasOnlyPendingTransactions) || isApprovedAnimationRunning,
[moneyRequestReport, policy, hasOnlyPendingTransactions, isApprovedAnimationRunning],
);

const shouldDisableApproveButton = shouldShowApproveButton && !isAllowedToApproveExpenseReport(moneyRequestReport);

Expand Down Expand Up @@ -226,12 +234,14 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
} else if (isAnyTransactionOnHold) {
setIsHoldMenuVisible(true);
} else if (isInvoiceReport(moneyRequestReport)) {
startAnimation();
payInvoice(type, chatReport, moneyRequestReport, payAsBusiness);
} else {
startAnimation();
payMoneyRequest(type, chatReport, moneyRequestReport, true);
}
},
[chatReport, isAnyTransactionOnHold, isDelegateAccessRestricted, moneyRequestReport],
[chatReport, isAnyTransactionOnHold, isDelegateAccessRestricted, moneyRequestReport, startAnimation],
);

const confirmApproval = () => {
Expand All @@ -241,6 +251,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
} else if (isAnyTransactionOnHold) {
setIsHoldMenuVisible(true);
} else {
startApprovedAnimation();
approveMoneyRequest(moneyRequestReport, true);
}
};
Expand Down Expand Up @@ -375,7 +386,11 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
)}
{shouldShowSettlementButton && !shouldUseNarrowLayout && (
<View style={styles.pv2}>
<SettlementButton
<AnimatedSettlementButton
isPaidAnimationRunning={isPaidAnimationRunning}
isApprovedAnimationRunning={isApprovedAnimationRunning}
onAnimationFinish={stopAnimation}
canIOUBePaid={canIOUBePaidAndApproved || isPaidAnimationRunning}
onlyShowPayElsewhere={onlyShowPayElsewhere}
currency={moneyRequestReport?.currency}
confirmApproval={confirmApproval}
Expand Down Expand Up @@ -440,7 +455,11 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
/>
)}
{shouldShowSettlementButton && shouldUseNarrowLayout && (
<SettlementButton
<AnimatedSettlementButton
isPaidAnimationRunning={isPaidAnimationRunning}
isApprovedAnimationRunning={isApprovedAnimationRunning}
onAnimationFinish={stopAnimation}
canIOUBePaid={canIOUBePaidAndApproved || isPaidAnimationRunning}
wrapperStyle={[styles.flex1]}
onlyShowPayElsewhere={onlyShowPayElsewhere}
currency={moneyRequestReport?.currency}
Expand Down Expand Up @@ -504,6 +523,13 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
paymentType={paymentType}
chatReport={chatReport}
moneyRequestReport={moneyRequestReport}
startAnimation={() => {
if (requestType === CONST.IOU.REPORT_ACTION_TYPE.APPROVE) {
startApprovedAnimation();
} else {
startAnimation();
}
}}
transactionCount={transactionIDs?.length ?? 0}
/>
)}
Expand Down
29 changes: 7 additions & 22 deletions src/components/ReportActionItem/ReportPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,21 @@ import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback';
import ProcessMoneyReportHoldMenu from '@components/ProcessMoneyReportHoldMenu';
import type {ActionHandledType} from '@components/ProcessMoneyReportHoldMenu';
import ProcessMoneyReportHoldMenu from '@components/ProcessMoneyReportHoldMenu';
import AnimatedSettlementButton from '@components/SettlementButton/AnimatedSettlementButton';
import {showContextMenuForReport} from '@components/ShowContextMenuContext';
import Text from '@components/Text';
import useDelegateUserDetails from '@hooks/useDelegateUserDetails';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import usePaymentAnimations from '@hooks/usePaymentAnimations';
import usePolicy from '@hooks/usePolicy';
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 HapticFeedback from '@libs/HapticFeedback';
import Navigation from '@libs/Navigation/Navigation';
import Performance from '@libs/Performance';
import {getConnectedIntegration} from '@libs/PolicyUtils';
Expand Down Expand Up @@ -173,8 +173,7 @@ function ReportPreview({
[transactions, iouReportID, action],
);

const [isPaidAnimationRunning, setIsPaidAnimationRunning] = useState(false);
const [isApprovedAnimationRunning, setIsApprovedAnimationRunning] = useState(false);
const {isPaidAnimationRunning, isApprovedAnimationRunning, stopAnimation, startAnimation, startApprovedAnimation} = usePaymentAnimations();
const [isHoldMenuVisible, setIsHoldMenuVisible] = useState(false);
const [requestType, setRequestType] = useState<ActionHandledType>();
const [paymentType, setPaymentType] = useState<PaymentMethodType>();
Expand Down Expand Up @@ -263,19 +262,6 @@ function ReportPreview({
const {isDelegateAccessRestricted} = useDelegateUserDetails();
const [isNoDelegateAccessMenuVisible, setIsNoDelegateAccessMenuVisible] = useState(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) {
Expand All @@ -288,16 +274,15 @@ function ReportPreview({
} else if (hasHeldExpensesReportUtils(iouReport?.reportID)) {
setIsHoldMenuVisible(true);
} else if (chatReport && iouReport) {
setIsPaidAnimationRunning(true);
HapticFeedback.longPress();
startAnimation();
if (isInvoiceReportUtils(iouReport)) {
payInvoice(type, chatReport, iouReport, payAsBusiness);
} else {
payMoneyRequest(type, chatReport, iouReport);
}
}
},
[chatReport, iouReport, isDelegateAccessRestricted],
[chatReport, iouReport, isDelegateAccessRestricted, startAnimation],
);

const confirmApproval = () => {
Expand All @@ -307,8 +292,7 @@ function ReportPreview({
} else if (hasHeldExpensesReportUtils(iouReport?.reportID)) {
setIsHoldMenuVisible(true);
} else {
setIsApprovedAnimationRunning(true);
HapticFeedback.longPress();
startApprovedAnimation();
approveMoneyRequest(iouReport, true);
}
};
Expand Down Expand Up @@ -639,6 +623,7 @@ function ReportPreview({
confirmApproval={confirmApproval}
enablePaymentsRoute={ROUTES.ENABLE_PAYMENTS}
addBankAccountRoute={bankAccountRoute}
shouldAddTopMargin
shouldHidePaymentOptions={!shouldShowPayButton}
shouldShowApproveButton={shouldShowApproveButton}
shouldDisableApproveButton={shouldDisableApproveButton}
Expand Down
Loading
Loading