-
Notifications
You must be signed in to change notification settings - Fork 94
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
feat(TokenEnterAmount): add new component to Swap flow #6247
base: main
Are you sure you want to change the base?
Conversation
styles.primaryAmountText, | ||
inputStyle, | ||
Platform.select({ ios: { lineHeight: undefined } }), | ||
<View> |
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 addition/removal is purely a result of adding extra View
wrapper around the input as it is needed for the new loading
check below. The actual TextField props are unchanged.
export function getDisplayTokenName<T extends { symbol: string; networkId: NetworkId }>(token: T) { | ||
return `${token.symbol} on ${NETWORK_NAMES[token.networkId]}` | ||
} | ||
|
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 was moved to a function just for the sake of being able to use it in the SwapScreenV2.test.tsx
file 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.
not related to the actual change, but it will show "on" without translation no matter what language is used? are we okay with that?
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.
@bakoushin You're right, my bad! Will add it to the translations.
networkId: fromToken?.networkId || networkConfig.defaultNetworkId, | ||
slippagePercentage: maxSlippagePercentage, | ||
enableAppFee: enableAppFee, | ||
onError: (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.
onError
and onSuccess
are the same as identical useEffect
s from SwapScreen.tsx
}) | ||
} | ||
|
||
function onSelectTokenFrom(selectedToken: TokenBalance, tokenPositionInList: number) { |
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.
In SwapScreen.tsx
functionality of onSelectTokenFrom
and onSelectTokenTo
functions is implemented in handleConfirmSelectToken
. These two functions execute the same logic but as a two separate handlers. When I was trying to understand the flow of the data in the existing component it seemed like separating this function into separate handlers with straightforward actions could make it more understandable.
Obviosuly, that's subjective so please let me know if you have any comments or objections!
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.
shall we keep the handle-
prefix?
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.
perhaps we can keep 2 handlers, but extract most of the logic into a separate function?
the main difference i noticed is the field
prop, the rest seems identical.
it feels not quite right to introduce that amount of ducplication
@@ -139,6 +145,13 @@ export function useEnterAmount(props: { | |||
* - `local.displayAmount` -> `localDisplayAmount` | |||
*/ | |||
const processedAmounts = useMemo(() => { | |||
if (!props.token) { |
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 it okay that this variable is not listed in the dependecies 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.
@bakoushin accidentally missed it! Will add it to the list.
P.S. Sometimes I really wish we had eslint rule for this 😄
@@ -183,7 +187,7 @@ function useSwapQuote({ | |||
return null | |||
} | |||
|
|||
if (!swapAmount[updatedField].gt(0)) { | |||
if (!swapAmount[updatedField] || !swapAmount[updatedField].gt(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.
i wonder if we need this condition: !swapAmount[updatedField].gt(0)
?
i noticed that we do almost the same check against the swpapAmountInWay
just below
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.
@bakoushin I've just adjusted the existing logic to reduce the amount of potential changes. But I can remove if needed!
onInputChange={handleAmountInputChange} | ||
amountType={amountType} | ||
toggleAmountType={handleToggleAmountType} | ||
onOpenTokenPicker={onOpenTokenPickerFrom} |
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.
supernit: shall we prefix this handler with handle-
instead of on-
for naming consistency?
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.
@bakoushin I think it makes sense to keep props of components with on
to follow the regular naming convention of event handlers for components (e.g. onClick
/onBlur
etc).
Naming function handlers with handle
totally makes sense to me though. Will change that!
amountType={amountType} | ||
onOpenTokenPicker={onOpenTokenPickerTo} | ||
loading={shouldShowSkeletons} | ||
testID="SwapAmountInput" |
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 testID
is identical to the "from" component. perhaps a typo?
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.
@bakoushin its per design. It was like that before rework and I've kept it the same cause in the test files it searches for the same testID
s but in different containers (from/to
)
> | ||
<View style={{ display: 'flex', justifyContent: 'space-between', flexGrow: 1 }}> | ||
<View style={{ flexShrink: 0 }}> | ||
<View style={[styles.warningsContainer, { gap: 4 }]}> |
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.
"warnings" sounds kind of confusing: there is no any warning inside this view
/> | ||
</View> | ||
|
||
<View> |
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.
do we need this <View>
wrapper?
if ( | ||
!quote || | ||
quote.preparedTransactions.type !== 'need-decrease-spend-amount-for-gas' | ||
) | ||
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.
why we need this condition? it seems to lead to a confusing ux.
if i correctly understand its behavior:
- the warning is displayed
- but the CTA press does nothing
why it does nothing? maybe we shoudn't display the warning in the first place?
feeCurrencies, | ||
})} | ||
style={styles.warning} | ||
onPressCta={onPressLearnMoreFees} |
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.
either the cta label is missing or this handler is redundant
</View> | ||
</View> | ||
|
||
<View> |
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.
do we need this <View>
wrapper?
contentContainerStyle={styles.contentContainer} | ||
keyboardShouldPersistTaps="handled" | ||
> | ||
<View style={{ display: 'flex', justifyContent: 'space-between', flexGrow: 1 }}> |
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 reason of inlining styles vs including them in the StyleSheet
?
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.
and do we need that View
at all?
keyboardShouldPersistTaps="handled" | ||
> | ||
<View style={{ display: 'flex', justifyContent: 'space-between', flexGrow: 1 }}> | ||
<View style={{ flexShrink: 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.
do we need this style?
} | ||
|
||
if (fromToken?.tokenId === selectedToken.tokenId) { | ||
setFromToken(toToken) |
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.
do we need to set "from" token in this handler?
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.
please ignore, got it 😅
// if in "from" we select the same token as in "to" then just swap | ||
if (toToken?.tokenId === selectedToken.tokenId) { | ||
setFromToken(toToken) | ||
setToToken(fromToken) |
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.
do we need to set "to" token in this handler?
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.
please ignore, got it 😅
AppAnalytics.track(SwapEvents.swap_screen_select_token, { fieldType: Field.FROM }) | ||
// use requestAnimationFrame so that the bottom sheet open animation is done | ||
// after the selectingField value is updated, so that the title of the | ||
// bottom sheet (which depends on selectingField) does not change on the | ||
// screen | ||
requestAnimationFrame(() => tokenBottomSheetFromRef.current?.snapToIndex(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.
- shall we consistenly use the
hadndle
prefix? - perhaps we can extract this code to a separate function e.g.
handleOpenTokenPicker(fieldType: Field)
to emphasize that hanlding is mostly identical
|
||
if (!fromToken || !toToken) { | ||
// Should never happen | ||
Logger.error(TAG, 'fromToken or toToken not found') |
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.
if that is a type guard (as "should never happen" comment suggests), do we need to log anything?
// clearQuote, | ||
// replaceAmountTo, |
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.
shall we remove 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.
or uncomment? 😅
Epic work! 🎉 Left few nits/questions |
Final PR for new Enter Amount component. This page implements a 2nd version of SwapScreen that uses new Enter Amount component. It is hidden behind the feature flag to mitigate the risks of such a big rework.
SwapScreenV2.test.tsx
file is an exact copy of the existingSwapScreen.test.tsx
. It seems like a huge addition (2k lines) BUT it is a 99% copy-paste just because the new flow is implemented as a new component. The only changes in that file aretestID
props and some translations excerpts. To make life A LOT easier in reviewing this specific file, I suggest doing the following in VSCode to only see the changed lines:SwapScreen.test.tsx
and click "Select for Compare"SwapScreenV2.test.tsx
and click "Compare with Selected"Details of the implementation for clarity:
SwapScreen.test.tsx
.useState
and it would be a mess to keep multipleuseState
s and Redux slice in sync - all the other fields from the slice were moved to the correspondinguseState
s.useMemo
)useEffect
sTest plan
Simulator.Screen.Recording.-.iPhone.15.Pro.Max.-.2024-12-19.at.14.56.55.mp4
Related issues
Relates to RET-1208
Backwards compatibility
Yes
Network scalability
If a new NetworkId and/or Network are added in the future, the changes in this PR will: