Compare commits

...

163 Commits

Author SHA1 Message Date
aballerr
bad1ce2618 fix: Prod has chevron (#4558)
Remove chevron from prod

Co-authored-by: Alex Ball <alexball@UNISWAP-MAC-038.local>
2022-09-01 10:20:09 -04:00
Vignesh Mohankumar
f194845b2b chore: remove theme.blue200 (#4533)
* chore: remove theme.blue200

* favorite button changes
2022-08-31 20:13:21 -04:00
Jack Short
98d4e108e6 fix: search icon bug (#4556) 2022-08-31 19:35:06 -04:00
lynn
ab43ed1900 fix: connect wallet button chevron styling fixes (#4554)
* fix

* simplify
2022-08-31 18:16:42 -04:00
Greg Bugyis
b147e047a5 fix: Bump height on LineNumberCell and pass header boolean (#4548) 2022-08-31 20:36:23 +03:00
Charles Bachmeier
bbb616f56c feat: auto set cursor on searchbar opening (#4552)
* auto set cursor on searchbar opening

* comment update

Co-authored-by: Charlie <charlie@uniswap.org>
2022-08-31 10:33:51 -07:00
Charles Bachmeier
35a429ea65 feat: highlight first result by default (#4551)
highlight first result by default

Co-authored-by: Charlie <charlie@uniswap.org>
2022-08-31 10:33:33 -07:00
Zach Pomerantz
bd16543c10 build: upgrade @uniswap/widgets to 2.5.0 (#4546)
* build: rm d3-curve-circlecorners entry

* build: upgrade @uniswap/widgets package

* fix: widget input typing
2022-08-31 10:29:50 -07:00
Charles Bachmeier
cbdeae276e feat: adjust bottom navbar padding (#4550)
adjust bottom navbar padding

Co-authored-by: Charlie <charlie@uniswap.org>
2022-08-31 09:57:42 -07:00
lynn
e733113963 fix: modify chart axis to to match crosshairs in year view (#4547)
* fix

* simplify

* refactor

* move logic to monthTickFormatter

* time to date string
2022-08-31 11:31:33 -04:00
aballerr
272b030b89 fix: Fixing border overlap and reducing button size (#4537)
* fixing styling on wallet border

Co-authored-by: Alex Ball <alexball@UNISWAP-MAC-038.local>
2022-08-31 11:11:37 -04:00
Jack Short
472a553d13 fix: catch invalid address token details (#4529) 2022-08-30 17:25:59 -04:00
cartcrom
3a1bff146c feat: adding token safety article link (#4532)
updated article link
2022-08-30 17:24:23 -04:00
lynn
b82b9acc54 fix: remove stablecoin usd val fetch in logging to reduce infura spend (#4543)
remove stablecoin usd val fetch reduce infura
2022-08-30 15:15:49 -04:00
cartcrom
fac3845756 fix: missing segments of price chart (#4541)
fixed missing segments of line
2022-08-30 12:28:52 -04:00
Charles Bachmeier
9381a74f1d feat: nav update to new responsive designs (#4542)
* uppdated mobile nav

* adjust searchbar for new styles

Co-authored-by: Charlie <charlie@uniswap.org>
2022-08-30 09:08:10 -07:00
Charles Bachmeier
f6662a3208 fix: hide right and left border on mobile dropdown (#4540)
hide right and left border on mobile

Co-authored-by: Charlie <charlie@uniswap.org>
2022-08-30 07:15:50 -07:00
pp-hh-ii-ll
134af82d90 fix: updated color definitions for backgroundModule, add scrolledSurface (#4538)
Updated color definitions for BackgroundModule and tokens list in Explore page

Update Dark Mode BackgroundModule to Grey800 to match new design spec

Update Widgets theme to use BackgroundModule for Module

Update Token List in Explore page to use Surface instead of Module

Add Scrolled Surface color definition
2022-08-29 22:00:34 -04:00
Vignesh Mohankumar
75175b8e54 fix: don't track balances/values (#4539) 2022-08-29 18:46:37 -04:00
Greg Bugyis
e3d8599dc7 fix: [TokenDetails] Glyph placement on line chart (#4525)
* Modify line curve on token price chart to fix inconsistency on steep drops/increases and glyph placement

* Make curve required on LineChart

* Add curve to SparkLine chart

* Remove dependency: d3-curve-circlecornders - no longer used

* Drop d3-curve-circlecorner from react-app-env

Co-authored-by: gbugyis <greg@bugyis.com>
2022-08-29 21:40:05 +03:00
Charles Bachmeier
77ee69ad52 fix: search flash of no tokens found when typing (#4531)
* add null check to collection floor price

* don't show 0 if floor is null

* use debouncedSearchValue

Co-authored-by: Charlie <charlie@uniswap.org>
2022-08-29 10:47:42 -07:00
lynn
4b82838f80 fix: add back cancel button in token safety for swap (#4527)
* add back cancel button in token safety for swap

* also add to swap flow
2022-08-29 13:08:30 -04:00
Jack Short
a177829976 style: switching hovered and idle colors (#4526) 2022-08-29 12:49:36 -04:00
Kaylee George
b8b4f960dd fix: tokens banner link works (#4522)
* maybe fixed?

* fix

* fix link
2022-08-29 12:48:13 -04:00
Charles Bachmeier
2466414307 feat: search when no results there is too much top padding (#4530)
* add null check to collection floor price

* don't show 0 if floor is null

* remove top margin for no results

Co-authored-by: Charlie <charlie@uniswap.org>
2022-08-29 09:43:50 -07:00
Charles Bachmeier
a3a32f0d68 fix: add null check to collection floor price (#4528)
* add null check to collection floor price

* don't show 0 if floor is null

* formatEthPrice accepts udnefined

Co-authored-by: Charlie <charlie@uniswap.org>
2022-08-29 09:38:22 -07:00
Vignesh Mohankumar
ee001f86f0 refactor: remove isChainAllowed (#4494) 2022-08-29 10:21:18 -04:00
lynn
87d6975bd8 fix: stop swap layout shifts when reverse token button clicked (#4523)
fix
2022-08-26 17:22:58 -04:00
Kaylee George
4cab4e27ff fix: fix favorites button responsiveness on tokens explore (#4520)
fav button responsiveness
2022-08-26 16:47:41 -04:00
Kaylee George
f105f0995b fix: formatted delta on mobile view of token explore (#4519)
fix delta
2022-08-26 13:42:38 -07:00
Kaylee George
18e89a7353 fix: rm right padding on token details (#4518)
rm padding on mobile
2022-08-26 16:32:23 -04:00
Charles Bachmeier
445f9a67a4 fix: mobile network switcher (#4516)
* fix: mobile network switcher

* fix zindex

Co-authored-by: Charlie <charlie@uniswap.org>
2022-08-26 13:01:20 -07:00
Jack Short
4c039c900c fix: wrapped native asset resolve to their appropriate native asset o… (#4497)
* fix: wrapped native asset resolve to their appropriate native asset on respective chains

* addressed comments
2022-08-26 14:42:34 -04:00
Kaylee George
2c2e0a3419 fix: vertically center the explore table header text (#4514)
center
2022-08-26 14:41:34 -04:00
aballerr
1b43e0b28a fix: wallet mobile ui changes (#4515)
fix wallet mobile

Co-authored-by: Alex Ball <alexball@UNISWAP-MAC-038.local>
2022-08-26 14:41:02 -04:00
Connor McEwen
a23f7782b2 fix: widget module color (#4513) 2022-08-26 14:40:33 -04:00
Zach Pomerantz
723db9d0ea feat: integrate widget settings/value (#4499)
* widgets: v2.2.0

* refactor: wrap Widget

* feat: integrate widget settings

* refactor: import from widgets

* fix: better settings integration

* fix: include types

* chore: formatting

* build: bump widgets version

* feat: integrate widget value

* feat: integrate widget token selection

* feat: clean up widget integration

* build: bump widgets version

* refactor: mv widget wrapper to components

* refactor: split widget integrations

* refactor: value -> inputs

* fix: consolidate slippage hooks

* fix: memoize currency search modal

* refactor: clarify widget code

* fix: allow loading token in widget

* fix: add TradeType helpers
2022-08-26 11:19:51 -07:00
lynn
cbf165dc40 fix: oops (#4511)
oops
2022-08-26 11:12:48 -07:00
Kaylee George
6f3579acf1 feat: add token explore promo banner (#4481)
* initial design

* progress

* add background image

* add logic

* update

* delete unneed state

* more

* redux

* redux

* more redux

* rebase main

* rm unused state

* rm unused export

* relative link
2022-08-26 11:00:50 -07:00
lynn
c9e2f86e57 feat: add i18n to token detail and token explore (#4510)
init
2022-08-26 13:51:29 -04:00
aballerr
8c0199119e fix: Fix to mobile wallet experience (#4508)
* fixing wallet on mobile and making useClickoutside more extensible


Co-authored-by: Alex Ball <alexball@UNISWAP-MAC-038.local>
2022-08-26 13:35:38 -04:00
Kaylee George
5e6e6be888 fix: fix decimal formatting of token price on details page (#4470)
* init

* 6 decimals

* dollar dollar bill yo
2022-08-26 10:19:29 -07:00
github-actions[bot]
79fb6485b1 chore(i18n): new Crowdin translations (#4495)
chore(i18n): synchronize translations from crowdin [skip ci]

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-08-26 13:16:06 -04:00
Greg Bugyis
29baaaf2ed fix: Token Warning dismiss/redirect (#4482)
* Remove cancel option from TokenSafety modal

* Only redirect if token is blocked

* Remove unused variables

Co-authored-by: gbugyis <greg@bugyis.com>
2022-08-26 20:10:54 +03:00
Kaylee George
f824fb25c2 fix: explore table hover state colors (#4479)
* fix hover state colors

* colors

* fixed
2022-08-26 09:41:05 -07:00
Charles Bachmeier
28a6ea7e1a feat: NavBar I18N (#4507)
* wrap tabs and searchbar

* wrap chain switcher and fix right align bug

* undo translate chains

* replace t with i18n

* revert i18n

Co-authored-by: Charlie <charlie@uniswap.org>
2022-08-26 09:22:05 -07:00
lynn
65566faf17 fix: yellow button redesign, improve add liquidity page readability (#4504)
* init

* add background overlay for hover states
2022-08-26 12:02:03 -04:00
lynn
eb4f90e669 feat: add volume trade info to swap completed event (#4506)
add volume trade info to swap completed event
2022-08-26 10:56:53 -04:00
aballerr
40308158ca fix: Updating search token width and adding margin between token delta and price (#4500)
Updating search token width and adding margin between token delta and price

Co-authored-by: Alex Ball <alexball@UNISWAP-MAC-038.local>
2022-08-26 09:46:39 -04:00
Greg Bugyis
d0d5240474 fix: update Token table headers to match latest Figma (#4505)
TokenTable: update table headers to match latest Figma

Co-authored-by: gbugyis <greg@bugyis.com>
2022-08-26 15:21:35 +03:00
Charles Bachmeier
751ce8e6d6 feat: Mobile Nav (#4501)
* working and cleaned up mobile nav

* delete old files

* fix wallet position

* update searchbar breakpoint

* update full screen search

* delete old comments

* cleanup eslint

* Update MenuDropdown.tsx

* Update SearchBar.tsx

Co-authored-by: Charlie <charlie@uniswap.org>
2022-08-26 04:39:58 -07:00
Greg Bugyis
748a5eadc0 fix: Token Details - move About section below Market Stats (#4483) 2022-08-26 10:50:55 +03:00
lynn
5659fe21ea fix: fix swap small layout shifts when clicking reverse tokens button (#4503)
fix
2022-08-25 23:57:14 -04:00
lynn
bc899b74a3 feat: make token safety speedbumps appear for tokens with warnings in token selector (#4496)
* checkpoint: token modal safety warning working, showing speedbump

* fix styling

* dont show token safety once user has already ack'd that token

* fix cancel button on token safety - always navigate back to search
2022-08-25 21:20:04 -04:00
Zach Pomerantz
516c8b05a4 feat: add group flag toggles (#4502) 2022-08-25 16:54:15 -07:00
aballerr
8502f9e303 fix: Wallet updates (#4498)
* Wallet Polishing

Co-authored-by: Alex Ball <alexball@UNISWAP-MAC-038.local>
2022-08-25 17:50:38 -04:00
Jordan Frankfurt
bc90d416e6 feat: add background-color transitions to table color changes (#4491)
* feat: add background-color transitions to table color changes:

* pr review--use theme transition info
2022-08-25 16:34:05 -05:00
Charles Bachmeier
2fd1cd72fd fix: bug with network with no info (#4489)
* fix bug with network with no info

* remove unneeded check

* add null checks

* check info for supported case

Co-authored-by: Charlie <charlie@uniswap.org>
2022-08-25 11:32:26 -07:00
github-actions[bot]
3a2276dcd1 chore(i18n): new Crowdin translations (#4478)
* chore(i18n): synchronize translations from crowdin [skip ci]

* empty

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Vignesh Mohankumar <me@vig.xyz>
2022-08-25 14:30:25 -04:00
Vignesh Mohankumar
c432c583f6 fix: rename search to filter tokens (#4485)
* fix: rename search to filter tokens

* translate
2022-08-25 14:04:35 -04:00
Connor McEwen
9b8f5ed8f4 fix: update widget color scheme (#4475)
* fix: update widget color scheme

* Map Uniswap Design System colors to Widget color styles

Co-authored-by: pp-hh-ii-ll <111304124+pp-hh-ii-ll@users.noreply.github.com>
2022-08-25 13:50:46 -04:00
Jack Short
0713a15028 fix: adding explore buttons on loading (#4490) 2022-08-25 13:17:45 -04:00
Charles Bachmeier
62c502615f feat: show searchbar on tablet size (#4484)
show searchbar on tablet size

Co-authored-by: Charlie <charlie@uniswap.org>
2022-08-25 10:00:41 -07:00
Charles Bachmeier
fdbe4b8f5e feat: mobile network switcher (#4486)
* feat: mobile network switcher

* adjust bottom border radius on mobile

Co-authored-by: Charlie <charlie@uniswap.org>
2022-08-25 09:31:47 -07:00
lynn
33c73f4dc8 fix: network balances summary styling (#4487)
init
2022-08-25 09:15:36 -07:00
aballerr
c207a576e7 feat: Sample transitions pr (#4477)
* Updating theming to include transitions


Co-authored-by: Alex Ball <alexball@UNISWAP-MAC-038.local>
2022-08-25 11:25:49 -04:00
Jack Short
aa426514f3 chore: updating navbar padding for token pages (#4474)
* chore: updating navbar padding for token pages

* removing margin on pools page
2022-08-25 11:20:19 -04:00
aballerr
ba9c28892e fix: Fixing token modal warning to match figma design (#4476)
* fixing token modal warning to match figma design


Co-authored-by: Alex Ball <alexball@UNISWAP-MAC-038.local>
2022-08-25 10:06:12 -04:00
Charles Bachmeier
49c31ddfc8 feat: update sprinkles breakpoints (#4480)
update breakpoints

Co-authored-by: Charlie <charlie@uniswap.org>
2022-08-24 19:01:11 -07:00
github-actions[bot]
4424205814 chore(i18n): new Crowdin translations (#4460)
* chore(i18n): synchronize translations from crowdin [skip ci]

* empty

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Vignesh Mohankumar <me@vig.xyz>
2022-08-24 18:04:37 -04:00
lynn
874f3fb737 fix: fix copy button styling (#4473)
* fix copy button styling

* fix positionig

* preserve original css for token safety warning use case
2022-08-24 17:57:25 -04:00
Kaylee George
ac27c89a44 fix: change top tokens query return type (#4453)
* query return

* fix return

* rm default empty string

* rm ?

* undef

* undefined

* null

* fix

* handle search

* .

* rebase

* top tokens

* rm blank

* rm console log

* make 100
2022-08-24 12:38:22 -07:00
aballerr
0e530cf92e fix: added in correct wallet breakpoints (#4472)
* adding in wallet breakpoints

Co-authored-by: Alex Ball <alexball@UNISWAP-MAC-038.local>
2022-08-24 15:16:51 -04:00
Charles Bachmeier
ed66b00b20 feat: update Chain Switcher Modal and Active State (#4471)
* Add unsupported network and active state to chain switcher

* update switcher dropdown modal

* undo

* undo new styles, adjust padding

* update padding

* use supported chain helper fn

Co-authored-by: Charlie <charlie@uniswap.org>
2022-08-24 11:51:38 -07:00
cartcrom
84fb05239b fix: data api loading states and repetitive calls (#4461)
* diagnosing issues
* fixed loading states
* addressed PR comments
* fixed missing symbol issue
* fixed merge conflcit
* fixed uppercase token symbol issue
2022-08-24 14:13:21 -04:00
Vignesh Mohankumar
7500bbc0be fix: don't translate UNI (#4467) 2022-08-24 14:00:20 -04:00
Jack Short
c43c8de6cd chore: updating breakpoints (#4465)
* chore: updating breakpoints

* deprecating old breakpoints

* using breakpoints in theme instead of media queries
2022-08-24 13:43:46 -04:00
Connor McEwen
9d40db5b21 fix: update swap CTA button color (#4464) 2022-08-24 10:42:56 -07:00
Kaylee George
a61eca36ae fix: change toggle and input placeholder colors on swap settings flyout (#4466)
change colors
2022-08-24 10:41:24 -07:00
Kaylee George
60479a442f fix: remove UNI currency logo from swap widget approval button (#4468)
take away logo
2022-08-24 10:41:11 -07:00
Kaylee George
5257188f70 fix: fix token details 'share to twitter' information (#4469)
add twitter correct link
2022-08-24 10:33:19 -07:00
lynn
7599239983 fix: Web 897 redesign general token selector from swap focusing text input has incorrect background color (#4463)
* init

* add search icon
2022-08-24 12:42:17 -04:00
lynn
1561c0d000 feat: fix visual bugs on truncate token description (#4462)
init
2022-08-24 12:28:07 -04:00
Charles Bachmeier
1f740cf8c0 feat: update search icon (#4459)
* feat: update search icon

* re add chevron on mobile

* icon uses currentColor

* icon uses currentColor

Co-authored-by: Charlie <charlie@uniswap.org>
2022-08-24 07:07:12 -07:00
aballerr
55c5f03004 feat: Adding in remaining transactions to transaction history (#4455)
* adding in remaining transactions: Wrap, Collect Fees, Approval and claim rewards

Co-authored-by: Alex Ball <alexball@UNISWAP-MAC-038.local>
2022-08-24 09:59:11 -04:00
Charles Bachmeier
a345cff614 fix: cleanup styles for overflow modal (#4458)
* improvements to overflow modal

* add active state to overflow modal

* fix right aligned models

Co-authored-by: Charlie <charlie@uniswap.org>
2022-08-24 06:32:32 -07:00
lynn
85d8566cfa feat: make currencyInputPanel redesign changes apply only to swap (#4456)
* init: revert to pre redesign swap changes

* differentiate between swapCurrencyInputPanel and regular

* it was inverted whoops

* add padding to fiat row from cal
2022-08-24 02:00:48 -04:00
Charles Bachmeier
44d68e3ef0 fix: /pool position second icon zindex issue (#4457)
fix /pool zindex issue

Co-authored-by: Charlie <charlie@uniswap.org>
2022-08-23 21:05:58 -07:00
Greg Bugyis
04bd4900b0 feat: adds banner section (carousel only) for NFT Explore (#4383)
* NFT Explore: Banner section and carousel

* Fixes from PR feedback

* PR feedback and slight refactor of Carousel Progress indicators

* Only render current collection, and simplify fullWidth class

* Add colors to sprinkles and drop zIndex on bannerContent

* Simplify component structure

* Separate out CarouselIndicator and other cleanup

* Restore CarouselProgress component

* Position carousel progress over bg overlay

Co-authored-by: gbugyis <greg@bugyis.com>
2022-08-24 01:06:54 +03:00
github-actions[bot]
81f277b36f chore(i18n): new Crowdin translations (#4454)
* chore(i18n): synchronize translations from crowdin [skip ci]

* empty

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Vignesh Mohankumar <me@vig.xyz>
2022-08-23 16:47:51 -04:00
Connor McEwen
575660d3e8 fix: respect padding in cells with proper hover (#4451) 2022-08-23 16:41:42 -04:00
lynn
1e692491f1 fix: truncates to token details descriptions (#4450)
* inti

* respond to vm comments

* add paragraphs as per fred, respond vm comments

* respond last vm comment

* simplify

* move fn out of component

* fix messed up styling

* simplify

* simplify

* fred nits

* remove xtra token detail props

* fix sentence casing
2022-08-23 15:25:37 -04:00
Vignesh Mohankumar
b3639b3453 fix: makes SearchBar focus border-width consistent (#4452) 2022-08-23 11:44:01 -07:00
Kaylee George
53ebf37b40 feat: add top tokens query for Explore tokens table (#4448)
* progress

* map??

* idk

* maybe progress?

* more

* hook

* rm favs

* add query

* comment

* get rid of page query

* fix autoimports

* Update TokenRow.tsx
2022-08-23 11:13:35 -07:00
lynn
624ec33652 build: patch vanilla extract to speed up build time (#4447)
patch ve to build

Co-authored-by: Connor McEwen <connor.mcewen@gmail.com>
2022-08-23 12:02:27 -04:00
Charles Bachmeier
2890040118 feat: Fixed light mode styles and added hover states to navbar (#4449)
* cleanup navbar lightmode

* lightmode and hover states

* move magical gradient to common stlyes

* inherit border radius

* hover transition

* further split common gradient style

Co-authored-by: Charlie <charlie@uniswap.org>
2022-08-23 08:30:37 -07:00
aballerr
68db8b3e23 feat: wallet hover styling and moving wallet to correct navbar (#4444)
* updating wallet to have correct hover highlighting and have correct scrolling

Co-authored-by: Alex Ball <alexball@UNISWAP-MAC-038.local>
2022-08-23 11:00:36 -04:00
Vignesh Mohankumar
9873491db1 fix: increases tokens search background contrast (#4443) 2022-08-22 15:17:40 -07:00
Vignesh Mohankumar
5d64ab0146 fix: flag NetworkFilter (#4442)
feat: flag token table network filter
2022-08-22 18:11:22 -04:00
github-actions[bot]
568267ce07 chore(i18n): new Crowdin translations (#4410)
* chore(i18n): synchronize translations from crowdin [skip ci]

* empty

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Vignesh Mohankumar <me@vig.xyz>
2022-08-22 17:29:40 -04:00
Vignesh Mohankumar
69cdefe996 fix: update Swap container padding (#4440)
* fix: update Swap container padding

* rm AlertWrapper

* empty
2022-08-22 17:28:17 -04:00
Charles Bachmeier
61758db589 fix: active state for Token Explore (#4439)
fix active state for Token Explore

Co-authored-by: Charlie <charlie@uniswap.org>
2022-08-22 14:23:34 -07:00
cartcrom
9e9d98bb31 fix: chart size responsiveness issue (#4441)
fixed responsiveness issue
2022-08-22 17:15:24 -04:00
lynn
6b2b771dc4 fix: correct mistaken usdc user property that should be weth (#4438)
fix
2022-08-22 16:33:00 -04:00
pp-hh-ii-ll
031bea0f50 fix: Update outdated colors (#4420)
* Update outdated colors and add shallowShadow

* Update colors.ts

Co-authored-by: Kaylee George <62825936+kayleegeorge@users.noreply.github.com>
2022-08-22 13:20:59 -07:00
Vignesh Mohankumar
6f2e447ec3 chore: remove fortmatic key (#4435)
chore: remove unused fortmatic key
2022-08-22 16:12:55 -04:00
Kaylee George
cf831fbcea refactor: removes flyoutDropShadow, adds shallowShadow (#4437)
flyout drop shadow
2022-08-22 12:58:58 -07:00
lynn
bcd4c1c182 feat: only track usdc, weth, native token balances (#4424)
* init

* fix unit test

* extract log token logic into custom hook
2022-08-22 15:31:28 -04:00
Kaylee George
d954026cea feat: add token details and token row table query (#4419)
* add query

* revert styles

* more style

* rename

* restructure

* fix

* more

* network

* uppercase symbol

* rm unused

* fix

* check nan

* add token row query

* small change

* nan?
2022-08-22 11:52:24 -07:00
Vignesh Mohankumar
2c2dad1415 build: enforce node/npm versions (#4434)
* build: enforce node/npm versions

* enforce yarn usage
2022-08-22 13:55:48 -04:00
Kaylee George
d42ed88845 fix: fix various selected states on Explore filters and update theme colors (#4433)
* fixed

* update more
2022-08-22 10:51:59 -07:00
Kaylee George
e12c00e980 fix: time selector updated to align with price chart (#4425)
* fix time

* displays
2022-08-22 09:37:49 -07:00
Jack Short
c25971e5d2 feat: collection asset cards (#4422)
* removing differentiating mobile and desktop collections

* feat: asset cards

* changed card to module + addressed other comments

* todo
2022-08-22 12:15:17 -04:00
Charles Bachmeier
293e56758c feat: Placeholder NFT Details and NFT Sell Pages (#4431)
* add placeholder pages for nft sell and nft details

* feature flag link in navbar dropdown

Co-authored-by: Charlie <charlie@uniswap.org>
2022-08-22 07:53:11 -07:00
tainguyen-kms
a6b17f0437 fix: Fix unresponsive image in vote page (#4416)
* Fix unresponsive image scale in vote page

* Create renderer function for Image
2022-08-19 23:19:05 -04:00
cartcrom
140d59b898 fix: updated schema (#4423)
updated schema
2022-08-19 17:40:58 -04:00
cartcrom
85742c5785 feat: data api connection (#4396)
* split relay into two environments, refactored schema grabbing, implemented api call for token price graph
2022-08-19 15:31:10 -04:00
aballerr
9b07d8ce64 feat: Wallet v1 transactions part 1 (#4398)
* Adding in tx history for 3 transactions: Swap, Add Liquidity, Remove Liquidty




Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
Co-authored-by: Alex Ball <alexball@UNISWAP-MAC-038.fios-router.home>
Co-authored-by: Alex Ball <alexball@UNISWAP-MAC-038.local>
2022-08-19 14:01:34 -04:00
Charles Bachmeier
b1b9da1b17 feat: enable NFT search results (#4413)
* enable NFT search results

* update usememo deps

* simplify flag trending token logic

* respond to comments

* lowercase bool

* rename flag

* improve truncation

Co-authored-by: Charlie <charlie@uniswap.org>
2022-08-19 09:58:26 -07:00
Vignesh Mohankumar
ffe670923e feat: remove fortmatic (#4167)
* rm fortmatic (wip for test)

* remove isChainAllowed

* handle unexpected connector

* clear on catch

* Revert "remove isChainAllowed"

This reverts commit d505cac39a446cb2c273016d03896469ba39eb60.

* fix to actually use ALLOWED_CHAIN_IDS

* fix
2022-08-19 12:06:14 -04:00
Charles Bachmeier
21649967aa refactor: rename phase1flag to nftflag (#4414)
* rename phase1flag to nftflag

* add new flag file

Co-authored-by: Charlie <charlie@uniswap.org>
2022-08-19 08:41:54 -07:00
lynn
3f40f60c1c fix: fix add liquidity select tokens alignment (#4403)
* init

* only remove this component in add liquidity screen
2022-08-18 23:30:15 -04:00
lynn
176c275a06 feat: Web 640 eng transaction states redesign (#4389)
* init

* pending and failed states

* simplify

* init

* modals

* fix drop shadow logic

* fixes for kaylee comments
2022-08-18 22:49:39 -04:00
Charles Bachmeier
ae2b4b1668 fix: fix skeleton hover state bug (#4409)
* fix skeleton hover state bug

* corretc hover color

* add transition

Co-authored-by: Charlie <charlie@uniswap.org>
2022-08-18 16:10:14 -07:00
Kaylee George
a27f8e2937 feat: add no information available state for token details (#4407)
* skeleton

* fix

* fix layout

* gap
2022-08-18 15:02:44 -07:00
Charles Bachmeier
818b1c84b0 feat: add NFTs tab to navbar (#4408)
add NFT tab to navbar

Co-authored-by: Charlie <charlie@uniswap.org>
2022-08-18 14:40:52 -07:00
Vignesh Mohankumar
75eceaa5e1 fix: Revert "feat: fix padding" (#4406)
Revert "feat: fix padding (#4400)"

This reverts commit 96c23af99c.
2022-08-18 17:36:32 -04:00
Charles Bachmeier
c6b4cc8e01 feat: add phase0 searchbar (#4377)
* feat: add phase0 searchbar

* exhaustive deps

* use router Link'

* use correct navigate for tokens

* useLocation

* add util function for organizing search results

* fix mobile navbar link

* remove exhausted depedencies

* split suggestion rows to their own file

* add new file

* use pathname instead of hash

* use imageholder classname

* fallback update

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2022-08-18 14:19:03 -07:00
Jack Short
819302b51f feat: adding collection stats to collection page (#4391)
* feat: adding collection stats

* removing debounced callback

* addressing comments

* updating marquee and updating isMobile hook

* adding bool to useIsMobile
2022-08-18 16:10:22 -04:00
Kaylee George
c53d7fcc32 fix: fix Explore search bar styling (#4405)
* fix search

* fix responsive

* fix width
2022-08-18 13:06:09 -07:00
lynn
3de2e65530 feat: fix hover color on vote page items (#4404)
init
2022-08-18 15:05:27 -04:00
Kaylee George
c5319b6bea fix: move favorites to right and fix header highlight (#4402)
* change favorite style

* more

* fix header

* fix styling

* small fix
2022-08-18 11:14:43 -07:00
github-actions[bot]
801ddc0886 chore(i18n): new Crowdin translations (#4395)
chore(i18n): synchronize translations from crowdin [skip ci]

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-08-18 11:06:14 -07:00
aballerr
dfd9196aa7 feat: Wallet p0 (#4368)
* P0 Wallet

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
Co-authored-by: Alex Ball <alexball@UNISWAP-MAC-038.fios-router.home>
2022-08-18 13:56:15 -04:00
lynn
c4362297f5 feat: Safety label speedbump: fix copy (#4401)
init
2022-08-18 13:17:22 -04:00
lynn
96c23af99c feat: fix padding (#4400)
fix padding
2022-08-18 12:13:20 -04:00
lynn
6a29dacdeb feat: fix explore and pools titles to match designs. (#4399)
init
2022-08-18 11:46:39 -04:00
lynn
9ddad80f2a feat: fix favorite tokens design (#4397)
fix favorites
2022-08-18 08:24:07 -07:00
Kaylee George
1944fe4029 fix: width of widget and network balances on token details (#4392)
* fix width

* nits

* width var

* nit

* px fix

Co-authored-by: Vignesh Mohankumar <me@vig.xyz>
2022-08-17 19:02:24 -04:00
lynn
9921736102 fix: add missing colon to end of drop shadow color (#4394)
init
2022-08-17 18:43:14 -04:00
lynn
11aa641dbc feat: wallet connect redesign (#4381)
* init

* pending and failed states

* simplify

* fixes in respond to fred nits

* update overlay
2022-08-17 18:15:53 -04:00
Kaylee George
2f3290592b feat: add L2 network label to Explore token row (#4361)
* initial

* add L2 badge

* meh

* png updates

* fix icons

* naming

* nits

* case

* rename

* rm
2022-08-17 17:35:25 -04:00
github-actions[bot]
7b3fe73474 chore(i18n): new Crowdin translations (#4378)
chore(i18n): synchronize translations from crowdin [skip ci]

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-08-17 13:27:37 -07:00
Kaylee George
4237354bb7 chore: rename Explore directory to Tokens (#4388)
* rename

* move file
2022-08-17 15:18:26 -04:00
Jack Short
f7354c9842 refactor: removes BodyWrapper padding (#4375)
* fix: removing bodywrapper padding for phase1

* reverting to previous version and adjusting padding across pages appropriately

* adjusting padding on add liquidity page

* prettier changes

* missed semicolon
2022-08-17 14:35:47 -04:00
Charles Bachmeier
1636786af8 feat: add the phase0 chain switcher (#4376)
* feat: add phase0 chain switcher

* update styles

* add chain switcher files

* remove unneeded eslint disable

* add Celo and remove unneeded null check

* remove old comment

* fix mobile routing

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2022-08-17 11:24:56 -07:00
aballerr
de0a716f41 chore: Usetheme hook fix (#4387)
* fixing hook


Co-authored-by: Alex Ball <alexball@UNISWAP-MAC-038.fios-router.home>
2022-08-17 17:53:46 +00:00
Zach Pomerantz
9f108c406b chore: clean up theme usage (#4335)
* chore: clean up DefaultTheme usage

* chore: simplify useTheme usage

* chore: consolidate multi-line imports
2022-08-17 16:55:58 +00:00
Vignesh Mohankumar
5346d13674 chore: rename flag explore -> tokens (#4386) 2022-08-17 16:43:21 +00:00
Charles Bachmeier
d054079eeb feat: add routing functionality to navbar (#4374)
* feat: add routingfunctionality to navbar

* update colors

* rename and pass strings directly

* rename symbol

* rename props

* better symbol name for mobile link

* more isPoolActive

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2022-08-17 16:31:30 +00:00
Vignesh Mohankumar
fe6324f84d feat: remove v2, v3 swap router (#4372)
* remove v3 router

* rm v2

* unused

* remove anytrade

* unused

* rm shared

* remove optimized trade

* celo fix
2022-08-17 16:05:16 +00:00
Vignesh Mohankumar
4a70eb5956 fix: rename /explore to /tokens (#4385)
* fix: rename /explore to /tokens

* name

* tokens

* oops
2022-08-17 16:05:01 +00:00
Vignesh Mohankumar
1a7b86d155 chore: remove unused exports (#4380) 2022-08-17 11:45:45 -04:00
Vignesh Mohankumar
f66f8c4d59 fix: undo polygon auto-slippage changes (#4379) 2022-08-17 11:37:54 -04:00
Jack Short
1a9c3c3632 feat: add a feature flagged blank collections page (#4371) 2022-08-17 10:42:27 -04:00
cartcrom
91f4892b0c feat: integrate relay (#4320)
* setup relay compiler
* refactored to use polling interval, fixed PR comments
* fixes, readded uninitialized state for liquidity chart
* updated cypress test
* reorganized graphql files into src/graphql
2022-08-16 20:01:12 -04:00
cartcrom
d6d0a98afe fix: chart design fixes & style updates (#4341)
* removed ticks outside of hover
* simplifying copyhelper
* finished implementing fred's feedback
* addressed PR comments
* fixed more of fred's feedback
2022-08-16 19:59:02 -04:00
Kaylee George
8efc5af2bc fix: update feature flag modal design & add headers (#4370)
* initial

* progress

* sike no progress

* design updates

* fix header

* remove colon

* add flag
2022-08-16 19:10:56 +00:00
Zach Pomerantz
104b62f4d8 feat: send web vitals (#4366) 2022-08-16 11:26:15 -07:00
Charles Bachmeier
8e9dbe31fa feat: add a feature flagged blank navbar (#4365)
* add some initial infra for blank navbar

* add blank navbar

* add new files

* use web3status

* remove unused mobile flag

* remove colors

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2022-08-16 17:46:50 +00:00
Jordan Frankfurt
cf0afa01c8 feat(widget): light and dark themes (#4367)
feat(widget): light and dark themes:
2022-08-16 12:20:23 -04:00
Vignesh Mohankumar
c5b67ac60b refactor: deprecate theme.none (#4362) 2022-08-16 09:05:43 -07:00
Charles Bachmeier
110c6fc08f feat: add in common nft components (#4363)
* add in common genie components

* update import and run linter

* better event type handling

* rotate 360

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2022-08-16 05:38:27 -07:00
324 changed files with 30775 additions and 5989 deletions

6
.env
View File

@@ -1,3 +1,7 @@
REACT_APP_INFURA_KEY="4bf032f2d38a4ed6bb975b80d6340847"
REACT_APP_FORTMATIC_KEY="pk_live_357F77728B8EB880"
REACT_APP_AMPLITUDE_TEST_KEY="add-the-real-test-key-if-you-need-to-test-amplitude-events"
REACT_APP_AWS_API_REGION="us-east-2"
REACT_APP_AWS_API_ACCESS_KEY="AKIAYJJWW6AQ47ODATHN"
REACT_APP_AWS_API_ACCESS_SECRET="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"
REACT_APP_AWS_X_API_KEY="z9dReS5UtHu7iTrUsTuWRozLthi3AxOZlvobrIdr14"
REACT_APP_AWS_API_ENDPOINT="https://beta.api.uniswap.org/v1/graphql"

View File

@@ -18,7 +18,6 @@
"src/abis/types",
"src/locales/**/*.js",
"src/locales/**/en-US.po",
"src/state/data/generated.ts",
"node_modules",
"coverage",
"build",

5
.gitignore vendored
View File

@@ -1,12 +1,15 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# generated contract types
/src/types/v3
/src/abis/types
/src/locales/**/*.js
/src/locales/**/en-US.po
/src/locales/**/pseudo.po
/src/state/data/generated.ts
# generated graphql types
__generated__/
# dependencies
/node_modules

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
engine-strict = true

View File

@@ -1 +1 @@
/src/state/data/generated.ts
/src/schema/schema.graphql

View File

@@ -1,11 +1,6 @@
overrideExisting: true
schema: 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3'
documents: 'src/**/!(*.d).{ts,tsx}'
generates:
./src/state/data/generated.ts:
./src/graphql/thegraph/schema/schema.graphql:
plugins:
- typescript
- typescript-operations
- typescript-rtk-query:
importBaseApiFrom: './slice'
exportHooks: true
- schema-ast

View File

@@ -37,8 +37,8 @@ describe('Add Liquidity', () => {
it('loads fee tier distribution', () => {
cy.fixture('feeTierDistribution.json').then((feeTierDistribution) => {
cy.intercept('POST', '/subgraphs/name/uniswap/uniswap-v3', (req: CyHttpMessages.IncomingHttpRequest) => {
if (hasQuery(req, 'feeTierDistribution')) {
req.alias = 'feeTierDistributionQuery'
if (hasQuery(req, 'FeeTierDistributionQuery')) {
req.alias = 'FeeTierDistributionQuery'
req.reply({
body: {
@@ -55,7 +55,7 @@ describe('Add Liquidity', () => {
cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85/0xc778417E063141139Fce010982780140Aa0cD5Ab')
cy.wait('@feeTierDistributionQuery')
cy.wait('@FeeTierDistributionQuery')
cy.get('#add-liquidity-selected-fee .selected-fee-label').should('contain.text', '0.3% fee tier')
cy.get('#add-liquidity-selected-fee .selected-fee-percentage').should('contain.text', '40%')

18
fetch-schema.js Normal file
View File

@@ -0,0 +1,18 @@
/* eslint-disable */
require('dotenv').config({ path: '.env.local' })
const { exec } = require('child_process')
const dataConfig = require('./relay.config')
const thegraphConfig = require('./relay_thegraph.config')
/* eslint-enable */
const THEGRAPH_API_URL = 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3'
exec(`get-graphql-schema ${THEGRAPH_API_URL} > ${thegraphConfig.schema}`)
const API_URL = process.env.REACT_APP_GQL_API_URL
const API_KEY = process.env.REACT_APP_GQL_API_KEY
if (API_URL && API_KEY) {
exec(`get-graphql-schema ${API_URL} --h X-API-KEY=${API_KEY} > ${dataConfig.schema}`)
} else {
console.log('REACT_APP_GQL_API_URL or REACT_APP_GQL_API_KEY is missing from env.local')
}

View File

@@ -8,7 +8,10 @@
"contracts:compile:abi": "typechain --target ethers-v5 --out-dir src/abis/types \"./src/abis/**/*.json\"",
"contracts:compile:v3": "typechain --target ethers-v5 --out-dir src/types/v3 \"./node_modules/@uniswap/**/artifacts/contracts/**/*[!dbg].json\"",
"contracts:compile": "yarn contracts:compile:abi && yarn contracts:compile:v3",
"graphql:generate": "graphql-codegen --config codegen.yml",
"relay": "relay-compiler relay.config.js",
"relay-thegraph": "relay-compiler relay_thegraph.config.js",
"graphql:fetch": "node fetch-schema.js",
"graphql:generate": "yarn relay && yarn relay-thegraph",
"prei18n:extract": "node prei18n-extract.js",
"i18n:extract": "lingui extract --locale en-US",
"i18n:compile": "yarn i18n:extract && lingui compile",
@@ -20,7 +23,8 @@
"lint": "yarn eslint .",
"test": "craco test --coverage",
"cypress:open": "cypress open --browser chrome --e2e",
"cypress:run": "cypress run --browser chrome --e2e"
"cypress:run": "cypress run --browser chrome --e2e",
"postinstall": "patch-package"
},
"jest": {
"collectCoverageFrom": [
@@ -57,10 +61,6 @@
"devDependencies": {
"@craco/craco": "6.4.3",
"@ethersproject/experimental": "^5.4.0",
"@graphql-codegen/cli": "1.21.5",
"@graphql-codegen/typescript": "1.22.3",
"@graphql-codegen/typescript-operations": "^1.18.2",
"@graphql-codegen/typescript-rtk-query": "^1.1.1",
"@lingui/cli": "^3.9.0",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.1",
@@ -92,6 +92,7 @@
"@typescript-eslint/parser": "^4",
"@vanilla-extract/babel-plugin": "^1.1.7",
"@vanilla-extract/webpack-plugin": "^2.1.11",
"babel-plugin-relay": "^14.1.0",
"cypress": "^10.3.1",
"env-cmd": "^10.1.0",
"eslint": "^7.11.0",
@@ -104,8 +105,11 @@
"eslint-plugin-unused-imports": "^2.0.0",
"jest-styled-components": "^7.0.8",
"ms.macro": "^2.0.0",
"patch-package": "^6.4.7",
"postinstall-postinstall": "^2.1.0",
"prettier": "^2.7.1",
"react-scripts": "^4.0.3",
"relay-compiler": "^14.1.0",
"serve": "^11.3.2",
"typechain": "^5.0.0",
"typescript": "^4.4.3"
@@ -126,6 +130,7 @@
"@reach/portal": "^0.10.3",
"@react-hook/window-scroll": "^1.3.0",
"@reduxjs/toolkit": "^1.6.1",
"@types/react-relay": "^13.0.2",
"@uniswap/governance": "^1.0.2",
"@uniswap/liquidity-staker": "^1.0.2",
"@uniswap/merkle-distributor": "1.0.1",
@@ -140,7 +145,7 @@
"@uniswap/v3-core": "1.0.0",
"@uniswap/v3-periphery": "^1.1.1",
"@uniswap/v3-sdk": "^3.9.0",
"@uniswap/widgets": "^2.1.1",
"@uniswap/widgets": "^2.5.0",
"@vanilla-extract/css": "^1.7.2",
"@vanilla-extract/css-utils": "^0.1.2",
"@vanilla-extract/dynamic": "^2.0.2",
@@ -165,16 +170,16 @@
"ajv": "^6.12.3",
"array.prototype.flat": "^1.2.4",
"array.prototype.flatmap": "^1.2.4",
"aws4fetch": "^1.0.13",
"cids": "^1.0.0",
"clsx": "^1.1.1",
"copy-to-clipboard": "^3.2.0",
"d3": "^7.6.1",
"d3-curve-circlecorners": "^0.1.6",
"ethers": "^5.1.4",
"firebase": "^9.1.3",
"focus-visible": "^5.2.0",
"fortmatic": "^2.4.0",
"graphql": "^15.5.0",
"get-graphql-schema": "^2.1.2",
"graphql": "^16.5.0",
"graphql-request": "^3.4.0",
"immer": "^9.0.6",
"inter-ui": "^3.13.1",
@@ -194,11 +199,13 @@
"react-dom": "^18.2.0",
"react-feather": "^2.0.8",
"react-ga4": "^1.4.1",
"react-infinite-scroll-component": "^6.1.0",
"react-is": "^17.0.2",
"react-markdown": "^4.3.1",
"react-popper": "^2.2.3",
"react-query": "^3.39.1",
"react-redux": "^8.0.2",
"react-relay": "^14.1.0",
"react-router-dom": "^6.3.0",
"react-spring": "^8.0.27",
"react-table": "^7.8.0",
@@ -223,5 +230,10 @@
"workbox-precaching": "^6.1.0",
"workbox-routing": "^6.1.0",
"zustand": "^4.0.0-rc.1"
},
"engines": {
"npm": "please-use-yarn",
"node": "14",
"yarn": ">=1.22"
}
}

View File

@@ -0,0 +1,22 @@
diff --git a/node_modules/@vanilla-extract/css/dist/vanilla-extract-css.cjs.dev.js b/node_modules/@vanilla-extract/css/dist/vanilla-extract-css.cjs.dev.js
index 6e40061..10283a2 100644
--- a/node_modules/@vanilla-extract/css/dist/vanilla-extract-css.cjs.dev.js
+++ b/node_modules/@vanilla-extract/css/dist/vanilla-extract-css.cjs.dev.js
@@ -177,11 +177,13 @@ function generateIdentifier(debugId) {
var fileScopeHash = hash__default["default"](packageName ? "".concat(packageName).concat(filePath) : filePath);
var identifier = "".concat(fileScopeHash).concat(refCount);
- if (adapter_dist_vanillaExtractCssAdapter.getIdentOption() === 'debug') {
- var devPrefix = getDevPrefix(debugId);
+ if (process.env.VANILLA_EXTRACT_DEV_PREFIX) {
+ if (adapter_dist_vanillaExtractCssAdapter.getIdentOption() === 'debug') {
+ var devPrefix = getDevPrefix(debugId);
- if (devPrefix) {
- identifier = "".concat(devPrefix, "__").concat(identifier);
+ if (devPrefix) {
+ identifier = "".concat(devPrefix, "__").concat(identifier);
+ }
}
}

View File

@@ -1,5 +1,5 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
const exec = require('child_process').exec
const { exec } = require('child_process')
const isWindows = process.platform === 'win32' || /^(msys|cygwin)$/.test(process.env.OSTYPE)
if (isWindows) {

View File

@@ -16,7 +16,6 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="theme-color" content="#ff007a" />
<meta name="fortmatic-site-verification" content="j93LgcVZk79qcgyo" />
<!--
manifest.json provides metadata used when the app is installed as a PWA.

6
relay.config.js Normal file
View File

@@ -0,0 +1,6 @@
module.exports = {
src: './src',
language: 'typescript',
schema: './src/graphql/data/schema.graphql',
exclude: ['**/node_modules/**', '**/__mocks__/**', '**/__generated__/**', '**/thegraph/**'],
}

9
relay_thegraph.config.js Normal file
View File

@@ -0,0 +1,9 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
const defaultConfig = require('./relay.config')
module.exports = {
src: defaultConfig.src,
language: defaultConfig.language,
schema: './src/graphql/thegraph/schema.graphql',
exclude: ['**/node_modules/**', '**/__mocks__/**', '**/__generated__/**', '**/data/**'],
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

3
src/assets/svg/share.svg Normal file
View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="#fff">
<path stroke-linecap="round" stroke-linejoin="round" d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4m14-7-5-5-5 5m5-5v12"/>
</svg>

After

Width:  |  Height:  |  Size: 257 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#99A1BD" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-x"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-x"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>

Before

Width:  |  Height:  |  Size: 294 B

After

Width:  |  Height:  |  Size: 300 B

View File

@@ -1,64 +0,0 @@
import { Trans } from '@lingui/macro'
import useCopyClipboard from 'hooks/useCopyClipboard'
import React, { useCallback } from 'react'
import { CheckCircle, Copy } from 'react-feather'
import styled from 'styled-components/macro'
import { LinkStyledButton } from 'theme'
const CopyIcon = styled(LinkStyledButton)`
color: ${({ color, theme }) => color || theme.accentAction};
flex-shrink: 0;
display: flex;
text-decoration: none;
:hover,
:active,
:focus {
text-decoration: none;
color: ${({ color, theme }) => color || theme.accentAction};
}
`
const StyledText = styled.span`
margin-left: 0.25rem;
${({ theme }) => theme.flexRowNoWrap};
align-items: center;
`
const Copied = ({ iconSize }: { iconSize?: number }) => (
<StyledText>
<CheckCircle size={iconSize ?? '16'} />
<StyledText>
<Trans>Copied</Trans>
</StyledText>
</StyledText>
)
const Icon = ({ iconSize }: { iconSize?: number }) => (
<StyledText>
<Copy size={iconSize ?? '16'} />
</StyledText>
)
interface BaseProps {
toCopy: string
color?: string
iconSize?: number
iconPosition?: 'left' | 'right'
}
export type CopyHelperProps = BaseProps & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, keyof BaseProps>
export default function CopyHelper({ color, toCopy, children, iconSize, iconPosition }: CopyHelperProps) {
const [isCopied, setCopied] = useCopyClipboard()
const copy = useCallback(() => {
setCopied(toCopy)
}, [toCopy, setCopied])
return (
<CopyIcon onClick={copy} color={color}>
{iconPosition === 'left' ? isCopied ? <Copied iconSize={iconSize} /> : <Icon iconSize={iconSize} /> : null}
{iconPosition === 'left' && <>&nbsp;</>}
{isCopied ? '' : children}
{iconPosition === 'right' && <>&nbsp;</>}
{iconPosition === 'right' ? isCopied ? <Copied iconSize={iconSize} /> : <Icon iconSize={iconSize} /> : null}
</CopyIcon>
)
}

View File

@@ -1,19 +1,17 @@
import { Trans } from '@lingui/macro'
import { useWeb3React } from '@web3-react/core'
import CopyHelper from 'components/AccountDetails/Copy'
import { getConnection, getConnectionName, getIsCoinbaseWallet, getIsMetaMask } from 'connection/utils'
import { Context, useCallback, useContext } from 'react'
import { useCallback } from 'react'
import { ExternalLink as LinkIcon } from 'react-feather'
import { useAppDispatch } from 'state/hooks'
import { updateSelectedWallet } from 'state/user/reducer'
import { removeConnectedWallet } from 'state/wallets/reducer'
import { DefaultTheme } from 'styled-components/macro'
import styled, { ThemeContext } from 'styled-components/macro'
import styled, { useTheme } from 'styled-components/macro'
import { isMobile } from 'utils/userAgent'
import { ReactComponent as Close } from '../../assets/images/x.svg'
import { clearAllTransactions } from '../../state/transactions/reducer'
import { ExternalLink, LinkStyledButton, ThemedText } from '../../theme'
import { CopyHelper, ExternalLink, LinkStyledButton, ThemedText } from '../../theme'
import { shortenAddress } from '../../utils'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { ButtonSecondary } from '../Button'
@@ -26,7 +24,7 @@ const HeaderRow = styled.div`
padding: 1rem 1rem;
font-weight: 500;
color: ${(props) => (props.color === 'blue' ? ({ theme }) => theme.deprecated_primary1 : 'inherit')};
${({ theme }) => theme.mediaWidth.upToMedium`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
padding: 1rem;
`};
`
@@ -76,7 +74,7 @@ const AccountGroupingRow = styled.div`
const AccountSection = styled.div`
padding: 0rem 1rem;
${({ theme }) => theme.mediaWidth.upToMedium`padding: 0rem 1rem 1.5rem 1rem;`};
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`padding: 0rem 1rem 1.5rem 1rem;`};
`
const YourAccount = styled.div`
@@ -130,11 +128,12 @@ const AccountControl = styled.div`
`
const AddressLink = styled(ExternalLink)`
font-size: 0.825rem;
color: ${({ theme }) => theme.deprecated_text3};
margin-left: 1rem;
font-size: 0.825rem;
display: flex;
gap: 6px;
text-decoration: none !important;
:hover {
color: ${({ theme }) => theme.deprecated_text2};
}
@@ -207,7 +206,7 @@ export default function AccountDetails({
const { chainId, account, connector } = useWeb3React()
const connectionType = getConnection(connector).type
const theme = useContext(ThemeContext as Context<DefaultTheme>)
const theme = useTheme()
const dispatch = useAppDispatch()
const isMetaMask = getIsMetaMask()
@@ -284,7 +283,7 @@ export default function AccountDetails({
<AccountControl>
<div>
{account && (
<CopyHelper toCopy={account} iconPosition="left">
<CopyHelper toCopy={account} gap={6} iconSize={16} fontSize={14}>
<Trans>Copy Address</Trans>
</CopyHelper>
)}

View File

@@ -0,0 +1,88 @@
import { useWeb3React } from '@web3-react/core'
import { UNI_ADDRESS } from 'constants/addresses'
import { TransactionInfo, TransactionType } from 'state/transactions/types'
import styled, { css } from 'styled-components/macro'
import { nativeOnChain } from '../../constants/tokens'
import { useCurrency } from '../../hooks/Tokens'
import CurrencyLogo from '../CurrencyLogo'
const CurrencyWrap = styled.div`
position: relative;
width: 36px;
height: 36px;
`
const CurrencyWrapStyles = css`
position: absolute;
height: 24px;
`
const CurrencyLogoWrap = styled.span<{ isCentered: boolean }>`
${CurrencyWrapStyles};
left: ${({ isCentered }) => (isCentered ? '50%' : '0')};
top: ${({ isCentered }) => (isCentered ? '50%' : '0')};
transform: ${({ isCentered }) => isCentered && 'translate(-50%, -50%)'};
`
const CurrencyLogoWrapTwo = styled.span`
${CurrencyWrapStyles};
bottom: 0px;
right: 0px;
`
interface CurrencyPair {
currencyId0: string | undefined
currencyId1: string | undefined
}
const getCurrency = ({ info, chainId }: { info: TransactionInfo; chainId: number | undefined }): CurrencyPair => {
switch (info.type) {
case TransactionType.ADD_LIQUIDITY_V3_POOL:
case TransactionType.REMOVE_LIQUIDITY_V3:
case TransactionType.CREATE_V3_POOL:
const { baseCurrencyId, quoteCurrencyId } = info
return { currencyId0: baseCurrencyId, currencyId1: quoteCurrencyId }
case TransactionType.SWAP:
const { inputCurrencyId, outputCurrencyId } = info
return { currencyId0: inputCurrencyId, currencyId1: outputCurrencyId }
case TransactionType.WRAP:
const { unwrapped } = info
const native = info.chainId ? nativeOnChain(info.chainId) : undefined
const base = 'ETH'
const wrappedCurrency = native?.wrapped.address ?? 'WETH'
return { currencyId0: unwrapped ? wrappedCurrency : base, currencyId1: unwrapped ? base : wrappedCurrency }
case TransactionType.COLLECT_FEES:
const { currencyId0, currencyId1 } = info
return { currencyId0, currencyId1 }
case TransactionType.APPROVAL:
return { currencyId0: info.tokenAddress, currencyId1: undefined }
case TransactionType.CLAIM:
const uniAddress = chainId ? UNI_ADDRESS[chainId] : undefined
return { currencyId0: uniAddress, currencyId1: undefined }
default:
return { currencyId0: undefined, currencyId1: undefined }
}
}
const LogoView = ({ info }: { info: TransactionInfo }) => {
const { chainId } = useWeb3React()
const { currencyId0, currencyId1 } = getCurrency({ info, chainId })
const currency0 = useCurrency(currencyId0)
const currency1 = useCurrency(currencyId1)
const isCentered = !(currency0 && currency1)
return (
<CurrencyWrap>
<CurrencyLogoWrap isCentered={isCentered}>
<CurrencyLogo size="24px" currency={currency0} />
</CurrencyLogoWrap>
{!isCentered && (
<CurrencyLogoWrapTwo>
<CurrencyLogo size="24px" currency={currency1} />
</CurrencyLogoWrapTwo>
)}
</CurrencyWrap>
)
}
export default LogoView

View File

@@ -0,0 +1,337 @@
import { Trans } from '@lingui/macro'
import { Fraction, TradeType } from '@uniswap/sdk-core'
import JSBI from 'jsbi'
import {
AddLiquidityV3PoolTransactionInfo,
ApproveTransactionInfo,
ClaimTransactionInfo,
CollectFeesTransactionInfo,
ExactInputSwapTransactionInfo,
ExactOutputSwapTransactionInfo,
RemoveLiquidityV3TransactionInfo,
TransactionInfo,
TransactionType,
WrapTransactionInfo,
} from 'state/transactions/types'
import styled from 'styled-components/macro'
import { nativeOnChain } from '../../constants/tokens'
import { useCurrency, useToken } from '../../hooks/Tokens'
import useENSName from '../../hooks/useENSName'
import { shortenAddress } from '../../utils'
import { TransactionState } from './index'
const HighlightText = styled.span`
color: ${({ theme }) => theme.textPrimary};
font-weight: 600;
`
const BodyWrap = styled.div`
line-height: 20px;
`
interface ActionProps {
pending: JSX.Element
success: JSX.Element
failed: JSX.Element
transactionState: TransactionState
}
const Action = ({ pending, success, failed, transactionState }: ActionProps) => {
switch (transactionState) {
case TransactionState.Failed:
return failed
case TransactionState.Success:
return success
default:
return pending
}
}
const formatAmount = (amountRaw: string, decimals: number, sigFigs: number): string =>
new Fraction(amountRaw, JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(decimals))).toSignificant(sigFigs)
const FailedText = ({ transactionState }: { transactionState: TransactionState }) =>
transactionState === TransactionState.Failed ? <Trans>failed</Trans> : <span />
const FormattedCurrencyAmount = ({
rawAmount,
currencyId,
sigFigs = 2,
}: {
rawAmount: string
currencyId: string
sigFigs: number
}) => {
const currency = useCurrency(currencyId)
return currency ? (
<HighlightText>
{formatAmount(rawAmount, currency.decimals, sigFigs)} {currency.symbol}
</HighlightText>
) : null
}
const getRawAmounts = (
info: ExactInputSwapTransactionInfo | ExactOutputSwapTransactionInfo
): { rawAmountFrom: string; rawAmountTo: string } => {
return info.tradeType === TradeType.EXACT_INPUT
? { rawAmountFrom: info.inputCurrencyAmountRaw, rawAmountTo: info.expectedOutputCurrencyAmountRaw }
: { rawAmountFrom: info.expectedInputCurrencyAmountRaw, rawAmountTo: info.outputCurrencyAmountRaw }
}
const SwapSummary = ({
info,
transactionState,
}: {
info: ExactInputSwapTransactionInfo | ExactOutputSwapTransactionInfo
transactionState: TransactionState
}) => {
const actionProps = {
transactionState,
pending: <Trans>Swapping</Trans>,
success: <Trans>Swapped</Trans>,
failed: <Trans>Swap</Trans>,
}
const { rawAmountFrom, rawAmountTo } = getRawAmounts(info)
return (
<BodyWrap>
<Action {...actionProps} />{' '}
<FormattedCurrencyAmount rawAmount={rawAmountFrom} currencyId={info.inputCurrencyId} sigFigs={2} />{' '}
<Trans>for </Trans>{' '}
<FormattedCurrencyAmount rawAmount={rawAmountTo} currencyId={info.outputCurrencyId} sigFigs={2} />{' '}
<FailedText transactionState={transactionState} />
</BodyWrap>
)
}
const AddLiquidityV3PoolSummary = ({
info,
transactionState,
}: {
info: AddLiquidityV3PoolTransactionInfo
transactionState: TransactionState
}) => {
const { createPool, quoteCurrencyId, baseCurrencyId } = info
const actionProps = {
transactionState,
pending: <Trans>Adding</Trans>,
success: <Trans>Added</Trans>,
failed: <Trans>Add</Trans>,
}
return (
<BodyWrap>
{createPool ? (
<CreateV3PoolSummary info={info} transactionState={transactionState} />
) : (
<>
<Action {...actionProps} />{' '}
<FormattedCurrencyAmount rawAmount={info.expectedAmountBaseRaw} currencyId={baseCurrencyId} sigFigs={2} />{' '}
<Trans>and</Trans>{' '}
<FormattedCurrencyAmount rawAmount={info.expectedAmountQuoteRaw} currencyId={quoteCurrencyId} sigFigs={2} />
</>
)}{' '}
<FailedText transactionState={transactionState} />
</BodyWrap>
)
}
const RemoveLiquidityV3Summary = ({
info: { baseCurrencyId, quoteCurrencyId, expectedAmountBaseRaw, expectedAmountQuoteRaw },
transactionState,
}: {
info: RemoveLiquidityV3TransactionInfo
transactionState: TransactionState
}) => {
const actionProps = {
transactionState,
pending: <Trans>Removing</Trans>,
success: <Trans>Removed</Trans>,
failed: <Trans>Remove</Trans>,
}
return (
<BodyWrap>
<Action {...actionProps} />{' '}
<FormattedCurrencyAmount rawAmount={expectedAmountBaseRaw} currencyId={baseCurrencyId} sigFigs={2} />{' '}
<Trans>and</Trans>{' '}
<FormattedCurrencyAmount rawAmount={expectedAmountQuoteRaw} currencyId={quoteCurrencyId} sigFigs={2} />{' '}
<FailedText transactionState={transactionState} />
</BodyWrap>
)
}
const CreateV3PoolSummary = ({
info: { baseCurrencyId, quoteCurrencyId },
transactionState,
}: {
info: AddLiquidityV3PoolTransactionInfo
transactionState: TransactionState
}) => {
const baseCurrency = useCurrency(baseCurrencyId)
const quoteCurrency = useCurrency(quoteCurrencyId)
const actionProps = {
transactionState,
pending: <Trans>Creating</Trans>,
success: <Trans>Created</Trans>,
failed: <Trans>Create</Trans>,
}
return (
<BodyWrap>
<Action {...actionProps} />{' '}
<HighlightText>
{baseCurrency?.symbol}/{quoteCurrency?.symbol}{' '}
</HighlightText>
<Trans>Pool</Trans> <FailedText transactionState={transactionState} />
</BodyWrap>
)
}
const CollectFeesSummary = ({
info,
transactionState,
}: {
info: CollectFeesTransactionInfo
transactionState: TransactionState
}) => {
const { currencyId0, expectedCurrencyOwed0 = '0', expectedCurrencyOwed1 = '0', currencyId1 } = info
const actionProps = {
transactionState,
pending: <Trans>Collecting</Trans>,
success: <Trans>Collected</Trans>,
failed: <Trans>Collect</Trans>,
}
return (
<BodyWrap>
<Action {...actionProps} />{' '}
<FormattedCurrencyAmount rawAmount={expectedCurrencyOwed0} currencyId={currencyId0} sigFigs={2} />{' '}
<Trans>and</Trans>{' '}
<FormattedCurrencyAmount rawAmount={expectedCurrencyOwed1} currencyId={currencyId1} sigFigs={2} />{' '}
<Trans>fees</Trans> <FailedText transactionState={transactionState} />
</BodyWrap>
)
}
const ApprovalSummary = ({
info,
transactionState,
}: {
info: ApproveTransactionInfo
transactionState: TransactionState
}) => {
const token = useToken(info.tokenAddress)
const actionProps = {
transactionState,
pending: <Trans>Approving</Trans>,
success: <Trans>Approved</Trans>,
failed: <Trans>Approve</Trans>,
}
return (
<BodyWrap>
<Action {...actionProps} /> <HighlightText>{token?.symbol}</HighlightText>{' '}
<FailedText transactionState={transactionState} />
</BodyWrap>
)
}
const ClaimSummary = ({
info: { recipient, uniAmountRaw },
transactionState,
}: {
info: ClaimTransactionInfo
transactionState: TransactionState
}) => {
const { ENSName } = useENSName()
const actionProps = {
transactionState,
pending: <Trans>Claiming</Trans>,
success: <Trans>Claimed</Trans>,
failed: <Trans>Claim</Trans>,
}
return (
<BodyWrap>
{uniAmountRaw && (
<>
<Action {...actionProps} />{' '}
<HighlightText>
{formatAmount(uniAmountRaw, 18, 4)}
UNI{' '}
</HighlightText>{' '}
<Trans>for</Trans> <HighlightText>{ENSName ?? shortenAddress(recipient)}</HighlightText>
</>
)}{' '}
<FailedText transactionState={transactionState} />
</BodyWrap>
)
}
const WrapSummary = ({
info: { chainId, currencyAmountRaw, unwrapped },
transactionState,
}: {
info: WrapTransactionInfo
transactionState: TransactionState
}) => {
const native = chainId ? nativeOnChain(chainId) : undefined
const from = unwrapped ? native?.wrapped.symbol ?? 'WETH' : native?.symbol ?? 'ETH'
const to = unwrapped ? native?.symbol ?? 'ETH' : native?.wrapped.symbol ?? 'WETH'
const amount = formatAmount(currencyAmountRaw, 18, 6)
const actionProps = unwrapped
? {
transactionState,
pending: <Trans>Unwrapping</Trans>,
success: <Trans>Unwrapped</Trans>,
failed: <Trans>Unwrap</Trans>,
}
: {
transactionState,
pending: <Trans>Wrapping</Trans>,
success: <Trans>Wrapped</Trans>,
failed: <Trans>Wrap</Trans>,
}
return (
<BodyWrap>
<Action {...actionProps} />{' '}
<HighlightText>
{amount} {from}
</HighlightText>{' '}
<Trans>to</Trans>{' '}
<HighlightText>
{amount} {to}
</HighlightText>{' '}
<FailedText transactionState={transactionState} />
</BodyWrap>
)
}
const TransactionBody = ({ info, transactionState }: { info: TransactionInfo; transactionState: TransactionState }) => {
switch (info.type) {
case TransactionType.SWAP:
return <SwapSummary info={info} transactionState={transactionState} />
case TransactionType.ADD_LIQUIDITY_V3_POOL:
return <AddLiquidityV3PoolSummary info={info} transactionState={transactionState} />
case TransactionType.REMOVE_LIQUIDITY_V3:
return <RemoveLiquidityV3Summary info={info} transactionState={transactionState} />
case TransactionType.WRAP:
return <WrapSummary info={info} transactionState={transactionState} />
case TransactionType.COLLECT_FEES:
return <CollectFeesSummary info={info} transactionState={transactionState} />
case TransactionType.APPROVAL:
return <ApprovalSummary info={info} transactionState={transactionState} />
case TransactionType.CLAIM:
return <ClaimSummary info={info} transactionState={transactionState} />
default:
return <span />
}
}
export default TransactionBody

View File

@@ -0,0 +1,90 @@
import { useWeb3React } from '@web3-react/core'
import { getChainInfoOrDefault } from 'constants/chainInfo'
import { SupportedChainId } from 'constants/chains'
import { useMemo } from 'react'
import { AlertTriangle, CheckCircle } from 'react-feather'
import styled from 'styled-components/macro'
import { colors } from 'theme/colors'
import { TransactionDetails } from '../../state/transactions/types'
import Loader from '../Loader'
import LogoView from './LogoView'
import TransactionBody from './TransactionBody'
export enum TransactionState {
Pending,
Success,
Failed,
}
const Grid = styled.a`
cursor: pointer;
display: grid;
grid-template-columns: 44px auto 24px;
width: 100%;
text-decoration: none;
border-bottom: ${({ theme }) => `1px solid ${theme.backgroundOutline}`};
padding: 12px;
&:hover {
background-color: ${({ theme }) => theme.backgroundModule};
transition: 250ms background-color ease;
}
`
const TextContainer = styled.span`
font-size: 14px;
margin-top: auto;
margin-bottom: auto;
color: ${({ theme }) => theme.textTertiary};
`
const IconStyleWrap = styled.span`
margin-top: auto;
margin-bottom: auto;
margin-left: auto;
height: 16px;
`
export const TransactionSummary = ({ transactionDetails }: { transactionDetails: TransactionDetails }) => {
const { chainId = 1 } = useWeb3React()
const tx = transactionDetails
const { explorer } = getChainInfoOrDefault(chainId ? chainId : SupportedChainId.MAINNET)
const { info, receipt, hash } = tx
const transactionState = useMemo(() => {
const pending = !receipt
const success = !pending && tx && (receipt?.status === 1 || typeof receipt?.status === 'undefined')
const transactionState = pending
? TransactionState.Pending
: success
? TransactionState.Success
: TransactionState.Failed
return transactionState
}, [receipt, tx])
const link = `${explorer}tx/${hash}`
return chainId ? (
<Grid href={link} target="_blank">
<LogoView info={info} />
<TextContainer as="span">
<TransactionBody info={info} transactionState={transactionState} />
</TextContainer>
{transactionState === TransactionState.Pending ? (
<IconStyleWrap>
<Loader />
</IconStyleWrap>
) : transactionState === TransactionState.Success ? (
<IconStyleWrap>
<CheckCircle color={colors.green200} size="16px" />
</IconStyleWrap>
) : (
<IconStyleWrap>
<AlertTriangle color={colors.gold200} size="16px" />
</IconStyleWrap>
)}
</Grid>
) : null
}

View File

@@ -2,8 +2,8 @@ import { Trans } from '@lingui/macro'
// eslint-disable-next-line no-restricted-imports
import { t } from '@lingui/macro'
import { useWeb3React } from '@web3-react/core'
import { ChangeEvent, Context, ReactNode, useCallback, useContext } from 'react'
import styled, { DefaultTheme, ThemeContext } from 'styled-components/macro'
import { ChangeEvent, ReactNode, useCallback } from 'react'
import styled, { useTheme } from 'styled-components/macro'
import useENS from '../../hooks/useENS'
import { ExternalLink, ThemedText } from '../../theme'
@@ -87,7 +87,7 @@ export default function AddressInputPanel({
onChange: (value: string) => void
}) {
const { chainId } = useWeb3React()
const theme = useContext(ThemeContext as Context<DefaultTheme>)
const theme = useTheme()
const { address, loading, name } = useENS(value)

View File

@@ -23,6 +23,7 @@ export enum EventName {
TOKEN_SELECTOR_OPENED = 'Token Selector Opened',
WALLET_CONNECT_TXN_COMPLETED = 'Wallet Connect Transaction Completed',
WALLET_SELECTED = 'Wallet Selected',
WEB_VITALS = 'Web Vitals',
WRAP_TOKEN_TXN_SUBMITTED = 'Wrap Token Transaction Submitted',
// alphabetize additional event names.
}
@@ -36,16 +37,9 @@ export enum CUSTOM_USER_PROPERTIES {
SCREEN_RESOLUTION_HEIGHT = 'screen_resolution_height',
SCREEN_RESOLUTION_WIDTH = 'screen_resolution_width',
WALLET_ADDRESS = 'wallet_address',
WALLET_NATIVE_CURRENCY_BALANCE_USD = 'wallet_native_currency_balance_usd',
WALLET_TOKENS_ADDRESSES = 'wallet_tokens_addresses',
WALLET_TOKENS_SYMBOLS = 'wallet_tokens_symbols',
WALLET_TYPE = 'wallet_type',
}
export enum CUSTOM_USER_PROPERTY_SUFFIXES {
WALLET_TOKEN_AMOUNT_SUFFIX = '_token_amount',
}
export enum BROWSER {
FIREFOX = 'Mozilla Firefox',
SAMSUNG = 'Samsung Internet',
@@ -74,7 +68,7 @@ export enum SWAP_PRICE_UPDATE_USER_RESPONSE {
* Known pages in the app. Highest order context.
*/
export enum PageName {
EXPLORE_PAGE = 'explore-page',
TOKENS_PAGE = 'tokens-page',
POOL_PAGE = 'pool-page',
SWAP_PAGE = 'swap-page',
VOTE_PAGE = 'vote-page',

View File

@@ -1,8 +1,7 @@
import useTheme from 'hooks/useTheme'
import { darken } from 'polished'
import { Check, ChevronDown } from 'react-feather'
import { Button as RebassButton, ButtonProps as ButtonPropsOriginal } from 'rebass/styled-components'
import styled from 'styled-components/macro'
import styled, { useTheme } from 'styled-components/macro'
import { RowBetween } from '../Row'
@@ -51,9 +50,12 @@ export const BaseButton = styled(RebassButton)<
}
`
export const ButtonPrimary = styled(BaseButton)`
background-color: ${({ theme }) => theme.deprecated_primary1};
color: white;
export const ButtonPrimary = styled(BaseButton)<{ redesignFlag?: boolean }>`
background-color: ${({ theme, redesignFlag }) => (redesignFlag ? theme.accentAction : theme.deprecated_primary1)};
font-size: ${({ redesignFlag }) => redesignFlag && '20px'};
font-weight: ${({ redesignFlag }) => redesignFlag && '600'};
padding: ${({ redesignFlag }) => redesignFlag && '16px'};
color: ${({ theme, redesignFlag }) => (redesignFlag ? theme.accentTextLightPrimary : 'white')};
&:focus {
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, theme.deprecated_primary1)};
background-color: ${({ theme }) => darken(0.05, theme.deprecated_primary1)};
@@ -105,7 +107,7 @@ export const ButtonLight = styled(BaseButton)<{ redesignFlag?: boolean }>`
opacity: 0.4;
:hover {
cursor: auto;
background-color: ${({ theme, redesignFlag }) => (redesignFlag ? theme.none : theme.deprecated_primary5)};
background-color: ${({ theme, redesignFlag }) => (redesignFlag ? 'transparent' : theme.deprecated_primary5)};
box-shadow: none;
border: 1px solid transparent;
outline: none;
@@ -174,23 +176,28 @@ export const ButtonOutlined = styled(BaseButton)`
}
`
export const ButtonYellow = styled(BaseButton)`
background-color: ${({ theme }) => theme.deprecated_yellow3};
color: white;
export const ButtonYellow = styled(BaseButton)<{ redesignFlag?: boolean }>`
background-color: ${({ theme, redesignFlag }) => (redesignFlag ? theme.accentWarningSoft : theme.deprecated_yellow3)};
color: ${({ theme, redesignFlag }) => (redesignFlag ? theme.accentWarning : 'white')};
&:focus {
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, theme.deprecated_yellow3)};
background-color: ${({ theme }) => darken(0.05, theme.deprecated_yellow3)};
box-shadow: ${({ theme, redesignFlag }) => !redesignFlag && `0 0 0 1pt ${theme.deprecated_yellow3}`};
background-color: ${({ theme, redesignFlag }) =>
redesignFlag ? theme.accentWarningSoft : darken(0.05, theme.deprecated_yellow3)};
}
&:hover {
background-color: ${({ theme }) => darken(0.05, theme.deprecated_yellow3)};
background: ${({ theme, redesignFlag }) => redesignFlag && theme.stateOverlayHover};
mix-blend-mode: ${({ redesignFlag }) => redesignFlag && 'normal'};
background-color: ${({ theme, redesignFlag }) => !redesignFlag && darken(0.05, theme.deprecated_yellow3)};
}
&:active {
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, theme.deprecated_yellow3)};
background-color: ${({ theme }) => darken(0.1, theme.deprecated_yellow3)};
box-shadow: ${({ theme, redesignFlag }) => !redesignFlag && `0 0 0 1pt ${darken(0.1, theme.deprecated_yellow3)}`};
background-color: ${({ theme, redesignFlag }) =>
redesignFlag ? theme.accentWarningSoft : darken(0.1, theme.deprecated_yellow3)};
}
&:disabled {
background-color: ${({ theme }) => theme.deprecated_yellow3};
opacity: 50%;
background-color: ${({ theme, redesignFlag }) =>
redesignFlag ? theme.accentWarningSoft : theme.deprecated_yellow3};
opacity: ${({ redesignFlag }) => (redesignFlag ? '60%' : '50%')};
cursor: auto;
}
`

View File

@@ -1,7 +1,6 @@
import { Group } from '@visx/group'
import { LinePath } from '@visx/shape'
import { CurveFactory } from 'd3'
import { radius } from 'd3-curve-circlecorners'
import React from 'react'
import { ReactNode } from 'react'
import { useTheme } from 'styled-components/macro'
@@ -11,8 +10,8 @@ interface LineChartProps<T> {
data: T[]
getX: (t: T) => number
getY: (t: T) => number
marginTop: number
curve?: CurveFactory
marginTop?: number
curve: CurveFactory
color?: Color
strokeWidth: number
children?: ReactNode
@@ -37,7 +36,7 @@ function LineChart<T>({
<svg width={width} height={height}>
<Group top={marginTop}>
<LinePath
curve={curve ?? radius(0.25)}
curve={curve}
stroke={color ?? theme.accentAction}
strokeWidth={strokeWidth}
data={data}

View File

@@ -1,6 +1,6 @@
import { scaleLinear } from 'd3'
import useTheme from 'hooks/useTheme'
import { curveCardinalOpen, scaleLinear } from 'd3'
import React from 'react'
import { useTheme } from 'styled-components/macro'
import data from './data.json'
import LineChart from './LineChart'
@@ -37,6 +37,7 @@ function SparklineChart({ width, height }: SparklineChartProps) {
data={pricePoints}
getX={(p: PricePoint) => timeScale(p.timestamp)}
getY={(p: PricePoint) => rdScale(p.value)}
curve={curveCardinalOpen.tension(0.9)}
marginTop={0}
color={isPositive ? theme.accentSuccess : theme.accentFailure}
strokeWidth={1.5}

View File

@@ -1,17 +1,17 @@
import { Trans } from '@lingui/macro'
import CopyHelper from 'components/AccountDetails/Copy'
import Column from 'components/Column'
import useTheme from 'hooks/useTheme'
import { AlertOctagon } from 'react-feather'
import styled from 'styled-components/macro'
import styled, { useTheme } from 'styled-components/macro'
import { ExternalLink, ThemedText } from 'theme'
import { CopyHelper } from '../../theme'
import Modal from '../Modal'
const ContentWrapper = styled(Column)`
align-items: center;
margin: 32px;
text-align: center;
font-size: 12px;
`
const WarningIcon = styled(AlertOctagon)`
min-height: 22px;
@@ -49,7 +49,14 @@ export default function ConnectedAccountBlocked(props: ConnectedAccountBlockedPr
<ThemedText.DeprecatedMain fontSize={12}>
<Trans>If you believe this is an error, please send an email including your address to </Trans>{' '}
</ThemedText.DeprecatedMain>
<Copy iconSize={12} toCopy="compliance@uniswap.org" color={theme.deprecated_primary1} iconPosition="right">
<Copy
toCopy="compliance@uniswap.org"
fontSize={14}
iconSize={16}
gap={6}
color={theme.deprecated_primary1}
iconPosition="right"
>
compliance@uniswap.org
</Copy>
</ContentWrapper>

View File

@@ -4,8 +4,8 @@ import { t } from '@lingui/macro'
import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core'
import HoverInlineText from 'components/HoverInlineText'
import { useMemo } from 'react'
import { useTheme } from 'styled-components/macro'
import useTheme from '../../hooks/useTheme'
import { ThemedText } from '../../theme'
import { warningSeverity } from '../../utils/prices'
import { MouseoverTooltip } from '../Tooltip'

View File

@@ -0,0 +1,423 @@
import { Trans } from '@lingui/macro'
import { Currency, CurrencyAmount, Percent, Token } from '@uniswap/sdk-core'
import { Pair } from '@uniswap/v2-sdk'
import { useWeb3React } from '@web3-react/core'
import { ElementName, Event, EventName } from 'components/AmplitudeAnalytics/constants'
import { TraceEvent } from 'components/AmplitudeAnalytics/TraceEvent'
import { AutoColumn } from 'components/Column'
import { LoadingOpacityContainer, loadingOpacityMixin } from 'components/Loader/styled'
import { isSupportedChain } from 'constants/chains'
import { RedesignVariant, useRedesignFlag } from 'featureFlags/flags/redesign'
import { darken } from 'polished'
import { ReactNode, useCallback, useState } from 'react'
import { Lock } from 'react-feather'
import { useLocation } from 'react-router-dom'
import styled, { useTheme } from 'styled-components/macro'
import { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg'
import { useCurrencyBalance } from '../../state/connection/hooks'
import { ThemedText } from '../../theme'
import { ButtonGray } from '../Button'
import CurrencyLogo from '../CurrencyLogo'
import DoubleCurrencyLogo from '../DoubleLogo'
import { Input as NumericalInput } from '../NumericalInput'
import { RowBetween, RowFixed } from '../Row'
import CurrencySearchModal from '../SearchModal/CurrencySearchModal'
import { FiatValue } from './FiatValue'
const InputPanel = styled.div<{ hideInput?: boolean; redesignFlag: boolean }>`
${({ theme }) => theme.flexColumnNoWrap}
position: relative;
border-radius: ${({ hideInput }) => (hideInput ? '16px' : '20px')};
background-color: ${({ theme, redesignFlag, hideInput }) =>
redesignFlag ? 'transparent' : hideInput ? 'transparent' : theme.deprecated_bg2};
z-index: 1;
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
transition: height 1s ease;
will-change: height;
`
const FixedContainer = styled.div<{ redesignFlag: boolean }>`
width: 100%;
height: 100%;
position: absolute;
border-radius: 20px;
background-color: ${({ theme, redesignFlag }) => (redesignFlag ? 'transparent' : theme.deprecated_bg2)};
display: flex;
align-items: center;
justify-content: center;
z-index: 2;
`
const Container = styled.div<{ hideInput: boolean; disabled: boolean; redesignFlag: boolean }>`
min-height: ${({ redesignFlag }) => redesignFlag && '69px'};
border-radius: ${({ hideInput }) => (hideInput ? '16px' : '20px')};
border: 1px solid ${({ theme, redesignFlag }) => (redesignFlag ? 'transparent' : theme.deprecated_bg0)};
background-color: ${({ theme, redesignFlag }) => (redesignFlag ? 'transparent' : theme.deprecated_bg1)};
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
${({ theme, hideInput, disabled, redesignFlag }) =>
!redesignFlag &&
!disabled &&
`
:focus,
:hover {
border: 1px solid ${hideInput ? ' transparent' : theme.deprecated_bg3};
}
`}
`
const CurrencySelect = styled(ButtonGray)<{
visible: boolean
selected: boolean
hideInput?: boolean
disabled?: boolean
redesignFlag: boolean
}>`
align-items: center;
background-color: ${({ selected, theme, redesignFlag }) =>
redesignFlag
? selected
? theme.backgroundSurface
: theme.accentAction
: selected
? theme.deprecated_bg2
: theme.deprecated_primary1};
opacity: ${({ disabled }) => (!disabled ? 1 : 0.4)};
box-shadow: ${({ selected }) => (selected ? 'none' : '0px 6px 10px rgba(0, 0, 0, 0.075)')};
color: ${({ selected, theme }) => (selected ? theme.deprecated_text1 : theme.deprecated_white)};
cursor: pointer;
height: ${({ hideInput, redesignFlag }) => (redesignFlag ? 'unset' : hideInput ? '2.8rem' : '2.4rem')};
border-radius: 16px;
outline: none;
user-select: none;
border: none;
font-size: 24px;
font-weight: 400;
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
padding: ${({ selected, redesignFlag }) =>
redesignFlag ? (selected ? '4px 8px 4px 4px' : '6px 6px 6px 8px') : '0 8px'};
gap: ${({ redesignFlag }) => (redesignFlag ? '8px' : '0px')};
justify-content: space-between;
margin-left: ${({ hideInput }) => (hideInput ? '0' : '12px')};
:focus,
:hover {
background-color: ${({ selected, theme, redesignFlag }) =>
selected
? redesignFlag
? theme.backgroundSurface
: theme.deprecated_bg3
: darken(0.05, theme.deprecated_primary1)};
}
visibility: ${({ visible }) => (visible ? 'visible' : 'hidden')};
`
const InputCurrencySelect = styled(CurrencySelect)<{ redesignFlag: boolean }>`
background-color: ${({ theme, selected, redesignFlag }) =>
redesignFlag && (selected ? theme.backgroundModule : theme.accentAction)};
:focus,
:hover {
background-color: ${({ selected, theme, redesignFlag }) =>
selected
? redesignFlag
? theme.backgroundInteractive
: theme.deprecated_bg3
: darken(0.05, theme.deprecated_primary1)};
}
`
const InputRow = styled.div<{ selected: boolean; redesignFlag: boolean }>`
${({ theme }) => theme.flexRowNoWrap}
align-items: center;
justify-content: space-between;
padding: ${({ selected, redesignFlag }) =>
redesignFlag ? '0px' : selected ? ' 1rem 1rem 0.75rem 1rem' : '1rem 1rem 1rem 1rem'};
`
const LabelRow = styled.div`
${({ theme }) => theme.flexRowNoWrap}
align-items: center;
color: ${({ theme }) => theme.deprecated_text1};
font-size: 0.75rem;
line-height: 1rem;
padding: 0 1rem 1rem;
span:hover {
cursor: pointer;
color: ${({ theme }) => darken(0.2, theme.deprecated_text2)};
}
`
const FiatRow = styled(LabelRow)<{ redesignFlag: boolean }>`
justify-content: flex-end;
min-height: ${({ redesignFlag }) => redesignFlag && '32px'};
padding: ${({ redesignFlag }) => redesignFlag && '8px 0px'};
height: ${({ redesignFlag }) => !redesignFlag && '24px'};
`
const NoBalanceState = styled.div`
color: ${({ theme }) => theme.textTertiary};
font-weight: 400;
justify-content: space-between;
padding: 0px 4px 1px 4px;
`
const NoBalanceDash = styled.span`
color: ${({ theme }) => theme.textTertiary};
font-variant: small-caps;
font-feature-settings: 'pnum' on, 'lnum' on;
`
const Aligner = styled.span`
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
`
const StyledDropDown = styled(DropDown)<{ selected: boolean; redesignFlag: boolean }>`
margin: 0 0.25rem 0 0.35rem;
height: 35%;
margin-left: ${({ redesignFlag }) => redesignFlag && '8px'};
path {
stroke: ${({ selected, theme }) => (selected ? theme.deprecated_text1 : theme.deprecated_white)};
stroke-width: ${({ redesignFlag }) => (redesignFlag ? '2px' : '1.5px')};
}
`
const StyledTokenName = styled.span<{ active?: boolean; redesignFlag: boolean }>`
${({ active }) => (active ? ' margin: 0 0.25rem 0 0.25rem;' : ' margin: 0 0.25rem 0 0.25rem;')}
font-size: ${({ active }) => (active ? '18px' : '18px')};
font-weight: ${({ redesignFlag }) => (redesignFlag ? '600' : '500')};
`
const StyledBalanceMax = styled.button<{ disabled?: boolean; redesignFlag: boolean }>`
background-color: transparent;
background-color: ${({ theme, redesignFlag }) => !redesignFlag && theme.deprecated_primary5};
border: none;
text-transform: ${({ redesignFlag }) => !redesignFlag && 'uppercase'};
border-radius: ${({ redesignFlag }) => !redesignFlag && '12px'};
color: ${({ theme, redesignFlag }) => (redesignFlag ? theme.accentAction : theme.deprecated_primary1)};
cursor: pointer;
font-size: ${({ redesignFlag }) => (redesignFlag ? '14px' : '11px')};
font-weight: ${({ redesignFlag }) => (redesignFlag ? '600' : '500')};
margin-left: ${({ redesignFlag }) => (redesignFlag ? '0px' : '0.25rem')};
opacity: ${({ disabled }) => (!disabled ? 1 : 0.4)};
padding: 4px 6px;
pointer-events: ${({ disabled }) => (!disabled ? 'initial' : 'none')};
:hover {
opacity: ${({ disabled }) => (!disabled ? 0.8 : 0.4)};
}
:focus {
outline: none;
}
`
const StyledNumericalInput = styled(NumericalInput)<{ $loading: boolean; redesignFlag: boolean }>`
${loadingOpacityMixin};
text-align: left;
font-variant: ${({ redesignFlag }) => redesignFlag && 'small-caps'};
font-feature-settings: ${({ redesignFlag }) => redesignFlag && 'pnum on, lnum on'};
`
interface SwapCurrencyInputPanelProps {
value: string
onUserInput: (value: string) => void
onMax?: () => void
showMaxButton: boolean
label?: ReactNode
onCurrencySelect?: (currency: Currency) => void
currency?: Currency | null
hideBalance?: boolean
pair?: Pair | null
hideInput?: boolean
otherCurrency?: Currency | null
fiatValue?: CurrencyAmount<Token> | null
priceImpact?: Percent
id: string
showCommonBases?: boolean
showCurrencyAmount?: boolean
disableNonToken?: boolean
renderBalance?: (amount: CurrencyAmount<Currency>) => ReactNode
locked?: boolean
loading?: boolean
}
export default function SwapCurrencyInputPanel({
value,
onUserInput,
onMax,
showMaxButton,
onCurrencySelect,
currency,
otherCurrency,
id,
showCommonBases,
showCurrencyAmount,
disableNonToken,
renderBalance,
fiatValue,
priceImpact,
hideBalance = false,
pair = null, // used for double token logo
hideInput = false,
locked = false,
loading = false,
...rest
}: SwapCurrencyInputPanelProps) {
const [modalOpen, setModalOpen] = useState(false)
const { account, chainId } = useWeb3React()
const redesignFlag = useRedesignFlag()
const redesignFlagEnabled = redesignFlag === RedesignVariant.Enabled
const selectedCurrencyBalance = useCurrencyBalance(account ?? undefined, currency ?? undefined)
const theme = useTheme()
const { pathname } = useLocation()
const isAddLiquidityPage = pathname.includes('/add') && !pathname.includes('/add/v2')
const handleDismissSearch = useCallback(() => {
setModalOpen(false)
}, [setModalOpen])
const chainAllowed = isSupportedChain(chainId)
return (
<InputPanel id={id} hideInput={hideInput} {...rest} redesignFlag={redesignFlagEnabled}>
{locked && (
<FixedContainer redesignFlag={redesignFlagEnabled}>
<AutoColumn gap="sm" justify="center">
<Lock />
<ThemedText.DeprecatedLabel fontSize="12px" textAlign="center" padding="0 12px">
<Trans>The market price is outside your specified price range. Single-asset deposit only.</Trans>
</ThemedText.DeprecatedLabel>
</AutoColumn>
</FixedContainer>
)}
<Container hideInput={hideInput} disabled={!chainAllowed} redesignFlag={redesignFlagEnabled}>
<InputRow
style={hideInput ? { padding: '0', borderRadius: '8px' } : {}}
selected={!onCurrencySelect}
redesignFlag={redesignFlagEnabled}
>
{!hideInput && (
<StyledNumericalInput
className="token-amount-input"
value={value}
onUserInput={onUserInput}
disabled={!chainAllowed}
$loading={loading}
redesignFlag={redesignFlagEnabled}
/>
)}
<InputCurrencySelect
disabled={!chainAllowed}
visible={currency !== undefined}
selected={!!currency}
hideInput={hideInput}
redesignFlag={redesignFlagEnabled}
className="open-currency-select-button"
onClick={() => {
if (onCurrencySelect) {
setModalOpen(true)
}
}}
>
<Aligner>
<RowFixed>
{pair ? (
<span style={{ marginRight: '0.5rem' }}>
<DoubleCurrencyLogo currency0={pair.token0} currency1={pair.token1} size={24} margin={true} />
</span>
) : currency ? (
<CurrencyLogo style={{ marginRight: '2px' }} currency={currency} size={'24px'} />
) : null}
{pair ? (
<StyledTokenName className="pair-name-container" redesignFlag={redesignFlagEnabled}>
{pair?.token0.symbol}:{pair?.token1.symbol}
</StyledTokenName>
) : (
<StyledTokenName
className="token-symbol-container"
active={Boolean(currency && currency.symbol)}
redesignFlag={redesignFlagEnabled}
>
{(currency && currency.symbol && currency.symbol.length > 20
? currency.symbol.slice(0, 4) +
'...' +
currency.symbol.slice(currency.symbol.length - 5, currency.symbol.length)
: currency?.symbol) || <Trans>Select token</Trans>}
</StyledTokenName>
)}
</RowFixed>
{onCurrencySelect && <StyledDropDown selected={!!currency} redesignFlag={redesignFlagEnabled} />}
</Aligner>
</InputCurrencySelect>
</InputRow>
{redesignFlagEnabled && !currency && !isAddLiquidityPage && (
<NoBalanceState>
<FiatRow redesignFlag={redesignFlagEnabled}>
<RowBetween>
<NoBalanceDash>-</NoBalanceDash>
<NoBalanceDash>-</NoBalanceDash>
</RowBetween>
</FiatRow>
</NoBalanceState>
)}
{!hideInput && !hideBalance && currency && (
<FiatRow redesignFlag={redesignFlagEnabled}>
<RowBetween>
<LoadingOpacityContainer $loading={loading}>
<FiatValue fiatValue={fiatValue} priceImpact={priceImpact} />
</LoadingOpacityContainer>
{account ? (
<RowFixed style={{ height: '17px' }}>
<ThemedText.DeprecatedBody
onClick={onMax}
color={theme.deprecated_text3}
fontWeight={500}
fontSize={14}
style={{ display: 'inline', cursor: 'pointer' }}
>
{!hideBalance && currency && selectedCurrencyBalance ? (
renderBalance ? (
renderBalance(selectedCurrencyBalance)
) : (
<Trans>Balance: {formatCurrencyAmount(selectedCurrencyBalance, 4)}</Trans>
)
) : null}
</ThemedText.DeprecatedBody>
{showMaxButton && selectedCurrencyBalance ? (
<TraceEvent
events={[Event.onClick]}
name={EventName.SWAP_MAX_TOKEN_AMOUNT_SELECTED}
element={ElementName.MAX_TOKEN_AMOUNT_BUTTON}
>
<StyledBalanceMax onClick={onMax} redesignFlag={redesignFlagEnabled}>
<Trans>Max</Trans>
</StyledBalanceMax>
</TraceEvent>
) : null}
</RowFixed>
) : (
<span />
)}
</RowBetween>
</FiatRow>
)}
</Container>
{onCurrencySelect && (
<CurrencySearchModal
isOpen={modalOpen}
onDismiss={handleDismissSearch}
onCurrencySelect={onCurrencySelect}
selectedCurrency={currency}
otherSelectedCurrency={otherCurrency}
showCommonBases={showCommonBases}
showCurrencyAmount={showCurrencyAmount}
disableNonToken={disableNonToken}
/>
)}
</InputPanel>
)
}

View File

@@ -11,11 +11,10 @@ import { RedesignVariant, useRedesignFlag } from 'featureFlags/flags/redesign'
import { darken } from 'polished'
import { ReactNode, useCallback, useState } from 'react'
import { Lock } from 'react-feather'
import styled from 'styled-components/macro'
import styled, { useTheme } from 'styled-components/macro'
import { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg'
import useTheme from '../../hooks/useTheme'
import { useCurrencyBalance } from '../../state/connection/hooks'
import { ThemedText } from '../../theme'
import { ButtonGray } from '../Button'
@@ -26,37 +25,35 @@ import { RowBetween, RowFixed } from '../Row'
import CurrencySearchModal from '../SearchModal/CurrencySearchModal'
import { FiatValue } from './FiatValue'
const InputPanel = styled.div<{ hideInput?: boolean; redesignFlag: boolean }>`
const InputPanel = styled.div<{ hideInput?: boolean }>`
${({ theme }) => theme.flexColumnNoWrap}
position: relative;
border-radius: ${({ hideInput }) => (hideInput ? '16px' : '20px')};
background-color: ${({ theme, redesignFlag, hideInput }) =>
redesignFlag ? theme.none : hideInput ? 'transparent' : theme.deprecated_bg2};
background-color: ${({ theme, hideInput }) => (hideInput ? 'transparent' : theme.deprecated_bg2)};
z-index: 1;
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
transition: height 1s ease;
will-change: height;
`
const FixedContainer = styled.div<{ redesignFlag: boolean }>`
const FixedContainer = styled.div`
width: 100%;
height: 100%;
position: absolute;
border-radius: 20px;
background-color: ${({ theme, redesignFlag }) => (redesignFlag ? theme.none : theme.deprecated_bg2)};
background-color: ${({ theme }) => theme.deprecated_bg2};
display: flex;
align-items: center;
justify-content: center;
z-index: 2;
`
const Container = styled.div<{ hideInput: boolean; disabled: boolean; redesignFlag: boolean }>`
const Container = styled.div<{ hideInput: boolean; disabled: boolean }>`
border-radius: ${({ hideInput }) => (hideInput ? '16px' : '20px')};
border: 1px solid ${({ theme, redesignFlag }) => (redesignFlag ? theme.none : theme.deprecated_bg0)};
background-color: ${({ theme, redesignFlag }) => (redesignFlag ? theme.none : theme.deprecated_bg1)};
border: 1px solid ${({ theme }) => theme.deprecated_bg0};
background-color: ${({ theme }) => theme.deprecated_bg1};
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
${({ theme, hideInput, disabled, redesignFlag }) =>
!redesignFlag &&
${({ theme, hideInput, disabled }) =>
!disabled &&
`
:focus,
@@ -71,65 +68,38 @@ const CurrencySelect = styled(ButtonGray)<{
selected: boolean
hideInput?: boolean
disabled?: boolean
redesignFlag: boolean
}>`
align-items: center;
background-color: ${({ selected, theme, redesignFlag }) =>
redesignFlag
? selected
? theme.backgroundSurface
: theme.accentAction
: selected
? theme.deprecated_bg2
: theme.deprecated_primary1};
background-color: ${({ selected, theme }) => (selected ? theme.deprecated_bg2 : theme.deprecated_primary1)};
opacity: ${({ disabled }) => (!disabled ? 1 : 0.4)};
box-shadow: ${({ selected }) => (selected ? 'none' : '0px 6px 10px rgba(0, 0, 0, 0.075)')};
box-shadow: 0px 6px 10px rgba(0, 0, 0, 0.075);
color: ${({ selected, theme }) => (selected ? theme.deprecated_text1 : theme.deprecated_white)};
cursor: pointer;
height: ${({ hideInput, redesignFlag }) => (redesignFlag ? 'unset' : hideInput ? '2.8rem' : '2.4rem')};
border-radius: 16px;
outline: none;
user-select: none;
border: none;
font-size: 24px;
font-weight: 400;
font-weight: 500;
height: ${({ hideInput }) => (hideInput ? '2.8rem' : '2.4rem')};
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
padding: ${({ selected, redesignFlag }) =>
redesignFlag ? (selected ? '4px 8px 4px 4px' : '6px 6px 6px 8px') : '0 8px'};
gap: ${({ redesignFlag }) => (redesignFlag ? '8px' : '0px')};
padding: 0 8px;
justify-content: space-between;
margin-left: ${({ hideInput }) => (hideInput ? '0' : '12px')};
:focus,
:hover {
background-color: ${({ selected, theme, redesignFlag }) =>
selected
? redesignFlag
? theme.backgroundSurface
: theme.deprecated_bg3
: darken(0.05, theme.deprecated_primary1)};
background-color: ${({ selected, theme }) =>
selected ? theme.deprecated_bg3 : darken(0.05, theme.deprecated_primary1)};
}
visibility: ${({ visible }) => (visible ? 'visible' : 'hidden')};
`
const InputCurrencySelect = styled(CurrencySelect)<{ redesignFlag: boolean }>`
background-color: ${({ theme, selected, redesignFlag }) =>
redesignFlag && (selected ? theme.backgroundModule : theme.accentAction)};
:focus,
:hover {
background-color: ${({ selected, theme, redesignFlag }) =>
selected
? redesignFlag
? theme.backgroundInteractive
: theme.deprecated_bg3
: darken(0.05, theme.deprecated_primary1)};
}
`
const InputRow = styled.div<{ selected: boolean; redesignFlag: boolean }>`
const InputRow = styled.div<{ selected: boolean }>`
${({ theme }) => theme.flexRowNoWrap}
align-items: center;
justify-content: space-between;
padding: ${({ selected, redesignFlag }) =>
redesignFlag ? '0px' : selected ? ' 1rem 1rem 0.75rem 1rem' : '1rem 1rem 1rem 1rem'};
padding: ${({ selected }) => (selected ? ' 1rem 1rem 0.75rem 1rem' : '1rem 1rem 1rem 1rem')};
`
const LabelRow = styled.div`
@@ -139,7 +109,6 @@ const LabelRow = styled.div`
font-size: 0.75rem;
line-height: 1rem;
padding: 0 1rem 1rem;
span:hover {
cursor: pointer;
color: ${({ theme }) => darken(0.2, theme.deprecated_text2)};
@@ -148,20 +117,8 @@ const LabelRow = styled.div`
const FiatRow = styled(LabelRow)<{ redesignFlag: boolean }>`
justify-content: flex-end;
padding: ${({ redesignFlag }) => redesignFlag && '8px 0px'};
height: ${({ redesignFlag }) => !redesignFlag && '24px'};
`
const NoBalanceState = styled.div`
color: ${({ theme }) => theme.textTertiary};
font-weight: 400;
justify-content: space-between;
padding: 0px 4px;
`
const NoBalanceDash = styled.span`
color: ${({ theme }) => theme.textTertiary};
font-variant: small-caps;
font-feature-settings: 'pnum' on, 'lnum' on;
padding: ${({ redesignFlag }) => redesignFlag && '0px 1rem 0.75rem'};
height: ${({ redesignFlag }) => (redesignFlag ? '32px' : '16px')};
`
const Aligner = styled.span`
@@ -171,34 +128,31 @@ const Aligner = styled.span`
width: 100%;
`
const StyledDropDown = styled(DropDown)<{ selected: boolean; redesignFlag: boolean }>`
const StyledDropDown = styled(DropDown)<{ selected: boolean }>`
margin: 0 0.25rem 0 0.35rem;
height: 35%;
margin-left: ${({ redesignFlag }) => redesignFlag && '8px'};
path {
stroke: ${({ selected, theme }) => (selected ? theme.deprecated_text1 : theme.deprecated_white)};
stroke-width: ${({ redesignFlag }) => (redesignFlag ? '2px' : '1.5px')};
stroke-width: 1.5px;
}
`
const StyledTokenName = styled.span<{ active?: boolean; redesignFlag: boolean }>`
const StyledTokenName = styled.span<{ active?: boolean }>`
${({ active }) => (active ? ' margin: 0 0.25rem 0 0.25rem;' : ' margin: 0 0.25rem 0 0.25rem;')}
font-size: ${({ active }) => (active ? '18px' : '18px')};
font-weight: ${({ redesignFlag }) => (redesignFlag ? '600' : '500')};
`
const StyledBalanceMax = styled.button<{ disabled?: boolean; redesignFlag: boolean }>`
const StyledBalanceMax = styled.button<{ disabled?: boolean }>`
background-color: transparent;
background-color: ${({ theme, redesignFlag }) => !redesignFlag && theme.deprecated_primary5};
background-color: ${({ theme }) => theme.deprecated_primary5};
border: none;
text-transform: ${({ redesignFlag }) => !redesignFlag && 'uppercase'};
border-radius: ${({ redesignFlag }) => !redesignFlag && '12px'};
color: ${({ theme, redesignFlag }) => (redesignFlag ? theme.accentAction : theme.deprecated_primary1)};
border-radius: 12px;
color: ${({ theme }) => theme.deprecated_primary1};
cursor: pointer;
font-size: ${({ redesignFlag }) => (redesignFlag ? '14px' : '11px')};
font-weight: ${({ redesignFlag }) => (redesignFlag ? '600' : '500')};
margin-left: ${({ redesignFlag }) => (redesignFlag ? '0px' : '0.25rem')};
font-size: 11px;
font-weight: 500;
margin-left: 0.25rem;
opacity: ${({ disabled }) => (!disabled ? 1 : 0.4)};
padding: 4px 6px;
pointer-events: ${({ disabled }) => (!disabled ? 'initial' : 'none')};
@@ -212,11 +166,9 @@ const StyledBalanceMax = styled.button<{ disabled?: boolean; redesignFlag: boole
}
`
const StyledNumericalInput = styled(NumericalInput)<{ $loading: boolean; redesignFlag: boolean }>`
const StyledNumericalInput = styled(NumericalInput)<{ $loading: boolean }>`
${loadingOpacityMixin};
text-align: left;
font-variant: ${({ redesignFlag }) => redesignFlag && 'small-caps'};
font-feature-settings: ${({ redesignFlag }) => redesignFlag && 'pnum on, lnum on'};
`
interface CurrencyInputPanelProps {
@@ -266,10 +218,10 @@ export default function CurrencyInputPanel({
}: CurrencyInputPanelProps) {
const [modalOpen, setModalOpen] = useState(false)
const { account, chainId } = useWeb3React()
const redesignFlag = useRedesignFlag()
const redesignFlagEnabled = redesignFlag === RedesignVariant.Enabled
const selectedCurrencyBalance = useCurrencyBalance(account ?? undefined, currency ?? undefined)
const theme = useTheme()
const redesignFlag = useRedesignFlag()
const redesignFlagEnabled = redesignFlag === RedesignVariant.Enabled
const handleDismissSearch = useCallback(() => {
setModalOpen(false)
@@ -278,9 +230,9 @@ export default function CurrencyInputPanel({
const chainAllowed = isSupportedChain(chainId)
return (
<InputPanel id={id} hideInput={hideInput} {...rest} redesignFlag={redesignFlagEnabled}>
<InputPanel id={id} hideInput={hideInput} {...rest}>
{locked && (
<FixedContainer redesignFlag={redesignFlagEnabled}>
<FixedContainer>
<AutoColumn gap="sm" justify="center">
<Lock />
<ThemedText.DeprecatedLabel fontSize="12px" textAlign="center" padding="0 12px">
@@ -289,12 +241,8 @@ export default function CurrencyInputPanel({
</AutoColumn>
</FixedContainer>
)}
<Container hideInput={hideInput} disabled={!chainAllowed} redesignFlag={redesignFlagEnabled}>
<InputRow
style={hideInput ? { padding: '0', borderRadius: '8px' } : {}}
selected={!onCurrencySelect}
redesignFlag={redesignFlagEnabled}
>
<Container hideInput={hideInput} disabled={!chainAllowed}>
<InputRow style={hideInput ? { padding: '0', borderRadius: '8px' } : {}} selected={!onCurrencySelect}>
{!hideInput && (
<StyledNumericalInput
className="token-amount-input"
@@ -302,16 +250,14 @@ export default function CurrencyInputPanel({
onUserInput={onUserInput}
disabled={!chainAllowed}
$loading={loading}
redesignFlag={redesignFlagEnabled}
/>
)}
<InputCurrencySelect
<CurrencySelect
disabled={!chainAllowed}
visible={currency !== undefined}
selected={!!currency}
hideInput={hideInput}
redesignFlag={redesignFlagEnabled}
className="open-currency-select-button"
onClick={() => {
if (onCurrencySelect) {
@@ -326,40 +272,26 @@ export default function CurrencyInputPanel({
<DoubleCurrencyLogo currency0={pair.token0} currency1={pair.token1} size={24} margin={true} />
</span>
) : currency ? (
<CurrencyLogo style={{ marginRight: '2px' }} currency={currency} size={'24px'} />
<CurrencyLogo style={{ marginRight: '0.5rem' }} currency={currency} size={'24px'} />
) : null}
{pair ? (
<StyledTokenName className="pair-name-container" redesignFlag={redesignFlagEnabled}>
<StyledTokenName className="pair-name-container">
{pair?.token0.symbol}:{pair?.token1.symbol}
</StyledTokenName>
) : (
<StyledTokenName
className="token-symbol-container"
active={Boolean(currency && currency.symbol)}
redesignFlag={redesignFlagEnabled}
>
<StyledTokenName className="token-symbol-container" active={Boolean(currency && currency.symbol)}>
{(currency && currency.symbol && currency.symbol.length > 20
? currency.symbol.slice(0, 4) +
'...' +
currency.symbol.slice(currency.symbol.length - 5, currency.symbol.length)
: currency?.symbol) || <Trans>Select token</Trans>}
: currency?.symbol) || <Trans>Select a token</Trans>}
</StyledTokenName>
)}
</RowFixed>
{onCurrencySelect && <StyledDropDown selected={!!currency} redesignFlag={redesignFlagEnabled} />}
{onCurrencySelect && <StyledDropDown selected={!!currency} />}
</Aligner>
</InputCurrencySelect>
</CurrencySelect>
</InputRow>
{redesignFlagEnabled && !currency && (
<NoBalanceState>
<FiatRow redesignFlag={redesignFlagEnabled}>
<RowBetween>
<NoBalanceDash>-</NoBalanceDash>
<NoBalanceDash>-</NoBalanceDash>
</RowBetween>
</FiatRow>
</NoBalanceState>
)}
{!hideInput && !hideBalance && currency && (
<FiatRow redesignFlag={redesignFlagEnabled}>
<RowBetween>
@@ -389,8 +321,8 @@ export default function CurrencyInputPanel({
name={EventName.SWAP_MAX_TOKEN_AMOUNT_SELECTED}
element={ElementName.MAX_TOKEN_AMOUNT_BUTTON}
>
<StyledBalanceMax onClick={onMax} redesignFlag={redesignFlagEnabled}>
<Trans>Max</Trans>
<StyledBalanceMax onClick={onMax}>
<Trans>MAX</Trans>
</StyledBalanceMax>
</TraceEvent>
) : null}

View File

@@ -18,7 +18,7 @@ interface DoubleCurrencyLogoProps {
}
const HigherLogo = styled(CurrencyLogo)`
z-index: 2;
z-index: 1;
`
const CoveredLogo = styled(CurrencyLogo)<{ sizeraw: number }>`
position: absolute;

View File

@@ -1,141 +0,0 @@
import { useOnClickOutside } from 'hooks/useOnClickOutside'
import { darken } from 'polished'
import { useRef, useState } from 'react'
import { Check, Link, Share, Twitter } from 'react-feather'
import { useModalIsOpen, useToggleModal } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer'
import styled, { useTheme } from 'styled-components/macro'
import { Z_INDEX } from 'theme'
const TWITTER_WIDTH = 560
const TWITTER_HEIGHT = 480
const ShareButtonDisplay = styled.div`
display: flex;
cursor: pointer;
position: relative;
z-index: ${Z_INDEX.dropdown};
&:hover {
color: ${({ theme }) => darken(0.1, theme.textSecondary)};
}
`
const ShareActions = styled.div`
position: absolute;
top: 28px;
right: 0px;
padding: 8px 0px;
display: flex;
flex-direction: column;
width: fit-content;
overflow: auto;
background-color: ${({ theme }) => theme.backgroundSurface};
border: 1px solid ${({ theme }) => theme.backgroundOutline};
box-shadow: ${({ theme }) => theme.flyoutDropShadow};
border-radius: 12px;
`
const ShareAction = styled.div`
display: flex;
align-items: center;
padding: 12px 16px;
font-size: 16px;
gap: 8px;
width: 200px;
height: 48px;
color: ${({ theme }) => theme.textPrimary};
cursor: pointer;
&:hover {
background-color: ${({ theme }) => theme.backgroundModule};
}
`
const LinkCopied = styled.div<{ show: boolean }>`
display: ${({ show }) => (show ? 'flex' : 'none')};
width: 328px;
height: 72px;
color: ${({ theme }) => theme.textPrimary};
background-color: ${({ theme }) => theme.backgroundBackdrop};
justify-content: flex-start;
align-items: center;
padding: 24px 16px;
position: absolute;
right: 32px;
bottom: 32px;
font-size: 14px;
gap: 8px;
border: 1px solid rgba(153, 161, 189, 0.08);
box-shadow: ${({ theme }) => theme.flyoutDropShadow};
border-radius: 20px;
animation: floatIn 0s ease-in 3s forwards;
@keyframes floatIn {
to {
width: 0;
height: 0;
overflow: hidden;
display: none;
}
}
`
interface TokenInfo {
tokenName: string
tokenSymbol: string
}
export default function ShareButton(tokenInfo: TokenInfo) {
const theme = useTheme()
const node = useRef<HTMLDivElement | null>(null)
const open = useModalIsOpen(ApplicationModal.SHARE)
const toggleShare = useToggleModal(ApplicationModal.SHARE)
useOnClickOutside(node, open ? toggleShare : undefined)
const [showCopied, setShowCopied] = useState(false)
const positionX = (window.screen.width - TWITTER_WIDTH) / 2
const positionY = (window.screen.height - TWITTER_HEIGHT) / 2
const shareTweet = () => {
toggleShare()
window.open(
`https://twitter.com/intent/tweet?text=Check%20out%20${tokenInfo.tokenName}%20(${tokenInfo.tokenSymbol})%20https://app.uniswap.org/%23/tokens/${tokenInfo.tokenSymbol}%20via%20@uniswap`,
'newwindow',
`left=${positionX}, top=${positionY}, width=${TWITTER_WIDTH}, height=${TWITTER_HEIGHT}`
)
}
const copyLink = () => {
navigator.clipboard.writeText(window.location.href).then(
function handleClipboardWriteSuccess() {
setShowCopied(true)
toggleShare()
setTimeout(() => setShowCopied(false), 3000)
},
function error() {
console.error('Clipboard copy failed.')
}
)
}
return (
<>
<ShareButtonDisplay ref={node}>
<Share size={18} onClick={toggleShare} aria-label={`ShareOptions`} />
{open && (
<ShareActions>
<ShareAction onClick={copyLink}>
<Link color={theme.textSecondary} size={18} />
Copy link
</ShareAction>
<ShareAction onClick={shareTweet}>
<Twitter color={theme.textSecondary} size={18} />
Share to Twitter
</ShareAction>
</ShareActions>
)}
</ShareButtonDisplay>
<LinkCopied show={showCopied}>
<Check color={theme.accentSuccess} />
Link Copied
</LinkCopied>
</>
)
}

View File

@@ -1,271 +0,0 @@
import { Trans } from '@lingui/macro'
import { ParentSize } from '@visx/responsive'
import PriceChart from 'components/Charts/PriceChart'
import CurrencyLogo from 'components/CurrencyLogo'
import { VerifiedIcon } from 'components/TokenSafety/TokenSafetyIcon'
import TokenSafetyModal from 'components/TokenSafety/TokenSafetyModal'
import { getChainInfo } from 'constants/chainInfo'
import { checkWarning } from 'constants/tokenSafety'
import { useCurrency, useIsUserAddedToken, useToken } from 'hooks/Tokens'
import { useAtomValue } from 'jotai/utils'
import { darken } from 'polished'
import { useCallback } from 'react'
import { useState } from 'react'
import { ArrowLeft, Copy, Heart } from 'react-feather'
import { Link, useNavigate } from 'react-router-dom'
import styled, { useTheme } from 'styled-components/macro'
import { MOBILE_MEDIA_BREAKPOINT } from '../constants'
import { favoritesAtom, useToggleFavorite } from '../state'
import { ClickFavorited } from '../TokenTable/TokenRow'
import Resource from './Resource'
import ShareButton from './ShareButton'
export const AboutSection = styled.div`
display: flex;
flex-direction: column;
gap: 16px;
padding: 24px 0px;
`
export const AboutHeader = styled.span`
font-size: 28px;
line-height: 36px;
`
export const BreadcrumbNavLink = styled(Link)`
display: flex;
color: ${({ theme }) => theme.textSecondary};
font-size: 14px;
line-height: 20px;
align-items: center;
gap: 4px;
text-decoration: none;
margin-bottom: 16px;
&:hover {
color: ${({ theme }) => theme.textTertiary};
}
`
export const ChartHeader = styled.div`
width: 100%;
display: flex;
flex-direction: column;
color: ${({ theme }) => theme.textPrimary};
gap: 4px;
margin-bottom: 24px;
`
const ContractAddress = styled.button`
display: flex;
color: ${({ theme }) => theme.textPrimary};
gap: 10px;
align-items: center;
background: transparent;
border: none;
padding: 0px;
cursor: pointer;
&:hover {
color: ${({ theme }) => darken(0.1, theme.textPrimary)};
}
`
export const ContractAddressSection = styled.div`
padding: 24px 0px;
`
const Contract = styled.div`
display: flex;
flex-direction: column;
color: ${({ theme }) => theme.textSecondary};
font-size: 14px;
gap: 4px;
`
export const ChartContainer = styled.div`
display: flex;
height: 436px;
align-items: center;
`
export const Stat = styled.div`
display: flex;
flex-direction: column;
color: ${({ theme }) => theme.textSecondary};
font-size: 14px;
min-width: 168px;
flex: 1;
gap: 4px;
padding: 24px 0px;
`
const StatPrice = styled.span`
font-size: 28px;
color: ${({ theme }) => theme.textPrimary};
`
export const StatsSection = styled.div`
display: flex;
flex-wrap: wrap;
`
export const StatPair = styled.div`
display: flex;
flex: 1;
flex-wrap: wrap;
`
export const TokenNameCell = styled.div`
display: flex;
gap: 8px;
font-size: 20px;
line-height: 28px;
align-items: center;
`
const TokenActions = styled.div`
display: flex;
gap: 24px;
color: ${({ theme }) => theme.textSecondary};
`
export const TokenInfoContainer = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
`
const TokenSymbol = styled.span`
color: ${({ theme }) => theme.textSecondary};
`
export const TopArea = styled.div`
max-width: 832px;
`
export const ResourcesContainer = styled.div`
display: flex;
gap: 14px;
`
const FullAddress = styled.span`
@media only screen and (max-width: ${MOBILE_MEDIA_BREAKPOINT}) {
display: none;
}
`
const TruncatedAddress = styled.span`
display: none;
@media only screen and (max-width: ${MOBILE_MEDIA_BREAKPOINT}) {
display: flex;
}
`
const NetworkBadge = styled.div<{ networkColor?: string; backgroundColor?: string }>`
border-radius: 5px;
padding: 4px 8px;
font-weight: 600;
font-size: 12px;
line-height: 12px;
color: ${({ theme, networkColor }) => networkColor ?? theme.textPrimary};
background-color: ${({ theme, backgroundColor }) => backgroundColor ?? theme.backgroundSurface};
`
export default function LoadedTokenDetail({ address }: { address: string }) {
const theme = useTheme()
const token = useToken(address)
const currency = useCurrency(address)
const favoriteTokens = useAtomValue<string[]>(favoritesAtom)
const isFavorited = favoriteTokens.includes(address)
const toggleFavorite = useToggleFavorite(address)
const warning = checkWarning(address)
const navigate = useNavigate()
const isUserAddedToken = useIsUserAddedToken(token)
const [warningModalOpen, setWarningModalOpen] = useState(!!warning && !isUserAddedToken)
const handleDismissWarning = useCallback(() => {
setWarningModalOpen(false)
}, [setWarningModalOpen])
const chainInfo = getChainInfo(token?.chainId)
const networkLabel = chainInfo?.label
const networkBadgebackgroundColor = chainInfo?.backgroundColor
// catch token error and loading state
if (!token || !token.name || !token.symbol) {
return <div>No Token</div>
}
const tokenName = token.name
const tokenSymbol = token.symbol
// TODO: format price, add sparkline
const aboutToken =
'Ethereum is a decentralized computing platform that uses ETH (Ether) to pay transaction fees (gas). Developers can use Ethereum to run decentralized applications (dApps) and issue new crypto assets, known as Ethereum tokens.'
const tokenMarketCap = '23.02B'
const tokenVolume = '1.6B'
const truncatedTokenAddress = `${address.slice(0, 4)}...${address.slice(-3)}`
return (
<TopArea>
<BreadcrumbNavLink to="/explore">
<ArrowLeft size={14} /> Explore
</BreadcrumbNavLink>
<ChartHeader>
<TokenInfoContainer>
<TokenNameCell>
<CurrencyLogo currency={currency} size={'32px'} />
{tokenName} <TokenSymbol>{tokenSymbol}</TokenSymbol>
{!warning && <VerifiedIcon size="24px" />}
{networkBadgebackgroundColor && (
<NetworkBadge networkColor={chainInfo?.color} backgroundColor={networkBadgebackgroundColor}>
{networkLabel}
</NetworkBadge>
)}
</TokenNameCell>
<TokenActions>
<ShareButton tokenName={tokenName} tokenSymbol={tokenSymbol} />
<ClickFavorited onClick={toggleFavorite}>
<Heart
size={15}
color={isFavorited ? theme.accentAction : theme.textSecondary}
fill={isFavorited ? theme.accentAction : theme.none}
/>
</ClickFavorited>
</TokenActions>
</TokenInfoContainer>
<ChartContainer>
<ParentSize>{({ width, height }) => <PriceChart width={width} height={height} />}</ParentSize>
</ChartContainer>
</ChartHeader>
<AboutSection>
<AboutHeader>
<Trans>About</Trans>
</AboutHeader>
{aboutToken}
<ResourcesContainer>
<Resource name={'Etherscan'} link={'https://etherscan.io/'} />
<Resource name={'Protocol Info'} link={`https://info.uniswap.org/#/tokens/${address}`} />
</ResourcesContainer>
</AboutSection>
<StatsSection>
<StatPair>
<Stat>
Market cap<StatPrice>${tokenMarketCap}</StatPrice>
</Stat>
<Stat>
{/* TODO: connect to chart's selected time */}
1h volume
<StatPrice>${tokenVolume}</StatPrice>
</Stat>
</StatPair>
<StatPair>
<Stat>
52W low
<StatPrice>$1,790.01</StatPrice>
</Stat>
<Stat>
52W high
<StatPrice>$4,420.71</StatPrice>
</Stat>
</StatPair>
</StatsSection>
<ContractAddressSection>
<Contract>
Contract Address
<ContractAddress onClick={() => navigator.clipboard.writeText(address)}>
<FullAddress>{address}</FullAddress>
<TruncatedAddress>{truncatedTokenAddress}</TruncatedAddress>
<Copy size={13} color={theme.textSecondary} />
</ContractAddress>
</Contract>
</ContractAddressSection>
<TokenSafetyModal
isOpen={warningModalOpen}
tokenAddress={address}
onCancel={() => navigate(-1)}
onContinue={handleDismissWarning}
/>
</TopArea>
)
}

View File

@@ -1,77 +0,0 @@
import searchIcon from 'assets/svg/search.svg'
import xIcon from 'assets/svg/x.svg'
import { useAtom } from 'jotai'
import { useState } from 'react'
import styled from 'styled-components/macro'
import { MEDIUM_MEDIA_BREAKPOINT } from '../constants'
import { filterStringAtom } from '../state'
const ICON_SIZE = '20px'
const SearchBarContainer = styled.div`
display: flex;
flex: 1;
`
const SearchInput = styled.input<{ expanded: boolean }>`
background: no-repeat scroll 7px 7px;
background-image: ${({ expanded }) => !expanded && `url(${searchIcon})`};
background-size: 20px 20px;
background-position: 14px center;
background-color: ${({ theme }) => theme.none};
border-radius: 12px;
border: 1px solid ${({ theme }) => theme.backgroundOutline};
height: 100%;
width: ${({ expanded }) => (expanded ? '100%' : '52px')};
font-size: 16px;
padding-left: 35px;
color: ${({ theme }) => theme.textPrimary};
transition: width 0.75s cubic-bezier(0, 0.795, 0, 1);
:hover {
cursor: ${({ expanded }) => !expanded && 'pointer'};
background-color: ${({ theme }) => theme.backgroundModule};
}
:focus {
outline: none;
background-color: ${({ theme }) => theme.accentActionSoft};
border: none;
}
::placeholder {
color: ${({ expanded, theme }) => (expanded ? theme.textTertiary : theme.none)};
}
::-webkit-search-cancel-button {
-webkit-appearance: none;
appearance: none;
height: ${ICON_SIZE};
width: ${ICON_SIZE};
background-image: url(${xIcon});
margin-right: 10px;
background-size: ${ICON_SIZE} ${ICON_SIZE};
cursor: pointer;
}
@media only screen and (max-width: ${MEDIUM_MEDIA_BREAKPOINT}) {
width: 100%;
}
`
export default function SearchBar() {
const [filterString, setFilterString] = useAtom(filterStringAtom)
const [isExpanded, setExpanded] = useState(false)
return (
<SearchBarContainer>
<SearchInput
expanded={isExpanded}
type="search"
placeholder="Search by name or token address"
id="searchBar"
onBlur={() => isExpanded && filterString.length === 0 && setExpanded(false)}
onFocus={() => setExpanded(true)}
autoComplete="off"
value={filterString}
onChange={({ target: { value } }) => setFilterString(value)}
/>
</SearchBarContainer>
)
}

View File

@@ -1,10 +1,12 @@
import { FeatureFlag, useUpdateFlag } from 'featureFlags'
import { ExploreVariant, useExploreFlag } from 'featureFlags/flags/explore'
import { BaseVariant, FeatureFlag, featureFlagSettings, useUpdateFlag } from 'featureFlags'
import { NavBarVariant, useNavBarFlag } from 'featureFlags/flags/navBar'
import { Phase1Variant, usePhase1Flag } from 'featureFlags/flags/phase1'
import { NftVariant, useNftFlag } from 'featureFlags/flags/nft'
import { RedesignVariant, useRedesignFlag } from 'featureFlags/flags/redesign'
import { TokensVariant, useTokensFlag } from 'featureFlags/flags/tokens'
import { TokenSafetyVariant, useTokenSafetyFlag } from 'featureFlags/flags/tokenSafety'
import { ReactNode } from 'react'
import { TokensNetworkFilterVariant, useTokensNetworkFilterFlag } from 'featureFlags/flags/tokensNetworkFilter'
import { useAtomValue, useUpdateAtom } from 'jotai/utils'
import { Children, PropsWithChildren, ReactElement, ReactNode, useCallback, useState } from 'react'
import { X } from 'react-feather'
import { useModalIsOpen, useToggleFeatureFlags } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer'
@@ -43,49 +45,151 @@ const Row = styled.div`
const CloseButton = styled.button`
cursor: pointer;
background: ${({ theme }) => theme.none};
background: transparent;
border: none;
color: ${({ theme }) => theme.textPrimary};
`
const ToggleButton = styled.button`
cursor: pointer;
background: transparent;
border: none;
color: ${({ theme }) => theme.textPrimary};
`
const Header = styled(Row)`
font-weight: 600;
font-size: 20px;
font-size: 16px;
border-bottom: 1px solid ${({ theme }) => theme.backgroundOutline};
margin-bottom: 8px;
`
const FlagName = styled.span`
font-size: 16px;
line-height: 20px;
color: ${({ theme }) => theme.textPrimary};
`
const FlagGroupName = styled.span`
font-size: 20px;
line-height: 24px;
color: ${({ theme }) => theme.textPrimary};
font-weight: 600;
`
const FlagDescription = styled.span`
font-size: 12px;
line-height: 16px;
color: ${({ theme }) => theme.textSecondary};
display: flex;
align-items: center;
`
const FlagVariantSelection = styled.select`
border-radius: 12px;
padding: 8px;
background: ${({ theme }) => theme.backgroundInteractive};
font-weight: 600;
font-size: 16px;
border: none;
color: ${({ theme }) => theme.textPrimary};
cursor: pointer;
:hover {
background: ${({ theme }) => theme.backgroundOutline};
}
`
const FlagInfo = styled.div`
display: flex;
flex-direction: column;
padding-left: 8px;
`
const SaveButton = styled.button`
border-radius: 12px;
padding: 8px;
background: ${({ theme }) => theme.backgroundInteractive};
font-weight: 600;
font-size: 16px;
border: none;
color: ${({ theme }) => theme.textPrimary};
cursor: pointer;
:hover {
background: ${({ theme }) => theme.backgroundOutline};
}
`
function Variant({ option }: { option: string }) {
return <option value={option}>{option}</option>
}
function FeatureFlagOption({
variants,
featureFlag,
value,
label,
}: {
variants: string[]
interface FeatureFlagProps {
variant: Record<string, string>
featureFlag: FeatureFlag
value: string
label: string
}) {
}
function FeatureFlagGroup({ name, children }: PropsWithChildren<{ name: string }>) {
// type FeatureFlagOption = { props: FeatureFlagProps }
const togglableOptions = Children.toArray(children)
.filter<ReactElement<FeatureFlagProps>>(
(child): child is ReactElement<FeatureFlagProps> =>
child instanceof Object && 'type' in child && child.type === FeatureFlagOption
)
.map(({ props }) => props)
.filter(({ variant }) => {
const values = Object.values(variant)
return values.includes(BaseVariant.Control) && values.includes(BaseVariant.Enabled)
})
const setFeatureFlags = useUpdateAtom(featureFlagSettings)
const allEnabled = togglableOptions.every(({ value }) => value === BaseVariant.Enabled)
const onToggle = useCallback(() => {
setFeatureFlags((flags) => ({
...flags,
...togglableOptions.reduce(
(flags, { featureFlag }) => ({
...flags,
[featureFlag]: allEnabled ? BaseVariant.Control : BaseVariant.Enabled,
}),
{}
),
}))
}, [allEnabled, setFeatureFlags, togglableOptions])
return (
<>
<Row key={name}>
<FlagGroupName>{name}</FlagGroupName>
<ToggleButton onClick={onToggle}>{allEnabled ? 'Disable' : 'Enable'} group</ToggleButton>
</Row>
{children}
</>
)
}
function FeatureFlagOption({ variant, featureFlag, value, label }: FeatureFlagProps) {
const updateFlag = useUpdateFlag()
const [count, setCount] = useState(0)
const featureFlags = useAtomValue(featureFlagSettings)
return (
<Row key={featureFlag}>
{featureFlag}: {label}
<select
<FlagInfo>
<FlagName>{featureFlag}</FlagName>
<FlagDescription>{label}</FlagDescription>
</FlagInfo>
<FlagVariantSelection
id={featureFlag}
value={value}
onChange={(e) => {
updateFlag(featureFlag, e.target.value)
window.location.reload()
setCount(count + 1)
}}
value={featureFlags[featureFlag]}
>
{variants.map((variant) => (
{Object.values(variant).map((variant) => (
<Variant key={variant} option={variant} />
))}
</select>
</FlagVariantSelection>
</Row>
)
}
@@ -102,37 +206,42 @@ export default function FeatureFlagModal() {
<X size={24} />
</CloseButton>
</Header>
<FeatureFlagOption
variants={Object.values(Phase1Variant)}
value={usePhase1Flag()}
featureFlag={FeatureFlag.phase1}
label="All Phase 1 changes (nft features)."
/>
<FeatureFlagOption
variants={Object.values(RedesignVariant)}
value={useRedesignFlag()}
featureFlag={FeatureFlag.redesign}
label="Redesign"
/>
<FeatureFlagOption
variants={Object.values(NavBarVariant)}
value={useNavBarFlag()}
featureFlag={FeatureFlag.navBar}
label="NavBar"
/>
<FeatureFlagOption
variants={Object.values(ExploreVariant)}
value={useExploreFlag()}
featureFlag={FeatureFlag.explore}
label="Explore"
/>
<FeatureFlagOption
variants={Object.values(TokenSafetyVariant)}
value={useTokenSafetyFlag()}
featureFlag={FeatureFlag.tokenSafety}
label="Token Safety"
/>
<FeatureFlagGroup name="Phase 0">
<FeatureFlagOption
variant={RedesignVariant}
value={useRedesignFlag()}
featureFlag={FeatureFlag.redesign}
label="Redesign"
/>
<FeatureFlagOption
variant={NavBarVariant}
value={useNavBarFlag()}
featureFlag={FeatureFlag.navBar}
label="NavBar"
/>
<FeatureFlagOption
variant={TokensVariant}
value={useTokensFlag()}
featureFlag={FeatureFlag.tokens}
label="Tokens"
/>
<FeatureFlagOption
variant={TokensNetworkFilterVariant}
value={useTokensNetworkFilterFlag()}
featureFlag={FeatureFlag.tokensNetworkFilter}
label="Tokens Network Filter"
/>
<FeatureFlagOption
variant={TokenSafetyVariant}
value={useTokenSafetyFlag()}
featureFlag={FeatureFlag.tokenSafety}
label="Token Safety"
/>
</FeatureFlagGroup>
<FeatureFlagGroup name="Phase 1">
<FeatureFlagOption variant={NftVariant} value={useNftFlag()} featureFlag={FeatureFlag.nft} label="NFTs" />
</FeatureFlagGroup>
<SaveButton onClick={() => window.location.reload()}>Reload</SaveButton>
</Modal>
)
}

View File

@@ -15,7 +15,7 @@ const ResponsiveText = styled(ThemedText.DeprecatedLabel)`
line-height: 16px;
font-size: 14px;
${({ theme }) => theme.mediaWidth.upToSmall`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
font-size: 12px;
line-height: 12px;
`};

View File

@@ -39,7 +39,7 @@ const Wrapper = styled.div`
padding: 16px 20px;
position: absolute;
right: 16px;
@media screen and (min-width: ${MEDIA_WIDTHS.upToMedium}px) {
@media screen and (min-width: ${MEDIA_WIDTHS.deprecated_upToMedium}px) {
display: block;
}
`

View File

@@ -11,7 +11,6 @@ import { useCloseModal, useModalIsOpen, useOpenModal, useToggleModal } from 'sta
import { ApplicationModal } from 'state/application/reducer'
import styled from 'styled-components/macro'
import { ExternalLink, MEDIA_WIDTHS } from 'theme'
import { isChainAllowed } from 'utils/switchChain'
import { isMobile } from 'utils/userAgent'
const ActiveRowLinkList = styled.div`
@@ -53,7 +52,7 @@ const FlyoutMenu = styled.div`
width: 272px;
z-index: 99;
padding-top: 10px;
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
@media screen and (min-width: ${MEDIA_WIDTHS.deprecated_upToSmall}px) {
top: 40px;
}
`
@@ -112,7 +111,7 @@ const NetworkLabel = styled.div`
`
const SelectorLabel = styled(NetworkLabel)`
display: none;
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
@media screen and (min-width: ${MEDIA_WIDTHS.deprecated_upToSmall}px) {
display: block;
margin-right: 8px;
}
@@ -126,7 +125,7 @@ const NetworkAlertLabel = styled(NetworkLabel)`
font-size: 1rem;
width: fit-content;
font-weight: 500;
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
@media screen and (min-width: ${MEDIA_WIDTHS.deprecated_upToSmall}px) {
display: block;
}
`
@@ -153,12 +152,12 @@ const SelectorControls = styled.div<{ supportedChain: boolean }>`
}
`
const SelectorLogo = styled(Logo)`
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
@media screen and (min-width: ${MEDIA_WIDTHS.deprecated_upToSmall}px) {
margin-right: 8px;
}
`
const SelectorWrapper = styled.div`
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
@media screen and (min-width: ${MEDIA_WIDTHS.deprecated_upToSmall}px) {
position: relative;
}
`
@@ -281,7 +280,7 @@ const NETWORK_SELECTOR_CHAINS = [
]
export default function NetworkSelector() {
const { chainId, provider, connector } = useWeb3React()
const { chainId, provider } = useWeb3React()
const node = useRef<HTMLDivElement>(null)
const isOpen = useModalIsOpen(ApplicationModal.NETWORK_SELECTOR)
@@ -328,18 +327,16 @@ export default function NetworkSelector() {
<FlyoutHeader>
<Trans>Select a {!onSupportedChain ? ' supported ' : ''}network</Trans>
</FlyoutHeader>
{NETWORK_SELECTOR_CHAINS.map((chainId: SupportedChainId) =>
isChainAllowed(connector, chainId) ? (
<Row
onSelectChain={async (targetChainId: SupportedChainId) => {
await selectChain(targetChainId)
closeModal()
}}
targetChain={chainId}
key={chainId}
/>
) : null
)}
{NETWORK_SELECTOR_CHAINS.map((chainId: SupportedChainId) => (
<Row
onSelectChain={async (targetChainId: SupportedChainId) => {
await selectChain(targetChainId)
closeModal()
}}
targetChain={chainId}
key={chainId}
/>
))}
</FlyoutMenuContents>
</FlyoutMenu>
)}

View File

@@ -5,12 +5,11 @@ import { getChainInfo } from 'constants/chainInfo'
import useCurrentBlockTimestamp from 'hooks/useCurrentBlockTimestamp'
import useGasPrice from 'hooks/useGasPrice'
import useMachineTimeMs from 'hooks/useMachineTime'
import useTheme from 'hooks/useTheme'
import JSBI from 'jsbi'
import useBlockNumber from 'lib/hooks/useBlockNumber'
import ms from 'ms.macro'
import { useEffect, useState } from 'react'
import styled, { keyframes } from 'styled-components/macro'
import styled, { keyframes, useTheme } from 'styled-components/macro'
import { ExternalLink, ThemedText } from 'theme'
import { ExplorerDataType, getExplorerLink } from 'utils/getExplorerLink'
@@ -27,7 +26,7 @@ const StyledPolling = styled.div<{ warning: boolean }>`
color: ${({ theme, warning }) => (warning ? theme.deprecated_yellow3 : theme.deprecated_green1)};
transition: 250ms ease color;
${({ theme }) => theme.mediaWidth.upToMedium`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
display: none;
`}
`

View File

@@ -3,8 +3,7 @@ import useScrollPosition from '@react-hook/window-scroll'
import { useWeb3React } from '@web3-react/core'
import { getChainInfoOrDefault } from 'constants/chainInfo'
import { SupportedChainId } from 'constants/chains'
import { ExploreVariant, useExploreFlag } from 'featureFlags/flags/explore'
import useTheme from 'hooks/useTheme'
import { TokensVariant, useTokensFlag } from 'featureFlags/flags/tokens'
import { darken } from 'polished'
import { NavLink, useLocation } from 'react-router-dom'
import { Text } from 'rebass'
@@ -13,7 +12,7 @@ import { useUserHasAvailableClaim } from 'state/claim/hooks'
import { useNativeCurrencyBalances } from 'state/connection/hooks'
import { useUserHasSubmittedClaim } from 'state/transactions/hooks'
import { useDarkModeManager } from 'state/user/hooks'
import styled from 'styled-components/macro'
import styled, { useTheme } from 'styled-components/macro'
import { ReactComponent as Logo } from '../../assets/svg/logo.svg'
import { ExternalLink, ThemedText } from '../../theme'
@@ -47,16 +46,16 @@ const HeaderFrame = styled.div<{ showBackground: boolean }>`
transition: background-position 0.1s, box-shadow 0.1s;
background-blend-mode: hard-light;
${({ theme }) => theme.mediaWidth.upToLarge`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToLarge`
grid-template-columns: 48px 1fr 1fr;
`};
${({ theme }) => theme.mediaWidth.upToMedium`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
padding: 1rem;
grid-template-columns: 1fr 1fr;
`};
${({ theme }) => theme.mediaWidth.upToSmall`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
padding: 1rem;
grid-template-columns: 36px 1fr;
`};
@@ -82,7 +81,7 @@ const HeaderElement = styled.div`
margin-left: 8px;
}
${({ theme }) => theme.mediaWidth.upToMedium`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
align-items: center;
`};
`
@@ -98,13 +97,13 @@ const HeaderLinks = styled(Row)`
grid-gap: 10px;
overflow: auto;
align-items: center;
${({ theme }) => theme.mediaWidth.upToLarge`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToLarge`
justify-self: start;
`};
${({ theme }) => theme.mediaWidth.upToMedium`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
justify-self: center;
`};
${({ theme }) => theme.mediaWidth.upToMedium`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
flex-direction: row;
justify-content: space-between;
justify-self: center;
@@ -158,7 +157,7 @@ const UNIWrapper = styled.span`
`
const BalanceText = styled(Text)`
${({ theme }) => theme.mediaWidth.upToExtraSmall`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToExtraSmall`
display: none;
`};
`
@@ -169,7 +168,7 @@ const Title = styled.a`
pointer-events: auto;
justify-self: flex-start;
margin-right: 12px;
${({ theme }) => theme.mediaWidth.upToSmall`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
justify-self: center;
`};
:hover {
@@ -245,7 +244,7 @@ const StyledExternalLink = styled(ExternalLink)`
`
export default function Header() {
const exploreFlag = useExploreFlag()
const tokensFlag = useTokensFlag()
const { account, chainId } = useWeb3React()
@@ -292,9 +291,9 @@ export default function Header() {
<StyledNavLink id={`swap-nav-link`} to={'/swap'}>
<Trans>Swap</Trans>
</StyledNavLink>
{exploreFlag === ExploreVariant.Enabled && (
<StyledNavLink id={`explore-nav-link`} to={'/explore'}>
<Trans>Explore</Trans>
{tokensFlag === TokensVariant.Enabled && (
<StyledNavLink id={`tokens-nav-link`} to={'/tokens'}>
<Trans>Tokens</Trans>
</StyledNavLink>
)}
<StyledNavLink

View File

@@ -0,0 +1,32 @@
import { ChevronDown, ChevronUp } from 'react-feather'
import styled from 'styled-components/macro'
export const StyledChevronDown = styled(ChevronDown)<{ customColor?: string }>`
color: ${({ theme, customColor }) => customColor ?? theme.textSecondary};
height: 20px;
width: 20px;
&:hover {
color: ${({ theme }) => theme.accentActionSoft};
transition: ${({
theme: {
transition: { duration, timing },
},
}) => `${duration.fast}ms color ${timing.in}`};
}
`
export const StyledChevronUp = styled(ChevronUp)<{ customColor?: string }>`
color: ${({ theme, customColor }) => customColor ?? theme.textSecondary};
height: 20px;
width: 20px;
&:hover {
color: ${({ theme }) => theme.accentActionSoft};
transition: ${({
theme: {
transition: { duration, timing },
},
}) => `${duration.fast}ms color ${timing.in}`};
}
`

View File

@@ -2,7 +2,6 @@ import { ConnectionType } from 'connection'
import styled from 'styled-components/macro'
import CoinbaseWalletIcon from '../../assets/images/coinbaseWalletIcon.svg'
import FortmaticIcon from '../../assets/images/fortmaticIcon.png'
import WalletConnectIcon from '../../assets/images/walletConnectIcon.svg'
import Identicon from '../Identicon'
@@ -16,12 +15,12 @@ const IconWrapper = styled.div<{ size?: number }>`
height: ${({ size }) => (size ? size + 'px' : '32px')};
width: ${({ size }) => (size ? size + 'px' : '32px')};
}
${({ theme }) => theme.mediaWidth.upToMedium`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
align-items: flex-end;
`};
`
export default function StatusIcon({ connectionType }: { connectionType: ConnectionType }) {
export default function StatusIcon({ connectionType, size }: { connectionType: ConnectionType; size?: number }) {
let image
switch (connectionType) {
case ConnectionType.INJECTED:
@@ -33,10 +32,7 @@ export default function StatusIcon({ connectionType }: { connectionType: Connect
case ConnectionType.COINBASE_WALLET:
image = <img src={CoinbaseWalletIcon} alt="Coinbase Wallet" />
break
case ConnectionType.FORTMATIC:
image = <img src={FortmaticIcon} alt="Fortmatic" />
break
}
return <IconWrapper size={16}>{image}</IconWrapper>
return <IconWrapper size={size ?? 16}>{image}</IconWrapper>
}

View File

@@ -48,11 +48,11 @@ const StyledInput = styled(NumericalInput)<{ usePercent?: boolean }>`
font-weight: 500;
padding: 0 10px;
${({ theme }) => theme.mediaWidth.upToSmall`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
font-size: 16px;
`};
${({ theme }) => theme.mediaWidth.upToExtraSmall`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToExtraSmall`
font-size: 12px;
`};
`

View File

@@ -14,7 +14,7 @@ export function useDensityChartData({
currencyB: Currency | undefined
feeAmount: FeeAmount | undefined
}) {
const { isLoading, isUninitialized, isError, error, data } = usePoolActiveLiquidity(currencyA, currencyB, feeAmount)
const { isLoading, error, data } = usePoolActiveLiquidity(currencyA, currencyB, feeAmount)
const formatData = useCallback(() => {
if (!data?.length) {
@@ -42,10 +42,8 @@ export function useDensityChartData({
return useMemo(() => {
return {
isLoading,
isUninitialized,
isError,
error,
formattedData: !isLoading && !isUninitialized ? formatData() : undefined,
formattedData: !isLoading ? formatData() : undefined,
}
}, [isLoading, isUninitialized, isError, error, formatData])
}, [isLoading, error, formatData])
}

View File

@@ -6,13 +6,12 @@ import { AutoColumn, ColumnCenter } from 'components/Column'
import Loader from 'components/Loader'
import { format } from 'd3'
import { useColor } from 'hooks/useColor'
import useTheme from 'hooks/useTheme'
import { saturate } from 'polished'
import React, { ReactNode, useCallback, useMemo } from 'react'
import { BarChart2, CloudOff, Inbox } from 'react-feather'
import { batch } from 'react-redux'
import { Bound } from 'state/mint/v3/actions'
import styled from 'styled-components/macro'
import styled, { useTheme } from 'styled-components/macro'
import { ThemedText } from '../../theme'
import { Chart } from './Chart'
@@ -96,7 +95,7 @@ export default function LiquidityChartRangeInput({
const isSorted = currencyA && currencyB && currencyA?.wrapped.sortsBefore(currencyB?.wrapped)
const { isLoading, isUninitialized, isError, error, formattedData } = useDensityChartData({
const { isLoading, error, formattedData } = useDensityChartData({
currencyA,
currencyB,
feeAmount,
@@ -157,10 +156,12 @@ export default function LiquidityChartRangeInput({
[isSorted, price, ticksAtLimit]
)
if (isError) {
if (error) {
sendEvent('exception', { description: error.toString(), fatal: false })
}
const isUninitialized = !currencyA || !currencyB || (formattedData === undefined && !isLoading)
return (
<AutoColumn gap="md" style={{ minHeight: '200px' }}>
{isUninitialized ? (
@@ -170,7 +171,7 @@ export default function LiquidityChartRangeInput({
/>
) : isLoading ? (
<InfoBox icon={<Loader size="40px" stroke={theme.deprecated_text4} />} />
) : isError ? (
) : error ? (
<InfoBox
message={<Trans>Liquidity data not available.</Trans>}
icon={<CloudOff size={56} stroke={theme.deprecated_text4} />}

View File

@@ -9,12 +9,13 @@ const rotate = keyframes`
}
`
const StyledSVG = styled.svg<{ size: string; stroke?: string }>`
const StyledSVG = styled.svg<{ size: string; stroke?: string; redesignFlag?: boolean }>`
animation: 2s ${rotate} linear infinite;
height: ${({ size }) => size};
width: ${({ size }) => size};
path {
stroke: ${({ stroke, theme }) => stroke ?? theme.deprecated_primary1};
stroke: ${({ stroke, redesignFlag, theme }) =>
redesignFlag ? theme.accentActive : stroke ?? theme.deprecated_primary1};
}
`
@@ -25,17 +26,29 @@ const StyledSVG = styled.svg<{ size: string; stroke?: string }>`
export default function Loader({
size = '16px',
stroke,
strokeWidth,
redesignFlag,
...rest
}: {
size?: string
stroke?: string
strokeWidth?: number
redesignFlag?: boolean
[k: string]: any
}) {
return (
<StyledSVG viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" size={size} stroke={stroke} {...rest}>
<StyledSVG
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
size={size}
stroke={stroke}
redesignFlag={redesignFlag}
{...rest}
>
<path
d="M12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22C17.5228 22 22 17.5228 22 12C22 9.27455 20.9097 6.80375 19.1414 5"
strokeWidth="2.5"
strokeWidth={strokeWidth ?? '2.5'}
strokeLinecap="round"
strokeLinejoin="round"
/>

View File

@@ -1,8 +1,7 @@
import { useState } from 'react'
import { Slash } from 'react-feather'
import { ImageProps } from 'rebass'
import useTheme from '../../hooks/useTheme'
import { useTheme } from 'styled-components/macro'
const BAD_SRCS: { [tokenAddress: string]: true } = {}

View File

@@ -14,6 +14,7 @@ import {
ChevronLeft,
Coffee,
FileText,
Flag,
Globe,
HelpCircle,
Info,
@@ -109,7 +110,7 @@ const MenuFlyout = styled.span<{ flyoutAlignment?: FlyoutAlignment }>`
: css`
left: 0rem;
`};
${({ theme }) => theme.mediaWidth.upToMedium`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
bottom: unset;
right: 0;
left: unset;
@@ -291,6 +292,11 @@ export default function Menu() {
</div>
<FileText opacity={0.6} size={16} />
</ToggleMenuItem>
{(isDevelopmentEnv() || isStagingEnv()) && (
<ToggleMenuItem onClick={openFeatureFlagsModal}>
Feature Flags <Flag opacity={0.6} size={16} />
</ToggleMenuItem>
)}
{showUNIClaimOption && (
<UNIbutton
onClick={openClaimModal}
@@ -302,9 +308,6 @@ export default function Menu() {
<Trans>Claim UNI</Trans>
</UNIbutton>
)}
{(isDevelopmentEnv() || isStagingEnv()) && (
<ToggleMenuItem onClick={openFeatureFlagsModal}>Feature Flags</ToggleMenuItem>
)}
</MenuFlyout>
)
}

View File

@@ -9,7 +9,7 @@ import { isMobile } from '../../utils/userAgent'
const AnimatedDialogOverlay = animated(DialogOverlay)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const StyledDialogOverlay = styled(AnimatedDialogOverlay)`
const StyledDialogOverlay = styled(AnimatedDialogOverlay)<{ redesignFlag?: boolean }>`
&[data-reach-dialog-overlay] {
z-index: 2;
background-color: transparent;
@@ -19,14 +19,14 @@ const StyledDialogOverlay = styled(AnimatedDialogOverlay)`
align-items: center;
justify-content: center;
background-color: ${({ theme }) => theme.deprecated_modalBG};
background-color: ${({ theme, redesignFlag }) => (redesignFlag ? theme.backgroundScrim : theme.deprecated_modalBG)};
}
`
const AnimatedDialogContent = animated(DialogContent)
// destructure to not pass custom props to Dialog DOM element
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const StyledDialogContent = styled(({ minHeight, maxHeight, mobile, isOpen, ...rest }) => (
const StyledDialogContent = styled(({ minHeight, maxHeight, mobile, isOpen, redesignFlag, ...rest }) => (
<AnimatedDialogContent {...rest} />
)).attrs({
'aria-label': 'dialog',
@@ -37,7 +37,8 @@ const StyledDialogContent = styled(({ minHeight, maxHeight, mobile, isOpen, ...r
margin: 0 0 2rem 0;
background-color: ${({ theme }) => theme.deprecated_bg0};
border: 1px solid ${({ theme }) => theme.deprecated_bg1};
box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.95, theme.shadow1)};
box-shadow: ${({ theme, redesignFlag }) =>
redesignFlag ? theme.deepShadow : `0 4px 8px 0 ${transparentize(0.95, theme.shadow1)}`};
padding: 0px;
width: 50vw;
overflow-y: auto;
@@ -58,11 +59,11 @@ const StyledDialogContent = styled(({ minHeight, maxHeight, mobile, isOpen, ...r
`}
display: flex;
border-radius: 20px;
${({ theme }) => theme.mediaWidth.upToMedium`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
width: 65vw;
margin: 0;
`}
${({ theme, mobile }) => theme.mediaWidth.upToSmall`
${({ theme, mobile }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
width: 85vw;
${
mobile &&
@@ -84,6 +85,7 @@ interface ModalProps {
maxHeight?: number
initialFocusRef?: React.RefObject<any>
children?: React.ReactNode
redesignFlag?: boolean
}
export default function Modal({
@@ -93,6 +95,7 @@ export default function Modal({
maxHeight = 90,
initialFocusRef,
children,
redesignFlag,
}: ModalProps) {
const fadeTransition = useTransition(isOpen, null, {
config: { duration: 200 },
@@ -124,6 +127,7 @@ export default function Modal({
onDismiss={onDismiss}
initialFocusRef={initialFocusRef}
unstable_lockFocusAcrossFrames={false}
redesignFlag={redesignFlag}
>
<StyledDialogContent
{...(isMobile
@@ -136,6 +140,7 @@ export default function Modal({
minHeight={minHeight}
maxHeight={maxHeight}
mobile={isMobile}
redesignFlag={redesignFlag}
>
{/* prevents the automatic focusing of inputs on mobile by the reach dialog */}
{!initialFocusRef && isMobile ? <div tabIndex={1} /> : null}

View File

@@ -1,8 +1,7 @@
import { Trans } from '@lingui/macro'
import { useWeb3React } from '@web3-react/core'
import { useContext } from 'react'
import { ArrowUpCircle } from 'react-feather'
import styled, { ThemeContext } from 'styled-components/macro'
import styled, { useTheme } from 'styled-components/macro'
import Circle from '../../assets/images/blue-loader.svg'
import { CloseIcon, CustomLightSpinner, ThemedText } from '../../theme'
@@ -49,7 +48,7 @@ export function SubmittedView({
onDismiss: () => void
hash: string | undefined
}) {
const theme = useContext(ThemeContext)
const theme = useTheme()
const { chainId } = useWeb3React()
return (

View File

@@ -0,0 +1,53 @@
import { style } from '@vanilla-extract/css'
import { lightGrayOverlayOnHover } from 'nft/css/common.css'
import { breakpoints, sprinkles } from '../../nft/css/sprinkles.css'
export const ChainSwitcher = style([
lightGrayOverlayOnHover,
sprinkles({
borderRadius: '8',
paddingY: '8',
paddingX: '12',
cursor: 'pointer',
border: 'none',
color: 'blackBlue',
background: 'none',
}),
])
export const ChainSwitcherRow = style([
lightGrayOverlayOnHover,
sprinkles({
border: 'none',
justifyContent: 'space-between',
paddingX: '8',
paddingY: '8',
cursor: 'pointer',
color: 'blackBlue',
borderRadius: '12',
width: { sm: 'full' },
}),
{
lineHeight: '24px',
'@media': {
[`screen and (min-width: ${breakpoints.sm}px)`]: {
width: '204px',
},
},
},
])
export const Image = style([
sprinkles({
width: '20',
height: '20',
}),
])
export const Icon = style([
Image,
sprinkles({
marginRight: '12',
}),
])

View File

@@ -0,0 +1,129 @@
import { useWeb3React } from '@web3-react/core'
import { StyledChevronDown, StyledChevronUp } from 'components/Icons'
import { getChainInfo } from 'constants/chainInfo'
import { SupportedChainId } from 'constants/chains'
import { useOnClickOutside } from 'hooks/useOnClickOutside'
import useSelectChain from 'hooks/useSelectChain'
import useSyncChainQuery from 'hooks/useSyncChainQuery'
import { Box } from 'nft/components/Box'
import { Portal } from 'nft/components/common/Portal'
import { Column, Row } from 'nft/components/Flex'
import { CheckMarkIcon, TokenWarningRedIcon } from 'nft/components/icons'
import { subhead } from 'nft/css/common.css'
import { themeVars, vars } from 'nft/css/sprinkles.css'
import { useIsMobile } from 'nft/hooks'
import { ReactNode, useReducer, useRef } from 'react'
import * as styles from './ChainSwitcher.css'
import { NavDropdown } from './NavDropdown'
const ChainRow = ({
targetChain,
onSelectChain,
}: {
targetChain: SupportedChainId
onSelectChain: (targetChain: number) => void
}) => {
const { chainId } = useWeb3React()
const active = chainId === targetChain
const { label, logoUrl } = getChainInfo(targetChain)
return (
<Column borderRadius="12">
<Row
as="button"
background="none"
className={`${styles.ChainSwitcherRow} ${subhead}`}
onClick={() => onSelectChain(targetChain)}
>
<ChainDetails>
<img src={logoUrl} alt={label} className={styles.Icon} />
{label}
</ChainDetails>
{active && <CheckMarkIcon width={20} height={20} color={vars.color.blue400} />}
</Row>
</Column>
)
}
const ChainDetails = ({ children }: { children: ReactNode }) => <Row>{children}</Row>
const NETWORK_SELECTOR_CHAINS = [
SupportedChainId.MAINNET,
SupportedChainId.POLYGON,
SupportedChainId.OPTIMISM,
SupportedChainId.ARBITRUM_ONE,
SupportedChainId.CELO,
]
interface ChainSwitcherProps {
leftAlign?: boolean
}
export const ChainSwitcher = ({ leftAlign }: ChainSwitcherProps) => {
const { chainId } = useWeb3React()
const [isOpen, toggleOpen] = useReducer((s) => !s, false)
const isMobile = useIsMobile()
const ref = useRef<HTMLDivElement>(null)
const modalRef = useRef<HTMLDivElement>(null)
useOnClickOutside(ref, isOpen ? toggleOpen : undefined, [modalRef])
const info = chainId ? getChainInfo(chainId) : undefined
const selectChain = useSelectChain()
useSyncChainQuery()
if (!chainId) {
return null
}
const isSupported = !!info
const dropdown = (
<NavDropdown top="56" left={leftAlign ? '0' : 'auto'} right={leftAlign ? 'auto' : '0'} ref={modalRef}>
<Column marginX="8">
{NETWORK_SELECTOR_CHAINS.map((chainId: SupportedChainId) => (
<ChainRow
onSelectChain={async (targetChainId: SupportedChainId) => {
await selectChain(targetChainId)
toggleOpen()
}}
targetChain={chainId}
key={chainId}
/>
))}
</Column>
</NavDropdown>
)
return (
<Box position="relative" ref={ref}>
<Row
as="button"
gap="8"
className={styles.ChainSwitcher}
background={isOpen ? 'accentActiveSoft' : 'none'}
onClick={toggleOpen}
>
{!isSupported ? (
<>
<TokenWarningRedIcon fill={themeVars.colors.darkGray} width={24} height={24} />
<Box as="span" className={subhead} display={{ sm: 'none', xxl: 'flex' }} style={{ lineHeight: '20px' }}>
Unsupported
</Box>
</>
) : (
<>
<img src={info.logoUrl} alt={info.label} className={styles.Image} />
<Box as="span" className={subhead} display={{ sm: 'none', xxl: 'flex' }} style={{ lineHeight: '20px' }}>
{info.label}
</Box>
</>
)}
{isOpen ? <StyledChevronUp /> : <StyledChevronDown />}
</Row>
{isOpen && (isMobile ? <Portal>{dropdown}</Portal> : <>{dropdown}</>)}
</Box>
)
}

View File

@@ -0,0 +1,67 @@
import { style } from '@vanilla-extract/css'
import { sprinkles, themeVars, vars } from '../../nft/css/sprinkles.css'
export const hover = style([
sprinkles({
transition: '250',
borderRadius: '12',
}),
{
':hover': {
background: vars.color.lightGrayOverlay,
},
},
])
export const MenuRow = style([
hover,
sprinkles({
color: 'blackBlue',
paddingY: '8',
paddingX: '8',
width: 'full',
whiteSpace: 'nowrap',
}),
{
lineHeight: '24px',
textDecoration: 'none',
},
])
export const PrimaryText = style([
{
lineHeight: '24px',
},
])
export const SecondaryText = style([
hover,
sprinkles({
paddingY: '8',
paddingX: '8',
color: 'darkGray',
}),
{
lineHeight: '20px',
},
])
export const Separator = style([
sprinkles({
height: '0',
marginX: '16',
}),
{
borderTop: 'solid',
borderColor: themeVars.colors.medGray,
borderWidth: '1px',
},
])
export const IconRow = style([
sprinkles({
paddingX: '16',
justifyContent: { sm: 'center', md: 'flex-start' },
}),
])

View File

@@ -0,0 +1,210 @@
import { Trans } from '@lingui/macro'
import FeatureFlagModal from 'components/FeatureFlagModal/FeatureFlagModal'
import { PrivacyPolicyModal } from 'components/PrivacyPolicy'
import { NftVariant, useNftFlag } from 'featureFlags/flags/nft'
import { useOnClickOutside } from 'hooks/useOnClickOutside'
import { Box } from 'nft/components/Box'
import { Column, Row } from 'nft/components/Flex'
import {
BarChartIcon,
DiscordIconMenu,
EllipsisIcon,
GithubIconMenu,
GovernanceIcon,
ThinTagIcon,
TwitterIconMenu,
} from 'nft/components/icons'
import { body, bodySmall } from 'nft/css/common.css'
import { themeVars } from 'nft/css/sprinkles.css'
import { ReactNode, useReducer, useRef } from 'react'
import { NavLink, NavLinkProps } from 'react-router-dom'
import { isDevelopmentEnv, isStagingEnv } from 'utils/env'
import { useToggleModal } from '../../state/application/hooks'
import { ApplicationModal } from '../../state/application/reducer'
import * as styles from './MenuDropdown.css'
import { NavDropdown } from './NavDropdown'
import { NavIcon } from './NavIcon'
const PrimaryMenuRow = ({
to,
href,
close,
children,
}: {
to?: NavLinkProps['to']
href?: string
close?: () => void
children: ReactNode
}) => {
return (
<>
{to ? (
<NavLink to={to} className={styles.MenuRow}>
<Row onClick={close}>{children}</Row>
</NavLink>
) : (
<Row as="a" href={href} target={'_blank'} rel={'noopener noreferrer'} className={styles.MenuRow}>
{children}
</Row>
)}
</>
)
}
const PrimaryMenuRowText = ({ children }: { children: ReactNode }) => {
return <Box className={`${styles.PrimaryText} ${body}`}>{children}</Box>
}
PrimaryMenuRow.Text = PrimaryMenuRowText
const SecondaryLinkedText = ({
href,
onClick,
children,
}: {
href?: string
onClick?: () => void
children: ReactNode
}) => {
return (
<Box
as={href ? 'a' : 'div'}
href={href ?? undefined}
target={href ? '_blank' : undefined}
rel={href ? 'noopener noreferrer' : undefined}
className={`${styles.SecondaryText} ${bodySmall}`}
onClick={onClick}
cursor="pointer"
>
{children}
</Box>
)
}
const Separator = () => {
return <Box className={styles.Separator} />
}
const IconRow = ({ children }: { children: ReactNode }) => {
return <Row className={styles.IconRow}>{children}</Row>
}
const Icon = ({ href, children }: { href?: string; children: ReactNode }) => {
return (
<>
<Box
as={href ? 'a' : 'div'}
href={href ?? undefined}
target={href ? '_blank' : undefined}
rel={href ? 'noopener noreferrer' : undefined}
display="flex"
flexDirection="column"
color="blackBlue"
background="none"
border="none"
justifyContent="center"
textAlign="center"
marginRight="12"
>
{children}
</Box>
</>
)
}
export const MenuDropdown = () => {
const [isOpen, toggleOpen] = useReducer((s) => !s, false)
const togglePrivacyPolicy = useToggleModal(ApplicationModal.PRIVACY_POLICY)
const openFeatureFlagsModal = useToggleModal(ApplicationModal.FEATURE_FLAGS)
const nftFlag = useNftFlag()
const ref = useRef<HTMLDivElement>(null)
useOnClickOutside(ref, isOpen ? toggleOpen : undefined)
return (
<>
<Box position="relative" ref={ref}>
<NavIcon isActive={isOpen} onClick={toggleOpen}>
<EllipsisIcon />
</NavIcon>
{isOpen && (
<NavDropdown top={{ sm: 'unset', lg: '56' }} bottom={{ sm: '56', lg: 'unset' }} right="0">
<Column gap="16">
<Column paddingX="8" gap="4">
{nftFlag === NftVariant.Enabled && (
<PrimaryMenuRow to="/nft/sell" close={toggleOpen}>
<Icon>
<ThinTagIcon width={24} height={24} />
</Icon>
<PrimaryMenuRow.Text>
<Trans>Sell NFTs</Trans>
</PrimaryMenuRow.Text>
</PrimaryMenuRow>
)}
<PrimaryMenuRow to="/vote" close={toggleOpen}>
<Icon>
<GovernanceIcon width={24} height={24} />
</Icon>
<PrimaryMenuRow.Text>
<Trans>Vote in governance</Trans>
</PrimaryMenuRow.Text>
</PrimaryMenuRow>
<PrimaryMenuRow href="https://info.uniswap.org/#/">
<Icon>
<BarChartIcon width={24} height={24} />
</Icon>
<PrimaryMenuRow.Text>
<Trans>View token analytics</Trans>
</PrimaryMenuRow.Text>
</PrimaryMenuRow>
</Column>
<Separator />
<Box
display="flex"
flexDirection={{ sm: 'row', md: 'column' }}
flexWrap="wrap"
alignItems={{ sm: 'center', md: 'flex-start' }}
paddingX="8"
>
<SecondaryLinkedText href="https://help.uniswap.org/en/">
<Trans>Help center</Trans>
</SecondaryLinkedText>
<SecondaryLinkedText href="https://docs.uniswap.org/">
<Trans>Documentation</Trans>
</SecondaryLinkedText>
<SecondaryLinkedText
onClick={() => {
toggleOpen()
togglePrivacyPolicy()
}}
>
<Trans>Legal & Privacy</Trans>
</SecondaryLinkedText>
{(isDevelopmentEnv() || isStagingEnv()) && (
<SecondaryLinkedText onClick={openFeatureFlagsModal}>
<Trans>Feature Flags</Trans>
</SecondaryLinkedText>
)}
</Box>
<IconRow>
<Icon href="https://discord.com/invite/FCfyBSbCU5">
<DiscordIconMenu className={styles.hover} width={24} height={24} color={themeVars.colors.darkGray} />
</Icon>
<Icon href="https://twitter.com/Uniswap">
<TwitterIconMenu className={styles.hover} width={24} height={24} color={themeVars.colors.darkGray} />
</Icon>
<Icon href="https://github.com/Uniswap">
<GithubIconMenu className={styles.hover} width={24} height={24} color={themeVars.colors.darkGray} />
</Icon>
</IconRow>
</Column>
</NavDropdown>
)}
</Box>
<PrivacyPolicyModal />
<FeatureFlagModal />
</>
)
}

View File

@@ -0,0 +1,45 @@
import { style } from '@vanilla-extract/css'
import { sprinkles } from '../../nft/css/sprinkles.css'
const baseNavDropdown = style([
sprinkles({
background: 'lightGray',
borderStyle: 'solid',
borderColor: 'medGray',
borderWidth: '1px',
paddingBottom: '8',
paddingTop: '8',
zIndex: '2',
}),
{
boxShadow: '0px 4px 12px 0px #00000026',
},
])
export const NavDropdown = style([
baseNavDropdown,
sprinkles({
position: 'absolute',
borderRadius: '12',
}),
{},
])
export const mobileNavDropdown = style([
baseNavDropdown,
sprinkles({
position: 'fixed',
borderTopRightRadius: '12',
borderTopLeftRadius: '12',
top: 'unset',
bottom: '56',
left: '0',
right: '0',
width: 'full',
}),
{
borderRightWidth: '0px',
borderLeftWidth: '0px',
},
])

View File

@@ -0,0 +1,12 @@
import { Box, BoxProps } from 'nft/components/Box'
import { useIsMobile } from 'nft/hooks'
import { ForwardedRef, forwardRef } from 'react'
import * as styles from './NavDropdown.css'
export const NavDropdown = forwardRef((props: BoxProps, ref: ForwardedRef<HTMLElement>) => {
const isMobile = useIsMobile()
return <Box ref={ref} className={isMobile ? styles.mobileNavDropdown : styles.NavDropdown} {...props} />
})
NavDropdown.displayName = 'NavDropdown'

View File

@@ -0,0 +1,24 @@
import { style } from '@vanilla-extract/css'
import { sprinkles, vars } from '../../nft/css/sprinkles.css'
export const navIcon = style([
sprinkles({
position: 'relative',
display: 'flex',
flexDirection: 'column',
border: 'none',
justifyContent: 'center',
textAlign: 'center',
cursor: 'pointer',
padding: '8',
borderRadius: '8',
transition: '250',
}),
{
':hover': {
background: vars.color.lightGrayOverlay,
},
zIndex: 1,
},
])

View File

@@ -0,0 +1,24 @@
import { Box } from 'nft/components/Box'
import { ReactNode } from 'react'
import * as styles from './NavIcon.css'
interface NavIconProps {
children: ReactNode
isActive?: boolean
onClick: () => void
}
export const NavIcon = ({ children, isActive, onClick }: NavIconProps) => {
return (
<Box
as="button"
className={styles.navIcon}
background={isActive ? 'accentActiveSoft' : 'none'}
color={isActive ? 'blackBlue' : 'darkGray'}
onClick={onClick}
>
{children}
</Box>
)
}

View File

@@ -0,0 +1,124 @@
import { style } from '@vanilla-extract/css'
import { subhead } from '../../nft/css/common.css'
import { sprinkles, vars } from '../../nft/css/sprinkles.css'
export const nav = style([
sprinkles({
paddingX: '20',
paddingY: '12',
width: 'full',
height: '72',
zIndex: '2',
background: 'white08',
}),
{
backdropFilter: 'blur(24px)',
},
])
export const logoContainer = style([
sprinkles({
display: 'flex',
marginRight: { sm: '12', xxl: '20' },
alignItems: 'center',
}),
])
export const logo = style([
sprinkles({
display: 'block',
color: 'blackBlue',
}),
])
export const baseContainer = style([
sprinkles({
alignItems: 'center',
}),
])
export const baseSideContainer = style([
baseContainer,
sprinkles({
display: 'flex',
width: 'full',
flex: '1',
flexShrink: '2',
}),
])
export const leftSideContainer = style([
baseSideContainer,
sprinkles({
justifyContent: 'flex-start',
}),
])
export const middleContainer = style([
baseContainer,
sprinkles({
flex: '1',
flexShrink: '1',
justifyContent: 'center',
display: { sm: 'none', xl: 'flex' },
}),
])
export const rightSideContainer = style([
baseSideContainer,
sprinkles({
justifyContent: 'flex-end',
}),
])
const baseMenuItem = style([
subhead,
sprinkles({
paddingY: '8',
paddingX: '16',
marginY: '4',
borderRadius: '12',
transition: '250',
height: 'min',
width: 'full',
textAlign: 'center',
}),
{
lineHeight: '24px',
textDecoration: 'none',
':hover': {
background: vars.color.lightGrayOverlay,
},
},
])
export const menuItem = style([
baseMenuItem,
sprinkles({
color: 'darkGray',
}),
])
export const activeMenuItem = style([
baseMenuItem,
sprinkles({
color: 'blackBlue',
background: 'backgroundFloating',
}),
])
export const mobileBottomBar = style([
sprinkles({
position: 'fixed',
display: { sm: 'flex', lg: 'none' },
bottom: '0',
right: '0',
left: '0',
justifyContent: 'space-between',
paddingY: '4',
paddingX: '8',
height: '56',
background: 'lightGray',
}),
])

View File

@@ -0,0 +1,112 @@
import { Trans } from '@lingui/macro'
import Web3Status from 'components/Web3Status'
import { NftVariant, useNftFlag } from 'featureFlags/flags/nft'
import { ReactNode } from 'react'
import { NavLink, NavLinkProps, useLocation } from 'react-router-dom'
import { Box } from '../../nft/components/Box'
import { Row } from '../../nft/components/Flex'
import { UniIcon } from '../../nft/components/icons'
import { ChainSwitcher } from './ChainSwitcher'
import { MenuDropdown } from './MenuDropdown'
import * as styles from './Navbar.css'
import { SearchBar } from './SearchBar'
interface MenuItemProps {
href: string
id?: NavLinkProps['id']
isActive?: boolean
children: ReactNode
}
const MenuItem = ({ href, id, isActive, children }: MenuItemProps) => {
return (
<NavLink
to={href}
className={isActive ? styles.activeMenuItem : styles.menuItem}
id={id}
style={{ textDecoration: 'none' }}
>
{children}
</NavLink>
)
}
const PageTabs = () => {
const { pathname } = useLocation()
const nftFlag = useNftFlag()
const isPoolActive =
pathname.startsWith('/pool') ||
pathname.startsWith('/add') ||
pathname.startsWith('/remove') ||
pathname.startsWith('/increase') ||
pathname.startsWith('/find')
return (
<>
<MenuItem href="/swap" isActive={pathname.startsWith('/swap')}>
<Trans>Swap</Trans>
</MenuItem>
<MenuItem href="/tokens" isActive={pathname.startsWith('/tokens')}>
<Trans>Tokens</Trans>
</MenuItem>
{nftFlag === NftVariant.Enabled && (
<MenuItem href="/nfts" isActive={pathname.startsWith('/nfts')}>
<Trans>NFTs</Trans>
</MenuItem>
)}
<MenuItem href="/pool" id={'pool-nav-link'} isActive={isPoolActive}>
<Trans>Pool</Trans>
</MenuItem>
</>
)
}
const Navbar = () => {
return (
<>
<nav className={styles.nav}>
<Box display="flex" height="full" flexWrap="nowrap" alignItems="stretch">
<Box className={styles.leftSideContainer}>
<Box as="a" href="#/swap" className={styles.logoContainer}>
<UniIcon width="48" height="48" className={styles.logo} />
</Box>
<Box display={{ sm: 'flex', lg: 'none' }}>
<ChainSwitcher leftAlign={true} />
</Box>
<Row gap="8" display={{ sm: 'none', lg: 'flex' }}>
<PageTabs />
</Row>
</Box>
<Box className={styles.middleContainer}>
<SearchBar />
</Box>
<Box className={styles.rightSideContainer}>
<Row gap="12">
<Box display={{ sm: 'flex', xl: 'none' }}>
<SearchBar />
</Box>
<Box display={{ sm: 'none', lg: 'flex' }}>
<MenuDropdown />
</Box>
<Box display={{ sm: 'none', lg: 'flex' }}>
<ChainSwitcher />
</Box>
<Web3Status />
</Row>
</Box>
</Box>
</nav>
<Box className={styles.mobileBottomBar}>
<PageTabs />
<Box marginY="4">
<MenuDropdown />
</Box>
</Box>
</>
)
}
export default Navbar

View File

@@ -0,0 +1,168 @@
import { style } from '@vanilla-extract/css'
import { buttonTextSmall, subhead, subheadSmall } from 'nft/css/common.css'
import { breakpoints, sprinkles, vars } from '../../nft/css/sprinkles.css'
const DESKTOP_NAVBAR_WIDTH = 360
const baseSearchStyle = style([
sprinkles({
paddingY: '12',
width: { sm: 'viewWidth' },
borderStyle: 'solid',
borderWidth: '1px',
borderColor: 'medGray',
}),
{
'@media': {
[`screen and (min-width: ${breakpoints.sm}px)`]: {
width: `${DESKTOP_NAVBAR_WIDTH}px`,
},
},
},
])
export const searchBarContainer = style([
sprinkles({
right: '0',
top: '0',
zIndex: '3',
}),
{
'@media': {
[`screen and (min-width: ${breakpoints.sm}px)`]: {
top: '-24px',
},
[`screen and (min-width: ${breakpoints.lg}px)`]: {
right: `-${DESKTOP_NAVBAR_WIDTH / 2}px`,
},
},
},
])
export const searchBar = style([
baseSearchStyle,
sprinkles({
height: 'full',
color: 'placeholder',
paddingX: '16',
cursor: 'pointer',
background: 'lightGray',
}),
])
export const searchBarInput = style([
sprinkles({
padding: '0',
fontWeight: 'normal',
fontSize: '16',
color: { default: 'blackBlue', placeholder: 'placeholder' },
border: 'none',
background: 'none',
}),
{ lineHeight: '24px' },
])
export const searchBarDropdown = style([
baseSearchStyle,
sprinkles({
borderBottomLeftRadius: '12',
borderBottomRightRadius: '12',
background: 'lightGray',
}),
{
borderTop: 'none',
},
])
export const suggestionRow = style([
sprinkles({
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingY: '8',
paddingX: '16',
transition: '250',
}),
{
':hover': {
cursor: 'pointer',
background: vars.color.lightGrayOverlay,
},
textDecoration: 'none',
},
])
export const suggestionImage = sprinkles({
width: '36',
height: '36',
borderRadius: 'round',
marginRight: '8',
})
export const suggestionPrimaryContainer = style([
sprinkles({
alignItems: 'flex-start',
width: 'full',
}),
])
export const suggestionSecondaryContainer = sprinkles({
textAlign: 'right',
alignItems: 'flex-end',
})
export const primaryText = style([
subhead,
sprinkles({
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
color: 'blackBlue',
}),
{
lineHeight: '24px',
},
])
export const secondaryText = style([
buttonTextSmall,
sprinkles({
color: 'darkGray',
}),
{
lineHeight: '20px',
},
])
export const imageHolder = style([
suggestionImage,
sprinkles({
background: 'loading',
flexShrink: '0',
}),
])
export const suggestionIcon = sprinkles({
display: 'flex',
flexShrink: '0',
})
export const sectionHeader = style([
subheadSmall,
sprinkles({
color: 'darkGray',
}),
{
lineHeight: '20px',
},
])
export const notFoundContainer = style([
sectionHeader,
sprinkles({
paddingY: '4',
paddingLeft: '16',
}),
])

View File

@@ -0,0 +1,395 @@
// eslint-disable-next-line no-restricted-imports
import { t, Trans } from '@lingui/macro'
import clsx from 'clsx'
import { NftVariant, useNftFlag } from 'featureFlags/flags/nft'
import useDebounce from 'hooks/useDebounce'
import { useOnClickOutside } from 'hooks/useOnClickOutside'
import { organizeSearchResults } from 'lib/utils/searchBar'
import { Box } from 'nft/components/Box'
import { Column, Row } from 'nft/components/Flex'
import { Overlay } from 'nft/components/modals/Overlay'
import { magicalGradientOnHover, subheadSmall } from 'nft/css/common.css'
import { useIsMobile, useSearchHistory } from 'nft/hooks'
import { fetchSearchCollections, fetchTrendingCollections } from 'nft/queries'
import { fetchSearchTokens } from 'nft/queries/genie/SearchTokensFetcher'
import { fetchTrendingTokens } from 'nft/queries/genie/TrendingTokensFetcher'
import { FungibleToken, GenieCollection, TimePeriod, TrendingCollection } from 'nft/types'
import { formatEthPrice } from 'nft/utils/currency'
import { ChangeEvent, useEffect, useMemo, useReducer, useRef, useState } from 'react'
import { useQuery } from 'react-query'
import { useLocation } from 'react-router-dom'
import {
ChevronLeftIcon,
ClockIcon,
MagnifyingGlassIcon,
NavMagnifyingGlassIcon,
TrendingArrow,
} from '../../nft/components/icons'
import { NavIcon } from './NavIcon'
import * as styles from './SearchBar.css'
import { CollectionRow, SkeletonRow, TokenRow } from './SuggestionRow'
interface SearchBarDropdownSectionProps {
toggleOpen: () => void
suggestions: (GenieCollection | FungibleToken)[]
header: JSX.Element
headerIcon?: JSX.Element
hoveredIndex: number | undefined
startingIndex: number
setHoveredIndex: (index: number | undefined) => void
}
export const SearchBarDropdownSection = ({
toggleOpen,
suggestions,
header,
headerIcon = undefined,
hoveredIndex,
startingIndex,
setHoveredIndex,
}: SearchBarDropdownSectionProps) => {
return (
<Column gap="12">
<Row paddingX="16" paddingY="4" gap="8" color="grey300" className={subheadSmall} style={{ lineHeight: '20px' }}>
{headerIcon ? headerIcon : null}
<Box>{header}</Box>
</Row>
<Column gap="12">
{suggestions?.map((suggestion, index) =>
isCollection(suggestion) ? (
<CollectionRow
key={suggestion.address}
collection={suggestion as GenieCollection}
isHovered={hoveredIndex === index + startingIndex}
setHoveredIndex={setHoveredIndex}
toggleOpen={toggleOpen}
index={index + startingIndex}
/>
) : (
<TokenRow
key={suggestion.address}
token={suggestion as FungibleToken}
isHovered={hoveredIndex === index + startingIndex}
setHoveredIndex={setHoveredIndex}
toggleOpen={toggleOpen}
index={index + startingIndex}
/>
)
)}
</Column>
</Column>
)
}
interface SearchBarDropdownProps {
toggleOpen: () => void
tokens: FungibleToken[]
collections: GenieCollection[]
hasInput: boolean
}
export const SearchBarDropdown = ({ toggleOpen, tokens, collections, hasInput }: SearchBarDropdownProps) => {
const [hoveredIndex, setHoveredIndex] = useState<number | undefined>(0)
const searchHistory = useSearchHistory(
(state: { history: (FungibleToken | GenieCollection)[] }) => state.history
).slice(0, 2)
const { pathname } = useLocation()
const isNFTPage = pathname.includes('/nfts')
const isTokenPage = pathname.includes('/tokens')
const phase1Flag = useNftFlag()
const tokenSearchResults =
tokens.length > 0 ? (
<SearchBarDropdownSection
hoveredIndex={hoveredIndex}
startingIndex={isNFTPage ? collections.length : 0}
setHoveredIndex={setHoveredIndex}
toggleOpen={toggleOpen}
suggestions={tokens}
header={<Trans>Tokens</Trans>}
/>
) : (
<Box className={styles.notFoundContainer}>
<Trans>No tokens found.</Trans>
</Box>
)
const collectionSearchResults =
phase1Flag === NftVariant.Enabled ? (
collections.length > 0 ? (
<SearchBarDropdownSection
hoveredIndex={hoveredIndex}
startingIndex={isNFTPage ? 0 : tokens.length}
setHoveredIndex={setHoveredIndex}
toggleOpen={toggleOpen}
suggestions={collections}
header={<Trans>NFT Collections</Trans>}
/>
) : (
<Box className={styles.notFoundContainer}>No NFT collections found.</Box>
)
) : null
const { data: trendingCollectionResults } = useQuery(['trendingCollections', 'eth', 'twenty_four_hours'], () =>
fetchTrendingCollections({ volumeType: 'eth', timePeriod: 'ONE_DAY' as TimePeriod, size: 3 })
)
const trendingCollections = useMemo(() => {
return trendingCollectionResults
?.map((collection) => {
return {
...collection,
collectionAddress: collection.address,
floorPrice: formatEthPrice(collection.floor?.toString()),
stats: {
total_supply: collection.totalSupply,
one_day_change: collection.floorChange,
},
}
})
.slice(0, isNFTPage ? 3 : 2)
}, [isNFTPage, trendingCollectionResults])
const showTrendingCollections: boolean = useMemo(
() => (trendingCollections?.length ?? 0) > 0 && !isTokenPage && phase1Flag === NftVariant.Enabled,
[trendingCollections?.length, isTokenPage, phase1Flag]
)
const { data: trendingTokenResults } = useQuery([], () => fetchTrendingTokens(4), {
refetchOnWindowFocus: false,
refetchOnMount: false,
refetchOnReconnect: false,
})
const trendingTokensLength = phase1Flag === NftVariant.Enabled ? (isTokenPage ? 3 : 2) : 4
const trendingTokens = useMemo(() => {
return trendingTokenResults?.slice(0, trendingTokensLength)
}, [trendingTokenResults, trendingTokensLength])
const totalSuggestions = hasInput
? tokens.length + collections.length
: Math.min(searchHistory.length, 2) +
(isNFTPage || !isTokenPage ? trendingCollections?.length ?? 0 : 0) +
(isTokenPage || !isNFTPage ? trendingTokens?.length ?? 0 : 0)
// Close the modal on escape
useEffect(() => {
const keyDownHandler = (event: KeyboardEvent) => {
if (event.key === 'ArrowUp') {
event.preventDefault()
if (!hoveredIndex) {
setHoveredIndex(totalSuggestions - 1)
} else {
setHoveredIndex(hoveredIndex - 1)
}
} else if (event.key === 'ArrowDown') {
event.preventDefault()
if (hoveredIndex && hoveredIndex === totalSuggestions - 1) {
setHoveredIndex(0)
} else {
setHoveredIndex((hoveredIndex ?? -1) + 1)
}
}
}
document.addEventListener('keydown', keyDownHandler)
return () => {
document.removeEventListener('keydown', keyDownHandler)
}
}, [toggleOpen, hoveredIndex, totalSuggestions])
return (
<Box className={styles.searchBarDropdown}>
{hasInput ? (
// Empty or Up to 8 combined tokens and nfts
<Column gap="20">
{isNFTPage ? (
<>
{collectionSearchResults}
{tokenSearchResults}
</>
) : (
<>
{tokenSearchResults}
{collectionSearchResults}
</>
)}
</Column>
) : (
// Recent Searches, Trending Tokens, Trending Collections
<Column gap="20">
{searchHistory.length > 0 && (
<SearchBarDropdownSection
hoveredIndex={hoveredIndex}
startingIndex={0}
setHoveredIndex={setHoveredIndex}
toggleOpen={toggleOpen}
suggestions={searchHistory}
header={<Trans>Recent searches</Trans>}
headerIcon={<ClockIcon />}
/>
)}
{(trendingTokens?.length ?? 0) > 0 && !isNFTPage && (
<SearchBarDropdownSection
hoveredIndex={hoveredIndex}
startingIndex={searchHistory.length}
setHoveredIndex={setHoveredIndex}
toggleOpen={toggleOpen}
suggestions={trendingTokens ?? []}
header={<Trans>Popular tokens</Trans>}
headerIcon={<TrendingArrow />}
/>
)}
{showTrendingCollections && (
<SearchBarDropdownSection
hoveredIndex={hoveredIndex}
startingIndex={searchHistory.length + (isNFTPage ? 0 : trendingTokens?.length ?? 0)}
setHoveredIndex={setHoveredIndex}
toggleOpen={toggleOpen}
suggestions={trendingCollections as unknown as GenieCollection[]}
header={<Trans>Popular NFT collections</Trans>}
headerIcon={<TrendingArrow />}
/>
)}
</Column>
)}
</Box>
)
}
function isCollection(suggestion: GenieCollection | FungibleToken | TrendingCollection) {
return (suggestion as FungibleToken).decimals === undefined
}
export const SearchBar = () => {
const [isOpen, toggleOpen] = useReducer((state: boolean) => !state, false)
const [searchValue, setSearchValue] = useState('')
const debouncedSearchValue = useDebounce(searchValue, 300)
const searchRef = useRef<HTMLDivElement>(null)
const inputRef = useRef<HTMLInputElement>(null)
const { pathname } = useLocation()
const phase1Flag = useNftFlag()
const isMobile = useIsMobile()
useOnClickOutside(searchRef, () => {
isOpen && toggleOpen()
})
const { data: collections, isLoading: collectionsAreLoading } = useQuery(
['searchCollections', debouncedSearchValue],
() => fetchSearchCollections(debouncedSearchValue),
{
refetchOnWindowFocus: false,
refetchOnMount: false,
refetchOnReconnect: false,
}
)
const { data: tokens, isLoading: tokensAreLoading } = useQuery(
['searchTokens', debouncedSearchValue],
() => fetchSearchTokens(debouncedSearchValue),
{
refetchOnWindowFocus: false,
refetchOnMount: false,
refetchOnReconnect: false,
}
)
const isNFTPage = pathname.includes('/nfts')
const [reducedTokens, reducedCollections] = organizeSearchResults(isNFTPage, tokens ?? [], collections ?? [])
useEffect(() => {
const escapeKeyDownHandler = (event: KeyboardEvent) => {
if (event.key === 'Escape' && isOpen) {
event.preventDefault()
toggleOpen()
}
}
document.addEventListener('keydown', escapeKeyDownHandler)
return () => {
document.removeEventListener('keydown', escapeKeyDownHandler)
}
}, [isOpen, toggleOpen, collections])
// clear searchbar when changing pages
useEffect(() => {
setSearchValue('')
}, [pathname])
// auto set cursor when searchbar is opened
useEffect(() => {
if (isOpen) {
inputRef.current?.focus()
}
}, [isOpen])
const placeholderText = phase1Flag === NftVariant.Enabled ? t`Search tokens and NFT collections` : t`Search tokens`
return (
<>
<Box position="relative">
<Box
position={isOpen ? { sm: 'fixed', md: 'absolute' } : 'static'}
width={{ sm: isOpen ? 'viewWidth' : 'auto', md: 'auto' }}
ref={searchRef}
className={styles.searchBarContainer}
>
<Row
className={clsx(`${styles.searchBar} ${!isOpen && magicalGradientOnHover}`)}
borderRadius={isOpen ? undefined : '12'}
borderTopRightRadius={isOpen && !isMobile ? '12' : undefined}
borderTopLeftRadius={isOpen && !isMobile ? '12' : undefined}
borderBottomWidth={isOpen ? '0px' : '1px'}
display={{ sm: isOpen ? 'flex' : 'none', xl: 'flex' }}
justifyContent={isOpen || phase1Flag === NftVariant.Enabled ? 'flex-start' : 'center'}
onFocus={() => !isOpen && toggleOpen()}
onClick={() => !isOpen && toggleOpen()}
gap="12"
>
<Box display={{ sm: 'none', md: 'flex' }}>
<MagnifyingGlassIcon />
</Box>
<Box display={{ sm: 'flex', md: 'none' }} color="placeholder" onClick={toggleOpen}>
<ChevronLeftIcon />
</Box>
<Box
as="input"
placeholder={placeholderText}
width={isOpen || phase1Flag === NftVariant.Enabled ? 'full' : '120'}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
!isOpen && toggleOpen()
setSearchValue(event.target.value)
}}
className={styles.searchBarInput}
value={searchValue}
ref={inputRef}
/>
</Row>
<Box display={{ sm: isOpen ? 'none' : 'flex', xl: 'none' }}>
<NavIcon onClick={toggleOpen}>
<NavMagnifyingGlassIcon width={28} height={28} />
</NavIcon>
</Box>
{isOpen &&
(debouncedSearchValue.length > 0 && (tokensAreLoading || collectionsAreLoading) ? (
<SkeletonRow />
) : (
<SearchBarDropdown
toggleOpen={toggleOpen}
tokens={reducedTokens}
collections={reducedCollections}
hasInput={debouncedSearchValue.length > 0}
/>
))}
</Box>
</Box>
{isOpen && <Overlay />}
</>
)
}

View File

@@ -0,0 +1,191 @@
import clsx from 'clsx'
import uriToHttp from 'lib/utils/uriToHttp'
import { Box } from 'nft/components/Box'
import { Column, Row } from 'nft/components/Flex'
import { vars } from 'nft/css/sprinkles.css'
import { useSearchHistory } from 'nft/hooks'
import { FungibleToken, GenieCollection } from 'nft/types'
import { ethNumberStandardFormatter } from 'nft/utils/currency'
import { putCommas } from 'nft/utils/putCommas'
import { useCallback, useEffect, useState } from 'react'
import { Link, useNavigate } from 'react-router-dom'
import { TokenWarningRedIcon, VerifiedIcon } from '../../nft/components/icons'
import * as styles from './SearchBar.css'
interface CollectionRowProps {
collection: GenieCollection
isHovered: boolean
setHoveredIndex: (index: number | undefined) => void
toggleOpen: () => void
index: number
}
export const CollectionRow = ({ collection, isHovered, setHoveredIndex, toggleOpen, index }: CollectionRowProps) => {
const [brokenImage, setBrokenImage] = useState(false)
const [loaded, setLoaded] = useState(false)
const addToSearchHistory = useSearchHistory(
(state: { addItem: (item: FungibleToken | GenieCollection) => void }) => state.addItem
)
const navigate = useNavigate()
const handleClick = useCallback(() => {
addToSearchHistory(collection)
toggleOpen()
}, [addToSearchHistory, collection, toggleOpen])
useEffect(() => {
const keyDownHandler = (event: KeyboardEvent) => {
if (event.key === 'Enter' && isHovered) {
event.preventDefault()
navigate(`/nfts/collection/${collection.address}`)
handleClick()
}
}
document.addEventListener('keydown', keyDownHandler)
return () => {
document.removeEventListener('keydown', keyDownHandler)
}
}, [toggleOpen, isHovered, collection, navigate, handleClick])
return (
<Link
to={`/nfts/collection/${collection.address}`}
onClick={handleClick}
onMouseEnter={() => !isHovered && setHoveredIndex(index)}
onMouseLeave={() => isHovered && setHoveredIndex(undefined)}
className={styles.suggestionRow}
style={{ background: isHovered ? vars.color.lightGrayOverlay : 'none' }}
>
<Row style={{ width: '60%' }}>
{!brokenImage && collection.imageUrl ? (
<Box
as="img"
src={collection.imageUrl}
alt={collection.name}
className={clsx(loaded ? styles.suggestionImage : styles.imageHolder)}
onError={() => setBrokenImage(true)}
onLoad={() => setLoaded(true)}
/>
) : (
<Box className={styles.imageHolder} />
)}
<Column className={styles.suggestionPrimaryContainer}>
<Row gap="4" width="full">
<Box className={styles.primaryText}>{collection.name}</Box>
{collection.isVerified && <VerifiedIcon className={styles.suggestionIcon} />}
</Row>
<Box className={styles.secondaryText}>{putCommas(collection.stats.total_supply)} items</Box>
</Column>
</Row>
{collection.floorPrice ? (
<Column className={styles.suggestionSecondaryContainer}>
<Row gap="4">
<Box className={styles.primaryText}>{ethNumberStandardFormatter(collection.floorPrice)} ETH</Box>
</Row>
<Box className={styles.secondaryText}>Floor</Box>
</Column>
) : null}
</Link>
)
}
interface TokenRowProps {
token: FungibleToken
isHovered: boolean
setHoveredIndex: (index: number | undefined) => void
toggleOpen: () => void
index: number
}
export const TokenRow = ({ token, isHovered, setHoveredIndex, toggleOpen, index }: TokenRowProps) => {
const [brokenImage, setBrokenImage] = useState(false)
const [loaded, setLoaded] = useState(false)
const addToSearchHistory = useSearchHistory(
(state: { addItem: (item: FungibleToken | GenieCollection) => void }) => state.addItem
)
const navigate = useNavigate()
const handleClick = useCallback(() => {
addToSearchHistory(token)
toggleOpen()
}, [addToSearchHistory, toggleOpen, token])
// Close the modal on escape
useEffect(() => {
const keyDownHandler = (event: KeyboardEvent) => {
if (event.key === 'Enter' && isHovered) {
event.preventDefault()
navigate(`/tokens/${token.address}`)
handleClick()
}
}
document.addEventListener('keydown', keyDownHandler)
return () => {
document.removeEventListener('keydown', keyDownHandler)
}
}, [toggleOpen, isHovered, token, navigate, handleClick])
return (
<Link
to={`/tokens/${token.address}`}
onClick={handleClick}
onMouseEnter={() => !isHovered && setHoveredIndex(index)}
onMouseLeave={() => isHovered && setHoveredIndex(undefined)}
className={styles.suggestionRow}
style={{ background: isHovered ? vars.color.lightGrayOverlay : 'none' }}
>
<Row style={{ width: '65%' }}>
{!brokenImage && token.logoURI ? (
<Box
as="img"
src={token.logoURI.includes('ipfs://') ? uriToHttp(token.logoURI)[0] : token.logoURI}
alt={token.name}
className={clsx(loaded ? styles.suggestionImage : styles.imageHolder)}
onError={() => setBrokenImage(true)}
onLoad={() => setLoaded(true)}
/>
) : (
<Box className={styles.imageHolder} />
)}
<Column className={styles.suggestionPrimaryContainer}>
<Row gap="4" width="full">
<Box className={styles.primaryText}>{token.name}</Box>
{token.onDefaultList ? (
<VerifiedIcon className={styles.suggestionIcon} />
) : (
<TokenWarningRedIcon className={styles.suggestionIcon} />
)}
</Row>
<Box className={styles.secondaryText}>{token.symbol}</Box>
</Column>
</Row>
<Column className={styles.suggestionSecondaryContainer}>
{token.priceUsd && (
<Row gap="4">
<Box className={styles.primaryText}>{ethNumberStandardFormatter(token.priceUsd, true)}</Box>
</Row>
)}
{token.price24hChange && (
<Box className={styles.secondaryText} color={token.price24hChange >= 0 ? 'green400' : 'red400'}>
{token.price24hChange.toFixed(2)}%
</Box>
)}
</Column>
</Link>
)
}
export const SkeletonRow = () => {
return (
<Box className={styles.searchBarDropdown}>
<Row className={styles.suggestionRow}>
<Row>
<Box className={styles.imageHolder} />
<Box borderRadius="round" height="16" width="160" background="loading" />
</Row>
</Row>
</Box>
)
}

View File

@@ -0,0 +1,3 @@
import Navbar from './Navbar'
export default Navbar

View File

@@ -1,6 +1,5 @@
import { Trans } from '@lingui/macro'
import { Percent } from '@uniswap/sdk-core'
import useTheme from 'hooks/useTheme'
import { ReactNode } from 'react'
import { ArrowLeft } from 'react-feather'
import { Link as HistoryLink, useLocation } from 'react-router-dom'
@@ -8,7 +7,7 @@ import { Box } from 'rebass'
import { useAppDispatch } from 'state/hooks'
import { resetMintState } from 'state/mint/actions'
import { resetMintState as resetMintV3State } from 'state/mint/v3/actions'
import styled from 'styled-components/macro'
import styled, { useTheme } from 'styled-components/macro'
import { ThemedText } from 'theme'
import Row, { RowBetween } from '../Row'
@@ -24,7 +23,7 @@ const Tabs = styled.div`
const StyledHistoryLink = styled(HistoryLink)<{ flex: string | undefined }>`
flex: ${({ flex }) => flex ?? 'none'};
${({ theme }) => theme.mediaWidth.upToMedium`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
flex: none;
margin-right: 10px;
`};

View File

@@ -12,7 +12,7 @@ const StyledInput = styled.input<{ error?: boolean; fontSize?: string; align?: s
outline: none;
border: none;
flex: 1 1 auto;
background-color: ${({ theme, redesignFlag }) => (redesignFlag ? theme.none : theme.deprecated_bg1)};
background-color: ${({ theme, redesignFlag }) => (redesignFlag ? 'transparent' : theme.deprecated_bg1)};
font-size: ${({ fontSize }) => fontSize ?? '28px'};
text-align: ${({ align }) => align && align};
white-space: nowrap;

View File

@@ -1,9 +1,8 @@
import { Trans } from '@lingui/macro'
import { getChainInfo } from 'constants/chainInfo'
import { SupportedChainId } from 'constants/chains'
import { useContext } from 'react'
import { AlertCircle } from 'react-feather'
import styled, { ThemeContext } from 'styled-components/macro'
import styled, { useTheme } from 'styled-components/macro'
import { ThemedText } from '../../theme'
import { AutoColumn } from '../Column'
@@ -15,7 +14,7 @@ const RowNoFlex = styled(AutoRow)`
export default function FailedNetworkSwitchPopup({ chainId }: { chainId: SupportedChainId }) {
const chainInfo = getChainInfo(chainId)
const theme = useContext(ThemeContext)
const theme = useTheme()
return (
<RowNoFlex>

View File

@@ -1,8 +1,9 @@
import { useCallback, useContext, useEffect } from 'react'
import { NavBarVariant, useNavBarFlag } from 'featureFlags/flags/navBar'
import { useCallback, useEffect } from 'react'
import { X } from 'react-feather'
import { animated } from 'react-spring'
import { useSpring } from 'react-spring/web'
import styled, { ThemeContext } from 'styled-components/macro'
import styled, { useTheme } from 'styled-components/macro'
import { useRemovePopup } from '../../state/application/hooks'
import { PopupContent } from '../../state/application/reducer'
@@ -29,7 +30,7 @@ const Popup = styled.div`
padding-right: 35px;
overflow: hidden;
${({ theme }) => theme.mediaWidth.upToSmall`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
min-width: 290px;
&:not(:last-of-type) {
margin-right: 20px;
@@ -57,6 +58,7 @@ export default function PopupItem({
popKey: string
}) {
const removePopup = useRemovePopup()
const navbarFlag = useNavBarFlag()
const removeThisPopup = useCallback(() => removePopup(popKey), [popKey, removePopup])
useEffect(() => {
if (removeAfterMs === null) return undefined
@@ -70,24 +72,25 @@ export default function PopupItem({
}
}, [removeAfterMs, removeThisPopup])
const theme = useContext(ThemeContext)
const theme = useTheme()
const faderStyle = useSpring({
from: { width: '100%' },
to: { width: '0%' },
config: { duration: removeAfterMs ?? undefined },
})
let popupContent
if ('txn' in content) {
const {
txn: { hash },
} = content
if (navbarFlag === NavBarVariant.Enabled) return null
popupContent = <TransactionPopup hash={hash} />
} else if ('failedSwitchNetwork' in content) {
popupContent = <FailedNetworkSwitchPopup chainId={content.failedSwitchNetwork} />
}
const faderStyle = useSpring({
from: { width: '100%' },
to: { width: '0%' },
config: { duration: removeAfterMs ?? undefined },
})
return (
<Popup>
<StyledClose color={theme.deprecated_text2} onClick={removeThisPopup} />

View File

@@ -6,11 +6,10 @@ import useCurrentBlockTimestamp from 'hooks/useCurrentBlockTimestamp'
import { useEffect } from 'react'
import { MessageCircle, X } from 'react-feather'
import { useShowSurveyPopup } from 'state/user/hooks'
import styled from 'styled-components/macro'
import styled, { useTheme } from 'styled-components/macro'
import { ExternalLink, ThemedText, Z_INDEX } from 'theme'
import BGImage from '../../assets/images/survey-orb.svg'
import useTheme from '../../hooks/useTheme'
const Wrapper = styled(AutoColumn)`
background: #edeef2;
@@ -22,7 +21,7 @@ const Wrapper = styled(AutoColumn)`
color: ${({ theme }) => theme.deprecated_text1};
overflow: hidden;
${({ theme }) => theme.mediaWidth.upToSmall`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
max-width: 100%;
`}
`

View File

@@ -1,7 +1,6 @@
import { useWeb3React } from '@web3-react/core'
import { useContext } from 'react'
import { AlertCircle, CheckCircle } from 'react-feather'
import styled, { ThemeContext } from 'styled-components/macro'
import styled, { useTheme } from 'styled-components/macro'
import { useTransaction } from '../../state/transactions/hooks'
import { ThemedText } from '../../theme'
@@ -19,7 +18,7 @@ export default function TransactionPopup({ hash }: { hash: string }) {
const { chainId } = useWeb3React()
const tx = useTransaction(hash)
const theme = useContext(ThemeContext)
const theme = useTheme()
if (!tx) return null
const success = Boolean(tx.receipt && tx.receipt.status === 1)

View File

@@ -17,7 +17,7 @@ const MobilePopupWrapper = styled.div<{ height: string | number }>`
margin-bottom: ${({ height }) => (height ? '20px' : 0)};
display: none;
${({ theme }) => theme.mediaWidth.upToSmall`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
display: block;
padding-top: 20px;
`};
@@ -35,8 +35,8 @@ const MobilePopupInner = styled.div`
}
`
const StopOverflowQuery = `@media screen and (min-width: ${MEDIA_WIDTHS.upToMedium + 1}px) and (max-width: ${
MEDIA_WIDTHS.upToMedium + 500
const StopOverflowQuery = `@media screen and (min-width: ${MEDIA_WIDTHS.deprecated_upToMedium + 1}px) and (max-width: ${
MEDIA_WIDTHS.deprecated_upToMedium + 500
}px)`
const FixedPopupColumn = styled(AutoColumn)<{ extraPadding: boolean; xlPadding: boolean }>`
@@ -47,7 +47,7 @@ const FixedPopupColumn = styled(AutoColumn)<{ extraPadding: boolean; xlPadding:
width: 100%;
z-index: 3;
${({ theme }) => theme.mediaWidth.upToSmall`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
display: none;
`};

View File

@@ -12,7 +12,7 @@ const DesktopHeader = styled.div`
font-weight: 500;
padding: 8px;
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
@media screen and (min-width: ${MEDIA_WIDTHS.deprecated_upToSmall}px) {
align-items: center;
display: flex;
justify-content: space-between;
@@ -32,11 +32,11 @@ const MobileHeader = styled.div`
justify-content: space-between;
align-items: center;
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
@media screen and (min-width: ${MEDIA_WIDTHS.deprecated_upToSmall}px) {
display: none;
}
@media screen and (max-width: ${MEDIA_WIDTHS.upToExtraSmall}px) {
@media screen and (max-width: ${MEDIA_WIDTHS.deprecated_upToExtraSmall}px) {
display: flex;
flex-direction: column;
align-items: start;
@@ -55,7 +55,7 @@ const ToggleLabel = styled.div`
`
const MobileTogglePosition = styled.div`
@media screen and (max-width: ${MEDIA_WIDTHS.upToExtraSmall}px) {
@media screen and (max-width: ${MEDIA_WIDTHS.deprecated_upToExtraSmall}px) {
position: absolute;
right: 20px;
}

View File

@@ -48,11 +48,11 @@ const LinkRow = styled(Link)`
background-color: ${({ theme }) => theme.deprecated_bg2};
}
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
@media screen and (min-width: ${MEDIA_WIDTHS.deprecated_upToSmall}px) {
/* flex-direction: row; */
}
${({ theme }) => theme.mediaWidth.upToSmall`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
flex-direction: column;
row-gap: 12px;
`};
@@ -61,7 +61,7 @@ const LinkRow = styled(Link)`
const BadgeText = styled.div`
font-weight: 500;
font-size: 14px;
${({ theme }) => theme.mediaWidth.upToSmall`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
font-size: 12px;
`};
`
@@ -78,7 +78,7 @@ const RangeLineItem = styled(DataLineItem)`
margin-top: 4px;
width: 100%;
${({ theme }) => theme.mediaWidth.upToSmall`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
background-color: ${({ theme }) => theme.deprecated_bg2};
border-radius: 12px;
padding: 8px 0;
@@ -88,7 +88,7 @@ const RangeLineItem = styled(DataLineItem)`
const DoubleArrow = styled.span`
margin: 0 2px;
color: ${({ theme }) => theme.deprecated_text3};
${({ theme }) => theme.mediaWidth.upToSmall`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
margin: 4px;
padding: 20px;
`};
@@ -104,7 +104,7 @@ const ExtentsText = styled.span`
color: ${({ theme }) => theme.deprecated_text3};
font-size: 14px;
margin-right: 4px;
${({ theme }) => theme.mediaWidth.upToSmall`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
display: none;
`};
`
@@ -122,7 +122,7 @@ const DataText = styled.div`
font-weight: 600;
font-size: 18px;
${({ theme }) => theme.mediaWidth.upToSmall`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
font-size: 14px;
`};
`

View File

@@ -10,9 +10,9 @@ import { Break } from 'components/earn/styled'
import RateToggle from 'components/RateToggle'
import { RowBetween, RowFixed } from 'components/Row'
import JSBI from 'jsbi'
import { ReactNode, useCallback, useContext, useState } from 'react'
import { ReactNode, useCallback, useState } from 'react'
import { Bound } from 'state/mint/v3/actions'
import { ThemeContext } from 'styled-components/macro'
import { useTheme } from 'styled-components/macro'
import { ThemedText } from 'theme'
import { formatTickPrice } from 'utils/formatTickPrice'
import { unwrappedToken } from 'utils/unwrappedToken'
@@ -30,7 +30,7 @@ export const PositionPreview = ({
baseCurrencyDefault?: Currency | undefined
ticksAtLimit: { [bound: string]: boolean | undefined }
}) => {
const theme = useContext(ThemeContext)
const theme = useTheme()
const currency0 = unwrappedToken(position.pool.token0)
const currency1 = unwrappedToken(position.pool.token1)

View File

@@ -1,6 +1,4 @@
import { useContext } from 'react'
import styled from 'styled-components/macro'
import { ThemeContext } from 'styled-components/macro'
import styled, { useTheme } from 'styled-components/macro'
import { ThemedText } from '../../theme'
import { AutoColumn } from '../Column'
@@ -54,7 +52,7 @@ interface ProgressCirclesProps {
* @param steps array of booleans where true means step is complete
*/
export default function ProgressCircles({ steps, disabled = false, ...rest }: ProgressCirclesProps) {
const theme = useContext(ThemeContext)
const theme = useTheme()
return (
<Wrapper justify={'center'} {...rest}>

View File

@@ -13,7 +13,7 @@ import styled from 'styled-components/macro'
import { currencyId } from 'utils/currencyId'
const MobileWrapper = styled(AutoColumn)`
${({ theme }) => theme.mediaWidth.upToSmall`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
display: none;
`};
`
@@ -24,7 +24,7 @@ const BaseWrapper = styled.div<{ disable?: boolean; redesignFlag?: boolean }>`
disable
? redesignFlag
? theme.accentAction
: theme.none
: 'transparent'
: redesignFlag
? theme.backgroundOutline
: theme.deprecated_bg3};

View File

@@ -9,12 +9,11 @@ import TokenSafetyIcon from 'components/TokenSafety/TokenSafetyIcon'
import { checkWarning } from 'constants/tokenSafety'
import { RedesignVariant, useRedesignFlag } from 'featureFlags/flags/redesign'
import { TokenSafetyVariant, useTokenSafetyFlag } from 'featureFlags/flags/tokenSafety'
import useTheme from 'hooks/useTheme'
import { CSSProperties, MutableRefObject, useCallback, useMemo } from 'react'
import { Check } from 'react-feather'
import { FixedSizeList } from 'react-window'
import { Text } from 'rebass'
import styled from 'styled-components/macro'
import styled, { useTheme } from 'styled-components/macro'
import TokenListLogo from '../../../assets/svg/tokenlist.svg'
import { useIsUserAddedToken } from '../../../hooks/Tokens'
@@ -129,7 +128,7 @@ function CurrencyRow({
eventProperties,
}: {
currency: Currency
onSelect: () => void
onSelect: (hasWarning: boolean) => void
isSelected: boolean
otherSelected: boolean
style: CSSProperties
@@ -160,8 +159,8 @@ function CurrencyRow({
redesignFlag={redesignFlagEnabled}
style={style}
className={`token-item-${key}`}
onKeyPress={(e) => (!isSelected && e.key === 'Enter' ? onSelect() : null)}
onClick={() => (isSelected ? null : onSelect())}
onKeyPress={(e) => (!isSelected && e.key === 'Enter' ? onSelect(!!warning) : null)}
onClick={() => (isSelected ? null : onSelect(!!warning))}
disabled={isSelected}
selected={otherSelected}
>
@@ -280,7 +279,7 @@ export default function CurrencyList({
currencies: Currency[]
otherListTokens?: WrappedTokenInfo[]
selectedCurrency?: Currency | null
onCurrencySelect: (currency: Currency) => void
onCurrencySelect: (currency: Currency, hasWarning?: boolean) => void
otherCurrency?: Currency | null
fixedListRef?: MutableRefObject<FixedSizeList | undefined>
showImportView: () => void
@@ -309,7 +308,7 @@ export default function CurrencyList({
const isSelected = Boolean(currency && selectedCurrency && selectedCurrency.equals(currency))
const otherSelected = Boolean(currency && otherCurrency && otherCurrency.equals(currency))
const handleSelect = () => currency && onCurrencySelect(currency)
const handleSelect = (hasWarning: boolean) => currency && onCurrencySelect(currency, hasWarning)
const token = currency?.wrapped

View File

@@ -8,7 +8,6 @@ import { sendEvent } from 'components/analytics'
import { RedesignVariant, useRedesignFlag } from 'featureFlags/flags/redesign'
import useDebounce from 'hooks/useDebounce'
import { useOnClickOutside } from 'hooks/useOnClickOutside'
import useTheme from 'hooks/useTheme'
import useToggle from 'hooks/useToggle'
import useNativeCurrency from 'lib/hooks/useNativeCurrency'
import { getTokenFilter } from 'lib/hooks/useTokenList/filtering'
@@ -19,7 +18,7 @@ import AutoSizer from 'react-virtualized-auto-sizer'
import { FixedSizeList } from 'react-window'
import { Text } from 'rebass'
import { useAllTokenBalances } from 'state/connection/hooks'
import styled from 'styled-components/macro'
import styled, { useTheme } from 'styled-components/macro'
import { useAllTokens, useIsUserAddedToken, useSearchInactiveTokenLists, useToken } from '../../hooks/Tokens'
import { ButtonText, CloseIcon, IconWrapper, ThemedText } from '../../theme'
@@ -52,7 +51,7 @@ interface CurrencySearchProps {
isOpen: boolean
onDismiss: () => void
selectedCurrency?: Currency | null
onCurrencySelect: (currency: Currency) => void
onCurrencySelect: (currency: Currency, hasWarning?: boolean) => void
otherSelectedCurrency?: Currency | null
showCommonBases?: boolean
showCurrencyAmount?: boolean
@@ -137,9 +136,9 @@ export function CurrencySearch({
}, [debouncedQuery, native, filteredSortedTokens])
const handleCurrencySelect = useCallback(
(currency: Currency) => {
onCurrencySelect(currency)
onDismiss()
(currency: Currency, hasWarning?: boolean) => {
onCurrencySelect(currency, hasWarning)
if (!hasWarning) onDismiss()
},
[onDismiss, onCurrencySelect]
)

View File

@@ -3,8 +3,9 @@ import { TokenList } from '@uniswap/token-lists'
import TokenSafety from 'components/TokenSafety'
import { TokenSafetyVariant, useTokenSafetyFlag } from 'featureFlags/flags/tokenSafety'
import usePrevious from 'hooks/usePrevious'
import { useCallback, useEffect, useState } from 'react'
import { memo, useCallback, useEffect, useState } from 'react'
import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo'
import { useUserAddedTokens } from 'state/user/hooks'
import useLast from '../../hooks/useLast'
import Modal from '../Modal'
@@ -29,9 +30,10 @@ export enum CurrencyModalView {
manage,
importToken,
importList,
tokenSafety,
}
export default function CurrencySearchModal({
export default memo(function CurrencySearchModal({
isOpen,
onDismiss,
onCurrencySelect,
@@ -43,6 +45,7 @@ export default function CurrencySearchModal({
}: CurrencySearchModalProps) {
const [modalView, setModalView] = useState<CurrencyModalView>(CurrencyModalView.manage)
const lastOpen = useLast(isOpen)
const userAddedTokens = useUserAddedTokens()
useEffect(() => {
if (isOpen && !lastOpen) {
@@ -50,12 +53,28 @@ export default function CurrencySearchModal({
}
}, [isOpen, lastOpen])
const showTokenSafetySpeedbump = (token: Token) => {
setWarningToken(token)
setModalView(CurrencyModalView.tokenSafety)
}
const tokenSafetyFlag = useTokenSafetyFlag()
const handleCurrencySelect = useCallback(
(currency: Currency) => {
onCurrencySelect(currency)
onDismiss()
(currency: Currency, hasWarning?: boolean) => {
if (
tokenSafetyFlag === TokenSafetyVariant.Enabled &&
hasWarning &&
currency.isToken &&
!userAddedTokens.find((token) => token.equals(currency))
) {
showTokenSafetySpeedbump(currency)
} else {
onCurrencySelect(currency)
onDismiss()
}
},
[onDismiss, onCurrencySelect]
[onDismiss, onCurrencySelect, tokenSafetyFlag, userAddedTokens]
)
// for token import view
@@ -68,6 +87,9 @@ export default function CurrencySearchModal({
const [importList, setImportList] = useState<TokenList | undefined>()
const [listURL, setListUrl] = useState<string | undefined>()
// used for token safety
const [warningToken, setWarningToken] = useState<Token | undefined>()
const showImportView = useCallback(() => setModalView(CurrencyModalView.importToken), [setModalView])
const showManageView = useCallback(() => setModalView(CurrencyModalView.manage), [setModalView])
const handleBackImport = useCallback(
@@ -75,8 +97,6 @@ export default function CurrencySearchModal({
[setModalView, prevView]
)
const tokenSafetyFlag = useTokenSafetyFlag()
// change min height if not searching
let minHeight: number | undefined = 80
let content = null
@@ -98,25 +118,34 @@ export default function CurrencySearchModal({
/>
)
break
case CurrencyModalView.tokenSafety:
minHeight = undefined
if (tokenSafetyFlag === TokenSafetyVariant.Enabled && warningToken) {
content = (
<TokenSafety
tokenAddress={warningToken.address}
onContinue={() => handleCurrencySelect(warningToken)}
onCancel={() => setModalView(CurrencyModalView.search)}
showCancel={true}
/>
)
}
break
case CurrencyModalView.importToken:
if (importToken) {
minHeight = undefined
content =
tokenSafetyFlag === TokenSafetyVariant.Enabled ? (
<TokenSafety
tokenAddress={importToken.address}
onContinue={() => handleCurrencySelect(importToken)}
onCancel={handleBackImport}
/>
) : (
<ImportToken
tokens={[importToken]}
onDismiss={onDismiss}
list={importToken instanceof WrappedTokenInfo ? importToken.list : undefined}
onBack={handleBackImport}
handleCurrencySelect={handleCurrencySelect}
/>
)
if (tokenSafetyFlag === TokenSafetyVariant.Enabled) {
showTokenSafetySpeedbump(importToken)
}
content = (
<ImportToken
tokens={[importToken]}
onDismiss={onDismiss}
list={importToken instanceof WrappedTokenInfo ? importToken.list : undefined}
onBack={handleBackImport}
handleCurrencySelect={handleCurrencySelect}
/>
)
}
break
case CurrencyModalView.importList:
@@ -142,4 +171,4 @@ export default function CurrencySearchModal({
{content}
</Modal>
)
}
})

View File

@@ -8,14 +8,13 @@ import ListLogo from 'components/ListLogo'
import { AutoRow, RowBetween, RowFixed } from 'components/Row'
import { SectionBreak } from 'components/swap/styleds'
import { useFetchListCallback } from 'hooks/useFetchListCallback'
import useTheme from 'hooks/useTheme'
import { transparentize } from 'polished'
import { useCallback, useState } from 'react'
import { AlertTriangle, ArrowLeft } from 'react-feather'
import { useAppDispatch } from 'state/hooks'
import { enableList, removeList } from 'state/lists/actions'
import { useAllLists } from 'state/lists/hooks'
import styled from 'styled-components/macro'
import styled, { useTheme } from 'styled-components/macro'
import { CloseIcon, ThemedText } from 'theme'
import { ExternalLink } from '../../theme'

View File

@@ -6,10 +6,9 @@ import CurrencyLogo from 'components/CurrencyLogo'
import ListLogo from 'components/ListLogo'
import { AutoRow, RowFixed } from 'components/Row'
import { useIsTokenActive, useIsUserAddedToken } from 'hooks/Tokens'
import useTheme from 'hooks/useTheme'
import { CSSProperties } from 'react'
import { CheckCircle } from 'react-feather'
import styled from 'styled-components/macro'
import styled, { useTheme } from 'styled-components/macro'
import { ThemedText } from 'theme'
import { WrappedTokenInfo } from '../../state/lists/wrappedTokenInfo'

View File

@@ -8,10 +8,9 @@ import { AutoColumn } from 'components/Column'
import { RowBetween } from 'components/Row'
import { SectionBreak } from 'components/swap/styleds'
import { useUnsupportedTokens } from 'hooks/Tokens'
import useTheme from 'hooks/useTheme'
import { AlertCircle, ArrowLeft } from 'react-feather'
import { useAddUserToken } from 'state/user/hooks'
import styled from 'styled-components/macro'
import styled, { useTheme } from 'styled-components/macro'
import { CloseIcon, ThemedText } from 'theme'
import BlockedToken from './BlockedToken'

View File

@@ -12,11 +12,10 @@ import { ChangeEvent, memo, useCallback, useEffect, useMemo, useRef, useState }
import { CheckCircle, Settings } from 'react-feather'
import { usePopper } from 'react-popper'
import { useAppDispatch, useAppSelector } from 'state/hooks'
import styled from 'styled-components/macro'
import styled, { useTheme } from 'styled-components/macro'
import { useFetchListCallback } from '../../hooks/useFetchListCallback'
import { useOnClickOutside } from '../../hooks/useOnClickOutside'
import useTheme from '../../hooks/useTheme'
import useToggle from '../../hooks/useToggle'
import { acceptListUpdate, disableList, enableList, removeList } from '../../state/lists/actions'
import { useActiveListUrls, useAllLists, useIsListActive } from '../../state/lists/hooks'

View File

@@ -8,11 +8,10 @@ import Row, { RowBetween, RowFixed } from 'components/Row'
import { useToken } from 'hooks/Tokens'
import { ChangeEvent, RefObject, useCallback, useMemo, useRef, useState } from 'react'
import { useRemoveUserAddedToken, useUserAddedTokens } from 'state/user/hooks'
import styled from 'styled-components/macro'
import styled, { useTheme } from 'styled-components/macro'
import { ButtonText, ExternalLink, ExternalLinkIcon, ThemedText, TrashIcon } from 'theme'
import { isAddress } from 'utils'
import useTheme from '../../hooks/useTheme'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { CurrencyModalView } from './CurrencySearchModal'
import ImportRow from './ImportRow'

View File

@@ -23,7 +23,7 @@ const AddressText = styled(ThemedText.DeprecatedBlue)`
font-size: 12px;
word-break: break-all;
${({ theme }) => theme.mediaWidth.upToSmall`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
font-size: 10px;
`}
`

View File

@@ -1,3 +1,4 @@
import searchIcon from 'assets/svg/search.svg'
import { LoadingRows as BaseLoadingRows } from 'components/Loader/styled'
import styled from 'styled-components/macro'
@@ -37,14 +38,18 @@ export const MenuItem = styled(RowBetween)<{ redesignFlag?: boolean }>`
`
export const SearchInput = styled.input<{ redesignFlag?: boolean }>`
background: no-repeat scroll 7px 7px;
background-image: url(${searchIcon});
background-size: 20px 20px;
background-position: 12px center;
position: relative;
display: flex;
padding: 16px;
padding-left: 40px;
height: ${({ redesignFlag }) => redesignFlag && '40px'};
align-items: center;
width: 100%;
white-space: nowrap;
background: none;
background-color: ${({ theme, redesignFlag }) => redesignFlag && theme.backgroundModule};
border: none;
outline: none;
@@ -62,8 +67,9 @@ export const SearchInput = styled.input<{ redesignFlag?: boolean }>`
}
transition: border 100ms;
:focus {
border: 1px solid ${({ theme, redesignFlag }) => (redesignFlag ? 'transparent' : theme.deprecated_primary1)};
background-color: ${({ theme, redesignFlag }) => redesignFlag && theme.accentActionSoft};
border: 1px solid
${({ theme, redesignFlag }) => (redesignFlag ? theme.accentActiveSoft : theme.deprecated_primary1)};
background-color: ${({ theme, redesignFlag }) => redesignFlag && theme.backgroundSurface};
outline: none;
}
`

View File

@@ -5,10 +5,10 @@ import { useWeb3React } from '@web3-react/core'
import { sendEvent } from 'components/analytics'
import { RedesignVariant, useRedesignFlag } from 'featureFlags/flags/redesign'
import { isSupportedChainId } from 'lib/hooks/routing/clientSideSmartOrderRouter'
import { useContext, useRef, useState } from 'react'
import { useRef, useState } from 'react'
import { Settings, X } from 'react-feather'
import { Text } from 'rebass'
import styled, { ThemeContext } from 'styled-components/macro'
import styled, { useTheme } from 'styled-components/macro'
import { useOnClickOutside } from '../../hooks/useOnClickOutside'
import { useModalIsOpen, useToggleSettingsMenu } from '../../state/application/hooks'
@@ -99,7 +99,7 @@ const MenuFlyout = styled.span<{ redesignFlag: boolean }>`
z-index: 100;
color: ${({ theme, redesignFlag }) => redesignFlag && theme.textPrimary};
${({ theme }) => theme.mediaWidth.upToMedium`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
min-width: 18.125rem;
`};
@@ -130,7 +130,7 @@ export default function SettingsTab({ placeholderSlippage }: { placeholderSlippa
const open = useModalIsOpen(ApplicationModal.SETTINGS)
const toggle = useToggleSettingsMenu()
const theme = useContext(ThemeContext)
const theme = useTheme()
const [expertMode, toggleExpertMode] = useExpertModeManager()

View File

@@ -6,13 +6,18 @@ import styled, { keyframes } from 'styled-components/macro'
const Wrapper = styled.button<{ isActive?: boolean; activeElement?: boolean; redesignFlag: boolean }>`
align-items: center;
background: ${({ isActive, theme, redesignFlag }) =>
redesignFlag && isActive ? theme.accentActionSoft : theme.deprecated_bg1};
border: none;
redesignFlag && isActive
? theme.accentActionSoft
: redesignFlag && !isActive
? 'transparent'
: theme.deprecated_bg1};
border: ${({ redesignFlag, theme, isActive }) =>
redesignFlag && !isActive ? `1px solid ${theme.backgroundOutline}` : 'none'};
border-radius: 20px;
cursor: pointer;
display: flex;
outline: none;
padding: 0.4rem 0.4rem;
padding: ${({ redesignFlag }) => (redesignFlag ? '4px' : '0.4rem 0.4rem')};
width: fit-content;
`

View File

@@ -8,7 +8,7 @@ import { ExternalLink } from 'theme'
import { Color } from 'theme/styled'
const Label = styled.div<{ color: Color }>`
width: 284px;
width: 100%;
padding: 12px 20px;
background-color: ${({ color }) => color + '1F'};
border-radius: 16px;

View File

@@ -7,6 +7,7 @@ interface TokenSafetyModalProps {
secondTokenAddress?: string
onContinue: () => void
onCancel: () => void
showCancel?: boolean
}
export default function TokenSafetyModal({
@@ -15,6 +16,7 @@ export default function TokenSafetyModal({
secondTokenAddress,
onContinue,
onCancel,
showCancel,
}: TokenSafetyModalProps) {
return (
<Modal isOpen={isOpen} onDismiss={onCancel}>
@@ -23,6 +25,7 @@ export default function TokenSafetyModal({
secondTokenAddress={secondTokenAddress}
onCancel={onCancel}
onContinue={onContinue}
showCancel={showCancel}
/>
</Modal>
)

View File

@@ -4,14 +4,13 @@ import { ButtonPrimary } from 'components/Button'
import { AutoColumn } from 'components/Column'
import CurrencyLogo from 'components/CurrencyLogo'
import TokenSafetyLabel from 'components/TokenSafety/TokenSafetyLabel'
import { checkWarning, getWarningCopy, TOKEN_SAFETY_ARTICLE, Warning, WARNING_LEVEL } from 'constants/tokenSafety'
import { checkWarning, getWarningCopy, TOKEN_SAFETY_ARTICLE, Warning } from 'constants/tokenSafety'
import { useToken } from 'hooks/Tokens'
import { ExternalLink as LinkIconFeather } from 'react-feather'
import { Text } from 'rebass'
import { useAddUserToken } from 'state/user/hooks'
import styled, { useTheme } from 'styled-components/macro'
import styled from 'styled-components/macro'
import { ButtonText, CopyLinkIcon, ExternalLink } from 'theme'
import { Color } from 'theme/styled'
import { ExplorerDataType, getExplorerLink } from 'utils/getExplorerLink'
const Wrapper = styled.div`
@@ -46,61 +45,52 @@ const InfoText = styled(Text)`
text-align: center;
`
const StyledButton = styled(ButtonPrimary)<{ buttonColor: Color; textColor: Color }>`
color: ${({ textColor }) => textColor};
background-color: ${({ buttonColor }) => buttonColor};
const StyledButton = styled(ButtonPrimary)`
margin-top: 24px;
width: 100%;
:hover {
background-color: ${({ buttonColor, theme }) => buttonColor ?? theme.accentAction};
}
font-weight: 600;
`
const StyledCancelButton = styled(ButtonText)<{ color?: Color }>`
const StyledCancelButton = styled(ButtonText)`
margin-top: 16px;
color: ${({ color, theme }) => color ?? theme.accentAction};
color: ${({ theme }) => theme.textSecondary};
font-weight: 600;
font-size: 14px;
`
const StyledCloseButton = styled(StyledButton)`
background-color: ${({ theme }) => theme.backgroundInteractive};
color: ${({ theme }) => theme.textPrimary};
&:hover {
background-color: ${({ theme }) => theme.backgroundInteractive};
opacity: 0.6;
transition: opacity 250ms ease;
}
`
const Buttons = ({
warning,
onContinue,
onCancel,
showCancel,
}: {
warning: Warning
onContinue: () => void
onCancel: () => void
showCancel?: boolean
}) => {
const theme = useTheme()
let textColor, buttonColor, cancelColor
switch (warning.level) {
case WARNING_LEVEL.MEDIUM:
textColor = theme.white
buttonColor = theme.accentAction
cancelColor = theme.accentAction
break
case WARNING_LEVEL.UNKNOWN:
textColor = theme.accentFailure
buttonColor = theme.accentFailureSoft
cancelColor = theme.textPrimary
break
case WARNING_LEVEL.BLOCKED:
textColor = theme.textPrimary
buttonColor = theme.backgroundInteractive
break
}
return warning.canProceed ? (
<>
<StyledButton buttonColor={buttonColor} textColor={textColor} onClick={onContinue}>
<Trans>I Understand</Trans>
<StyledButton onClick={onContinue}>
<Trans>I understand</Trans>
</StyledButton>
<StyledCancelButton color={cancelColor} onClick={onCancel}>
Cancel
</StyledCancelButton>
{showCancel && <StyledCancelButton onClick={onCancel}>Cancel</StyledCancelButton>}
</>
) : (
<StyledButton buttonColor={buttonColor} textColor={textColor} onClick={onCancel}>
<StyledCloseButton onClick={onCancel}>
<Trans>Close</Trans>
</StyledButton>
</StyledCloseButton>
)
}
@@ -124,8 +114,8 @@ const ExplorerContainer = styled.div`
height: 32px;
margin-top: 10px;
font-size: 20px;
background-color: ${({ theme }) => theme.accentActiveSoft};
color: ${({ theme }) => theme.accentActive};
background-color: ${({ theme }) => theme.accentActionSoft};
color: ${({ theme }) => theme.accentAction};
border-radius: 8px;
padding: 2px 12px;
display: flex;
@@ -190,14 +180,25 @@ function ExplorerView({ token }: { token: Token }) {
}
}
const StyledExternalLink = styled(ExternalLink)`
font-weight: 600;
`
interface TokenSafetyProps {
tokenAddress: string | null
secondTokenAddress?: string
onContinue: () => void
onCancel: () => void
showCancel?: boolean
}
export default function TokenSafety({ tokenAddress, secondTokenAddress, onContinue, onCancel }: TokenSafetyProps) {
export default function TokenSafety({
tokenAddress,
secondTokenAddress,
onContinue,
onCancel,
showCancel,
}: TokenSafetyProps) {
const logos = []
const urls = []
@@ -254,13 +255,13 @@ export default function TokenSafety({ tokenAddress, secondTokenAddress, onContin
<ShortColumn>
<InfoText>
{description}{' '}
<ExternalLink href={TOKEN_SAFETY_ARTICLE}>
<Trans>Learn More</Trans>
</ExternalLink>
<StyledExternalLink href={TOKEN_SAFETY_ARTICLE}>
<Trans>Learn more</Trans>
</StyledExternalLink>
</InfoText>
</ShortColumn>
<LinkColumn>{urls}</LinkColumn>
<Buttons warning={displayWarning} onContinue={acknowledge} onCancel={onCancel} />
<Buttons warning={displayWarning} onContinue={acknowledge} onCancel={onCancel} showCancel={showCancel} />
</Container>
</Wrapper>
)

View File

@@ -9,15 +9,16 @@ import styled, { useTheme } from 'styled-components/macro'
import NetworkBalance from './NetworkBalance'
const BalancesCard = styled.div`
width: 284px;
width: 100%;
height: fit-content;
color: ${({ theme }) => theme.textPrimary};
font-size: 12px;
line-height: 20px;
line-height: 16px;
padding: 20px;
box-shadow: ${({ theme }) => theme.shallowShadow};
background-color: ${({ theme }) => theme.backgroundSurface};
border-radius: 12px;
border: 1px solid ${({ theme }) => theme.backgroundOutline};
border: ${({ theme }) => `1px solid ${theme.backgroundOutline}`};
border-radius: 16px;
`
const ErrorState = styled.div`
display: flex;

View File

@@ -1,3 +1,4 @@
import { Trans } from '@lingui/macro'
import { useToken } from 'hooks/Tokens'
import { useNetworkTokenBalances } from 'hooks/useNetworkTokenBalances'
import { useState } from 'react'
@@ -82,7 +83,7 @@ const SwapButton = styled.button`
display: flex;
align-items: center;
border: none;
color: ${({ theme }) => theme.textPrimary};
color: ${({ theme }) => theme.accentTextLightPrimary};
padding: 12px 16px;
width: 120px;
height: 44px;
@@ -152,7 +153,9 @@ export default function FooterBalanceSummary({
) : error ? (
<ErrorState>
<AlertTriangle size={17} />
<ErrorText>There was an error fetching your balance</ErrorText>
<ErrorText>
<Trans>There was an error fetching your balance</Trans>
</ErrorText>
</ErrorState>
) : (
<BalanceInfo>
@@ -165,16 +168,21 @@ export default function FooterBalanceSummary({
</BalanceTotal>
{multipleBalances && (
<ViewAll onClick={() => setShowMultipleBalances(!showMultipleBalances)}>
{showMultipleBalances ? 'Hide' : 'View'} all balances
<Trans>{showMultipleBalances ? 'Hide' : 'View'} all balances</Trans>
</ViewAll>
)}
</BalanceInfo>
)}
<SwapButton onClick={() => (window.location.href = 'https://app.uniswap.org/#/swap')}>Swap</SwapButton>
<SwapButton onClick={() => (window.location.href = 'https://app.uniswap.org/#/swap')}>
<Trans>Swap</Trans>
</SwapButton>
</TotalBalancesSection>
{showMultipleBalances && (
<NetworkBalancesSection>
<NetworkBalancesLabel>Your balances by network</NetworkBalancesLabel> {networkBalances}
<NetworkBalancesLabel>
<Trans>Your balances by network</Trans>
</NetworkBalancesLabel>
{networkBalances}
</NetworkBalancesSection>
)}
<FakeFooterNavBar>**leaving space for updated nav footer**</FakeFooterNavBar>

View File

@@ -1,10 +1,10 @@
import styled from 'styled-components/macro'
import styled, { useTheme } from 'styled-components/macro'
import { ChartWrapper, DeltaContainer, TokenPrice } from '../../Charts/PriceChart'
import { LoadingBubble } from '../loading'
import { DeltaContainer, TokenPrice } from './PriceChart'
import {
AboutContainer,
AboutHeader,
AboutSection,
BreadcrumbNavLink,
ChartContainer,
ChartHeader,
@@ -16,7 +16,12 @@ import {
TokenInfoContainer,
TokenNameCell,
TopArea,
} from './TokenDetail'
} from './TokenDetailContainers'
const LoadingChartContainer = styled(ChartContainer)`
height: 336px;
overflow: hidden;
`
/* Loading state bubbles */
const LoadingDetailBubble = styled(LoadingBubble)`
@@ -29,15 +34,17 @@ const TitleLoadingBubble = styled(LoadingDetailBubble)`
const SquareLoadingBubble = styled(LoadingDetailBubble)`
height: 32px;
border-radius: 8px;
margin-top: 4px;
margin-bottom: 10px;
`
const PriceLoadingBubble = styled(SquareLoadingBubble)`
height: 40px;
`
const LongLoadingBubble = styled(LoadingDetailBubble)`
margin-top: 6px;
width: 100%;
`
const HalfLoadingBubble = styled(LoadingDetailBubble)`
margin-top: 6px;
width: 50%;
`
const IconLoadingBubble = styled(LoadingDetailBubble)`
@@ -71,6 +78,15 @@ const Space = styled.div<{ heightSize: number }>`
height: ${({ heightSize }) => `${heightSize}px`};
`
export function Wave() {
const theme = useTheme()
return (
<svg width="416" height="160" xmlns="http://www.w3.org/2000/svg">
<path d="M 0 80 Q 104 10, 208 80 T 416 80" stroke={theme.backgroundOutline} fill="transparent" strokeWidth="2" />
</svg>
)
}
/* Loading State: row component with loading bubbles */
export default function LoadingTokenDetail() {
return (
@@ -85,47 +101,25 @@ export default function LoadingTokenDetail() {
<TitleLoadingBubble />
</TokenNameCell>
</TokenInfoContainer>
<ChartContainer>
<ChartWrapper>
<ChartHeader>
<TokenPrice>
<PriceLoadingBubble />
</TokenPrice>
<DeltaContainer>
<Space heightSize={20} />
</DeltaContainer>
</ChartHeader>
<TokenPrice>
<PriceLoadingBubble />
</TokenPrice>
<DeltaContainer>
<Space heightSize={20} />
</DeltaContainer>
<LoadingChartContainer>
<div>
<ChartAnimation>
<svg width="416" height="160" xmlns="http://www.w3.org/2000/svg">
<path d="M 0 80 Q 104 10, 208 80 T 416 80" stroke="#2e3138" fill="transparent" strokeWidth="2" />
</svg>
<svg width="416" height="160" xmlns="http://www.w3.org/2000/svg">
<path d="M 0 80 Q 104 10, 208 80 T 416 80" stroke="#2e3138" fill="transparent" strokeWidth="2" />
</svg>
<svg width="416" height="160" xmlns="http://www.w3.org/2000/svg">
<path d="M 0 80 Q 104 10, 208 80 T 416 80" stroke="#2e3138" fill="transparent" strokeWidth="2" />
</svg>
<svg width="416" height="160" xmlns="http://www.w3.org/2000/svg">
<path d="M 0 80 Q 104 10, 208 80 T 416 80" stroke="#2e3138" fill="transparent" strokeWidth="2" />
</svg>
<svg width="416" height="160" xmlns="http://www.w3.org/2000/svg">
<path d="M 0 80 Q 104 10, 208 80 T 416 80" stroke="#2e3138" fill="transparent" strokeWidth="2" />
</svg>
<Wave />
<Wave />
<Wave />
<Wave />
<Wave />
</ChartAnimation>
</ChartWrapper>
</ChartContainer>
</div>
</LoadingChartContainer>
<Space heightSize={32} />
</ChartHeader>
<AboutSection>
<AboutHeader>
<SquareLoadingBubble />
</AboutHeader>
<LongLoadingBubble />
<LongLoadingBubble />
<HalfLoadingBubble />
<ResourcesContainer>{null}</ResourcesContainer>
</AboutSection>
<StatsSection>
<StatsLoadingContainer>
<StatPair>
@@ -150,6 +144,16 @@ export default function LoadingTokenDetail() {
</StatPair>
</StatsLoadingContainer>
</StatsSection>
<AboutContainer>
<AboutHeader>
<SquareLoadingBubble />
</AboutHeader>
<LongLoadingBubble />
<LongLoadingBubble />
<HalfLoadingBubble />
<ResourcesContainer>{null}</ResourcesContainer>
</AboutContainer>
<ContractAddressSection>{null}</ContractAddressSection>
</TopArea>
)

Some files were not shown because too many files have changed in this diff Show More