Commit 8d567e4d authored by Justin Domingue's avatar Justin Domingue Committed by GitHub

fix: add liquidity flow polish (#2017)

* addressed feedback

* set initial, min and max zoom levels

* better handle 0

* avoid formatting range selector

* polish `not created` state

* remove unused import
parent 77fbccd3
...@@ -75,7 +75,7 @@ export const Brush = ({ ...@@ -75,7 +75,7 @@ export const Brush = ({
const previousBrushExtent = usePrevious(brushExtent) const previousBrushExtent = usePrevious(brushExtent)
const brushed = useCallback( const brushed = useCallback(
({ mode, type, selection }: D3BrushEvent<unknown>) => { ({ type, selection }: D3BrushEvent<unknown>) => {
if (!selection) { if (!selection) {
setLocalBrushExtent(null) setLocalBrushExtent(null)
return return
...@@ -83,15 +83,14 @@ export const Brush = ({ ...@@ -83,15 +83,14 @@ export const Brush = ({
const scaled = (selection as [number, number]).map(xScale.invert) as [number, number] const scaled = (selection as [number, number]).map(xScale.invert) as [number, number]
// undefined `mode` means brush was programatically moved // avoid infinite render loop by checking for change
// skip calling the handler to avoid a loop if (type === 'end' && (brushExtent[0] !== scaled[0] || brushExtent[1] !== scaled[1])) {
if (type === 'end' && mode !== undefined) {
setBrushExtent(scaled) setBrushExtent(scaled)
} }
setLocalBrushExtent(scaled) setLocalBrushExtent(scaled)
}, },
[xScale.invert, setBrushExtent] [xScale.invert, brushExtent, setBrushExtent]
) )
// keep local and external brush extent in sync // keep local and external brush extent in sync
......
...@@ -20,7 +20,7 @@ export function Chart({ ...@@ -20,7 +20,7 @@ export function Chart({
brushDomain, brushDomain,
brushLabels, brushLabels,
onBrushDomainChange, onBrushDomainChange,
initialZoom, zoomLevels,
}: LiquidityChartRangeInputProps) { }: LiquidityChartRangeInputProps) {
const svgRef = useRef<SVGSVGElement | null>(null) const svgRef = useRef<SVGSVGElement | null>(null)
...@@ -34,7 +34,7 @@ export function Chart({ ...@@ -34,7 +34,7 @@ export function Chart({
const { xScale, yScale } = useMemo(() => { const { xScale, yScale } = useMemo(() => {
const scales = { const scales = {
xScale: scaleLinear() xScale: scaleLinear()
.domain([(1 - initialZoom) * current, (1 + initialZoom) * current] as number[]) .domain([(1 - zoomLevels.initial) * current, (1 + zoomLevels.initial) * current] as number[])
.range([0, innerWidth]), .range([0, innerWidth]),
yScale: scaleLinear() yScale: scaleLinear()
.domain([0, max(series, yAccessor)] as number[]) .domain([0, max(series, yAccessor)] as number[])
...@@ -47,7 +47,7 @@ export function Chart({ ...@@ -47,7 +47,7 @@ export function Chart({
} }
return scales return scales
}, [initialZoom, current, innerWidth, series, innerHeight, zoom]) }, [zoomLevels.initial, current, innerWidth, series, innerHeight, zoom])
useEffect(() => { useEffect(() => {
if (!brushDomain) { if (!brushDomain) {
...@@ -67,6 +67,7 @@ export function Chart({ ...@@ -67,6 +67,7 @@ export function Chart({
innerWidth={innerWidth} innerWidth={innerWidth}
innerHeight={innerHeight} innerHeight={innerHeight}
showClear={Boolean(zoom && zoom.k !== 1)} showClear={Boolean(zoom && zoom.k !== 1)}
zoomLevels={zoomLevels}
/> />
<svg ref={svgRef} width="100%" height="100%" viewBox={`0 0 ${width} ${height}`} style={{ overflow: 'visible' }}> <svg ref={svgRef} width="100%" height="100%" viewBox={`0 0 ${width} ${height}`} style={{ overflow: 'visible' }}>
<defs> <defs>
......
...@@ -3,6 +3,7 @@ import { ButtonGray } from 'components/Button' ...@@ -3,6 +3,7 @@ import { ButtonGray } from 'components/Button'
import styled from 'styled-components/macro' import styled from 'styled-components/macro'
import { ScaleLinear, select, ZoomBehavior, zoom, ZoomTransform } from 'd3' import { ScaleLinear, select, ZoomBehavior, zoom, ZoomTransform } from 'd3'
import { RefreshCcw, ZoomIn, ZoomOut } from 'react-feather' import { RefreshCcw, ZoomIn, ZoomOut } from 'react-feather'
import { ZoomLevels } from './types'
const Wrapper = styled.div<{ count: number }>` const Wrapper = styled.div<{ count: number }>`
display: grid; display: grid;
...@@ -32,6 +33,7 @@ export default function Zoom({ ...@@ -32,6 +33,7 @@ export default function Zoom({
innerWidth, innerWidth,
innerHeight, innerHeight,
showClear, showClear,
zoomLevels,
}: { }: {
svg: SVGSVGElement | null svg: SVGSVGElement | null
xScale: ScaleLinear<number, number> xScale: ScaleLinear<number, number>
...@@ -39,6 +41,7 @@ export default function Zoom({ ...@@ -39,6 +41,7 @@ export default function Zoom({
innerWidth: number innerWidth: number
innerHeight: number innerHeight: number
showClear: boolean showClear: boolean
zoomLevels: ZoomLevels
}) { }) {
const zoomBehavior = useRef<ZoomBehavior<Element, unknown>>() const zoomBehavior = useRef<ZoomBehavior<Element, unknown>>()
...@@ -71,7 +74,7 @@ export default function Zoom({ ...@@ -71,7 +74,7 @@ export default function Zoom({
// zoom // zoom
zoomBehavior.current = zoom() zoomBehavior.current = zoom()
.scaleExtent([0.3, 10]) .scaleExtent([zoomLevels.min, zoomLevels.max])
.translateExtent([ .translateExtent([
[0, 0], [0, 0],
[innerWidth, innerHeight], [innerWidth, innerHeight],
...@@ -85,7 +88,7 @@ export default function Zoom({ ...@@ -85,7 +88,7 @@ export default function Zoom({
select(svg as Element) select(svg as Element)
.call(zoomBehavior.current) .call(zoomBehavior.current)
.on('mousedown.zoom', null) .on('mousedown.zoom', null)
}, [innerHeight, innerWidth, setZoom, svg, xScale, zoomBehavior]) }, [innerHeight, innerWidth, setZoom, svg, xScale, zoomBehavior, zoomLevels.max, zoomLevels.min])
return ( return (
<Wrapper count={showClear ? 3 : 2}> <Wrapper count={showClear ? 3 : 2}>
......
...@@ -36,7 +36,9 @@ export function useDensityChartData({ ...@@ -36,7 +36,9 @@ export function useDensityChartData({
price0: parseFloat(t.price0), price0: parseFloat(t.price0),
} }
newData.push(chartEntry) if (chartEntry.activeLiquidity > 0) {
newData.push(chartEntry)
}
} }
return newData return newData
......
...@@ -16,6 +16,25 @@ import { format } from 'd3' ...@@ -16,6 +16,25 @@ import { format } from 'd3'
import { Bound } from 'state/mint/v3/actions' import { Bound } from 'state/mint/v3/actions'
import { FeeAmount } from '@uniswap/v3-sdk' import { FeeAmount } from '@uniswap/v3-sdk'
import ReactGA from 'react-ga' import ReactGA from 'react-ga'
import { ZoomLevels } from './types'
const ZOOM_LEVELS: Record<FeeAmount, ZoomLevels> = {
[FeeAmount.LOW]: {
initial: 0.002,
min: 0.001,
max: 2,
},
[FeeAmount.MEDIUM]: {
initial: 0.3,
min: 0.01,
max: 20,
},
[FeeAmount.HIGH]: {
initial: 0.3,
min: 0.01,
max: 20,
},
}
const ChartWrapper = styled.div` const ChartWrapper = styled.div`
position: relative; position: relative;
...@@ -51,7 +70,7 @@ export default function LiquidityChartRangeInput({ ...@@ -51,7 +70,7 @@ export default function LiquidityChartRangeInput({
}: { }: {
currencyA: Currency | undefined currencyA: Currency | undefined
currencyB: Currency | undefined currencyB: Currency | undefined
feeAmount?: number feeAmount?: FeeAmount
ticksAtLimit: { [bound in Bound]?: boolean | undefined } ticksAtLimit: { [bound in Bound]?: boolean | undefined }
price: number | undefined price: number | undefined
priceLower?: Price<Token, Token> priceLower?: Price<Token, Token>
...@@ -73,7 +92,7 @@ export default function LiquidityChartRangeInput({ ...@@ -73,7 +92,7 @@ export default function LiquidityChartRangeInput({
const onBrushDomainChangeEnded = useCallback( const onBrushDomainChangeEnded = useCallback(
(domain) => { (domain) => {
const leftRangeValue = Number(domain[0]) let leftRangeValue = Number(domain[0])
const rightRangeValue = Number(domain[1]) const rightRangeValue = Number(domain[1])
ReactGA.event({ ReactGA.event({
...@@ -81,6 +100,10 @@ export default function LiquidityChartRangeInput({ ...@@ -81,6 +100,10 @@ export default function LiquidityChartRangeInput({
action: 'Chart brushed', action: 'Chart brushed',
}) })
if (leftRangeValue <= 0) {
leftRangeValue = 1 / 10 ** 6
}
batch(() => { batch(() => {
// simulate user input for auto-formatting and other validations // simulate user input for auto-formatting and other validations
leftRangeValue > 0 && onLeftRangeInput(leftRangeValue.toFixed(6)) leftRangeValue > 0 && onLeftRangeInput(leftRangeValue.toFixed(6))
...@@ -165,7 +188,7 @@ export default function LiquidityChartRangeInput({ ...@@ -165,7 +188,7 @@ export default function LiquidityChartRangeInput({
brushLabels={brushLabelValue} brushLabels={brushLabelValue}
brushDomain={brushDomain} brushDomain={brushDomain}
onBrushDomainChange={onBrushDomainChangeEnded} onBrushDomainChange={onBrushDomainChangeEnded}
initialZoom={feeAmount === FeeAmount.LOW ? 0.02 : 0.3} zoomLevels={ZOOM_LEVELS[feeAmount ?? FeeAmount.MEDIUM]}
/> />
</ChartWrapper> </ChartWrapper>
)} )}
......
...@@ -15,6 +15,12 @@ export interface Margins { ...@@ -15,6 +15,12 @@ export interface Margins {
left: number left: number
} }
export interface ZoomLevels {
initial: number
min: number
max: number
}
export interface LiquidityChartRangeInputProps { export interface LiquidityChartRangeInputProps {
// to distringuish between multiple charts in the DOM // to distringuish between multiple charts in the DOM
id?: string id?: string
...@@ -47,5 +53,5 @@ export interface LiquidityChartRangeInputProps { ...@@ -47,5 +53,5 @@ export interface LiquidityChartRangeInputProps {
brushDomain: [number, number] | undefined brushDomain: [number, number] | undefined
onBrushDomainChange: (domain: [number, number]) => void onBrushDomainChange: (domain: [number, number]) => void
initialZoom: number zoomLevels: ZoomLevels
} }
...@@ -4,7 +4,6 @@ import StepCounter from 'components/InputStepCounter/InputStepCounter' ...@@ -4,7 +4,6 @@ import StepCounter from 'components/InputStepCounter/InputStepCounter'
import { RowBetween } from 'components/Row' import { RowBetween } from 'components/Row'
import { AutoColumn } from 'components/Column' import { AutoColumn } from 'components/Column'
import { Bound } from 'state/mint/v3/actions' import { Bound } from 'state/mint/v3/actions'
import { formatTickPrice } from 'utils/formatTickPrice'
// currencyA is the base token // currencyA is the base token
export default function RangeSelector({ export default function RangeSelector({
...@@ -45,7 +44,7 @@ export default function RangeSelector({ ...@@ -45,7 +44,7 @@ export default function RangeSelector({
<AutoColumn gap="md"> <AutoColumn gap="md">
<RowBetween> <RowBetween>
<StepCounter <StepCounter
value={formatTickPrice(leftPrice, ticksAtLimit, Bound.LOWER, '')} value={ticksAtLimit[Bound.LOWER] ? '0' : leftPrice?.toSignificant(5) ?? ''}
onUserInput={onLeftRangeInput} onUserInput={onLeftRangeInput}
width="48%" width="48%"
decrement={isSorted ? getDecrementLower : getIncrementUpper} decrement={isSorted ? getDecrementLower : getIncrementUpper}
...@@ -59,7 +58,7 @@ export default function RangeSelector({ ...@@ -59,7 +58,7 @@ export default function RangeSelector({
tokenB={currencyB?.symbol} tokenB={currencyB?.symbol}
/> />
<StepCounter <StepCounter
value={formatTickPrice(rightPrice, ticksAtLimit, Bound.UPPER, '')} value={ticksAtLimit[Bound.UPPER] ? '' : rightPrice?.toSignificant(5) ?? ''}
onUserInput={onRightRangeInput} onUserInput={onRightRangeInput}
width="48%" width="48%"
decrement={isSorted ? getDecrementUpper : getIncrementLower} decrement={isSorted ? getDecrementUpper : getIncrementLower}
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment