-
Notifications
You must be signed in to change notification settings - Fork 3k
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
Feature: Transfer balance #4177
Conversation
cc: @stitesExpensify .Please test it to see the flow. I got no balance 💰 to see the transfer.
only this part is left and other things are done. Please review it if you have some balance in your wallet and let me know what is missing. Thanks. |
Please don't merge it yet |
Yep, I'm deferring to @stitesExpensify for final review on the final two points: #4177 (comment) |
@stitesExpensify Do you think we can move forward now or there is still something holding it back? |
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.
Hi @parasharrajat, Sorry for the delay I've been hyperfocused on an internal project, but will now be available per usual. Just some small review comments here, but there was actually a misunderstanding when I wrote the issue that I will describe in another post.
Onyx.merge(ONYXKEYS.USER_WALLET, {balance: 0}); | ||
Onyx.merge(ONYXKEYS.WALLET_TRANSFER, {completed: true}); | ||
}).catch((error) => { | ||
console.debug(`[Payments] Failed to transfer wallet balance: ${error.message}`); |
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.
Should we show something to the user here too?
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 am yet to finish the API call part. I was awaiting answers to few questions #4177 (comment)
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.
Better to actually log something to the server instead of console.debug
which is only shown in development
👋 As I mentioned, there was a misunderstanding in the original issue proposal that needs to be taken care of. My apologies for the changes. The For this reason we want to change the choices to say
When the user loads into the page, whatever their default account is should decide which transfer option is chosen. If the user switches the transfer type, we need to send them to the choose/add account page and show only their debit cards, or only their bank accounts (whichever is relevant). The button should also say "add debit card" or "add bank account" depending on which type of account we are showing. If the user goes to the "which account" page and chooses an account, the transfer type should change based on the account type. If you have any questions feel free to reach out, I'm back to being very available. Thanks again for your patience! |
The method only takes 1 param,
Like I mentioned in my above comment, we won't be passing that anymore, it will be automatic based on the account type. |
Testing notes:
|
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.
Sure you're still cleaning things up but have a few more high level comments.
src/pages/EnablePayments/index.js
Outdated
|
||
// Steps | ||
import OnfidoStep from './OnfidoStep'; | ||
import AdditionalDetailsStep from './AdditionalDetailsStep'; | ||
import TermsStep from './TermsStep'; | ||
import ActivateStep from './ActivateStep'; | ||
import {userWalletPropTypes} from '../settings/Payments/paymentPropTypes'; |
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 appreciate the thought to improve it, but let's start with the necessary changes as it just makes it easier to review large features like this.
* @param {Object} nativeEvent | ||
* @param {String} accountID id of the selected account. | ||
*/ | ||
paymentMethodSelected(nativeEvent, accountID) { |
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.
Is the nativeEvent
used?
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.
Also this should be called something like setPaymentMethodAndNavigate()
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.
Yeah, this is used to anchor the popover menu on the PaymentsPage
.
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 see where it's used in this method. Why is it being passed to this method?
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.
PaymentsPage was expecting the first parameter to be an event object so I had to define one for keeping the same func defintion. But I think as per your prev comment, we should divide this into two methods and the problem will be solved.
enableSelection | ||
filterType={this.props.walletTransfer.filterPaymentMethodType} | ||
selectedAccountID={this.props.walletTransfer.selectedAccountID} | ||
/> |
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.
Shouldn't there be an + Add Payment Method
button next to this list?
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.
That button is handled inside the paymentMethodList
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.
Yes it's duplicating logic see the comment below this one 😄
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.
It is not completely duplicating the code. It is just changing the title of that button based on the payment method and we can directly use that button to navigate to the adding selected payment method type. I don't think it is good to show the popover menu if there is always going to be one menu item.
And again, the same component is used on that page.
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 it is good to show the popover menu if there is always going to be one menu item.
I agree, but that's not what I'm asking. We can choose to only display the popover when there are multiple. Keeping the logic all contained together is the important piece.
Anyway, this is not really negotiable. We're using this component in another PR as part of the same project and it makes 100% sense to use it here - please trust me and make the change.
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.
Sure, I will do that change.
I will move the + Add Payment Method
button to that component and use it. I think it would make more sense then. and filter it based on prop.
case CONST.PAYMENT_METHODS.BANK_ACCOUNT: addPaymentMethodButtonTitle = this.props.translate('paymentMethodList.addBankAccount'); break; | ||
case CONST.PAYMENT_METHODS.DEBIT_CARD: addPaymentMethodButtonTitle = this.props.translate('paymentMethodList.addDebitCard'); break; | ||
default: break; | ||
} |
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 be using the AddPaymentMethodMenu
for this.
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.
But on this page ChooseTransferAccountPage.js
, only one action is shown based on the type of paymentMethods shown.
For example,
In the list is bank accounts,
then + Add Bank Account
and AddPaymentMethodMenu is a menu with all options.
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.
Ok, well we have a component that basically does the same thing, but without being able to change the title. Maybe you can think of some ways to re-use it and the make it do whatever we need with props?
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.
Ok slowly starting to understand these changes... sorry it's taking me a while. Some more comments...
type: CONST.PAYMENT_METHODS.BANK_ACCOUNT, | ||
}, | ||
]; | ||
PaymentMethods.startWalletTransfer(this.props.userWallet.currentBalance - Fee); |
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 does it mean to "start" a wallet transfer?
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 needed to use the set for the first time use to I had to create separate action which usages Onyx.set
and later operations are handled by Onyx.merge
so for that purpose I created updateWalletTransfer
. What do you suggest here?
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 needed to use the set for the first time use to I had to create separate action which usages Onyx.set and later operations are handled by Onyx.merge so for that purpose I created updateWalletTransfer
Sorry, I don't get it. What does it mean to "start" a wallet transfer?
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 have to set some data that is necessary to manage the component state before users interact with anything on Wallet TransferPage. What about renaming it setWalletTransferInitialData
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.
Can we try to think of a name that communicates why we need to do this? Which state needs to be reset and how are we using it?
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.
Oh yeah, setTransferAmountAndSelectedAccount
. the three principles.
PR updated. The remaining changes either need more clarification or are not needed. Video is added for Web to check the new flow and the rest will be added after we are done with changes. Test steps are added as well. |
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.
There are quite a few more issues to resolve.
const combinedPaymentMethods = []; | ||
|
||
_.each(bankAccountList, (bankAccount) => { | ||
// Add all bank accounts besides the wallet |
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.
It's not clear why the wallet should not be in this list.
It would be better to remove the wallet wherever we don't want to include it. The caller should decide which to filter out depending on the context.
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 really know :rofl. Picked this code from existing so no Idea. I would keep it like this for now. Maybe Brandon can help later or a follow-up PR.
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.
engineering team is ready to answer any questions you have in Slack if you are unsure about something.
}); | ||
|
||
_.each(cardList, (card) => { | ||
// Add all cards besides the "cash" card |
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.
Explain why the cash card is not included here.
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 really know :rofl. Pick this code from existing so no Idea.
src/libs/actions/PaymentMethods.js
Outdated
return API.TransferWalletBalance(parameters) | ||
.then((response) => { | ||
if (response.jsonCode !== 200) { | ||
throw new Error(); |
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 are we trying to accomplish with this throw?
onCloseButtonPress={() => Navigation.dismissModal(true)} | ||
/> | ||
<View style={[styles.flex1, styles.flexBasisAuto, styles.justifyContentCenter]}> | ||
<CurrentWalletBalance balanceStyles={[styles.text7XLarge]} /> |
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.
Where are we getting 7X
from? First time I've seen a number next to a font style.
* @param {Number} currentBalance | ||
* @param {Number} selectedAccountID | ||
*/ | ||
function saveWalletTransferAmountAndAccount(currentBalance, selectedAccountID) { |
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.
This method is used for sharing data across multiple screens.
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.
Why are the screens not able to share this data?
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.
Does this mean we will have something like a "local transfer" that is temporarily stored until we are ready to make an API request? Let's try to be as descriptive as possible here please. I can help write the comment but I'm confused about why we even need this.
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.
Actually, screens change based on the Route and thus I am following the pattern of using Onyx to share data among navigation screens.
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.
Ok, I think in that case this method name sounds too much like something that would call an API?
What about splitting these up into more specific methods like:
setWalletTransferAmount()
and setWalletTransferAccountID()
With a comment like:
The Transfer Balance flow requires the use of several different screens. These methods enables us to share data about the current transfer between those screens.
Probably outside the scope of this issue, but kind of feels like we should have some distinction between "actions" that call APIs and "actions" that just set data.
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.
Probably outside the scope of this issue, but kind of feels like we should have some distinction between "actions" that call APIs and "actions" that just set data.
I don't feel the urge to do it. Probably follow a common naming convention for such functions.
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.
Leaving a few more comments because I noticed more incorrect things 🥲
I think we should maybe stop adding changes at this point and try to discuss a plan to make sure we are on the same page before proceeding further.
anchorPosition: { | ||
top: 0, | ||
left: 0, | ||
}, |
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 need to use this component in this PR and having it internally track the anchorPosition
isn't going to work.
default: break; | ||
} | ||
return this.props.translate('paymentMethodList.addPaymentMethod'); | ||
} |
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 need to make a new component that:
- Has
AddPaymentMethodMenu
inside it - Shows the different button options and only opens the popover when we don't want one or the other.
AddPaymentMethodMenu
itself shouldn't be modified because we need the popover to open when the "Transfer Balance" button is eventually pressed. Does that make sense? There's an issue here describing what the ultimate goal is: https://github.com/Expensify/Expensify/issues/187802
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'd suggest calling it AddPaymentMethodButton
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.
AddPaymentMethodMenu itself shouldn't be modified because we need the popover to open when the "Transfer Balance" button is eventually pressed.
I didn't get it and I don't have access to that issue. But I got the idea.
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.
Ah I'm sorry, I forgot that you cannot see those issues.
left: position.left + 20, | ||
}, | ||
}); | ||
} |
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.
This would need to go into that other component.
if (paymentType === CONST.PAYMENT_METHODS.BANK_ACCOUNT) { | ||
Navigation.navigate(ROUTES.SETTINGS_ADD_BANK_ACCOUNT); | ||
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.
None of these routes should be in this component because we will eventually need to navigate to different routes depending on the context in which this component will be used. I would suggest passing in the route as a prop to keep this component reusable.
@@ -124,13 +123,14 @@ const MenuItem = props => ( | |||
/> | |||
</View> | |||
)} | |||
{props.shouldShowSelectedState && <SelectCircle isChecked={props.isSelected} />} |
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.
Just FYI I tried to help out a bit and moved this change to a new PR to lighten things up here.
Onyx.merge(ONYXKEYS.USER_WALLET, {balance: 0}); | ||
Onyx.merge(ONYXKEYS.WALLET_TRANSFER, {completed: true}); | ||
}).catch((error) => { | ||
console.debug(`[Payments] Failed to transfer wallet balance: ${error.message}`); |
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.
Better to actually log something to the server instead of console.debug
which is only shown in development
* @param {Number} currentBalance | ||
* @param {Number} selectedAccountID | ||
*/ | ||
function saveWalletTransferAmountAndAccount(currentBalance, selectedAccountID) { |
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.
Why are the screens not able to share this data?
* @param {Number} currentBalance | ||
* @param {Number} selectedAccountID | ||
*/ | ||
function saveWalletTransferAmountAndAccount(currentBalance, selectedAccountID) { |
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.
Does this mean we will have something like a "local transfer" that is temporarily stored until we are ready to make an API request? Let's try to be as descriptive as possible here please. I can help write the comment but I'm confused about why we even need this.
/** | ||
* Cancel the wallet transfer | ||
*/ | ||
function cancelWalletTransfer() { |
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 think this would be clearer if we added "pending wallet transfer request" to all of these method names? I'm having trouble telling the difference between the action that calls the API and the one that "prepares" the data we need to make a request.
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.
cancelWalletTransfer()
sounds like an action that will call the API and cancel something.
I agree, lets first discuss the complete plan before I proceed to changes. Please let me know if you have something on your mind. |
Proceeding to changes... |
Here's a rough idea about how to do this that seems logical to me and can allow us to focus on smaller parts and improve things as we go...
|
Sounds like a great plan. I will break the PR now. Closing in favor of that. |
I'm going to make a tracing issue for this so that we don't miss anything :) |
Just to make sure my understanding is correct, the plan is to add this feature across several PRs (using the steps discussed here) rather than all at once? |
Yup, that's correct. |
Sounds good! Is there somewhere I can follow along with the progress for the new PRs? |
@joekaufmanexpensify I added a checklist here #3922 (comment) |
Great, thanks! |
Details
Fixed Issues
$ #3922
Tests | QA Steps
Open Payments page.
Click TransferBalance Link.
Confirm that the transfer button is disabled if the following conditions are true.
i. Transfer Amount is less than $ 0.30.
ii. Which Account is not selected.
Click instant Mode.
It should take you to the new page where you will select an account. This page should show you Debit cards only and Add Debit Card Button.
Clicking
+ Add Debit Card
Button opens a new page to add Debit Card.From the Select Account page, click on the any debit card.
You should be taken to the old page where you can see the selected debit card as
Which account?
If your balance is > Fee then
Transfer
Button should be active and it should show the transfer amount - fee.Click 1-3 Business Days Mode.
It should take you to the new page where you will select an account. This page should show you Bank Accounts only and Add bank account Button.
Clicking
+ Add bank Account
Button opens a new page to add bank Account.From the Select Account page, click on any bank account.
You should be taken to the old page where you can see the selected bank account card as
Which account?
If your balance is > Fee then
Transfer
Button should be active and it should show the transfer amount - fee.If your balance is > Fee and which account is set.
i. Click Transfer button.
ii. Now you should be taken to the Payments Page and a confirm modal should be shown.
Tested On
Screenshots
Web
output_file.mp4
Mobile Web
Desktop
iOS
Android