-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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 settlement button when pay and trigger a haptic feedback #48615
Animate settlement button when pay and trigger a haptic feedback #48615
Conversation
@ZhenjaHorbach Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
if (ReportUtils.isInvoiceReport(iouReport)) { | ||
IOU.payInvoice(type, chatReport, iouReport, payAsBusiness); | ||
} else { | ||
IOU.payMoneyRequest(type, chatReport, iouReport); | ||
} | ||
return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding this concern, I move the state whether to play the animation or not to AnimatedSettlementButton. To know whether pressing the pay button will pay the request or show a hold notification modal, I return true here.
Related to the hold notification modal, do we also want to play the pay animation (and trigger the haptic feedback) when confirming paying from the hold notification modal?
This is how it look now.
hold.mp4
if (!isAnimationRunning) { | ||
resetAnimation(); | ||
return; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reset the animation here in case of a pay error.
error.mp4
@bernhardoj And I will check PR today |
Done |
Reviewer Checklist
Screenshots/VideosAndroid: Nativeandroid.movAndroid: mWeb Chromeandroid-web.moviOS: Nativeios.moviOS: mWeb Safariios-web.movMacOS: Chrome / Safariweb.movMacOS: Desktopdesktop.mov |
@bernhardoj 2024-09-06.08.51.36.mov |
Hmm, I can't repro both. Can you share the crash log? web.mp4ios.mp4 |
I will recheck today or tomorrow |
About second bug But about first one Looks like this crash related with our changes And following console 2024-09-08.21.23.02.movWhen the animation starts options becomes equal []
And here
|
@ZhenjaHorbach one thing I noticed in your video is that the payment option is Pay $xx with Expensify. It will be shown as long as the below condition is satisfied. App/src/components/SettlementButton.tsx Lines 168 to 170 in 498d45c
After paying the request,
So, I decided to move back to the state to play the animation to ReportPreview so we can keep showing the payment option when the animation is still running. The root cause is just a guess though because Pay $xx with Expensify requires BA connected, but I was never able to get connected to the BA in staging, so please test if it fixes the issue. |
Hmm |
@@ -136,6 +137,7 @@ function ReportPreview({ | |||
[transactions, iouReportID, action], | |||
); | |||
|
|||
const [shouldStartPaidAnimation, setShouldStartPaidAnimation] = useState(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes look good !
The only thing I would prefer
Is to pass only ref and use the state inside AnimatedSettlementButton since stopAnimation looks redundant when we pass this function
But it's optional
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think using ref is a good idea since it won't trigger a re-render.
return; | ||
} | ||
// eslint-disable-next-line react-compiler/react-compiler | ||
buttonScale.value = withTiming(0, {duration: 200}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's create constants for 200 and 1200
So as not to change many values in case we want to change the animation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
And the crash was fixed |
LGTM ! |
conflicts |
Fixed. |
@@ -158,6 +65,10 @@ function SettlementButton({ | |||
const session = useSession(); | |||
// The app would crash due to subscribing to the entire report collection if chatReportID is an empty string. So we should have a fallback ID here. | |||
const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID || -1}`); | |||
// The "nvpLastPaymentMethod" object needs to be stable to prevent the "useMemo" | |||
// hook from being recreated unnecessarily, hence the use of CONST.EMPTY_OBJECT | |||
const [nvpLastPaymentMethod = CONST.EMPTY_OBJECT as LastPaymentMethod] = useOnyx(ONYXKEYS.NVP_LAST_PAYMENT_METHOD, {selector: (paymentMethod) => paymentMethod ?? {}}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the purpose of this selector?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure, I'm just copying the selector.
App/src/components/SettlementButton.tsx
Lines 333 to 336 in 00bc167
nvpLastPaymentMethod: { | |
key: ONYXKEYS.NVP_LAST_PAYMENT_METHOD, | |
selector: (paymentMethod) => paymentMethod ?? {}, | |
}, |
Maybe it's to default the value to an empty object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the usage of nvpLastPaymentMethod
, I think we can simplify this:
diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx
index cb63436badc..75499b48305 100644
--- a/src/components/SettlementButton/index.tsx
+++ b/src/components/SettlementButton/index.tsx
@@ -17,7 +17,6 @@ import * as IOU from '@userActions/IOU';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
-import type {LastPaymentMethod} from '@src/types/onyx';
import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import type SettlementButtonProps from './types';
@@ -47,7 +46,7 @@ function SettlementButton({
formattedAmount = '',
onPress,
pressOnEnter = false,
- policyID = '',
+ policyID = '-1',
shouldHidePaymentOptions = false,
shouldShowApproveButton = false,
shouldDisableApproveButton = false,
@@ -65,9 +64,7 @@ function SettlementButton({
const session = useSession();
// The app would crash due to subscribing to the entire report collection if chatReportID is an empty string. So we should have a fallback ID here.
const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID || -1}`);
- // The "nvpLastPaymentMethod" object needs to be stable to prevent the "useMemo"
- // hook from being recreated unnecessarily, hence the use of CONST.EMPTY_OBJECT
- const [nvpLastPaymentMethod = CONST.EMPTY_OBJECT as LastPaymentMethod] = useOnyx(ONYXKEYS.NVP_LAST_PAYMENT_METHOD, {selector: (paymentMethod) => paymentMethod ?? {}});
+ const [lastPaymentMethod = '-1'] = useOnyx(ONYXKEYS.NVP_LAST_PAYMENT_METHOD, {selector: (paymentMethod) => paymentMethod?.[policyID]});
const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`);
const isInvoiceReport = (!isEmptyObject(iouReport) && ReportUtils.isInvoiceReport(iouReport)) || false;
const isPaidGroupPolicy = ReportUtils.isPaidGroupPolicyExpenseChat(chatReport);
@@ -109,9 +106,6 @@ function SettlementButton({
}
// To achieve the one tap pay experience we need to choose the correct payment type as default.
- // If the user has previously chosen a specific payment option or paid for some expense,
- // let's use the last payment method or use default.
- const paymentMethod = nvpLastPaymentMethod?.[policyID] ?? '-1';
if (canUseWallet) {
buttonOptions.push(paymentMethods[CONST.IOU.PAYMENT_TYPE.EXPENSIFY]);
}
@@ -160,14 +154,27 @@ function SettlementButton({
buttonOptions.push(approveButtonOption);
}
- // Put the preferred payment method to the front of the array, so it's shown as default
- if (paymentMethod) {
- return buttonOptions.sort((method) => (method.value === paymentMethod ? -1 : 0));
+ // Put the preferred payment method to the front of the array, so it's shown as default. We assume their last payment method is their preferred.
+ if (lastPaymentMethod) {
+ return buttonOptions.sort((method) => (method.value === lastPaymentMethod ? -1 : 0));
}
return buttonOptions;
// We don't want to reorder the options when the preferred payment method changes while the button is still visible
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
- }, [currency, formattedAmount, iouReport, chatReport, policyID, translate, shouldHidePaymentOptions, shouldShowApproveButton, shouldDisableApproveButton]);
+ }, [
+ iouReport,
+ translate,
+ formattedAmount,
+ shouldDisableApproveButton,
+ isInvoiceReport,
+ currency,
+ shouldHidePaymentOptions,
+ shouldShowApproveButton,
+ shouldShowPaywithExpensifyOption,
+ shouldShowPayElsewhereOption,
+ chatReport,
+ onPress,
+ ]);
const selectPaymentType = (event: KYCFlowEvent, iouPaymentType: PaymentMethodType, triggerKYCFlow: TriggerKYCFlow) => {
if (policy && SubscriptionUtils.shouldRestrictUserBillableActions(policy.id)) {
@@ -226,7 +233,12 @@ function SettlementButton({
onPress={(event, iouPaymentType) => selectPaymentType(event, iouPaymentType, triggerKYCFlow)}
pressOnEnter={pressOnEnter}
options={paymentButtonOptions}
- onOptionSelected={(option) => savePreferredPaymentMethod(policyID, option.value)}
+ onOptionSelected={(option) => {
+ if (policyID === '-1') {
+ return;
+ }
+ savePreferredPaymentMethod(policyID, option.value);
+ }}
style={style}
disabledStyle={disabledStyle}
buttonSize={buttonSize}
Note that I also updated the useMemo
dependencies for paymentButtonOptions
that were suppressed to only exclude lastPaymentMethod
. Other values such as isInvoiceReport
were likely excluded as dependencies by mistake
@roryabraham Done |
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
🚀 Deployed to staging by https://github.com/roryabraham in version: 9.0.35-0 🚀
|
🚀 Deployed to production by https://github.com/grgia in version: 9.0.35-7 🚀
|
@@ -136,6 +137,7 @@ function ReportPreview({ | |||
[transactions, iouReportID, action], | |||
); | |||
|
|||
const [isPaidAnimationRunning, setIsPaidAnimationRunning] = useState(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You forgot to animate when paying a held expense, which caused #49262
<Animated.View style={containerStyles}> | ||
{isPaidAnimationRunning && ( | ||
<Animated.View style={paymentCompleteTextStyles}> | ||
<Text style={[styles.buttonMediumText]}>Payment complete</Text> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should've translated this
Details
We want to animate the settlement button when pay and trigger a heavy haptic feedback.
Fixed Issues
$ #48036
PROPOSAL: #48036 (comment)
Tests
Same as QA Steps
Offline tests
Same as QA Steps
QA Steps
PR Author Checklist
### Fixed Issues
section aboveTests
sectionOffline steps
sectionQA steps
sectiontoggleReport
and notonIconClick
)myBool && <MyComponent />
.src/languages/*
files and using the translation methodSTYLE.md
) were followedAvatar
, I verified the components usingAvatar
are working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG)
)Avatar
is modified, I verified thatAvatar
is working as expected in all cases)Design
label and/or tagged@Expensify/design
so the design team can review the changes.ScrollView
component to make it scrollable when more elements are added to the page.main
branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTest
steps.Screenshots/Videos
Android: Native
WhatsApp.Video.2024-09-05.at.12.48.28.mp4
Android: mWeb Chrome
android.mweb.mp4
iOS: Native
ios.mp4
iOS: mWeb Safari
ios.mweb.mp4
MacOS: Chrome / Safari
web.mp4
MacOS: Desktop
desktop.mp4