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: refresh swap flow progress indicator #7496
Conversation
WEB-2764 [swaptober] progress indicator v2
Implement WEB-2452 Time-To-Swap success metric: https://app.amplitude.com/analytics/uniswap/chart/0z25ec81/edit/jitgde31 10/5 value: 110 - 115 seconds on Ethereum mainnet |
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
Codecov Report
Flags with carried forward coverage won't be shown. Click here to find out more. |
@@ -288,6 +294,7 @@ export default function ConfirmSwapModal({ | |||
fiatValueOutput: { data?: number; isLoading: boolean } | |||
}) { | |||
const { chainId } = useWeb3React() | |||
const inputTokenColor = useColor(trade?.inputAmount.currency.wrapped) |
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 is a very nice touch, looks great in the animation
[ConfirmModalState.WRAPPING]: { | ||
icon: <CurrencyLogo currency={trade?.inputAmount.currency} />, | ||
rippleColor: inputTokenColor, | ||
previewTitle: `Wrap ${nativeCurrency.symbol}`, |
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.
all of the titles and link texts should be wrapped in Trans
components and be passed as ReactElements to support i18n
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.
Thank you for calling out. Tagged for translation using the t
macro.
previewTitle: `Wrap ${nativeCurrency.symbol}`, | ||
actionRequiredTitle: `Wrap ${nativeCurrency.symbol} in wallet`, | ||
inProgressTitle: `Wrapping ${nativeCurrency.symbol}...`, | ||
timeToEnd: chainId === ChainId.MAINNET ? 20 : 10, |
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 is the same value every time it's used, should this just be part of the Step
component?
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.
Passing a timeToEnd
attribute implicitly indicates that a timer should be shown, which is not true for all steps, e.g. permit signature. So even if we move this value into the step component, we still need to pass an attribute here; it would just be boolean instead.
Also, as requested by design, I've made this value dynamic (displays actual estimate of transaction confirmation time), so it should be set by the ProgressIndicator
, where the logic exists for computing it.
}, | ||
[ConfirmModalState.PERMITTING]: { | ||
icon: <Sign />, | ||
rippleColor: '#FC72FF', |
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 these ripple colors update darkMode vs lightMode?
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.
The icons for these steps remain the same, regardless of light and dark mode, so rippleColor
is constant to always match the icon's fill
.
actionRequiredTitle: `Approve in wallet`, | ||
inProgressTitle: 'Approval pending...', | ||
timeToEnd: chainId === ChainId.MAINNET ? 20 : 10, | ||
delayedTitle: 'Longer than expected...', |
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 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.
Nice catch, small fix
opacity: 0.3; | ||
} | ||
` | ||
const Ring = styled.div<{ width: string; color: string; animation: Keyframes }>` |
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.
const Ring = styled.div<{ width: string; color: string; animation: Keyframes }>` | |
const Ring = styled.div<{ $borderWidth: string; $borderColor: string; $animation: Keyframes }>` |
return isTimeRemaining ? ( | ||
<ThemedText.BodyPrimary>{stepDetails.actionRequiredTitle}</ThemedText.BodyPrimary> | ||
) : ( | ||
<ThemedText.BodyPrimary>{stepDetails.delayedStartTitle}</ThemedText.BodyPrimary> | ||
) |
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.
the component never changes, you can just change the content, same with the in_progress step
return isTimeRemaining ? ( | |
<ThemedText.BodyPrimary>{stepDetails.actionRequiredTitle}</ThemedText.BodyPrimary> | |
) : ( | |
<ThemedText.BodyPrimary>{stepDetails.delayedStartTitle}</ThemedText.BodyPrimary> | |
) | |
return ( | |
<ThemedText.BodyPrimary> | |
{isTimeRemaining ? stepDetails.actionRequiredTitle : stepDetails.delayedStartTitle} | |
</ThemedText.BodyPrimary> | |
) |
const minutesText = minutes < 10 ? `0${minutes}` : minutes | ||
const secondsText = seconds < 10 ? `0${seconds}` : seconds |
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.
translate
export function Step({ stepStatus, stepDetails }: { stepStatus: StepStatus; stepDetails: StepDetails }) { | ||
// Timer is shown in three cases: | ||
// (1) User has a specified amount of time to perform a required action. Timer starts running as soon as the step becomes active. | ||
// (2) Step has an estimated amount of time in which it should be completed. Timer is set but not running when step becomes active. |
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 we should still hide the timer until the step is in progress, it seems strange to me to have the timer in view and paused while waiting for user input
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 was thinking of it like a shot clock in sports; always remains on but doesn't start until activity begins. I agree with you though that it's simpler to just hide the timer until it's running. I've made the changes and also simplified the time-keeping logic.
const isSingleStep = confirmModalState !== ConfirmModalState.REVIEWING && pendingModalSteps.length === 1 | ||
if (showProgressIndicatorV2 && (isSingleStep || lastStepComplete)) { | ||
return null | ||
} else if (!showProgressIndicatorV2 && confirmModalState !== ConfirmModalState.REVIEWING && !showAcceptChanges) { | ||
return null | ||
} | ||
return <SwapModalHeader inputCurrency={inputCurrency} trade={trade} allowedSlippage={allowedSlippage} /> |
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 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.
Added ClickableStyle
to the shared CloseIcon
component
Description
Create new swap flow progress indicator that increases the perceptual speed of swapping. This is accomplished by laying out the steps vertically, and clearly displaying their current status and expected behavior.
Main issue (design link included): https://linear.app/uniswap/issue/WEB-2452/[design]-faster-perceptual-swap
Sub-issues also included:
Screen capture
Before
After
Test plan
QA (ie manual testing)
Test various swap flows on desktop and mobile devices:
Devices
Desktop and mobile web
Automated testing