1
1
import Big from 'big.js' ;
2
- import { useMemo } from 'preact/hooks' ;
3
- import { useEffect , useRef } from 'preact/compat' ;
2
+ import { useEffect , useRef , useMemo } from 'preact/hooks' ;
4
3
import { useQuery } from '@tanstack/react-query' ;
5
4
import { ChevronDownIcon } from '@heroicons/react/20/solid' ;
6
5
@@ -10,26 +9,39 @@ import { Skeleton } from '../Skeleton';
10
9
import { QuoteProvider , quoteProviders } from './quoteProviders' ;
11
10
import { OfframpingParameters , useEventsContext } from '../../contexts/events' ;
12
11
13
- type FeeProviderRowProps = FeeComparisonProps & { provider : QuoteProvider } ;
12
+ interface BaseComparisonProps {
13
+ amount : Big ;
14
+ sourceAssetSymbol : string ;
15
+ targetAssetSymbol : string ;
16
+ vortexPrice : Big ;
17
+ network : Networks ;
18
+ }
14
19
15
- function VortexRow ( {
16
- targetAssetSymbol,
17
- vortexPrice,
18
- } : Omit < FeeProviderRowProps , 'provider' | 'sourceAssetSymbol' | 'amount' > ) {
20
+ interface FeeComparisonProps extends BaseComparisonProps {
21
+ enabled : boolean ;
22
+ }
23
+
24
+ type VortexRowProps = Pick < BaseComparisonProps , 'targetAssetSymbol' | 'vortexPrice' > ;
25
+
26
+ function VortexRow ( { targetAssetSymbol, vortexPrice } : VortexRowProps ) {
19
27
return (
20
28
< div className = "flex items-center justify-between w-full" >
21
29
< div className = "flex items-center w-full gap-4 ml-4 grow" >
22
30
< img src = { vortexIcon } className = "h-10 w-36" alt = "Vortex" />
23
31
</ div >
24
32
< div className = "flex items-center justify-center w-full gap-4 grow" >
25
33
< div className = "flex flex-col items-center" >
26
- < span className = "font-bold text-md" > { vortexPrice . toFixed ( 2 ) + ' ' + targetAssetSymbol } </ span >
34
+ < span className = "font-bold text-md" > { ` ${ vortexPrice . toFixed ( 2 ) } ${ targetAssetSymbol } ` } </ span >
27
35
</ div >
28
36
</ div >
29
37
</ div >
30
38
) ;
31
39
}
32
40
41
+ interface FeeProviderRowProps extends BaseComparisonProps {
42
+ provider : QuoteProvider ;
43
+ }
44
+
33
45
function FeeProviderRow ( {
34
46
provider,
35
47
amount,
@@ -39,6 +51,9 @@ function FeeProviderRow({
39
51
network,
40
52
} : FeeProviderRowProps ) {
41
53
const { scheduleQuote } = useEventsContext ( ) ;
54
+ // The vortex price is sometimes lagging behind the amount (as it first has to be calculated asynchronously)
55
+ // We keep a reference to the previous vortex price to avoid spamming the server with the same quote.
56
+ const prevVortexPrice = useRef < Big > ( ) ;
42
57
43
58
const {
44
59
isLoading,
@@ -49,31 +64,26 @@ function FeeProviderRow({
49
64
queryFn : ( ) => provider . query ( sourceAssetSymbol , targetAssetSymbol , amount , network ) ,
50
65
retry : false , // We don't want to retry the request to avoid spamming the server
51
66
} ) ;
52
- // The vortex price is sometimes lagging behind the amount (as it first has to be calculated asynchronously)
53
- // We keep a reference to the previous vortex price to avoid spamming the server with the same quote.
54
- const prevVortexPrice = useRef < Big | undefined > ( undefined ) ;
55
67
56
68
const priceDiff = useMemo ( ( ) => {
57
- if ( isLoading || error || ! providerPrice ) {
58
- return undefined ;
59
- }
60
-
69
+ if ( isLoading || error || ! providerPrice ) return undefined ;
61
70
return providerPrice . minus ( vortexPrice ) ;
62
71
} , [ isLoading , error , providerPrice , vortexPrice ] ) ;
63
72
64
73
useEffect ( ( ) => {
65
- if ( ! isLoading && ( providerPrice || error ) ) {
66
- const parameters : OfframpingParameters = {
67
- from_amount : amount . toFixed ( 2 ) ,
68
- from_asset : sourceAssetSymbol ,
69
- to_amount : vortexPrice . toFixed ( 2 ) ,
70
- to_asset : targetAssetSymbol ,
71
- } ;
72
- if ( ! prevVortexPrice . current || vortexPrice !== prevVortexPrice . current ) {
73
- scheduleQuote ( provider . name , providerPrice ? providerPrice . toFixed ( 2 , 0 ) : '-1' , parameters ) ;
74
- prevVortexPrice . current = vortexPrice ;
75
- }
76
- }
74
+ if ( isLoading || ( ! providerPrice && ! error ) ) return ;
75
+
76
+ const parameters : OfframpingParameters = {
77
+ from_amount : amount . toFixed ( 2 ) ,
78
+ from_asset : sourceAssetSymbol ,
79
+ to_amount : vortexPrice . toFixed ( 2 ) ,
80
+ to_asset : targetAssetSymbol ,
81
+ } ;
82
+
83
+ if ( prevVortexPrice . current ?. eq ( vortexPrice ) ) return ;
84
+
85
+ scheduleQuote ( provider . name , providerPrice ? providerPrice . toFixed ( 2 , 0 ) : '-1' , parameters ) ;
86
+ prevVortexPrice . current = vortexPrice ;
77
87
} , [
78
88
amount ,
79
89
provider . name ,
@@ -97,12 +107,12 @@ function FeeProviderRow({
97
107
) : (
98
108
< div className = "flex flex-col items-center" >
99
109
< span className = "font-bold text-md" >
100
- { error || ! providerPrice ? 'N/A' : providerPrice . toFixed ( 2 ) + ' ' + targetAssetSymbol }
110
+ { error || ! providerPrice ? 'N/A' : ` ${ providerPrice . toFixed ( 2 ) } ${ targetAssetSymbol } ` }
101
111
</ span >
102
112
{ priceDiff && (
103
113
< span className = { `flex items-center ${ priceDiff . gt ( 0 ) ? 'text-green-600' : 'text-red-600' } ` } >
104
- < ChevronDownIcon className = { `w-5 h-5 ${ priceDiff . gt ( 0 ) ? 'rotate-180' : '' } ` } /> { priceDiff . toFixed ( 2 ) } { ' ' }
105
- { targetAssetSymbol }
114
+ < ChevronDownIcon className = { `w-5 h-5 ${ priceDiff . gt ( 0 ) ? 'rotate-180' : '' } ` } />
115
+ { ` ${ priceDiff . toFixed ( 2 ) } ${ targetAssetSymbol } ` }
106
116
</ span >
107
117
) }
108
118
</ div >
@@ -112,15 +122,9 @@ function FeeProviderRow({
112
122
) ;
113
123
}
114
124
115
- type FeeComparisonTableProps = FeeComparisonProps ;
125
+ function FeeComparisonTable ( props : BaseComparisonProps ) {
126
+ const { amount, sourceAssetSymbol, network, targetAssetSymbol, vortexPrice } = props ;
116
127
117
- function FeeComparisonTable ( {
118
- amount,
119
- sourceAssetSymbol,
120
- targetAssetSymbol,
121
- vortexPrice,
122
- network,
123
- } : FeeComparisonTableProps ) {
124
128
return (
125
129
< div className = "w-full p-4 grow rounded-3xl shadow-custom" >
126
130
< div className = "flex items-center justify-center w-full mb-3" >
@@ -146,42 +150,41 @@ function FeeComparisonTable({
146
150
</ div >
147
151
</ div >
148
152
< div className = "w-full my-4 border-b border-gray-200" />
149
- < VortexRow targetAssetSymbol = { targetAssetSymbol } vortexPrice = { vortexPrice } network = { network } />
150
- { quoteProviders . map ( ( provider , index ) => (
151
- < >
153
+ < VortexRow targetAssetSymbol = { targetAssetSymbol } vortexPrice = { vortexPrice } />
154
+ { quoteProviders . map ( ( provider ) => (
155
+ < div key = { provider . name } >
152
156
< div className = "w-full my-4 border-b border-gray-200" />
153
- < FeeProviderRow
154
- key = { index }
155
- amount = { amount }
156
- provider = { provider }
157
- sourceAssetSymbol = { sourceAssetSymbol }
158
- targetAssetSymbol = { targetAssetSymbol }
159
- vortexPrice = { vortexPrice }
160
- network = { network }
161
- />
162
- </ >
157
+ < FeeProviderRow { ...props } provider = { provider } />
158
+ </ div >
163
159
) ) }
164
160
</ div >
165
161
) ;
166
162
}
167
163
168
- interface FeeComparisonProps {
169
- amount : Big ;
170
- sourceAssetSymbol : string ;
171
- targetAssetSymbol : string ;
172
- vortexPrice : Big ;
173
- network : Networks ;
174
- }
164
+ export function FeeComparison ( { enabled, ...props } : FeeComparisonProps ) {
165
+ const feeComparisonRef = useRef < HTMLDivElement > ( null ) ;
166
+
167
+ useEffect ( ( ) => {
168
+ if ( ! enabled ) return ;
169
+ if ( ! feeComparisonRef . current ) return ;
170
+
171
+ const timer = setTimeout ( ( ) => {
172
+ window . scrollTo ( {
173
+ top : feeComparisonRef . current ! . offsetTop ,
174
+ behavior : 'smooth' ,
175
+ } ) ;
176
+ } , 300 ) ;
177
+
178
+ return ( ) => clearTimeout ( timer ) ;
179
+ } , [ enabled ] ) ;
180
+
181
+ if ( ! enabled ) return null ;
175
182
176
- export function FeeComparison ( {
177
- amount,
178
- sourceAssetSymbol,
179
- targetAssetSymbol,
180
- vortexPrice,
181
- network,
182
- } : FeeComparisonProps ) {
183
183
return (
184
- < div className = "flex flex-col items-center max-w-4xl px-4 py-8 rounded-lg md:flex-row gap-x-8 gap-y-8 md:mx-auto md:w-3/4" >
184
+ < div
185
+ ref = { feeComparisonRef }
186
+ className = "flex flex-col items-center max-w-4xl px-4 py-8 rounded-lg md:flex-row gap-x-8 gap-y-8 md:mx-auto md:w-3/4"
187
+ >
185
188
< div className = "w-full gap-6 overflow-auto grow" >
186
189
< h1 className = "text-2xl font-bold" > Save on exchange rate markups</ h1 >
187
190
< p className = "mt-4 text-lg" >
@@ -190,13 +193,7 @@ export function FeeComparison({
190
193
</ p >
191
194
< p className = "mt-4 text-lg" > At Vortex, we’ll never do that and show our fees upfront.</ p >
192
195
</ div >
193
- < FeeComparisonTable
194
- amount = { amount }
195
- sourceAssetSymbol = { sourceAssetSymbol }
196
- targetAssetSymbol = { targetAssetSymbol }
197
- vortexPrice = { vortexPrice }
198
- network = { network }
199
- />
196
+ < FeeComparisonTable { ...props } />
200
197
</ div >
201
198
) ;
202
199
}
0 commit comments