Commit 32e62376 authored by cartcrom's avatar cartcrom Committed by GitHub

feat: 🤑 Portfolios 🖼 (#6216)

* feat: squash mgtm differences w/ public repo into new base commit to fix rebase issues going forward

* feat: new settings menu (#85)

* feat: new settings flow

* feat: add statsig geo gate for MGTM assets (#91)

* init
* feat: geo-gate mgtm features & wallet tab

* feat: new theme toggle (#86)

* feat: new theme toggle

* fix: import

* refactor: polish

* refactor: use enum instead of string union

* feat: mini portfolio tabs (#88)

* feat: mini portfolio tabs

* feat: feature flag

* feat: portfolio query (#89)

* feat: portfolio balance query

* polish

* fix: added todo for api key

* feat: tokens mini-portfolio tab (#93)

* feat: tokens tab

* fix: lint

* fix: pr comment polish

* fix: snapshot update

* feat: common portfolio row component (#99)

* feat: porfolio row component

* fix: updated layout

* feat: update token row design (#100)

* feat: porfolio row component

* fix: updated layout

* fix: updated tokens tab to latest design

* fix: unnused export

* feat: dropdown drawer (#95)

* inital drawer

* feat: animated drawer

* fix: attempt animation perf fix

* fix: lint

* feat: better animations

* fix: scrim

* refactor: const name

* test: update chain switcher test

* test: update chain switcher test id

* feat: Add NFT tab for mini porftolio (#104)

* add NFT tab

* add min width to verified icon size

* add keyArgs to nftBalances query so that different callers dont override query cache

* revert yarn node changes

* use flex shrink

* move styled components to top

* navigate to nft page

* update snapshot test after adding gap to Row

* Update src/components/WalletDropdown/MiniPortfolio/NFT.tsx
Co-authored-by: default avatarcartcrom <39385577+cartcrom@users.noreply.github.com>

---------
Co-authored-by: default avatarcartcrom <39385577+cartcrom@users.noreply.github.com>

* fix scroll behavior (#105)

* feat: portfolio loading state (#106)

* feat: loading state for token row

* fix: revert hardcoded loading states

* fix: removed unused component

* feat: activity feed tab (#103)

* init

* feat: ens avatars

* feat: etherscan api experiment

* fix: ignore error policy

* polish

* fix: query pageSize

* fix: revert redux change

* fix: small polish item

* fix: pr comments

* fix: translated activity titles

* fix: typing and todo comment

* todo comment

* fix: accidental chain name mismatch

* feat: remove dropdown chevron (#110)

* feat: small drawer UI updates (#109)

* feat: small changes

* fix: resize status icon w/ ens

* fix: commented css

* feat: activity loading state (#108)

* feat: activity loading state

* fix: unused wrapper

* fix: activity/nftbalance cache overlap

* fix: mp sidebar width adjustment (#112)

* fix: mp sidebar width adjustment

* fix: navbar breakpoints

* feat: nav bar MenuDropdown updates (#107)

* feat: nav bar MenuDropdown updates

* fix: pool breakpoints

* fix: pool in menu dropdown

* fix: lints

* fix: tests

* feat: swap click updates (#111)

* feat: swap click updates

* fix: updates

* fix: simplify

* fix: snapshots

* fix: snapshots

* feat: collapse button (#113)

* feat: collapse button

* fix: esc keypress and animation

* fix: MP scrollbar (#115)

* feat: hide small balances in token list (#116)

* feat: collapse button (#113)

* feat: collapse button

* fix: esc keypress and animation

* chore: merge

* fix: updates

* feat: show the hidden tokens at the bottom

* feat: empty balance state for tokens (#118)

* feat: empty balance state for tokens

* fix: balance change check

* fix: token inputs on TDP widget (#120)

* feat: remove quick swap button (#125)

* fix: nft activity descriptor (#119)

* fix: improper substring usage error

* pr comments + small fix

* feat: add mini portfolio events (#126)

* add events

* fix failing snapshot test

* incorporate eddie comment

* fix chain switching (#127)

* feat: pre-parse activity to catch errors (#129)

* feat: update wallet option icons (#128)

* feat: update wallet option icons

* feat: theme-aware injected logo, svg logos

* fix: rabby extension

* feat: uni icons avatars (#130)

* feat: update wallet option icons

* feat: theme-aware injected logo, svg logos

* fix: rabby extension

* feat: update avatar / icon logic

* fix: remove testing hardcoded socks balance

* fix: add isTrustWallet check

* feat: pools tab (#122)

* init

* feat: working cross-chain calls

* feat: inline range text

* feat: better multicall perf

* feat: loading state

* feat: pools persist between mounts

* feat: polish

* fix: small refactors

* remove stuff to split into sep pr

* fix: remove comment

* fix: judo PR comments

* fix: currencyKey case

* fix: eddie's comment

* fix (#134)

* feat: removed microsite content, updated responsiveness for wallet tab (#137)

* fix: injector unit tests (#136)

* fix: injector unit tests

* fix: lint

---------
Co-authored-by: default avatarcartcrom <cartergcromer@gmail.com>

* feat: toggle closed positions (#138)

* rename file

* refactored hidden row to use for closed positions

* fix: remove unnused atom

* fix: lint

* feat: MP tab empty states (#132)

* feat: squash mgtm differences w/ public repo into new base commit to fix rebase issues going forward

* feat: new settings menu (#85)

* feat: new settings flow

* feat: add statsig geo gate for MGTM assets (#91)

* init
* feat: geo-gate mgtm features & wallet tab

* feat: new theme toggle (#86)

* feat: new theme toggle

* fix: import

* refactor: polish

* refactor: use enum instead of string union

* feat: mini portfolio tabs (#88)

* feat: mini portfolio tabs

* feat: feature flag

* feat: portfolio query (#89)

* feat: portfolio balance query

* polish

* fix: added todo for api key

* feat: tokens mini-portfolio tab (#93)

* feat: tokens tab

* fix: lint

* fix: pr comment polish

* fix: snapshot update

* feat: common portfolio row component (#99)

* feat: porfolio row component

* fix: updated layout

* feat: update token row design (#100)

* feat: porfolio row component

* fix: updated layout

* fix: updated tokens tab to latest design

* fix: unnused export

* feat: dropdown drawer (#95)

* inital drawer

* feat: animated drawer

* fix: attempt animation perf fix

* fix: lint

* feat: better animations

* fix: scrim

* refactor: const name

* test: update chain switcher test

* test: update chain switcher test id

* feat: Add NFT tab for mini porftolio (#104)

* add NFT tab

* add min width to verified icon size

* add keyArgs to nftBalances query so that different callers dont override query cache

* revert yarn node changes

* use flex shrink

* move styled components to top

* navigate to nft page

* update snapshot test after adding gap to Row

* Update src/components/WalletDropdown/MiniPortfolio/NFT.tsx
Co-authored-by: default avatarcartcrom <39385577+cartcrom@users.noreply.github.com>

---------
Co-authored-by: default avatarcartcrom <39385577+cartcrom@users.noreply.github.com>

* fix scroll behavior (#105)

* feat: portfolio loading state (#106)

* feat: loading state for token row

* fix: revert hardcoded loading states

* fix: removed unused component

* feat: activity feed tab (#103)

* init

* feat: ens avatars

* feat: etherscan api experiment

* fix: ignore error policy

* polish

* fix: query pageSize

* fix: revert redux change

* fix: small polish item

* fix: pr comments

* fix: translated activity titles

* fix: typing and todo comment

* todo comment

* fix: accidental chain name mismatch

* feat: remove dropdown chevron (#110)

* feat: small drawer UI updates (#109)

* feat: small changes

* fix: resize status icon w/ ens

* fix: commented css

* init

* feat: working cross-chain calls

* feat: inline range text

* feat: better multicall perf

* feat: activity loading state (#108)

* feat: activity loading state

* fix: unused wrapper

* feat: loading state

* feat: pools persist between mounts

* fix: activity/nftbalance cache overlap

* fix: mp sidebar width adjustment (#112)

* fix: mp sidebar width adjustment

* fix: navbar breakpoints

* feat: nav bar MenuDropdown updates (#107)

* feat: nav bar MenuDropdown updates

* fix: pool breakpoints

* fix: pool in menu dropdown

* fix: lints

* fix: tests

* feat: swap click updates (#111)

* feat: swap click updates

* fix: updates

* fix: simplify

* fix: snapshots

* fix: snapshots

* feat: collapse button (#113)

* feat: collapse button

* fix: esc keypress and animation

* fix: MP scrollbar (#115)

* feat: hide small balances in token list (#116)

* feat: collapse button (#113)

* feat: collapse button

* fix: esc keypress and animation

* chore: merge

* fix: updates

* feat: show the hidden tokens at the bottom

* feat: empty balance state for tokens (#118)

* feat: empty balance state for tokens

* fix: balance change check

* fix: token inputs on TDP widget (#120)

* feat: polish

* fix: small refactors

* remove stuff to split into sep pr

* fix: remove comment

* feat: remove quick swap button (#125)

* fix: nft activity descriptor (#119)

* fix: improper substring usage error

* pr comments + small fix

* feat: add mini portfolio events (#126)

* add events

* fix failing snapshot test

* incorporate eddie comment

* fix chain switching (#127)

* feat: pre-parse activity to catch errors (#129)

* fix: activity/nftbalance cache overlap

* fix: mp sidebar width adjustment (#112)

* fix: mp sidebar width adjustment

* fix: navbar breakpoints

* feat: nav bar MenuDropdown updates (#107)

* feat: nav bar MenuDropdown updates

* fix: pool breakpoints

* fix: pool in menu dropdown

* fix: lints

* fix: tests

* feat: swap click updates (#111)

* feat: swap click updates

* fix: updates

* fix: simplify

* fix: snapshots

* fix: snapshots

* feat: collapse button (#113)

* feat: collapse button

* fix: esc keypress and animation

* fix: MP scrollbar (#115)

* feat: hide small balances in token list (#116)

* feat: collapse button (#113)

* feat: collapse button

* fix: esc keypress and animation

* chore: merge

* fix: updates

* feat: show the hidden tokens at the bottom

* feat: empty balance state for tokens (#118)

* feat: empty balance state for tokens

* fix: balance change check

* fix: token inputs on TDP widget (#120)

* feat: remove quick swap button (#125)

* fix: nft activity descriptor (#119)

* fix: improper substring usage error

* pr comments + small fix

* feat: add mini portfolio events (#126)

* add events

* fix failing snapshot test

* incorporate eddie comment

* fix chain switching (#127)

* feat: pre-parse activity to catch errors (#129)

* feat: start empty state updates

* feat: update wallet option icons (#128)

* feat: update wallet option icons

* feat: theme-aware injected logo, svg logos

* fix: rabby extension

* feat: empty wallet states w/ good positioning

* feat: finish empty states w/ theme aware icons

* fix: judo PR comments

* fix: currencyKey case

* feat: uni icons avatars (#130)

* feat: update wallet option icons

* feat: theme-aware injected logo, svg logos

* fix: rabby extension

* feat: update avatar / icon logic

* fix: remove testing hardcoded socks balance

* fix: add isTrustWallet check

* fix: eddie's comment

* feat: pools tab (#122)

* init

* feat: working cross-chain calls

* feat: inline range text

* feat: better multicall perf

* feat: loading state

* feat: pools persist between mounts

* feat: polish

* fix: small refactors

* remove stuff to split into sep pr

* fix: remove comment

* fix: judo PR comments

* fix: currencyKey case

* fix: eddie's comment

* fix (#134)

* feat: removed microsite content, updated responsiveness for wallet tab (#137)

* fix: injector unit tests (#136)

* fix: injector unit tests

* fix: lint

---------
Co-authored-by: default avatarcartcrom <cartergcromer@gmail.com>

* fix: pool loading state

* fix: simplify pools loading state

* fix: removed unused var

* feat: toggle closed positions (#138)

* rename file

* refactored hidden row to use for closed positions

* fix: remove unnused atom

* fix: lint

* fix: dry

* fix: lint

* fix: address review comments

* fix: lints

* fix: bad merge

---------
Co-authored-by: default avatarcartcrom <cartergcromer@gmail.com>
Co-authored-by: default avatarcartcrom <39385577+cartcrom@users.noreply.github.com>
Co-authored-by: default avatarTina <59578595+tinaszheng@users.noreply.github.com>
Co-authored-by: default avatarlynn <41491154+lynnshaoyu@users.noreply.github.com>

* make sure mp is above tax service banner (#142)

* feat: add git version number to settings menu (#141)

* fix: remove unicon tooltip (#146)

* feat: remove unicon tooltip from mobile and timer prop

* fix: flip bool logic

* fix: small nits for mp from fred (#150)

* fixes

* fix

* feat: Local tx activity (#148)

* local swap working

* feat: cross-chain

* fix: revert query changes

* feat: local approvals

* feat: wrapped activity

* feat: local lp tx history

* fix: add doc comment

* fix: linted

* fix: no pools render error (#152)

* feat: mp activity feed design tweaks  (#145)

* feat: squash mgtm differences w/ public repo into new base commit to fix rebase issues going forward

* feat: new settings menu (#85)

* feat: new settings flow

* feat: add statsig geo gate for MGTM assets (#91)

* init
* feat: geo-gate mgtm features & wallet tab

* feat: new theme toggle (#86)

* feat: new theme toggle

* fix: import

* refactor: polish

* refactor: use enum instead of string union

* feat: mini portfolio tabs (#88)

* feat: mini portfolio tabs

* feat: feature flag

* feat: portfolio query (#89)

* feat: portfolio balance query

* polish

* fix: added todo for api key

* feat: tokens mini-portfolio tab (#93)

* feat: tokens tab

* fix: lint

* fix: pr comment polish

* fix: snapshot update

* feat: common portfolio row component (#99)

* feat: porfolio row component

* fix: updated layout

* feat: update token row design (#100)

* feat: porfolio row component

* fix: updated layout

* fix: updated tokens tab to latest design

* fix: unnused export

* feat: dropdown drawer (#95)

* inital drawer

* feat: animated drawer

* fix: attempt animation perf fix

* fix: lint

* feat: better animations

* fix: scrim

* refactor: const name

* test: update chain switcher test

* test: update chain switcher test id

* feat: Add NFT tab for mini porftolio (#104)

* add NFT tab

* add min width to verified icon size

* add keyArgs to nftBalances query so that different callers dont override query cache

* revert yarn node changes

* use flex shrink

* move styled components to top

* navigate to nft page

* update snapshot test after adding gap to Row

* Update src/components/WalletDropdown/MiniPortfolio/NFT.tsx
Co-authored-by: default avatarcartcrom <39385577+cartcrom@users.noreply.github.com>

---------
Co-authored-by: default avatarcartcrom <39385577+cartcrom@users.noreply.github.com>

* fix scroll behavior (#105)

* feat: portfolio loading state (#106)

* feat: loading state for token row

* fix: revert hardcoded loading states

* fix: removed unused component

* feat: activity feed tab (#103)

* init

* feat: ens avatars

* feat: etherscan api experiment

* fix: ignore error policy

* polish

* fix: query pageSize

* fix: revert redux change

* fix: small polish item

* fix: pr comments

* fix: translated activity titles

* fix: typing and todo comment

* todo comment

* fix: accidental chain name mismatch

* feat: remove dropdown chevron (#110)

* feat: small drawer UI updates (#109)

* feat: small changes

* fix: resize status icon w/ ens

* fix: commented css

* feat: activity loading state (#108)

* feat: activity loading state

* fix: unused wrapper

* fix: activity/nftbalance cache overlap

* fix: mp sidebar width adjustment (#112)

* fix: mp sidebar width adjustment

* fix: navbar breakpoints

* feat: nav bar MenuDropdown updates (#107)

* feat: nav bar MenuDropdown updates

* fix: pool breakpoints

* fix: pool in menu dropdown

* fix: lints

* fix: tests

* feat: swap click updates (#111)

* feat: swap click updates

* fix: updates

* fix: simplify

* fix: snapshots

* fix: snapshots

* feat: collapse button (#113)

* feat: collapse button

* fix: esc keypress and animation

* fix: MP scrollbar (#115)

* feat: hide small balances in token list (#116)

* feat: collapse button (#113)

* feat: collapse button

* fix: esc keypress and animation

* chore: merge

* fix: updates

* feat: show the hidden tokens at the bottom

* feat: empty balance state for tokens (#118)

* feat: empty balance state for tokens

* fix: balance change check

* fix: token inputs on TDP widget (#120)

* feat: remove quick swap button (#125)

* fix: nft activity descriptor (#119)

* fix: improper substring usage error

* pr comments + small fix

* feat: add mini portfolio events (#126)

* add events

* fix failing snapshot test

* incorporate eddie comment

* fix chain switching (#127)

* feat: pre-parse activity to catch errors (#129)

* feat: update wallet option icons (#128)

* feat: update wallet option icons

* feat: theme-aware injected logo, svg logos

* fix: rabby extension

* wip

* feat: uni icons avatars (#130)

* feat: update wallet option icons

* feat: theme-aware injected logo, svg logos

* fix: rabby extension

* feat: update avatar / icon logic

* fix: remove testing hardcoded socks balance

* fix: add isTrustWallet check

* feat: pools tab (#122)

* init

* feat: working cross-chain calls

* feat: inline range text

* feat: better multicall perf

* feat: loading state

* feat: pools persist between mounts

* feat: polish

* fix: small refactors

* remove stuff to split into sep pr

* fix: remove comment

* fix: judo PR comments

* fix: currencyKey case

* fix: eddie's comment

* fix (#134)

* wip with activity status icons

* feat: removed microsite content, updated responsiveness for wallet tab (#137)

* fix: injector unit tests (#136)

* fix: injector unit tests

* fix: lint

---------
Co-authored-by: default avatarcartcrom <cartergcromer@gmail.com>

* temp

* in progress

* text, and activity status changes working. missing logo changes

* fix lint issues

* refactor: square logo location

* feat: merge other activity changes

---------
Co-authored-by: default avatarcartcrom <cartergcromer@gmail.com>
Co-authored-by: default avatarcartcrom <39385577+cartcrom@users.noreply.github.com>
Co-authored-by: default avatarTina <59578595+tinaszheng@users.noreply.github.com>
Co-authored-by: default avatareddie <66155195+just-toby@users.noreply.github.com>

* feat: portfolio polling/refetching/performance (#154)

* init

* feat: implement asset polling & tx updating balances

* add refetching to activity tab

* fix: re-add error policy

* fix: add TODO

* fix: fix scroll + console warnings on mini portfolio nfts (#155)

* fixes

* rename

* init (#158)

* fix: browser wallet icons (#156)

* fix: moved border radius css (#157)

* feat: token details state for BNB (#151)

* fix: update token details missing flow

* refactor

* fix: lint

* fix: add bnb to queries

* feat: add comment explaining unsupported chains

* fix: remove buy crypto animation (#160)

* fix: price display pools tab (#159)

* fix: price display pools tab

* fix: lint

* feat: pools tab performance (#123)

* init

* fix: pr comments

* update cache return type

* refactor: rename type

* fix: further pr comments

* fix: remove stringify

* refactor: readability and caching

* fix: zach pr comments

* fix: removed hardcoded value

* feat: catch position errors for chains

* fix: add todo comment for followup ticket

* fix: build issue from merge conflict

* refactor: split up token caching function

* feat: separate array slicing into util with tests

* feat: close wallet drawer on wallet connection (#161)

* feat: close wallet drawer on wallet connection

* feat: added comment explaining fetchPolicy

* fix: only close if open

* refactor: add comment about ref

* fix: revert change to useAllTokens & rename with more descriptive name (#163)

* fix: square Arbitrum logo design changes  (#162)

* feat: close wallet drawer on wallet connection

* feat: added comment explaining fetchPolicy

* fix: only close if open

* init

* fixes

* fix border radius

---------
Co-authored-by: default avatarcartcrom <cartergcromer@gmail.com>

* fix: token loading state (#165)

* fix: remove unnused code and comments

* fix: privacy policy date

* fix: revert readme change

* fix: remove unnused FOR file

* fix: missed query id

* fix: add id to portfolios query

* fix: widget cypress test

---------
Co-authored-by: default avatarTina <59578595+tinaszheng@users.noreply.github.com>
Co-authored-by: default avatareddie <66155195+just-toby@users.noreply.github.com>
Co-authored-by: default avatarlynn <41491154+lynnshaoyu@users.noreply.github.com>
parent cee19aeb
......@@ -25,6 +25,7 @@ describe('Landing Page', () => {
})
it('allows navigation to pool', () => {
cy.viewport(2000, 1600)
cy.get(getTestSelector('pool-nav-link')).first().click()
cy.url().should('include', '/pools')
})
......
// see https://github.com/Uniswap/interface/pull/4115
describe('Link', () => {
it('should update route', () => {
cy.viewport(2000, 1600)
cy.visit('/')
cy.contains('Pool').click()
cy.get('[data-cy="join-pool-button"]').should('exist')
......
......@@ -17,7 +17,7 @@ describe('swap widget integration tests', () => {
// open token selector...
cy.contains('Select token').click()
// select token...
cy.contains(outputText).click()
cy.contains(outputText).click({ force: true })
cy.get('body')
.then(($body) => {
......
......@@ -69,6 +69,6 @@ describe('Token explore', () => {
cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Optimism')
cy.reload()
cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Optimism')
cy.get(getTestSelector('chain-selector')).last().should('contain', 'Ethereum')
cy.get(getTestSelector('chain-selector-logo')).invoke('attr', 'alt').should('eq', 'Ethereum')
})
})
import { getTestSelector } from '../utils'
function visit(darkMode: boolean) {
cy.visit('/swap', {
onBeforeLoad(win) {
cy.stub(win, 'matchMedia')
.withArgs('(prefers-color-scheme: dark)')
.returns({
matches: darkMode,
addEventListener() {
// do nothing
},
})
},
})
}
describe('Wallet Dropdown', () => {
before(() => {
cy.visit('/pools')
......@@ -7,12 +22,25 @@ describe('Wallet Dropdown', () => {
it('should change the theme', () => {
cy.get(getTestSelector('web3-status-connected')).click()
cy.get(getTestSelector('wallet-select-theme')).click()
cy.get(getTestSelector('wallet-select-theme')).contains('Light theme').should('exist')
cy.get(getTestSelector('wallet-settings')).click()
cy.get(getTestSelector('theme-lightmode')).click()
cy.get(getTestSelector('theme-lightmode')).should('not.have.css', 'background-color', 'rgba(0, 0, 0, 0)')
cy.get(getTestSelector('theme-darkmode')).should('have.css', 'background-color', 'rgba(0, 0, 0, 0)')
cy.get(getTestSelector('theme-auto')).should('have.css', 'background-color', 'rgba(0, 0, 0, 0)')
cy.get(getTestSelector('theme-darkmode')).click()
cy.get(getTestSelector('theme-lightmode')).should('have.css', 'background-color', 'rgba(0, 0, 0, 0)')
cy.get(getTestSelector('theme-darkmode')).should('not.have.css', 'background-color', 'rgba(0, 0, 0, 0)')
cy.get(getTestSelector('theme-auto')).should('have.css', 'background-color', 'rgba(0, 0, 0, 0)')
cy.get(getTestSelector('theme-auto')).click()
cy.get(getTestSelector('theme-lightmode')).should('have.css', 'background-color', 'rgba(0, 0, 0, 0)')
cy.get(getTestSelector('theme-darkmode')).should('have.css', 'background-color', 'rgba(0, 0, 0, 0)')
cy.get(getTestSelector('theme-auto')).should('not.have.css', 'background-color', 'rgba(0, 0, 0, 0)')
})
it('should select a language', () => {
cy.get(getTestSelector('wallet-select-language')).click()
cy.get(getTestSelector('wallet-language-item')).contains('Deutsch').click({ force: true })
cy.get(getTestSelector('wallet-header')).should('contain', 'Sprache')
cy.get(getTestSelector('wallet-language-item')).contains('English').click({ force: true })
......@@ -20,22 +48,13 @@ describe('Wallet Dropdown', () => {
cy.get(getTestSelector('wallet-back')).click()
})
it('should be able to view transactions', () => {
cy.get(getTestSelector('wallet-transactions')).click()
cy.get(getTestSelector('wallet-empty-transaction-text')).should('exist')
cy.get(getTestSelector('wallet-back')).click()
})
it('should change the theme when not connected', () => {
cy.get(getTestSelector('wallet-disconnect')).click()
cy.get(getTestSelector('wallet-select-theme')).click()
cy.get(getTestSelector('wallet-select-theme')).contains('Dark theme').should('exist')
cy.get(getTestSelector('wallet-select-theme')).click()
cy.get(getTestSelector('wallet-select-theme')).contains('Light theme').should('exist')
cy.get(getTestSelector('wallet-settings')).click()
cy.get(getTestSelector('theme-lightmode')).should('exist')
})
it('should select a language when not connected', () => {
cy.get(getTestSelector('wallet-select-language')).click()
cy.get(getTestSelector('wallet-language-item')).contains('Deutsch').click({ force: true })
cy.get(getTestSelector('wallet-header')).should('contain', 'Sprache')
cy.get(getTestSelector('wallet-language-item')).contains('English').click({ force: true })
......@@ -43,9 +62,19 @@ describe('Wallet Dropdown', () => {
cy.get(getTestSelector('wallet-back')).click()
})
it('should open the wallet connect modal from the drop down when not connected', () => {
cy.get(getTestSelector('wallet-connect-wallet')).click()
cy.get(getTestSelector('wallet-modal')).should('exist')
cy.get(getTestSelector('wallet-modal-close')).click()
it('should properly use dark system theme when auto theme setting is selected', () => {
visit(true)
cy.get(getTestSelector('web3-status-connected')).click()
cy.get(getTestSelector('wallet-settings')).click()
cy.get(getTestSelector('theme-auto')).click()
cy.get(getTestSelector('wallet-header')).should('have.css', 'color', 'rgb(152, 161, 192)')
})
it('should properly use light system theme when auto theme setting is selected', () => {
visit(false)
cy.get(getTestSelector('web3-status-connected')).click()
cy.get(getTestSelector('wallet-settings')).click()
cy.get(getTestSelector('theme-auto')).click()
cy.get(getTestSelector('wallet-header')).should('have.css', 'color', 'rgb(119, 128, 160)')
})
})
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 2496 2496" style="enable-background:new 0 0 2496 2496;" xml:space="preserve">
<g>
<path style="fill-rule:evenodd;clip-rule:evenodd;fill:#F0B90B;" d="M1248,0c689.3,0,1248,558.7,1248,1248s-558.7,1248-1248,1248
S0,1937.3,0,1248S558.7,0,1248,0L1248,0z"/>
<path style="fill:#FFFFFF;" d="M685.9,1248l0.9,330l280.4,165v193.2l-444.5-260.7v-524L685.9,1248L685.9,1248z M685.9,918v192.3
l-163.3-96.6V821.4l163.3-96.6l164.1,96.6L685.9,918L685.9,918z M1084.3,821.4l163.3-96.6l164.1,96.6L1247.6,918L1084.3,821.4
L1084.3,821.4z"/>
<path style="fill:#FFFFFF;" d="M803.9,1509.6v-193.2l163.3,96.6v192.3L803.9,1509.6L803.9,1509.6z M1084.3,1812.2l163.3,96.6
l164.1-96.6v192.3l-164.1,96.6l-163.3-96.6V1812.2L1084.3,1812.2z M1645.9,821.4l163.3-96.6l164.1,96.6v192.3l-164.1,96.6V918
L1645.9,821.4L1645.9,821.4L1645.9,821.4z M1809.2,1578l0.9-330l163.3-96.6v524l-444.5,260.7v-193.2L1809.2,1578L1809.2,1578
L1809.2,1578z"/>
<polygon style="fill:#FFFFFF;" points="1692.1,1509.6 1528.8,1605.3 1528.8,1413 1692.1,1316.4 1692.1,1509.6 "/>
<path style="fill:#FFFFFF;" d="M1692.1,986.4l0.9,193.2l-281.2,165v330.8l-163.3,95.7l-163.3-95.7v-330.8l-281.2-165V986.4
L968,889.8l279.5,165.8l281.2-165.8l164.1,96.6H1692.1L1692.1,986.4z M803.9,656.5l443.7-261.6l444.5,261.6l-163.3,96.6
l-281.2-165.8L967.2,753.1L803.9,656.5L803.9,656.5z"/>
</g>
</svg>
This diff is collapsed.
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="24" height="24" rx="8" fill="#F7F9FB"/>
<path d="M19.6128 4L13.2335 8.73803L14.4132 5.94266L19.6128 4Z" fill="#E2761B" stroke="#E2761B" stroke-width="0.0641141" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4.88603 4L11.2141 8.78291L10.0921 5.94266L4.88603 4ZM17.3177 14.9827L15.6187 17.5858L19.254 18.586L20.2991 15.0404L17.3177 14.9827ZM4.21283 15.0404L5.25148 18.586L8.88675 17.5858L7.18772 14.9827L4.21283 15.0404Z" fill="#E4761B" stroke="#E4761B" stroke-width="0.0641141" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8.68189 10.5847L7.66888 12.117L11.2785 12.2773L11.1503 8.3984L8.68189 10.5847ZM15.8178 10.5847L13.3173 8.35352L13.234 12.2773L16.8372 12.117L15.8178 10.5847ZM8.88705 17.5859L11.0541 16.5281L9.18198 15.0663L8.88705 17.5859ZM13.4456 16.5281L15.619 17.5859L15.3177 15.0663L13.4456 16.5281Z" fill="#E4761B" stroke="#E4761B" stroke-width="0.0641141" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M15.6191 17.5857L13.4456 16.5278L13.6187 17.9448L13.5995 18.541L15.6191 17.5857ZM8.88708 17.5857L10.9067 18.541L10.8939 17.9448L11.0541 16.5278L8.88708 17.5857Z" fill="#D7C1B3" stroke="#D7C1B3" stroke-width="0.0641141" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10.9392 14.1297L9.13123 13.5976L10.4071 13.0142L10.9392 14.1297ZM13.5615 14.1297L14.0937 13.0142L15.3759 13.5976L13.5615 14.1297Z" fill="#233447" stroke="#233447" stroke-width="0.0641141" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8.88652 17.5856L9.19427 14.9826L7.1875 15.0403L8.88652 17.5856ZM15.3108 14.9826L15.6185 17.5856L17.3175 15.0403L15.3108 14.9826ZM16.8367 12.1167L13.2335 12.277L13.5669 14.1299L14.099 13.0143L15.3813 13.5977L16.8367 12.1167ZM9.13016 13.5977L10.4124 13.0143L10.9382 14.1299L11.278 12.277L7.66836 12.1167L9.13016 13.5977Z" fill="#CD6116" stroke="#CD6116" stroke-width="0.0641141" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7.66888 12.1167L9.18198 15.0659L9.13069 13.5977L7.66888 12.1167ZM15.3818 13.5977L15.3177 15.0659L16.8372 12.1167L15.3818 13.5977ZM11.2785 12.277L10.9387 14.1299L11.3619 16.3162L11.458 13.4374L11.2785 12.277ZM13.234 12.277L13.0609 13.431L13.1378 16.3162L13.5674 14.1299L13.234 12.277Z" fill="#E4751F" stroke="#E4751F" stroke-width="0.0641141" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13.5679 14.1298L13.1384 16.3161L13.4461 16.5277L15.3182 15.0659L15.3824 13.5977L13.5679 14.1298ZM9.13123 13.5977L9.18252 15.0659L11.0546 16.5277L11.3624 16.3161L10.9392 14.1298L9.13123 13.5977Z" fill="#F6851B" stroke="#F6851B" stroke-width="0.0641141" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13.5995 18.5412L13.6187 17.945L13.4584 17.8039H11.0413L10.8939 17.945L10.9067 18.5412L8.88708 17.5859L9.59234 18.163L11.0221 19.1567H13.4777L14.9138 18.163L15.6191 17.5859L13.5995 18.5412Z" fill="#C0AD9E" stroke="#C0AD9E" stroke-width="0.0641141" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13.4455 16.528L13.1377 16.3164H11.3618L11.054 16.528L10.8937 17.9449L11.0412 17.8039H13.4583L13.6186 17.9449L13.4455 16.528Z" fill="#161616" stroke="#161616" stroke-width="0.0641141" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M19.8824 9.04578L20.4273 6.42992L19.6131 4L13.4453 8.57775L15.8175 10.5845L19.1707 11.5655L19.9144 10.6999L19.5939 10.4691L20.1068 10.0011L19.7093 9.69333L20.2222 9.30224L19.8824 9.04578ZM4.07825 6.42992L4.62322 9.04578L4.277 9.30224L4.78991 9.69333L4.39882 10.0011L4.91173 10.4691L4.59116 10.6999L5.32847 11.5655L8.68164 10.5845L11.0539 8.57775L4.88608 4L4.07825 6.42992Z" fill="#763D16" stroke="#763D16" stroke-width="0.0641141" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M19.1706 11.5657L15.8175 10.5847L16.8369 12.1171L15.3174 15.0663L17.3177 15.0407H20.2991L19.1706 11.5657ZM8.68158 10.5847L5.32841 11.5657L4.21283 15.0407H7.18772L9.18167 15.0663L7.66858 12.1171L8.68158 10.5847ZM13.2337 12.2773L13.4453 8.57796L14.4198 5.94287H10.0921L11.0538 8.57796L11.2782 12.2773L11.3551 13.4442L11.3616 16.3165H13.1375L13.1503 13.4442L13.2337 12.2773Z" fill="#F6851B" stroke="#F6851B" stroke-width="0.0641141" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
This diff is collapsed.
This diff is collapsed.
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 22.773 22.773" style="enable-background:new 0 0 22.773 22.773;" xml:space="preserve">
<g>
<g>
<path d="M15.769,0c0.053,0,0.106,0,0.162,0c0.13,1.606-0.483,2.806-1.228,3.675c-0.731,0.863-1.732,1.7-3.351,1.573
c-0.108-1.583,0.506-2.694,1.25-3.561C13.292,0.879,14.557,0.16,15.769,0z"/>
<path d="M20.67,16.716c0,0.016,0,0.03,0,0.045c-0.455,1.378-1.104,2.559-1.896,3.655c-0.723,0.995-1.609,2.334-3.191,2.334
c-1.367,0-2.275-0.879-3.676-0.903c-1.482-0.024-2.297,0.735-3.652,0.926c-0.155,0-0.31,0-0.462,0
c-0.995-0.144-1.798-0.932-2.383-1.642c-1.725-2.098-3.058-4.808-3.306-8.276c0-0.34,0-0.679,0-1.019
c0.105-2.482,1.311-4.5,2.914-5.478c0.846-0.52,2.009-0.963,3.304-0.765c0.555,0.086,1.122,0.276,1.619,0.464
c0.471,0.181,1.06,0.502,1.618,0.485c0.378-0.011,0.754-0.208,1.135-0.347c1.116-0.403,2.21-0.865,3.652-0.648
c1.733,0.262,2.963,1.032,3.723,2.22c-1.466,0.933-2.625,2.339-2.427,4.74C17.818,14.688,19.086,15.964,20.67,16.716z"/>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 470.287 514.251" enable-background="new 0 0 470.287 514.251" xml:space="preserve">
<g id="Background">
</g>
<g id="Logos_and_symbols">
<g id="SYMBOL_VER_3">
</g>
<g id="SYMBOL_VER_3_3_">
</g>
<g id="SYMBOL_VER_4">
</g>
<g id="SYMBOL_VER_4_1_">
<g id="SYMBOL_VER_4_3_">
</g>
</g>
<g id="SYMBOL_VER_5_1_">
</g>
<g id="off_2_1_">
</g>
<g id="VER_3_1_">
<g id="SYMBOL_VER_2_1_">
</g>
</g>
<g id="VER_3">
<g id="SYMBOL_VER_2">
</g>
</g>
<g id="off_2">
</g>
<g id="SYMBOL_VER_5">
</g>
<g id="SYMBOL_VER_1">
</g>
<g id="SYMBOL_VER_1_1_">
</g>
<g id="SYMBOL_VER_1-1_3_">
</g>
<g id="SYMBOL_VER_1-1_2_">
</g>
<g id="SYMBOL_VER_1-1">
</g>
<g id="SYMBOL_VER_1-1_1_">
<g id="_x31_-3">
</g>
<g id="Symbol_-_Original_14_">
<path fill="#2D374B" d="M291.134,237.469l35.654-60.5l96.103,149.684l0.046,28.727l-0.313-197.672
c-0.228-4.832-2.794-9.252-6.887-11.859L242.715,46.324c-4.045-1.99-9.18-1.967-13.22,0.063c-0.546,0.272-1.06,0.57-1.548,0.895
l-0.604,0.379L59.399,144.983l-0.651,0.296c-0.838,0.385-1.686,0.875-2.48,1.444c-3.185,2.283-5.299,5.66-5.983,9.448
c-0.103,0.574-0.179,1.158-0.214,1.749l0.264,161.083l89.515-138.745c11.271-18.397,35.825-24.323,58.62-24.001l26.753,0.706
L67.588,409.765l18.582,10.697L245.692,157.22l70.51-0.256L157.091,426.849l66.306,38.138l7.922,4.556
c3.351,1.362,7.302,1.431,10.681,0.21l175.453-101.678l-33.544,19.438L291.134,237.469z M304.736,433.395l-66.969-105.108
l40.881-69.371l87.952,138.628L304.736,433.395z"/>
<polygon fill="#28A0F0" points="237.768,328.286 304.736,433.395 366.601,397.543 278.648,258.915 "/>
<path fill="#28A0F0" d="M422.937,355.379l-0.046-28.727l-96.103-149.684l-35.654,60.5l92.774,150.043l33.544-19.438
c3.29-2.673,5.281-6.594,5.49-10.825L422.937,355.379z"/>
<path fill="#FFFFFF" d="M20.219,382.469l47.369,27.296l157.634-252.801l-26.753-0.706c-22.795-0.322-47.35,5.604-58.62,24.001
L50.334,319.004l-30.115,46.271V382.469z"/>
<polygon fill="#FFFFFF" points="316.202,156.964 245.692,157.22 86.17,420.462 141.928,452.565 157.091,426.849 "/>
<path fill="#96BEDC" d="M452.65,156.601c-0.59-14.746-8.574-28.245-21.08-36.104L256.28,19.692
c-12.371-6.229-27.825-6.237-40.218-0.004c-1.465,0.739-170.465,98.752-170.465,98.752c-2.339,1.122-4.592,2.458-6.711,3.975
c-11.164,8.001-17.969,20.435-18.668,34.095v208.765l30.115-46.271L50.07,157.921c0.035-0.589,0.109-1.169,0.214-1.741
c0.681-3.79,2.797-7.171,5.983-9.456c0.795-0.569,172.682-100.064,173.228-100.337c4.04-2.029,9.175-2.053,13.22-0.063
l173.022,99.523c4.093,2.607,6.659,7.027,6.887,11.859v199.542c-0.209,4.231-1.882,8.152-5.172,10.825l-33.544,19.438
l-17.308,10.031l-61.864,35.852l-62.737,36.357c-3.379,1.221-7.33,1.152-10.681-0.21l-74.228-42.693l-15.163,25.717
l66.706,38.406c2.206,1.255,4.171,2.367,5.784,3.272c2.497,1.4,4.199,2.337,4.8,2.629c4.741,2.303,11.563,3.643,17.71,3.643
c5.636,0,11.132-1.035,16.332-3.072l182.225-105.531c10.459-8.104,16.612-20.325,17.166-33.564V156.601z"/>
</g>
<g id="Symbol_-_Original_13_">
</g>
<g id="Symbol_-_Original_6_">
</g>
<g id="Symbol_-_Original_4_">
</g>
<g id="One_color_version_-_White_3_">
<g id="Symbol_-_Original_15_">
</g>
</g>
<g id="One_color_version_-_White">
<g id="Symbol_-_Original">
</g>
</g>
<g id="Symbol_-_Monochromatic_3_">
<g id="_x33__7_">
</g>
</g>
<g id="Symbol_-_Monochromatic">
<g id="_x33__3_">
</g>
</g>
<g id="_x33__2_">
</g>
<g id="_x33__1_">
</g>
<g id="_x33_">
</g>
<g id="Symbol_-_Original_10_">
</g>
<g id="Symbol_-_Original_1_">
</g>
<g id="Symbol_-_Original_2_">
</g>
<g id="_x34__1_">
</g>
<g id="Symbol_-_Monochromatic_2_">
<g id="_x33__6_">
</g>
</g>
<g id="One_color_version_-_White_2_">
<g id="Symbol_-_Original_11_">
</g>
</g>
<g id="Symbol_-_Original_5_">
<g id="Symbol_-_Original_12_">
</g>
</g>
<g id="One_color_version_-_White_1_">
<g id="Symbol_-_Original_9_">
</g>
</g>
</g>
<g id="SYMBOL_VER_1_2_">
<g id="SYMBOL_VER_2_4_">
</g>
<g id="SYMBOL_VER_2-1-1_1_">
</g>
<g id="SYMBOL_VER_2-2-1_1_">
</g>
<g id="SYMBOL_VER_2-3-1_4_">
</g>
<g id="New_Symbol_1_">
<g id="SYMBOL_VER_2-3-1_3_">
</g>
</g>
<g id="New_Symbol">
<g id="SYMBOL_VER_2-3-1_1_">
</g>
</g>
</g>
<g id="SYMBOL_VER_2_2_">
</g>
<g id="SYMBOL_VER_4_2_">
</g>
<g id="SYMBOL_VER_3_2_">
</g>
<g id="SYMBOL_VER_3_1_">
</g>
<g id="SYMBOL_VER_1-1-1_1_">
</g>
<g id="SYMBOL_VER_1-1-1">
</g>
<g id="SYMBOL_VER_1-1-1_2_2_">
</g>
<g id="SYMBOL_VER_1-1-1_2">
</g>
<g id="SYMBOL_VER_1-1-1_2_1_">
</g>
<g id="Symbol_-_Original_7_">
</g>
<g id="Symbol_-_Original_8_">
</g>
<g id="SYMBOL_VER_2-1-1">
</g>
<g id="SYMBOL_VER_2-2-1">
</g>
<g id="SYMBOL_VER_2-3-1">
</g>
<g id="SYMBOL_VER_5-1_1_">
</g>
<g id="SYMBOL_VER_5-1">
</g>
<g id="SYMBOL_VER_5-2_1_">
</g>
<g id="SYMBOL_VER_5-2">
</g>
<g id="Symbol_-_Monochromatic_1_">
<g id="_x33__4_">
</g>
</g>
</g>
<svg width="14" height="16" viewBox="0 0 14 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.53125 5.04465V10.9554C0.53125 11.3328 0.742546 11.6817 1.08477 11.8698L6.44755 14.8258C6.78977 15.0139 7.21107 15.0139 7.55329 14.8258L12.9161 11.8698C13.2583 11.6817 13.4696 11.3328 13.4696 10.9554V5.04465C13.4696 4.66726 13.2583 4.31833 12.9161 4.13026L7.55329 1.17426C7.21107 0.986184 6.78977 0.986184 6.44755 1.17426L1.08347 4.13026C0.74125 4.31833 0.53125 4.66726 0.53125 5.04465Z" fill="#213147"/>
<path d="M8.17051 9.14643L7.40569 11.1484C7.38495 11.2041 7.38495 11.2648 7.40569 11.3204L8.72143 14.7652L10.2433 13.9263L8.4168 9.14643C8.37532 9.03631 8.21199 9.03631 8.17051 9.14643Z" fill="#12AAFF"/>
<path d="M9.70391 5.77961C9.66243 5.66949 9.4991 5.66949 9.45762 5.77961L8.6928 7.78162C8.67206 7.83731 8.67206 7.89793 8.6928 7.95361L10.8485 13.5934L12.3704 12.7545L9.70391 5.77961Z" fill="#12AAFF"/>
<path d="M7 1.39574C7.03759 1.39574 7.07519 1.40564 7.10889 1.42296L12.9124 4.62147C12.9798 4.65859 13.0213 4.72789 13.0213 4.80089V11.1967C13.0213 11.2709 12.9798 11.339 12.9124 11.3761L7.10889 14.5746C7.07648 14.5932 7.03759 14.6018 7 14.6018C6.96241 14.6018 6.92482 14.5919 6.89111 14.5746L1.08759 11.3786C1.02019 11.3415 0.978704 11.2722 0.978704 11.1992V4.80213C0.978704 4.72789 1.02019 4.65983 1.08759 4.62271L6.89111 1.4242C6.92482 1.40564 6.96241 1.39574 7 1.39574ZM7 0.461548C6.79389 0.461548 6.58648 0.512279 6.40111 0.614978L0.598889 3.81226C0.228148 4.01642 0 4.3938 0 4.80213V11.1979C0 11.6062 0.228148 11.9836 0.598889 12.1878L6.40241 15.3863C6.58778 15.4878 6.79389 15.5397 7.0013 15.5397C7.20741 15.5397 7.41482 15.489 7.60019 15.3863L13.4037 12.1878C13.7744 11.9836 14.0026 11.6062 14.0026 11.1979V4.80213C14.0026 4.3938 13.7744 4.01642 13.4037 3.81226L7.59889 0.614978C7.41352 0.512279 7.20611 0.461548 7 0.461548Z" fill="#9DCCED"/>
<path d="M3.16162 13.6008L3.6957 12.2051L4.77033 13.0576L3.7657 13.9336L3.16162 13.6008Z" fill="#213147"/>
<path d="M6.51113 4.3443H5.03983C4.92965 4.3443 4.83113 4.40988 4.79354 4.50887L1.63965 12.7619L3.1615 13.6008L6.63428 4.51258C6.66669 4.43091 6.60317 4.3443 6.51113 4.3443Z" fill="white"/>
<path d="M9.08579 4.3443H7.6145C7.50431 4.3443 7.40579 4.40988 7.3682 4.50887L3.76709 13.9324L5.28894 14.7713L9.20894 4.51258C9.24005 4.43091 9.17653 4.3443 9.08579 4.3443Z" fill="white"/>
</svg>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<circle cx="8" cy="8" r="8" stroke-width="3" fill="#98A1C0"></circle>
</svg>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect x="0" y="0" width="16" height="16" rx="3" fill="#F0B90B"/>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 2496 2496" style="enable-background:new 0 0 2496 2496;" xml:space="preserve">
<g>
<path style="fill-rule:evenodd;clip-rule:evenodd;fill:#F0B90B;" d="M1248,0c689.3,0,1248,558.7,1248,1248s-558.7,1248-1248,1248
S0,1937.3,0,1248S558.7,0,1248,0L1248,0z"/>
<path style="fill:#FFFFFF;" d="M685.9,1248l0.9,330l280.4,165v193.2l-444.5-260.7v-524L685.9,1248L685.9,1248z M685.9,918v192.3
l-163.3-96.6V821.4l163.3-96.6l164.1,96.6L685.9,918L685.9,918z M1084.3,821.4l163.3-96.6l164.1,96.6L1247.6,918L1084.3,821.4
L1084.3,821.4z"/>
<path style="fill:#FFFFFF;" d="M803.9,1509.6v-193.2l163.3,96.6v192.3L803.9,1509.6L803.9,1509.6z M1084.3,1812.2l163.3,96.6
l164.1-96.6v192.3l-164.1,96.6l-163.3-96.6V1812.2L1084.3,1812.2z M1645.9,821.4l163.3-96.6l164.1,96.6v192.3l-164.1,96.6V918
L1645.9,821.4L1645.9,821.4L1645.9,821.4z M1809.2,1578l0.9-330l163.3-96.6v524l-444.5,260.7v-193.2L1809.2,1578L1809.2,1578
L1809.2,1578z"/>
<polygon style="fill:#FFFFFF;" points="1692.1,1509.6 1528.8,1605.3 1528.8,1413 1692.1,1316.4 1692.1,1509.6 "/>
<path style="fill:#FFFFFF;" d="M1692.1,986.4l0.9,193.2l-281.2,165v330.8l-163.3,95.7l-163.3-95.7v-330.8l-281.2-165V986.4
L968,889.8l279.5,165.8l281.2-165.8l164.1,96.6H1692.1L1692.1,986.4z M803.9,656.5l443.7-261.6l444.5,261.6l-163.3,96.6
l-281.2-165.8L967.2,753.1L803.9,656.5L803.9,656.5z"/>
</g>
</svg>
</svg>
<svg width="250" height="250" viewBox="0 0 250 250" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect width="250" height="250" rx="40" fill="#FCFF52"/>
<path style="fill:black;" d="M188.9,60.7H60.7v128.2h128.2v-44.8h-21.3c-7.3,16.3-23.8,27.7-42.7,27.7c-26,0-47.1-21.3-47.1-47.1c0-25.9,21.1-47,47.1-47
c19.3,0,35.8,11.7,43.1,28.4h20.9V60.7z"/>
</svg>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17 3H7C5 3 4 4 4 6V18C4 20 5 21 7 21H17C19 21 20 20 20 18V6C20 4 19 3 17 3ZM8 16.75C7.586 16.75 7.25 16.414 7.25 16C7.25 15.586 7.586 15.25 8 15.25C8.414 15.25 8.75 15.586 8.75 16C8.75 16.414 8.414 16.75 8 16.75ZM8 12.75C7.586 12.75 7.25 12.414 7.25 12C7.25 11.586 7.586 11.25 8 11.25C8.414 11.25 8.75 11.586 8.75 12C8.75 12.414 8.414 12.75 8 12.75ZM8 8.75C7.586 8.75 7.25 8.414 7.25 8C7.25 7.586 7.586 7.25 8 7.25C8.414 7.25 8.75 7.586 8.75 8C8.75 8.414 8.414 8.75 8 8.75ZM16 16.75H11C10.586 16.75 10.25 16.414 10.25 16C10.25 15.586 10.586 15.25 11 15.25H16C16.414 15.25 16.75 15.586 16.75 16C16.75 16.414 16.414 16.75 16 16.75ZM16 12.75H11C10.586 12.75 10.25 12.414 10.25 12C10.25 11.586 10.586 11.25 11 11.25H16C16.414 11.25 16.75 11.586 16.75 12C16.75 12.414 16.414 12.75 16 12.75ZM16 8.75H11C10.586 8.75 10.25 8.414 10.25 8C10.25 7.586 10.586 7.25 11 7.25H16C16.414 7.25 16.75 7.586 16.75 8C16.75 8.414 16.414 8.75 16 8.75Z" fill="currentColor"/>
</svg>
<!-- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.63 3H6.38C4.13 3 3 4.12 3 6.37V17.62C3 19.87 4.13 21 6.38 21H17.63C19.88 21 21 19.87 21 17.62V6.37C21 4.12 19.88 3 17.63 3ZM9.75 13.69C10.05 13.98 10.05 14.46 9.75 14.75C9.61 14.9 9.41 14.97 9.22 14.97C9.03 14.97 8.84 14.9 8.69 14.75L6.47 12.53C6.18 12.24 6.18 11.76 6.47 11.47L8.69 9.25C8.98 8.95 9.46 8.95 9.75 9.25C10.05 9.54 10.05 10.02 9.75 10.31L8.06 12L9.75 13.69ZM13.73 8.17999L11.73 16.18C11.64 16.52 11.34 16.75 11 16.75C10.94 16.75 10.88 16.74 10.82 16.73C10.42 16.63 10.17 16.22 10.27 15.82L12.27 7.82001C12.37 7.42001 12.78 7.16999 13.18 7.26999C13.58 7.36999 13.83 7.77999 13.73 8.17999ZM17.53 12.53L15.31 14.75C15.16 14.9 14.97 14.97 14.78 14.97C14.59 14.97 14.39 14.9 14.25 14.75C13.95 14.46 13.95 13.98 14.25 13.69L15.94 12L14.25 10.31C13.95 10.02 13.95 9.54 14.25 9.25C14.54 8.95 15.02 8.95 15.31 9.25L17.53 11.47C17.82 11.76 17.82 12.24 17.53 12.53Z" fill="currentColor"/>
</svg> -->
<!-- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.75 6V3.75L19.25 8.25H17C15.42 8.25 14.75 7.58 14.75 6ZM20 9.75V18C20 20 19 21 17 21H8C6 21 5 20 5 18V6C5 4 6 3 8 3H13.25V6C13.25 8.42 14.58 9.75 17 9.75H20ZM9.06104 16L10.531 14.53C10.824 14.237 10.824 13.762 10.531 13.469C10.238 13.176 9.76297 13.176 9.46997 13.469L7.46997 15.469C7.17697 15.762 7.17697 16.237 7.46997 16.53L9.46997 18.53C9.61597 18.676 9.808 18.75 10 18.75C10.192 18.75 10.384 18.677 10.53 18.53C10.823 18.237 10.823 17.762 10.53 17.469L9.06104 16ZM15.53 15.47L13.53 13.47C13.237 13.177 12.762 13.177 12.469 13.47C12.176 13.763 12.176 14.238 12.469 14.531L13.939 16.001L12.469 17.471C12.176 17.764 12.176 18.239 12.469 18.532C12.615 18.678 12.807 18.752 12.999 18.752C13.191 18.752 13.3831 18.679 13.5291 18.532L15.5291 16.532C15.8231 16.238 15.823 15.762 15.53 15.47Z" fill="currentColor"/>
</svg> -->
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect x="0" y="0" width="16" height="16" rx="3" fill="#627EEA"/>
<circle cx="8" cy="8" r="8"/>
<g clip-path="url(#clip0_12246_121533)">
<circle cx="8" cy="8" r="8" fill="url(#pattern0)"/>
</g>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_12246_121533" transform="scale(0.0078125)"/>
</pattern>
<clipPath id="clip0_12246_121533">
<rect x="0" y="0" width="16" height="16" rx="8" fill="white"/>
</clipPath>
<image id="image0_12246_121533" width="128" height="128" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAAAXNSR0IArs4c6QAADd9JREFUeNrtXdtzE+cV38kweWpm+tI8JZP8EZ0pL+UhferkfyAzFqEpadq0hbRNUlsYQ2ycBEgCgQQIgVxMbC6BAHbEJZg7dW4OpeCWwDTNDOyuLFuybMsXfT2/tWVkWavd1e5K58g6M9+Q8cTS+ju//b5z/R1Nq0F5Ppr46cpm8xcroubyFU3m+oYmY39Do97d0KRfoH/76d/b9K9JP89Yy/pv62f9s/9PN34Hv4vPwGfhM7W68JNVUfWTp9fqv440Ge2RRqOXFHiPFKhCWfTZ+A58F74T313XQIUlGlUP0lv5RCRqtkQa9YukjMnQFO6w8N3WM9Cz4JnwbHUNhSQrovGlkSZ9K216vFoKdwGIOJ4Rz1rXWADyTDTxeCSqv0wbe5Or0kuA4SaeHX9DXZMepSFq/Jw28FBDo5GVpviFy8hafwv9TXXNOh3zzfov6Qjtka90u1NB78HfWNd0gUTWmr+yLPgaVfwCIMCToL+5/sZH44+Qa9W5WBRfxK3sxB4sRlduSSRqrKYjMbVolX//WkhhL7Ani+O4j+rL6Ai8ttgVX+RauIa9qfG3Xm+tDcs+RI+B9qjmToNVLfFHKWJ2vq5gt6eBfr5mbIPIWuNJK9lSV6zXZWLvJB/5D1AApK1+5PsOIrVhL8UlbMiy7agrMKArgfZSTKKpoc14iHLpMUkb/OZHQ6pxa5x5zID2lPaWefIm9TC5M32SlP/MOl0Zg1PqWG9aUY6fu6vY99yG1M94GnsticfozhqQdryeOJ9W09NZ9cU/Rq2TQIBdMIC9ZvfmS1T+39+Kq6mp7BwATl8dVX/caIoAAfaczZ0v7djPrYE7EwqSAwDW/u6UlMhhX9VtAlim0gy+3Np9KKlykg8ArPXvDApJJhmxqnkHlp8v1NV77hVDJUembQHQfSFtGYeCXMTKxwlmgjwy/erevlGVL4UAwHqna1hS+VlbFcK7MiN8698dVNls1hEAWC+8bsqJGFYqbIzEjtTY/oqorn64O6kKxQ4AB0+NIF8vJncA3YSf0hWc1YOFX0zsAIDVvichKosYaip5Jp8vU/l/ftVUY+NZzwCIXRpVz24wBOUN9NbQKnkkZ/a+vD6u7KQUALDeP5KUVlSyLISjX24Z16Z9Q6qUOAEA6+U344KuAuNaoFcBoWqNVOX/pllXOiV7/ALg6NkR9XRUVC3BmsBKtyVX70JxTuIGAFhvfDgkKDagpwIpKyPLskuq8l98I64mJ7OBAeD0lVH1fJshySvo8t2xI7mS5vr3GeVG3AIA6+MTKVF74KsDSXK7FkK5bsULALCatw9KMgh7y27UlKp8+O1DqenQAIAiEhiXYiKg5TSkSu7SxV3tRbwCAGt7p6Rkkd7jze2jXnapysfxDIWGDQCs1a+ZcvbGCz/BDDmDyEZLdefHCeVVygXAgZicZBF06rK+L/G41JDvh8eSqhwpFwBYbbsTYkLEruhqwGsjUfko5hwdm644AGKX0mrVehmxAejWTdh3QCIArvSPqXLFDwCwdh9OSrkGbjqFfZdKVP6rlLP3I34BgPXiFhnJopIUduC4k6b8ldTNc9ecrDoAjpwZsSqOBBjKW+0bOhmTMNqtw6dHlF8JAgBYSDsLuAbiRUvJQXUqTfl/3WyqiYksGwCcogDU71sNAdeA+UQR699skQaA7/6dUUFIUADAgivK3xswW4qlfS9KUv62/cMqKAkSAFjRbbyTRdD1/FJvoj2vJuu217WqxVCDw1NsAXD8XNoyThnbAZPzqO7BfS/p7f/8YloFKUEDAGtrB2+DEDrPj/23S1F+07a452RPNQBwhlrNUYrO+BRoF1n4ceuHiUCVjzax67cy6pOeVOAg6Pw8xdgOyCsUCXXMSoAL9flBSYbcR/QKwHWDsta+PahadyXU3qNJq54gKBBs2MnUICSdW8rHMCQJyod/PTI67VvxQ8lpdfGbsQVKBgByC/wAOw8Mqx6yNfwCAPbKb1t47qk1CAsTsSQA4MLXY74U/6M+qc5SW/gZG0XlAyC31lFxyVtkzKG03A8Idh7kWT0E3dP9bz7FXfk4msuVgTsZetud3+RiAMhfm/YlrDu9XBAgaskwIrgc+f8NrN0VSrDg7fUiE9QL8PUNut8vuz/CnQCQWxupAOSDz5IWoZQXACBnwa16CLrXZocqsgVAVyzlWvGgfbn07ZirN75cAMzZCUQ0sYuO9pgHO+G195lVD5HuWVf/riGGjvGMs89/15hUvV/a3+9hACC3mncMqm1kJxxzYSecujxq8ROxqhbmnAP45sZ4ScX/578ZdSYgl61cAOSvzWQnHIiVthP2HU3yygk0YF4uU/7eYjJJxI7f3oT/ng40aBMEAHKr/b2ElRW0sxMY8RL3a7NDk9nx95qJ+ckexACufDcWaJAmLADk1gayE3YfGlYnL80HKxteYtK9xpHwCdm0nNyLT6lzX83E1sNQfJgAmIsnUGBp+/4hdbz3PhCY8BKb2uz4dHb8vbf/NxG60isFgDmDkQJLW4hj4OCpFA9eYtI9OwAcOzcyF5+v5KoEAObZCXvIaPxgiAEAmF0BSKGCzq3WAfA3KiNn0GFssjQCc02ex3rTNQcAXHG/40I7N2sE9nMOA2+hYxIBFOkAQJ0g7nxm4WDLDbwggd17z6dJsQAA5zDLcTSke5wA3XL6AOIWf68UALxEJFVcawFmVze7ZJBT2hRH6Cs7E6rnAt9IICJ9bhpE/lBtxjErGcQsHbyRwqiXKaPnxL6BaCF6A05f5QOAKC14MU73fO5KA41dVesBmsz1aAlbzu1ogvWPmj3k0J1cJRhWHx1PVR0AuJ5WNjuWYltkk4hzcIgEWgUhHEvCYP3nqn+RE8Cb7vQ7OHaPfjFScQDArXvWBUEE3FqEuPFdqHHg4A1YJWFci0L/sokYP8bvF4HeuJ2xegKcBkKg6OLk5fABALfODWsorrL8knPQz3NpILWKQjmXhReSPaKBA/kBpw3EG7mLsnBhAQCFKk7E0Whfe/fAwmdYt2OQiwt4T0RjCEq4CyVNqWHc+06EDPC/u2KpwAAAo81pmhieCYwlsUsLvRSMqmPZGMK5NQxvkh6fsi31dlNnhzp/sHuWCwDYF25KuWAPgCnEjm5+JSN20XmtYdybQ3FsIkVsJ6gAdoofYPNhedsVlNi5dX9qd3brYAugAsiWZfwqvwlk85pDJbSHoya/lIAWHlY2TgynDiPU5TkBAEaoU4s3XFR0ATvFIl7fy6saeEF7uASCCLyFbujf0fq1y0Unzks0AubTvON6LnxLP3cCERbKvbpdRCM7GNLLLyCIkEIRg+M4lXbXH/g9VRS1OMwABqgQeUT/HlxMN6FZXDUHTrqLNyBczXHiWFGKGCkkUYikeWn9Rk8hgOMUVnbyKJDDf++wt4wko+pfZ5IoSTRxqA/wIpgXCHewHNoW+Pub93mvSeBKJ29LEyeJKBIWfbERsE4Cd9JLDB5eQDlVSchhcJ0wZksUKY0qFj53uRyB/yRGkFJzAJHR6yizLhGJHqcrhy1V7GxQ6KYUEMCd88MLhJxBfn0eijd2dA77yiqilV0sWbREuviv/jXuizgCXgVoYXLegB/lc+r7K5suXtrACIRo/XIGBsEShiCUU55AxMAIiSNjQNrghzpuMdDFux4ZI3Vo1Gdn01UDAJM+v9LLy9AoiWPjVkTL5xD0AwDkKLgPjvI8Nk7q4MgXCqqIwgYAcv4i6OHLGRwpdXSsl5GxfgHAprrHbeHHYhke7ZVTsBwAoORMwl74Gh49Wy/YKQ0ASOfei0+FBgBUH6+UMDuYdKf5FQodPkJGREoaCHA8T05lAwcAij/WCBgZC51Bd1oQEokaqyVeBU5VROUAgB3Xn23Uz1itBSWUPlxCxsQ1aQCwqohuZQIDwMcnUjL+btIVdKYFKRRHXiZxpjAyc2AQ9QsAq7pHxIhYIwtdaWEIfXCrxKsAJBN+AYD0s4yjX2/VwpKZq0A/LxEEpaqInADw9idCXD7STeBHf6Gsaok/ypFb0E8VUSkAHDrFt7qnkPAJutEqIZG1xpMS7QFUAWWKVBHZAQCnRtW5/Nze+6QTrZJC6cU2iVfB3iJVRHYAABOJDG/HaNMqLXTXPEC+ZodEEGBYlBMA9h5JSvH3O6ALrRqC8mLimYlJA0BhFVEhAPhX98xx/MRsS7wrJQ1txkMUeOiTBoK2vCqiQgCA7kVAsKcPe69xkGeiqYfJEBmQBgK0bBcCAJ1HAoy+Aey5xkkiLYnHpIEgV0WUA4CE6h7sMfZa4yhApbTrAH37YBxBdQ+neT52xz67N7+YTSDNMEQfH1i8uBt8bO58N96BVBeRsav3oCZJrDiBFSySFzFkFeGjPayanx9c2Fhe7oBDbL/i4d0wE0hSs4jVyupVLLFTwSthyUw9Qf1KcCjmaA09pVvVK4GqVSSWl1WijCu0Sh6OpwGhfY3EauMwqnexFzX91pcsOW/UuxbxXd8VWOm26GuBulcktqH5adfy3bFTkycCNTFK60r22qVbdqPmYhL0ss+QVNSCx2AFcw557s+vywxdzQxnkbxUs5W1o2d3TctSF0eDcSk47jiTWeLZ8IyOVGx18ZdoAtUp+G5BelxNlnN8t/UM9Cx4JnEJm5oIMxPtObjvMQDB8iTCHH9Dn21Z8PRd+M4FlOt14SEYhoSJWJFG8ylrLiINSIQFPkuJ3z87MNvE+HRrWf9t/ax/5lQhTwS/Q7+Lz8BnzQ1YqjH5P29N0rBVv2N5AAAAAElFTkSuQmCC"/>
</defs>
</svg>
This diff is collapsed.
This diff is collapsed.
import { TraceEvent } from '@uniswap/analytics'
import { BrowserEvent, InterfaceElementName, SharedEventName } from '@uniswap/analytics-events'
import { useIsDarkMode } from 'state/user/hooks'
import styled from 'styled-components/macro'
import { BREAKPOINTS, ExternalLink, StyledRouterLink } from 'theme'
import { useIsDarkMode } from 'theme/components/ThemeToggle'
import { DiscordIcon, GithubIcon, TwitterIcon } from './Icons'
import darkUnicornImgSrc from './images/unicornEmbossDark.png'
......
import { TraceEvent } from '@uniswap/analytics'
import { BrowserEvent, SharedEventName } from '@uniswap/analytics-events'
import { Link } from 'react-router-dom'
import { useIsDarkMode } from 'state/user/hooks'
import styled, { DefaultTheme } from 'styled-components/macro'
import { BREAKPOINTS } from 'theme'
import { useIsDarkMode } from 'theme/components/ThemeToggle'
export enum CardType {
Primary = 'Primary',
......
import { ButtonEmpty } from 'components/Button'
import { useIsDarkMode } from 'state/user/hooks'
import styled from 'styled-components/macro'
import { BREAKPOINTS } from 'theme'
import { useIsDarkMode } from 'theme/components/ThemeToggle'
import meshSrc from './images/Mesh.png'
......
import { useWeb3React } from '@web3-react/core'
import { CheckCircle, Triangle } from 'react-feather'
import styled from 'styled-components/macro'
import { useAllTransactions } from '../../state/transactions/hooks'
import { ExternalLink } from '../../theme'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import Loader from '../Loader'
import { RowFixed } from '../Row'
import { TransactionSummary } from './TransactionSummary'
const TransactionStatusText = styled.div`
margin-right: 0.5rem;
display: flex;
align-items: center;
:hover {
text-decoration: underline;
}
`
const TransactionState = styled(ExternalLink)<{ pending: boolean; success?: boolean }>`
display: flex;
justify-content: space-between;
align-items: center;
text-decoration: none !important;
border-radius: 0.5rem;
padding: 0.25rem 0rem;
font-weight: 500;
font-size: 0.825rem;
color: ${({ theme }) => theme.accentAction};
`
const IconWrapper = styled.div<{ pending: boolean; success?: boolean }>`
color: ${({ pending, success, theme }) =>
pending ? theme.accentAction : success ? theme.accentSuccess : theme.accentFailure};
`
export default function Transaction({ hash }: { hash: string }) {
const { chainId } = useWeb3React()
const allTransactions = useAllTransactions()
const tx = allTransactions?.[hash]
const info = tx?.info
const pending = !tx?.receipt
const success = !pending && tx && (tx.receipt?.status === 1 || typeof tx.receipt?.status === 'undefined')
if (!chainId) return null
return (
<div>
<TransactionState
href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}
pending={pending}
success={success}
>
<RowFixed>
<TransactionStatusText>
<TransactionSummary info={info} />
</TransactionStatusText>
</RowFixed>
<IconWrapper pending={pending} success={success}>
{pending ? <Loader /> : success ? <CheckCircle size="16" /> : <Triangle size="16" />}
</IconWrapper>
</TransactionState>
</div>
)
}
import { Trans } from '@lingui/macro'
import { useWeb3React } from '@web3-react/core'
import { getConnection, getConnectionName, getIsCoinbaseWallet, getIsMetaMaskWallet } from 'connection/utils'
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 styled, { useTheme } from 'styled-components/macro'
import { flexColumnNoWrap, flexRowNoWrap } from 'theme/styles'
import { isMobile } from 'utils/userAgent'
import { ReactComponent as Close } from '../../assets/images/x.svg'
import { clearAllTransactions } from '../../state/transactions/reducer'
import { CopyHelper, ExternalLink, LinkStyledButton, ThemedText } from '../../theme'
import { shortenAddress } from '../../utils'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { ButtonSecondary } from '../Button'
import StatusIcon from '../Identicon/StatusIcon'
import { AutoRow } from '../Row'
import Transaction from './Transaction'
const HeaderRow = styled.div`
${flexRowNoWrap};
padding: 1rem 1rem;
font-weight: 500;
color: ${(props) => (props.color === 'blue' ? ({ theme }) => theme.accentAction : 'inherit')};
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
padding: 1rem;
`};
`
const UpperSection = styled.div`
position: relative;
h5 {
margin: 0;
margin-bottom: 0.5rem;
font-size: 1rem;
font-weight: 400;
}
h5:last-child {
margin-bottom: 0px;
}
h4 {
margin-top: 0;
font-weight: 500;
}
`
const InfoCard = styled.div`
padding: 1rem;
border: 1px solid ${({ theme }) => theme.deprecated_bg3};
border-radius: 20px;
position: relative;
display: grid;
grid-row-gap: 12px;
margin-bottom: 20px;
`
const AccountGroupingRow = styled.div`
${flexColumnNoWrap};
justify-content: space-between;
align-items: center;
font-weight: 400;
color: ${({ theme }) => theme.textPrimary};
div {
${flexColumnNoWrap};
align-items: center;
}
`
const AccountSection = styled.div`
padding: 0rem 1rem;
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`padding: 0rem 1rem 1.5rem 1rem;`};
`
const YourAccount = styled.div`
h5 {
margin: 0 0 1rem 0;
font-weight: 400;
}
h4 {
margin: 0;
font-weight: 500;
}
`
const LowerSection = styled.div`
${flexColumnNoWrap};
padding: 1.5rem;
flex-grow: 1;
overflow: auto;
background-color: ${({ theme }) => theme.backgroundInteractive};
border-bottom-left-radius: 20px;
border-bottom-right-radius: 20px;
h5 {
margin: 0;
font-weight: 400;
color: ${({ theme }) => theme.textTertiary};
}
`
const AccountControl = styled.div`
display: flex;
justify-content: space-between;
min-width: 0;
width: 100%;
font-weight: 500;
font-size: 1.25rem;
a:hover {
text-decoration: underline;
}
p {
min-width: 0;
margin: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
`
const AddressLink = styled(ExternalLink)`
color: ${({ theme }) => theme.textTertiary};
margin-left: 1rem;
font-size: 0.825rem;
display: flex;
gap: 6px;
text-decoration: none !important;
:hover {
color: ${({ theme }) => theme.textSecondary};
}
`
const CloseIcon = styled.div`
position: absolute;
right: 1rem;
top: 14px;
&:hover {
cursor: pointer;
opacity: ${({ theme }) => theme.opacity.hover};
}
`
const CloseColor = styled(Close)`
path {
stroke: ${({ theme }) => theme.deprecated_text4};
}
`
const WalletName = styled.div`
width: initial;
font-size: 0.825rem;
font-weight: 500;
color: ${({ theme }) => theme.textTertiary};
`
const TransactionListWrapper = styled.div`
${flexColumnNoWrap};
`
const WalletAction = styled(ButtonSecondary)`
width: fit-content;
font-weight: 400;
margin-left: 8px;
font-size: 0.825rem;
padding: 4px 6px;
:hover {
cursor: pointer;
text-decoration: underline;
}
`
function renderTransactions(transactions: string[]) {
return (
<TransactionListWrapper>
{transactions.map((hash, i) => {
return <Transaction key={i} hash={hash} />
})}
</TransactionListWrapper>
)
}
interface AccountDetailsProps {
toggleWalletModal: () => void
pendingTransactions: string[]
confirmedTransactions: string[]
ENSName?: string
openOptions: () => void
}
export default function AccountDetails({
toggleWalletModal,
pendingTransactions,
confirmedTransactions,
ENSName,
openOptions,
}: AccountDetailsProps) {
const { chainId, account, connector } = useWeb3React()
const connectionType = getConnection(connector).type
const theme = useTheme()
const dispatch = useAppDispatch()
const hasMetaMaskExtension = getIsMetaMaskWallet()
const hasCoinbaseExtension = getIsCoinbaseWallet()
const isInjectedMobileBrowser = (hasMetaMaskExtension || hasCoinbaseExtension) && isMobile
function formatConnectorName() {
return (
<WalletName>
<Trans>Connected with</Trans> {getConnectionName(connectionType, hasMetaMaskExtension)}
</WalletName>
)
}
const clearAllTransactionsCallback = useCallback(() => {
if (chainId) dispatch(clearAllTransactions({ chainId }))
}, [dispatch, chainId])
return (
<>
<UpperSection>
<CloseIcon onClick={toggleWalletModal}>
<CloseColor />
</CloseIcon>
<HeaderRow>
<Trans>Account</Trans>
</HeaderRow>
<AccountSection>
<YourAccount>
<InfoCard>
<AccountGroupingRow>
{formatConnectorName()}
<div>
{!isInjectedMobileBrowser && (
<>
<WalletAction
style={{ fontSize: '.825rem', fontWeight: 400, marginRight: '8px' }}
onClick={() => {
const walletType = getConnectionName(getConnection(connector).type)
if (connector.deactivate) {
connector.deactivate()
} else {
connector.resetState()
}
dispatch(updateSelectedWallet({ wallet: undefined }))
dispatch(removeConnectedWallet({ account, walletType }))
openOptions()
}}
>
<Trans>Disconnect</Trans>
</WalletAction>
<WalletAction
style={{ fontSize: '.825rem', fontWeight: 400 }}
onClick={() => {
openOptions()
}}
>
<Trans>Change</Trans>
</WalletAction>
</>
)}
</div>
</AccountGroupingRow>
<AccountGroupingRow data-testid="web3-account-identifier-row">
<AccountControl>
<div>
<StatusIcon connectionType={connectionType} />
<p>{ENSName ? ENSName : account && shortenAddress(account)}</p>
</div>
</AccountControl>
</AccountGroupingRow>
<AccountGroupingRow>
<AccountControl>
<div>
{account && (
<CopyHelper toCopy={account} gap={6} iconSize={16} fontSize={14}>
<Trans>Copy Address</Trans>
</CopyHelper>
)}
{chainId && account && (
<AddressLink href={getExplorerLink(chainId, ENSName ?? account, ExplorerDataType.ADDRESS)}>
<LinkIcon size={16} />
<Trans>View on Explorer</Trans>
</AddressLink>
)}
</div>
</AccountControl>
</AccountGroupingRow>
</InfoCard>
</YourAccount>
</AccountSection>
</UpperSection>
{!!pendingTransactions.length || !!confirmedTransactions.length ? (
<LowerSection>
<AutoRow mb="1rem" style={{ justifyContent: 'space-between' }}>
<ThemedText.DeprecatedBody>
<Trans>Recent Transactions</Trans>
</ThemedText.DeprecatedBody>
<LinkStyledButton onClick={clearAllTransactionsCallback}>
<Trans>(clear all)</Trans>
</LinkStyledButton>
</AutoRow>
{renderTransactions(pendingTransactions)}
{renderTransactions(confirmedTransactions)}
</LowerSection>
) : (
<LowerSection>
<ThemedText.DeprecatedBody color={theme.textPrimary}>
<Trans>Your transactions will appear here...</Trans>
</ThemedText.DeprecatedBody>
</LowerSection>
)}
</>
)
}
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 '../Logo/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
This diff is collapsed.
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 { ExternalLink } from 'theme'
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(ExternalLink)<{ isLastTransactionInList?: boolean }>`
cursor: pointer;
display: grid;
grid-template-columns: 44px auto 24px;
width: 100%;
text-decoration: none;
border-bottom: ${({ theme, isLastTransactionInList }) =>
isLastTransactionInList ? 'none' : `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,
isLastTransactionInList = false,
}: {
transactionDetails: TransactionDetails
isLastTransactionInList?: boolean
}) => {
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" isLastTransactionInList={isLastTransactionInList}>
<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
}
......@@ -5,7 +5,7 @@ import uniswapNftAirdropClaim from 'abis/uniswap-nft-airdrop-claim.json'
import airdropBackgroundv2 from 'assets/images/airdopBackground.png'
import { ButtonEmphasis, ButtonSize, ThemeButton } from 'components/Button'
import { OpacityHoverState } from 'components/Common'
import Loader from 'components/Loader'
import Loader from 'components/Icons/LoadingSpinner'
import { UNISWAP_NFT_AIRDROP_CLAIM_ADDRESS } from 'constants/addresses'
import { useContract } from 'hooks/useContract'
import { ChevronRightIcon } from 'nft/components/icons'
......
......@@ -8,6 +8,9 @@ export enum BadgeVariant {
POSITIVE = 'POSITIVE',
PRIMARY = 'PRIMARY',
WARNING = 'WARNING',
PROMOTIONAL = 'PROMOTIONAL',
BRANDED = 'BRANDED',
SOFT = 'SOFT',
WARNING_OUTLINE = 'WARNING_OUTLINE',
}
......@@ -18,10 +21,16 @@ interface BadgeProps {
function pickBackgroundColor(variant: BadgeVariant | undefined, theme: DefaultTheme): string {
switch (variant) {
case BadgeVariant.BRANDED:
return theme.brandedGradient
case BadgeVariant.PROMOTIONAL:
return theme.promotionalGradient
case BadgeVariant.NEGATIVE:
return theme.accentFailure
return theme.accentCritical
case BadgeVariant.POSITIVE:
return theme.accentSuccess
case BadgeVariant.SOFT:
return theme.accentActionSoft
case BadgeVariant.PRIMARY:
return theme.accentAction
case BadgeVariant.WARNING:
......@@ -44,10 +53,14 @@ function pickBorder(variant: BadgeVariant | undefined, theme: DefaultTheme): str
function pickFontColor(variant: BadgeVariant | undefined, theme: DefaultTheme): string {
switch (variant) {
case BadgeVariant.BRANDED:
return theme.darkMode ? theme.accentTextDarkPrimary : theme.white
case BadgeVariant.NEGATIVE:
return readableColor(theme.accentFailure)
case BadgeVariant.POSITIVE:
return readableColor(theme.accentSuccess)
case BadgeVariant.SOFT:
return theme.accentAction
case BadgeVariant.WARNING:
return readableColor(theme.accentWarning)
case BadgeVariant.WARNING_OUTLINE:
......@@ -59,7 +72,7 @@ function pickFontColor(variant: BadgeVariant | undefined, theme: DefaultTheme):
const Badge = styled.div<PropsWithChildren<BadgeProps>>`
align-items: center;
background-color: ${({ theme, variant }) => pickBackgroundColor(variant, theme)};
background: ${({ theme, variant }) => pickBackgroundColor(variant, theme)};
border: ${({ theme, variant }) => pickBorder(variant, theme)};
border-radius: 0.5rem;
color: ${({ theme, variant }) => pickFontColor(variant, theme)};
......@@ -70,3 +83,8 @@ const Badge = styled.div<PropsWithChildren<BadgeProps>>`
`
export default Badge
export const SmallBadge = styled(Badge)`
border-radius: 5px;
padding: 2px 4px;
`
import { Trans } from '@lingui/macro'
import { AutoColumn } from 'components/Column'
import Row, { RowBetween } from 'components/Row'
import { useWalletDrawer } from 'components/WalletDropdown'
import { DownloadButton, LearnMoreButton } from 'components/WalletDropdown/DownloadButton'
import { X } from 'react-feather'
import { useLocation } from 'react-router-dom'
import { useHideUniswapWalletBanner } from 'state/user/hooks'
import styled, { useTheme } from 'styled-components/macro'
import { ThemedText } from 'theme'
import { Z_INDEX } from 'theme/zIndex'
import { isIOS } from 'utils/userAgent'
import bannerImageDark from '../../assets/images/uniswapWalletBannerDark.png'
import bannerImageLight from '../../assets/images/uniswapWalletBannerLight.png'
const PopupContainer = styled.div<{ show: boolean }>`
${({ show }) => !show && 'display: none'};
box-shadow: ${({ theme }) =>
theme.darkMode
? '0px -16px 24px rgba(0, 0, 0, 0.4), 0px -8px 12px rgba(0, 0, 0, 0.4), 0px -4px 8px rgba(0, 0, 0, 0.32)'
: '0px -12px 20px rgba(51, 53, 72, 0.04), 0px -6px 12px rgba(51, 53, 72, 0.02), 0px -4px 8px rgba(51, 53, 72, 0.04)'};
background-image: ${({ theme }) => (theme.darkMode ? `url(${bannerImageDark})` : `url(${bannerImageLight})`)};
background-repeat: no-repeat;
background-size: cover;
cursor: pointer;
color: ${({ theme }) => theme.textPrimary};
position: fixed;
z-index: ${Z_INDEX.sticky};
transition: ${({
theme: {
transition: { duration, timing },
},
}) => `${duration.slow} opacity ${timing.in}`};
width: 100%;
bottom: 56px;
height: 20%;
`
const InnerContainer = styled.div`
overflow: hidden;
display: flex;
flex-direction: column;
position: relative;
justify-content: space-between;
height: 100%;
padding: 24px 16px;
`
const ButtonRow = styled(Row)`
gap: 16px;
`
const StyledXButton = styled(X)`
color: ${({ theme }) => theme.textSecondary};
&:hover {
opacity: ${({ theme }) => theme.opacity.hover};
}
&:active {
opacity: ${({ theme }) => theme.opacity.click};
}
`
export default function UniswapWalletBanner() {
const [hideUniswapWalletBanner, toggleHideUniswapWalletBanner] = useHideUniswapWalletBanner()
const [walletDrawerOpen] = useWalletDrawer()
const theme = useTheme()
const { pathname } = useLocation()
// hardcodeToFalse hardcodes the banner to never display, temporarily:
const hardcodeToFalse = false
const shouldDisplay = Boolean(
!walletDrawerOpen && !hideUniswapWalletBanner && isIOS && !pathname.startsWith('/wallet') && hardcodeToFalse
)
return (
<PopupContainer show={shouldDisplay}>
<InnerContainer>
<AutoColumn gap="8px">
<RowBetween>
<ThemedText.SubHeader>
<Trans>Get the power of Uniswap in your pocket</Trans>
</ThemedText.SubHeader>
<StyledXButton
data-testid="uniswap-wallet-banner"
color={theme.textSecondary}
size={20}
onClick={(e) => {
e.preventDefault()
e.stopPropagation()
toggleHideUniswapWalletBanner()
}}
/>
</RowBetween>
<ThemedText.BodySmall>
<Trans>Download in the App Store today.</Trans>{' '}
</ThemedText.BodySmall>
</AutoColumn>
<ButtonRow>
<LearnMoreButton />
<DownloadButton onClick={() => toggleHideUniswapWalletBanner()} />
</ButtonRow>
</InnerContainer>
</PopupContainer>
)
}
......@@ -18,11 +18,13 @@ export const ColumnCenter = styled(Column)`
export const AutoColumn = styled.div<{
gap?: Gap | string
justify?: 'stretch' | 'center' | 'start' | 'end' | 'flex-start' | 'flex-end' | 'space-between'
grow?: true
}>`
display: grid;
grid-auto-rows: auto;
grid-row-gap: ${({ gap, theme }) => (gap && theme.grids[gap as Gap]) || gap};
justify-items: ${({ justify }) => justify && justify};
flex-grow: ${({ grow }) => grow && 1};
`
export default Column
......@@ -13,10 +13,6 @@ const ContentWrapper = styled(Column)`
text-align: center;
font-size: 12px;
`
const Copy = styled(CopyHelper)`
font-size: 12px;
`
interface ConnectedAccountBlockedProps {
account: string | null | undefined
isOpen: boolean
......@@ -44,16 +40,16 @@ 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
<CopyHelper
toCopy="compliance@uniswap.org"
fontSize={14}
iconSize={16}
gap={6}
color={theme.accentAction}
iconPosition="right"
>
compliance@uniswap.org
</Copy>
</CopyHelper>
</ContentWrapper>
</Modal>
)
......
import { BaseVariant, FeatureFlag, featureFlagSettings, useUpdateFlag } from 'featureFlags'
import { BaseVariant, FeatureFlag, featureFlagSettings, useBaseFlag, useUpdateFlag } from 'featureFlags'
import { MgtmVariant, useMgtmFlag } from 'featureFlags/flags/mgtm'
import { useMiniPortfolioFlag } from 'featureFlags/flags/miniPortfolio'
import { NftGraphqlVariant, useNftGraphqlFlag } from 'featureFlags/flags/nftlGraphql'
import { PayWithAnyTokenVariant, usePayWithAnyTokenFlag } from 'featureFlags/flags/payWithAnyToken'
import { SwapWidgetVariant, useSwapWidgetFlag } from 'featureFlags/flags/swapWidget'
import { TaxServiceVariant, useTaxServiceBannerFlag } from 'featureFlags/flags/taxServiceBanner'
import { TraceJsonRpcVariant, useTraceJsonRpcFlag } from 'featureFlags/flags/traceJsonRpc'
import { useAtomValue, useUpdateAtom } from 'jotai/utils'
import { 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'
......@@ -166,10 +168,9 @@ function FeatureFlagGroup({ name, children }: PropsWithChildren<{ name: string }
)
}
function FeatureFlagOption({ variant, featureFlag, label }: FeatureFlagProps) {
function FeatureFlagOption({ value, variant, featureFlag, label }: FeatureFlagProps) {
const updateFlag = useUpdateFlag()
const [count, setCount] = useState(0)
const featureFlags = useAtomValue(featureFlagSettings)
return (
<Row key={featureFlag}>
......@@ -183,7 +184,7 @@ function FeatureFlagOption({ variant, featureFlag, label }: FeatureFlagProps) {
updateFlag(featureFlag, e.target.value)
setCount(count + 1)
}}
value={featureFlags[featureFlag]}
value={value}
>
{Object.values(variant).map((variant) => (
<Variant key={variant} option={variant} />
......@@ -205,6 +206,24 @@ export default function FeatureFlagModal() {
<X size={24} />
</CloseButton>
</Header>
<FeatureFlagOption
variant={MgtmVariant}
value={useMgtmFlag()}
featureFlag={FeatureFlag.mgtm}
label="Mobile Wallet go-to-market assets"
/>
<FeatureFlagOption
variant={BaseVariant}
value={useBaseFlag(FeatureFlag.walletMicrosite)}
featureFlag={FeatureFlag.walletMicrosite}
label="Mobile Wallet microsite (requires mgtm to also be enabled)"
/>
<FeatureFlagOption
variant={BaseVariant}
value={useMiniPortfolioFlag()}
featureFlag={FeatureFlag.miniPortfolio}
label="MiniPortfolio"
/>
<FeatureFlagOption
variant={PayWithAnyTokenVariant}
value={usePayWithAnyTokenFlag()}
......
......@@ -3,9 +3,9 @@ import { useWeb3React } from '@web3-react/core'
import { useCallback, useEffect, useState } from 'react'
import { useCloseModal, useModalIsOpen } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer'
import { useIsDarkMode } from 'state/user/hooks'
import styled, { useTheme } from 'styled-components/macro'
import { CustomLightSpinner, ThemedText } from 'theme'
import { useIsDarkMode } from 'theme/components/ThemeToggle'
import Circle from '../../assets/images/blue-loader.svg'
import Modal from '../Modal'
......
import { useTheme } from 'styled-components/macro'
import { StyledSVG } from './shared'
export default function AlertTriangleFilled({ size = '16px', ...rest }: { size?: string; [k: string]: any }) {
const theme = useTheme()
return (
<StyledSVG viewBox="0 0 16 16" fill={theme.accentWarning} xmlns="http://www.w3.org/2000/svg" size={size} {...rest}>
<path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5zm.002 6a1 1 0 1 1 0 2a1 1 0 0 1 0-2z" />
</StyledSVG>
)
}
import { useTheme } from 'styled-components/macro'
import { StyledRotatingSVG, StyledSVG } from './shared'
/**
* Takes in custom size and stroke for circle color, default to primary color as fill,
* need ...rest for layered styles on top
*/
export default function Loader({
size = '16px',
stroke,
strokeWidth,
...rest
}: {
size?: string
stroke?: string
strokeWidth?: number
[k: string]: any
}) {
const theme = useTheme()
return (
<StyledRotatingSVG
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
size={size}
stroke={stroke ?? theme.accentActive}
{...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"
strokeWidth={strokeWidth ?? '2.5'}
strokeLinecap="round"
strokeLinejoin="round"
/>
</StyledRotatingSVG>
)
}
export function LoaderV2() {
const theme = useTheme()
return (
<StyledRotatingSVG size="16px" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<StyledSVG size="16px" viewBox="0 0 16 16" fill={theme.backgroundOutline} xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M8 2.66667C5.05448 2.66667 2.66667 5.05448 2.66667 8C2.66667 10.9455 5.05448 13.3333 8 13.3333C10.9455 13.3333 13.3333 10.9455 13.3333 8C13.3333 5.05448 10.9455 2.66667 8 2.66667ZM0 8C0 3.58172 3.58172 0 8 0C12.4183 0 16 3.58172 16 8C16 12.4183 12.4183 16 8 16C3.58172 16 0 12.4183 0 8Z"
/>
</StyledSVG>
<StyledSVG size="16px" viewBox="0 0 16 16" fill={theme.accentAction} xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M6.66669 1.33333C6.66669 0.596954 7.26364 0 8.00002 0C9.0506 0 10.0909 0.206926 11.0615 0.608964C12.0321 1.011 12.914 1.60028 13.6569 2.34315C14.3997 3.08601 14.989 3.96793 15.3911 4.93853C15.7931 5.90914 16 6.94943 16 8C16 8.73638 15.4031 9.33333 14.6667 9.33333C13.9303 9.33333 13.3334 8.73638 13.3334 8C13.3334 7.29962 13.1954 6.60609 12.9274 5.95902C12.6594 5.31195 12.2665 4.72401 11.7713 4.22876C11.276 3.73352 10.6881 3.34067 10.041 3.07264C9.39393 2.80462 8.7004 2.66667 8.00002 2.66667C7.26364 2.66667 6.66669 2.06971 6.66669 1.33333Z"
/>
</StyledSVG>
</StyledRotatingSVG>
)
}
import styled, { css, keyframes } from 'styled-components/macro'
const rotateAnimation = keyframes`
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
`
const RotationStyle = css`
animation: 2s ${rotateAnimation} linear infinite;
`
export const StyledSVG = styled.svg<{ size: string; stroke?: string; fill?: string }>`
height: ${({ size }) => size};
width: ${({ size }) => size};
path {
stroke: ${({ stroke }) => stroke};
background: ${({ theme }) => theme.textSecondary};
fill: ${({ fill }) => fill};
}
`
export const StyledRotatingSVG = styled(StyledSVG)`
${RotationStyle}
`
import { getWalletMeta } from '@uniswap/conedison/provider/meta'
import { useWeb3React } from '@web3-react/core'
import { MouseoverTooltip } from 'components/Tooltip'
import { Unicon } from 'components/Unicon'
import { ConnectionType } from 'connection'
import { Connection, ConnectionType } from 'connection'
import useENSAvatar from 'hooks/useENSAvatar'
import ms from 'ms.macro'
import { useIsMobile } from 'nft/hooks'
import { PropsWithChildren } from 'react'
import styled from 'styled-components/macro'
import { ThemedText } from 'theme'
import { flexColumnNoWrap } from 'theme/styles'
import CoinbaseWalletIcon from '../../assets/images/coinbaseWalletIcon.svg'
import WalletConnectIcon from '../../assets/images/walletConnectIcon.svg'
import sockImg from '../../assets/svg/socks.svg'
import { useHasSocks } from '../../hooks/useSocksBalance'
import Identicon from '../Identicon'
......@@ -32,27 +29,43 @@ export const IconWrapper = styled.div<{ size?: number }>`
`};
`
const SockContainer = styled.div`
const MiniIconContainer = styled.div<{ side: 'left' | 'right' }>`
position: absolute;
display: flex;
justify-content: center;
border-radius: 50%;
align-items: center;
width: 16px;
height: 16px;
bottom: -4px;
right: -4px;
${({ side }) => `${side === 'left' ? 'left' : 'right'}: -4px;`}
border-radius: 50%;
outline: 2px solid ${({ theme }) => theme.backgroundSurface};
outline-offset: -0.1px;
background-color: ${({ theme }) => theme.backgroundSurface};
overflow: hidden;
@supports (overflow: clip) {
overflow: clip;
}
`
const SockImg = styled.img`
const MiniImg = styled.img`
width: 16px;
height: 16px;
`
const Socks = () => {
return (
<SockContainer>
<SockImg src={sockImg} />
</SockContainer>
<MiniIconContainer side="left">
<MiniImg src={sockImg} />
</MiniIconContainer>
)
}
const MiniWalletIcon = ({ connection, side }: { connection: Connection; side: 'left' | 'right' }) => {
return (
<MiniIconContainer side={side}>
<MiniImg src={connection.icon} alt={`${connection.name} icon`} />
</MiniIconContainer>
)
}
......@@ -64,7 +77,6 @@ const Divider = styled.div`
function UniconTooltip({ children, enabled }: PropsWithChildren<{ enabled?: boolean }>) {
return (
<MouseoverTooltip
timeout={ms`3s`}
offsetY={8}
disableHover={!enabled}
text={
......@@ -86,46 +98,52 @@ function UniconTooltip({ children, enabled }: PropsWithChildren<{ enabled?: bool
)
}
const useIcon = (connectionType: ConnectionType, size?: number, enableInfotips?: boolean) => {
const { account, provider } = useWeb3React()
const MainWalletIcon = ({
connection,
size,
enableInfotips,
}: {
connection: Connection
size: number
enableInfotips?: boolean
}) => {
const { account } = useWeb3React()
const { avatar } = useENSAvatar(account ?? undefined)
const isUniswapWallet = Boolean(provider && getWalletMeta(provider)?.name === 'Uniswap Wallet')
const isMobile = useIsMobile()
if (!account) return null
if (avatar || connectionType === ConnectionType.INJECTED) {
return <Identicon />
} else if (connectionType === ConnectionType.WALLET_CONNECT) {
return isUniswapWallet ? (
if (!account) {
return null
} else if (avatar || (connection.type === ConnectionType.INJECTED && connection.name === 'MetaMask')) {
return <Identicon size={size} />
} else {
return isMobile ? (
<Unicon address={account} size={size} />
) : (
<UniconTooltip enabled={enableInfotips}>
<Unicon address={account} size={size} />
</UniconTooltip>
) : (
<img src={WalletConnectIcon} alt="WalletConnect" />
)
} else if (connectionType === ConnectionType.COINBASE_WALLET) {
return <img src={CoinbaseWalletIcon} alt="Coinbase Wallet" />
}
return undefined
}
export default function StatusIcon({
connectionType,
size,
connection,
size = 16,
enableInfotips,
showMiniIcons = true,
}: {
connectionType: ConnectionType
connection: Connection
size?: number
enableInfotips?: boolean
showMiniIcons?: boolean
}) {
const hasSocks = useHasSocks()
const icon = useIcon(connectionType, size, enableInfotips)
return (
<IconWrapper size={size ?? 16}>
{hasSocks && <Socks />}
{icon}
<IconWrapper size={size}>
{hasSocks && showMiniIcons && <Socks />}
<MainWalletIcon connection={connection} size={size} enableInfotips={enableInfotips} />
{showMiniIcons && <MiniWalletIcon connection={connection} side="right" />}
</IconWrapper>
)
}
......@@ -2,7 +2,7 @@ import { Trans } from '@lingui/macro'
import { Currency, Price, Token } from '@uniswap/sdk-core'
import { FeeAmount } from '@uniswap/v3-sdk'
import { AutoColumn, ColumnCenter } from 'components/Column'
import Loader from 'components/Loader'
import Loader from 'components/Icons/LoadingSpinner'
import { format } from 'd3'
import { useColor } from 'hooks/useColor'
import { saturate } from 'polished'
......
import styled, { keyframes } from 'styled-components/macro'
const rotate = keyframes`
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
`
const StyledSVG = styled.svg<{ size: string; stroke?: string }>`
animation: 2s ${rotate} linear infinite;
height: ${({ size }) => size};
width: ${({ size }) => size};
path {
stroke: ${({ stroke, theme }) => stroke ?? theme.accentActive};
}
`
/**
* Takes in custom size and stroke for circle color, default to primary color as fill,
* need ...rest for layered styles on top
*/
export default function Loader({
size = '16px',
stroke,
strokeWidth,
...rest
}: {
size?: string
stroke?: string
strokeWidth?: number
[k: string]: any
}) {
return (
<StyledSVG viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" size={size} stroke={stroke} {...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={strokeWidth ?? '2.5'}
strokeLinecap="round"
strokeLinejoin="round"
/>
</StyledSVG>
)
}
......@@ -2,9 +2,9 @@ import { getChainInfo } from 'constants/chainInfo'
import { SupportedChainId } from 'constants/chains'
import useTokenLogoSource from 'hooks/useAssetLogoSource'
import React from 'react'
import styled from 'styled-components/macro'
import styled, { css } from 'styled-components/macro'
const MissingImageLogo = styled.div<{ size?: string }>`
export const MissingImageLogo = styled.div<{ size?: string }>`
--size: ${({ size }) => size};
border-radius: 100px;
color: ${({ theme }) => theme.textPrimary};
......@@ -17,12 +17,17 @@ const MissingImageLogo = styled.div<{ size?: string }>`
width: ${({ size }) => size ?? '24px'};
`
const LogoImage = styled.img<{ size: string }>`
export const LogoImage = styled.img<{ size: string; useBG?: boolean }>`
width: ${({ size }) => size};
height: ${({ size }) => size};
background: radial-gradient(white 60%, #ffffff00 calc(70% + 1px));
border-radius: 50%;
${({ useBG }) =>
useBG &&
css`
background: radial-gradient(white 60%, #ffffff00 calc(70% + 1px));
box-shadow: 0 0 1px white;
`}
`
export type AssetLogoBaseProps = {
......@@ -76,7 +81,7 @@ export default function AssetLogo({
return (
<LogoContainer style={style}>
{src ? (
<LogoImage {...imageProps} src={src} onError={nextSrc} />
<LogoImage {...imageProps} src={src} onError={nextSrc} useBG={true} />
) : (
<MissingImageLogo size={size}>
{/* use only first 3 characters of Symbol for design reasons */}
......
......@@ -16,7 +16,12 @@ export default function QueryTokenLogo(
return (
<AssetLogo
isNative={props.token?.standard === TokenStandard.Native || props.token?.address === NATIVE_CHAIN_ID}
isNative={
// TODO(cartcrom): simplify this check after backend fixes token standard on assetActivities tokens
!props.token?.address ||
props.token?.standard === TokenStandard.Native ||
props.token?.address === NATIVE_CHAIN_ID
}
chainId={chainId}
address={props.token?.address}
symbol={props.token?.symbol}
......
import styled from 'styled-components/macro'
const MAX_STRENGTH = 5
const BLUR_STEPS = 20
const BLUR_FADE = '#fff'
const NAV_HEIGHT = 72
const BlurGroup = styled.div`
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-image: linear-gradient(${BLUR_FADE}, rgba(${BLUR_FADE}, 0));
`
const BlurLayer = styled.div<{ index: number }>`
position: absolute;
top: 0;
left: 0;
right: 0;
height: ${({ index }) => (NAV_HEIGHT / BLUR_STEPS) * index}px;
backdrop-filter: blur(${({ index }) => (MAX_STRENGTH / BLUR_STEPS) * (BLUR_STEPS - index)}px);
`
export default function Blur() {
return (
<BlurGroup>
{Array.from(Array(BLUR_STEPS), (_, index) => (
<BlurLayer index={index} key={`blur-${index}`} />
))}
</BlurGroup>
)
}
import { t } from '@lingui/macro'
import { useWeb3React } from '@web3-react/core'
import { MouseoverTooltip } from 'components/Tooltip'
import { ConnectionType } from 'connection'
import { useGetConnection } from 'connection'
import { getChainInfo } from 'constants/chainInfo'
import { SupportedChainId } from 'constants/chains'
import { useOnClickOutside } from 'hooks/useOnClickOutside'
......@@ -7,12 +11,9 @@ 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 { TokenWarningRedIcon } from 'nft/components/icons'
import { subhead } from 'nft/css/common.css'
import { themeVars } from 'nft/css/sprinkles.css'
import { useIsMobile } from 'nft/hooks'
import { useCallback, useRef, useState } from 'react'
import { ChevronDown, ChevronUp } from 'react-feather'
import { AlertTriangle, ChevronDown, ChevronUp } from 'react-feather'
import { useTheme } from 'styled-components/macro'
import * as styles from './ChainSelector.css'
......@@ -33,7 +34,7 @@ interface ChainSelectorProps {
}
export const ChainSelector = ({ leftAlign }: ChainSelectorProps) => {
const { chainId } = useWeb3React()
const { chainId, connector } = useWeb3React()
const [isOpen, setIsOpen] = useState<boolean>(false)
const isMobile = useIsMobile()
......@@ -60,6 +61,10 @@ export const ChainSelector = ({ leftAlign }: ChainSelectorProps) => {
[selectChain, setIsOpen]
)
const getConnection = useGetConnection()
const connectionType = getConnection(connector).type
const isUniWallet = connectionType === ConnectionType.UNIWALLET
if (!chainId) {
return null
}
......@@ -71,6 +76,7 @@ export const ChainSelector = ({ leftAlign }: ChainSelectorProps) => {
<Column paddingX="8">
{NETWORK_SELECTOR_CHAINS.map((chainId: SupportedChainId) => (
<ChainSelectorRow
disabled={isUniWallet && chainId === SupportedChainId.CELO}
onSelectChain={onSelectChain}
targetChain={chainId}
key={chainId}
......@@ -89,31 +95,22 @@ export const ChainSelector = ({ leftAlign }: ChainSelectorProps) => {
return (
<Box position="relative" ref={ref}>
<MouseoverTooltip text={t`Your wallet's current network is unsupported.`} disableHover={isSupported}>
<Row
as="button"
gap="8"
className={styles.ChainSelector}
background={isOpen ? 'accentActiveSoft' : 'none'}
data-testid="chain-selector"
onClick={() => setIsOpen(!isOpen)}
>
{!isSupported ? (
<>
<TokenWarningRedIcon fill={themeVars.colors.textSecondary} width={24} height={24} />
<Box as="span" className={subhead} display={{ sm: 'none', xxl: 'flex' }} style={{ lineHeight: '20px' }}>
Unsupported
</Box>
</>
<AlertTriangle size={20} color={theme.textSecondary} />
) : (
<>
<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>
</>
<img src={info.logoUrl} alt={info.label} className={styles.Image} data-testid="chain-selector-logo" />
)}
{isOpen ? <ChevronUp {...chevronProps} /> : <ChevronDown {...chevronProps} />}
</Row>
</MouseoverTooltip>
{isOpen && (isMobile ? <Portal>{dropdown}</Portal> : <>{dropdown}</>)}
</Box>
)
......
import { Trans } from '@lingui/macro'
import { useWeb3React } from '@web3-react/core'
import Loader from 'components/Loader'
import Loader from 'components/Icons/LoadingSpinner'
import { getChainInfo } from 'constants/chainInfo'
import { SupportedChainId } from 'constants/chains'
import { CheckMarkIcon } from 'nft/components/icons'
......@@ -7,29 +8,30 @@ import styled, { useTheme } from 'styled-components/macro'
const LOGO_SIZE = 20
const Container = styled.button`
display: grid;
background: none;
grid-template-columns: min-content 1fr min-content;
const Container = styled.button<{ disabled: boolean }>`
align-items: center;
text-align: left;
line-height: 24px;
background: none;
border: none;
justify-content: space-between;
padding: 10px 8px;
cursor: pointer;
border-radius: 12px;
color: ${({ theme }) => theme.textPrimary};
width: 240px;
cursor: ${({ disabled }) => (disabled ? 'auto' : 'pointer')};
display: grid;
grid-template-columns: min-content 1fr min-content;
justify-content: space-between;
line-height: 24px;
opacity: ${({ disabled }) => (disabled ? 0.6 : 1)};
padding: 10px 8px;
text-align: left;
transition: ${({ theme }) => theme.transition.duration.medium} ${({ theme }) => theme.transition.timing.ease}
background-color;
width: 240px;
@media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) {
width: 100%;
}
&:hover {
background-color: ${({ theme }) => theme.backgroundOutline};
background-color: ${({ disabled, theme }) => (disabled ? 'none' : theme.backgroundOutline)};
}
`
......@@ -47,7 +49,7 @@ const Status = styled.div`
width: ${LOGO_SIZE}px;
`
const ApproveText = styled.div`
const CaptionText = styled.div`
color: ${({ theme }) => theme.textSecondary};
font-size: 12px;
grid-column: 2;
......@@ -59,16 +61,13 @@ const Logo = styled.img`
width: ${LOGO_SIZE}px;
margin-right: 12px;
`
export default function ChainSelectorRow({
targetChain,
onSelectChain,
isPending,
}: {
interface ChainSelectorRowProps {
disabled?: boolean
targetChain: SupportedChainId
onSelectChain: (targetChain: number) => void
isPending: boolean
}) {
}
export default function ChainSelectorRow({ disabled, targetChain, onSelectChain, isPending }: ChainSelectorRowProps) {
const { chainId } = useWeb3React()
const active = chainId === targetChain
const { label, logoUrl } = getChainInfo(targetChain)
......@@ -76,10 +75,25 @@ export default function ChainSelectorRow({
const theme = useTheme()
return (
<Container onClick={() => onSelectChain(targetChain)} data-testid={`chain-selector-option-${label.toLowerCase()}`}>
<Container
disabled={!!disabled}
onClick={() => {
if (!disabled) onSelectChain(targetChain)
}}
data-testid={`chain-selector-option-${label.toLowerCase()}`}
>
<Logo src={logoUrl} alt={label} />
<Label>{label}</Label>
{isPending && <ApproveText>Approve in wallet</ApproveText>}
{disabled && (
<CaptionText>
<Trans>Unsupported by your wallet</Trans>
</CaptionText>
)}
{isPending && (
<CaptionText>
<Trans>Approve in wallet</Trans>
</CaptionText>
)}
<Status>
{active && <CheckMarkIcon width={LOGO_SIZE} height={LOGO_SIZE} color={theme.accentActive} />}
{isPending && <Loader width={LOGO_SIZE} height={LOGO_SIZE} />}
......
This diff is collapsed.
......@@ -9,9 +9,16 @@ interface NavIconProps {
isActive?: boolean
label?: string
onClick: () => void
activeBackground?: boolean
}
export const NavIcon = ({ children, isActive, label = t`Navigation button`, onClick }: NavIconProps) => {
export const NavIcon = ({
children,
isActive,
label = t`Navigation button`,
onClick,
activeBackground,
}: NavIconProps) => {
return (
<Box
as="button"
......@@ -21,6 +28,7 @@ export const NavIcon = ({ children, isActive, label = t`Navigation button`, onCl
height="40"
width="40"
aria-label={label}
backgroundColor={activeBackground ? 'accentActiveSoft' : 'transparent'}
>
{children}
</Box>
......
......@@ -15,6 +15,7 @@ import { Box } from 'nft/components/Box'
import { Row } from 'nft/components/Flex'
import { magicalGradientOnHover } from 'nft/css/common.css'
import { useIsMobile, useIsTablet } from 'nft/hooks'
import { useIsNavSearchInputVisible } from 'nft/hooks/useIsNavSearchInputVisible'
import { fetchSearchCollections } from 'nft/queries'
import { ChangeEvent, useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react'
import { useQuery } from 'react-query'
......@@ -52,6 +53,7 @@ export const SearchBar = () => {
const isMobile = useIsMobile()
const isTablet = useIsTablet()
const isNftGraphqlEnabled = useNftGraphqlEnabled()
const isNavSearchInputVisible = useIsNavSearchInputVisible()
useOnClickOutside(searchRef, () => {
isOpen && toggleOpen()
......@@ -117,7 +119,7 @@ export const SearchBar = () => {
}
}, [isOpen])
const isMobileOrTablet = isMobile || isTablet
const isMobileOrTablet = isMobile || isTablet || !isNavSearchInputVisible
const trace = useTrace({ section: InterfaceSectionName.NAVBAR_SEARCH })
......
import { Trans } from '@lingui/macro'
import { useWeb3React } from '@web3-react/core'
import NewBadge from 'components/WalletModal/NewBadge'
import Web3Status from 'components/Web3Status'
import { useMGTMMicrositeEnabled } from 'featureFlags/flags/mgtm'
import { chainIdToBackendName } from 'graphql/data/util'
import { useIsNftPage } from 'hooks/useIsNftPage'
import { useIsPoolsPage } from 'hooks/useIsPoolsPage'
......@@ -14,6 +16,7 @@ import { NavLink, NavLinkProps, useLocation, useNavigate } from 'react-router-do
import styled from 'styled-components/macro'
import { Bag } from './Bag'
import Blur from './Blur'
import { ChainSelector } from './ChainSelector'
import { MenuDropdown } from './MenuDropdown'
import { SearchBar } from './SearchBar'
......@@ -55,6 +58,7 @@ export const PageTabs = () => {
const isPoolActive = useIsPoolsPage()
const isNftPage = useIsNftPage()
const micrositeEnabled = useMGTMMicrositeEnabled()
return (
<>
......@@ -67,20 +71,34 @@ export const PageTabs = () => {
<MenuItem dataTestId="nft-nav" href="/nfts" isActive={isNftPage}>
<Trans>NFTs</Trans>
</MenuItem>
<Box display={{ sm: 'flex', lg: 'none', xxl: 'flex' }} width="full">
<MenuItem href="/pools" dataTestId="pool-nav-link" isActive={isPoolActive}>
<Trans>Pools</Trans>
</MenuItem>
</Box>
{micrositeEnabled && (
<Box display={{ sm: 'none', xxxl: 'flex' }}>
<MenuItem href="/wallet" isActive={pathname.startsWith('/wallet')}>
<Trans>Wallet</Trans>
<NewBadge />
</MenuItem>
</Box>
)}
<Box marginY={{ sm: '4', md: 'unset' }}>
<MenuDropdown />
</Box>
</>
)
}
const Navbar = () => {
const Navbar = ({ blur }: { blur: boolean }) => {
const isNftPage = useIsNftPage()
const sellPageState = useProfilePageState((state) => state.state)
const navigate = useNavigate()
return (
<>
{blur && <Blur />}
<Nav>
<Box display="flex" height="full" flexWrap="nowrap">
<Box className={styles.leftSideContainer}>
......@@ -103,7 +121,7 @@ const Navbar = () => {
<ChainSelector leftAlign={true} />
</Box>
)}
<Row gap={{ xl: '0', xxl: '8' }} display={{ sm: 'none', lg: 'flex' }}>
<Row display={{ sm: 'none', lg: 'flex' }}>
<PageTabs />
</Row>
</Box>
......@@ -112,12 +130,9 @@ const Navbar = () => {
</Box>
<Box className={styles.rightSideContainer}>
<Row gap="12">
<Box position="relative" display={{ sm: 'flex', xl: 'none' }}>
<Box position="relative" display={{ sm: 'flex', navSearchInputVisible: 'none' }}>
<SearchBar />
</Box>
<Box display={{ sm: 'none', lg: 'flex' }}>
<MenuDropdown />
</Box>
{isNftPage && sellPageState !== ProfilePageStateType.LISTING && <Bag />}
{!isNftPage && (
<Box display={{ sm: 'none', lg: 'flex' }}>
......
......@@ -6,7 +6,8 @@ import { sprinkles, vars } from '../../nft/css/sprinkles.css'
export const logoContainer = style([
sprinkles({
display: 'flex',
marginRight: { sm: '12', xxl: '20' },
marginRight: '12',
alignItems: 'center',
cursor: 'pointer',
}),
])
......@@ -40,7 +41,7 @@ export const searchContainer = style([
flex: '1',
flexShrink: '1',
justifyContent: { lg: 'flex-end', xl: 'center' },
display: { sm: 'none', xl: 'flex' },
display: { sm: 'none', navSearchInputVisible: 'flex' },
alignSelf: 'center',
height: '48',
alignItems: 'flex-start',
......@@ -59,13 +60,16 @@ const baseMenuItem = style([
subhead,
sprinkles({
paddingY: '8',
paddingX: '16',
paddingX: '14',
marginY: '4',
borderRadius: '12',
transition: '250',
height: 'min',
width: 'full',
textAlign: 'center',
display: 'flex',
alignItems: 'center',
gap: '4',
}),
{
lineHeight: '24px',
......
......@@ -3,10 +3,10 @@ import { useWeb3React } from '@web3-react/core'
import { getChainInfo } from 'constants/chainInfo'
import { SupportedChainId } from 'constants/chains'
import { ArrowUpRight } from 'react-feather'
import { useDarkModeManager } from 'state/user/hooks'
import styled from 'styled-components/macro'
import { ExternalLink, HideSmall } from 'theme'
import { colors } from 'theme/colors'
import { useDarkModeManager } from 'theme/components/ThemeToggle'
import { AutoRow } from '../Row'
......
......@@ -6,7 +6,7 @@ import Badge from 'components/Badge'
import RangeBadge from 'components/Badge/RangeBadge'
import DoubleCurrencyLogo from 'components/DoubleLogo'
import HoverInlineText from 'components/HoverInlineText'
import Loader from 'components/Loader'
import Loader from 'components/Icons/LoadingSpinner'
import { RowBetween } from 'components/Row'
import { useToken } from 'hooks/Tokens'
import useIsTickAtLimit from 'hooks/useIsTickAtLimit'
......
......@@ -2,28 +2,7 @@
exports[`renders multi route 1`] = `
<DocumentFragment>
.c7 {
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
background-color: #E8ECFB;
border: unset;
border-radius: 0.5rem;
color: #000;
display: -webkit-inline-box;
display: -webkit-inline-flex;
display: -ms-inline-flexbox;
display: inline-flex;
padding: 4px 6px;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
font-weight: 500;
}
.c1 {
.c1 {
box-sizing: border-box;
margin: 0;
min-width: 0;
......@@ -54,16 +33,55 @@ exports[`renders multi route 1`] = `
}
.c12 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: start;
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
gap: 1px;
}
.c13 {
-webkit-flex-wrap: wrap;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin: -1px;
}
.c12 > * {
.c13 > * {
margin: 1px !important;
}
.c7 {
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
background: #E8ECFB;
border: unset;
border-radius: 0.5rem;
color: #000;
display: -webkit-inline-box;
display: -webkit-inline-flex;
display: -ms-inline-flexbox;
display: inline-flex;
padding: 4px 6px;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
font-weight: 500;
}
.c0 {
-webkit-align-items: center;
-webkit-box-align: center;
......@@ -180,7 +198,7 @@ exports[`renders multi route 1`] = `
</div>
</div>
<div
class="c11 c2 c12"
class="c11 c12 c13"
style="justify-content: space-evenly; z-index: 2;"
width="100%"
>
......@@ -225,7 +243,7 @@ exports[`renders multi route 1`] = `
</div>
</div>
<div
class="c11 c2 c12"
class="c11 c12 c13"
style="justify-content: space-evenly; z-index: 2;"
width="100%"
>
......@@ -240,28 +258,7 @@ exports[`renders multi route 1`] = `
exports[`renders single route 1`] = `
<DocumentFragment>
.c7 {
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
background-color: #E8ECFB;
border: unset;
border-radius: 0.5rem;
color: #000;
display: -webkit-inline-box;
display: -webkit-inline-flex;
display: -ms-inline-flexbox;
display: inline-flex;
padding: 4px 6px;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
font-weight: 500;
}
.c1 {
.c1 {
box-sizing: border-box;
margin: 0;
min-width: 0;
......@@ -292,16 +289,55 @@ exports[`renders single route 1`] = `
}
.c12 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: start;
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
gap: 1px;
}
.c13 {
-webkit-flex-wrap: wrap;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin: -1px;
}
.c12 > * {
.c13 > * {
margin: 1px !important;
}
.c7 {
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
background: #E8ECFB;
border: unset;
border-radius: 0.5rem;
color: #000;
display: -webkit-inline-box;
display: -webkit-inline-flex;
display: -ms-inline-flexbox;
display: inline-flex;
padding: 4px 6px;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
font-weight: 500;
}
.c0 {
-webkit-align-items: center;
-webkit-box-align: center;
......@@ -418,7 +454,7 @@ exports[`renders single route 1`] = `
</div>
</div>
<div
class="c11 c2 c12"
class="c11 c12 c13"
style="justify-content: space-evenly; z-index: 2;"
width="100%"
>
......
......@@ -8,6 +8,7 @@ const Row = styled(Box)<{
padding?: string
border?: string
borderRadius?: string
gap?: string
}>`
width: ${({ width }) => width ?? '100%'};
display: flex;
......@@ -17,6 +18,7 @@ const Row = styled(Box)<{
padding: ${({ padding }) => padding};
border: ${({ border }) => border};
border-radius: ${({ borderRadius }) => borderRadius};
gap: ${({ gap }) => gap};
`
export const RowBetween = styled(Row)`
......
......@@ -2,7 +2,43 @@
exports[`renders currency rows correctly when currencies list is non-empty 1`] = `
<DocumentFragment>
.c10 {
.c0 {
box-sizing: border-box;
margin: 0;
min-width: 0;
}
.c1 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: start;
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
}
.c2 {
-webkit-box-pack: justify;
-webkit-justify-content: space-between;
-ms-flex-pack: justify;
justify-content: space-between;
}
.c11 {
width: -webkit-fit-content;
width: -moz-fit-content;
width: fit-content;
}
.c10 {
color: #98A1C0;
}
......@@ -43,42 +79,6 @@ exports[`renders currency rows correctly when currencies list is non-empty 1`] =
grid-auto-rows: auto;
}
.c0 {
box-sizing: border-box;
margin: 0;
min-width: 0;
}
.c1 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: start;
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
}
.c2 {
-webkit-box-pack: justify;
-webkit-justify-content: space-between;
-ms-flex-pack: justify;
justify-content: space-between;
}
.c11 {
width: -webkit-fit-content;
width: -moz-fit-content;
width: fit-content;
}
.c3 {
padding: 4px 20px;
height: 56px;
......@@ -90,7 +90,7 @@ exports[`renders currency rows correctly when currencies list is non-empty 1`] =
}
.c3:hover {
background-color: #B8C0DC14;
background-color: #98A1C014;
}
.c6 {
......
......@@ -2,6 +2,7 @@ import { TraceEvent } from '@uniswap/analytics'
import { BrowserEvent, InterfaceElementName, InterfaceEventName } from '@uniswap/analytics-events'
import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import Loader from 'components/Icons/LoadingSpinner'
import TokenSafetyIcon from 'components/TokenSafety/TokenSafetyIcon'
import { checkWarning } from 'constants/tokenSafety'
import { CSSProperties, MutableRefObject, useCallback, useMemo } from 'react'
......@@ -15,7 +16,6 @@ import { useCurrencyBalance } from '../../../state/connection/hooks'
import { WrappedTokenInfo } from '../../../state/lists/wrappedTokenInfo'
import { ThemedText } from '../../../theme'
import Column, { AutoColumn } from '../../Column'
import Loader from '../../Loader'
import CurrencyLogo from '../../Logo/CurrencyLogo'
import Row, { RowFixed } from '../../Row'
import { MouseoverTooltip } from '../../Tooltip'
......
......@@ -19,7 +19,7 @@ import { useAllTokenBalances } from 'state/connection/hooks'
import styled, { useTheme } from 'styled-components/macro'
import { UserAddedToken } from 'types/tokens'
import { useAllTokens, useIsUserAddedToken, useSearchInactiveTokenLists, useToken } from '../../hooks/Tokens'
import { useDefaultActiveTokens, useIsUserAddedToken, useSearchInactiveTokenLists, useToken } from '../../hooks/Tokens'
import { CloseIcon, ThemedText } from '../../theme'
import { isAddress } from '../../utils'
import Column from '../Column'
......@@ -85,7 +85,7 @@ export function CurrencySearch({
}
}, [isAddressSearch])
const defaultTokens = useAllTokens()
const defaultTokens = useDefaultActiveTokens()
const filteredTokens: Token[] = useMemo(() => {
return Object.values(defaultTokens).filter(getTokenFilter(debouncedQuery))
}, [defaultTokens, debouncedQuery])
......
......@@ -8,8 +8,8 @@ import { X } from 'react-feather'
import { useModalIsOpen, useToggleTaxServiceModal } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer'
import { useTaxServiceDismissal } from 'state/user/hooks'
import { useIsDarkMode } from 'state/user/hooks'
import styled from 'styled-components/macro'
import { useIsDarkMode } from 'theme/components/ThemeToggle'
import { opacify } from 'theme/utils'
import { Z_INDEX } from 'theme/zIndex'
......
......@@ -5,7 +5,7 @@ import styled, { keyframes } from 'styled-components/macro'
const Wrapper = styled.button<{ isActive?: boolean; activeElement?: boolean }>`
align-items: center;
background: ${({ isActive, theme }) => (isActive ? theme.accentActionSoft : 'transparent')};
border: ${({ theme, isActive }) => (isActive ? 'none' : `1px solid ${theme.backgroundOutline}`)};
border: ${({ theme, isActive }) => (isActive ? '1px solid transparent' : `1px solid ${theme.backgroundOutline}`)};
border-radius: 20px;
cursor: pointer;
display: flex;
......
......@@ -79,7 +79,7 @@ export function AboutSection({ address, chainId, description, homepageUrl, twitt
const tokenDescription = shouldTruncate && isDescriptionTruncated ? truncateDescription(description) : description
const baseExplorerUrl = getChainInfo(chainId).explorer
const { explorer, infoLink } = getChainInfo(chainId)
return (
<AboutContainer data-testid="token-details-about-section">
......@@ -106,9 +106,9 @@ export function AboutSection({ address, chainId, description, homepageUrl, twitt
<ResourcesContainer data-cy="resources-container">
<Resource
name={chainId === SupportedChainId.MAINNET ? 'Etherscan' : 'Block Explorer'}
link={`${baseExplorerUrl}${address === 'NATIVE' ? '' : 'address/' + address}`}
link={`${explorer}${address === 'NATIVE' ? '' : 'address/' + address}`}
/>
<Resource name="More analytics" link={`https://info.uniswap.org/#/tokens/${address}`} />
<Resource name="More analytics" link={`${infoLink}tokens/${address}`} />
{homepageUrl && <Resource name="Website" link={homepageUrl} />}
{twitterName && <Resource name="Twitter" link={`https://twitter.com/${twitterName}`} />}
</ResourcesContainer>
......
import { Trans } from '@lingui/macro'
import { useWeb3React } from '@web3-react/core'
import { ButtonPrimary } from 'components/Button'
import { getChainInfo } from 'constants/chainInfo'
import { SupportedChainId } from 'constants/chains'
import useSelectChain from 'hooks/useSelectChain'
import { useNavigate } from 'react-router-dom'
import styled from 'styled-components/macro'
import { ThemedText } from 'theme'
import { ReactComponent as EyeIcon } from '../../../assets/svg/eye.svg'
......@@ -23,32 +29,56 @@ const InvalidDetailsText = styled.span`
line-height: 28px;
`
const TokenExploreButton = styled.button`
border: none;
border-radius: 12px;
background-color: ${({ theme }) => theme.accentAction};
const TokenExploreButton = styled(ButtonPrimary)`
width: fit-content;
padding: 12px 16px;
border-radius: 12px;
color: ${({ theme }) => theme.textPrimary};
font-size: 16px;
font-weight: 600;
`
export default function InvalidTokenDetails({ chainName }: { chainName?: string }) {
export default function InvalidTokenDetails({
pageChainId,
isInvalidAddress,
}: {
pageChainId: SupportedChainId
isInvalidAddress?: boolean
}) {
const { chainId } = useWeb3React()
const navigate = useNavigate()
const selectChain = useSelectChain()
// if the token's address is valid and the chains match, it's a non-existant token
const isNonExistantToken = !isInvalidAddress && pageChainId === chainId
return (
<InvalidDetailsContainer>
<EyeIcon />
{isInvalidAddress || isNonExistantToken ? (
<>
<InvalidDetailsText>
{chainName ? (
<Trans>{`This token doesn't exist on ${chainName}`}</Trans>
) : (
<Trans>This token doesn&apos;t exist</Trans>
)}
</InvalidDetailsText>
<TokenExploreButton onClick={() => navigate('/tokens')}>
<ThemedText.SubHeader>
<Trans>Explore tokens</Trans>
</ThemedText.SubHeader>
</TokenExploreButton>
</>
) : (
<>
<InvalidDetailsText>
<Trans>This token doesn&apos;t exist on {getChainInfo(chainId)?.label}</Trans>
</InvalidDetailsText>
<TokenExploreButton onClick={() => selectChain(pageChainId)}>
<ThemedText.SubHeader>
<Trans>Switch to {getChainInfo(pageChainId).label}</Trans>
</ThemedText.SubHeader>
</TokenExploreButton>
</>
)}
</InvalidDetailsContainer>
)
}
......@@ -4,7 +4,7 @@ import { NATIVE_CHAIN_ID } from 'constants/tokens'
import { chainIdToBackendName } from 'graphql/data/util'
import { useOnClickOutside } from 'hooks/useOnClickOutside'
import { useRef } from 'react'
import { Twitter } from 'react-feather'
import { Link, Twitter } from 'react-feather'
import { useModalIsOpen, useToggleModal } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer'
import styled, { useTheme } from 'styled-components/macro'
......@@ -96,9 +96,10 @@ export default function ShareButton({ currency }: { currency: Currency }) {
<ShareActions>
<ShareAction onClick={() => copyHelperRef.current?.forceCopy()}>
<CopyHelper
link
InitialIcon={Link}
color={theme.textPrimary}
iconPosition="left"
gap={12}
toCopy={window.location.href}
ref={copyHelperRef}
>
......
import { Trans } from '@lingui/macro'
import { formatNumber, NumberType } from '@uniswap/conedison/format'
import { MouseoverTooltip } from 'components/Tooltip'
import { getChainInfo } from 'constants/chainInfo'
import { SupportedChainId } from 'constants/chains'
import { ReactNode } from 'react'
import styled from 'styled-components/macro'
import { ThemedText } from 'theme'
import { ExternalLink, ThemedText } from 'theme'
import { textFadeIn } from 'theme/styles'
import { UNSUPPORTED_METADATA_CHAINS } from '../constants'
import { TokenSortMethod } from '../state'
import { HEADER_DESCRIPTIONS } from '../TokenTable/TokenRow'
......@@ -65,13 +68,17 @@ function Stat({
}
type StatsSectionProps = {
chainId: SupportedChainId
address: string
priceLow52W?: NumericStat
priceHigh52W?: NumericStat
TVL?: NumericStat
volume24H?: NumericStat
}
export default function StatsSection(props: StatsSectionProps) {
const { priceLow52W, priceHigh52W, TVL, volume24H } = props
const { chainId, address, priceLow52W, priceHigh52W, TVL, volume24H } = props
const { label, infoLink } = getChainInfo(chainId)
if (TVL || volume24H || priceLow52W || priceHigh52W) {
return (
<StatsWrapper data-testid="token-details-stats">
......@@ -105,6 +112,22 @@ export default function StatsSection(props: StatsSectionProps) {
</StatsWrapper>
)
} else {
return <NoData>No stats available</NoData>
return UNSUPPORTED_METADATA_CHAINS.includes(chainId) ? (
<>
<Header>
<Trans>Stats</Trans>
</Header>
<ThemedText.BodySecondary paddingTop="12px">
<Trans>
Token stats and charts for {label} are available on{' '}
<ExternalLink color="currentColor" href={`${infoLink}tokens/${address}`}>
info.uniswap.org
</ExternalLink>
</Trans>
</ThemedText.BodySecondary>
</>
) : (
<NoData>No stats available</NoData>
)
}
}
......@@ -24,7 +24,6 @@ import TokenSafetyMessage from 'components/TokenSafety/TokenSafetyMessage'
import TokenSafetyModal from 'components/TokenSafety/TokenSafetyModal'
import Widget from 'components/Widget'
import { SwapTokens } from 'components/Widget/inputs'
import { getChainInfo } from 'constants/chainInfo'
import { NATIVE_CHAIN_ID, nativeOnChain } from 'constants/tokens'
import { checkWarning } from 'constants/tokenSafety'
import { TokenPriceQuery } from 'graphql/data/__generated__/types-and-hooks'
......@@ -182,10 +181,9 @@ export default function TokenDetails({
},
[continueSwap, setContinueSwap]
)
// address will never be undefined if token is defined; address is checked here to appease typechecker
if (detailedToken === undefined || !address) {
return <InvalidTokenDetails chainName={address && getChainInfo(pageChainId)?.label} />
return <InvalidTokenDetails pageChainId={pageChainId} isInvalidAddress={!address} />
}
return (
<Trace
......@@ -211,7 +209,10 @@ export default function TokenDetails({
</TokenActions>
</TokenInfoContainer>
<ChartSection tokenPriceQuery={tokenPriceQuery} onChangeTimePeriod={onChangeTimePeriod} />
<StatsSection
chainId={pageChainId}
address={address}
TVL={tokenQueryData?.market?.totalValueLocked?.value}
volume24H={tokenQueryData?.market?.volume24H?.value}
priceHigh52W={tokenQueryData?.market?.priceHigh52W?.value}
......
......@@ -8,6 +8,7 @@ import { useNavigate, useParams } from 'react-router-dom'
import { useModalIsOpen, useToggleModal } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer'
import styled, { css, useTheme } from 'styled-components/macro'
import { EllipsisStyle } from 'theme'
import FilterOption from './FilterOption'
......@@ -80,6 +81,7 @@ const Chevron = styled.span<{ open: boolean }>`
color: ${({ open, theme }) => (open ? theme.accentActive : theme.textSecondary)};
`
const NetworkLabel = styled.div`
${EllipsisStyle}
display: flex;
gap: 8px;
align-items: center;
......@@ -93,7 +95,7 @@ const CheckContainer = styled.div`
flex-direction: flex-end;
`
const NetworkFilterOption = styled(FilterOption)`
width: 156px;
min-width: 156px;
`
const Tag = styled(Badge)`
background-color: ${({ theme }) => theme.backgroundModule};
......
......@@ -7,7 +7,7 @@ import SparklineChart from 'components/Charts/SparklineChart'
import QueryTokenLogo from 'components/Logo/QueryTokenLogo'
import { MouseoverTooltip } from 'components/Tooltip'
import { SparklineMap, TopToken } from 'graphql/data/TopTokens'
import { CHAIN_NAME_TO_CHAIN_ID, getTokenDetailsURL } from 'graphql/data/util'
import { CHAIN_NAME_TO_CHAIN_ID, getTokenDetailsURL, validateUrlChainParam } from 'graphql/data/util'
import { useAtomValue } from 'jotai/utils'
import { ForwardedRef, forwardRef } from 'react'
import { CSSProperties, ReactNode } from 'react'
......@@ -434,8 +434,7 @@ export const LoadedRow = forwardRef((props: LoadedRowProps, ref: ForwardedRef<HT
const { tokenListIndex, tokenListLength, token, sortRank } = props
const filterString = useAtomValue(filterStringAtom)
const lowercaseChainName = useParams<{ chainName?: string }>().chainName?.toUpperCase() ?? 'ethereum'
const filterNetwork = lowercaseChainName.toUpperCase()
const filterNetwork = validateUrlChainParam(useParams<{ chainName?: string }>().chainName?.toUpperCase())
const chainId = CHAIN_NAME_TO_CHAIN_ID[filterNetwork]
const timePeriod = useAtomValue(filterTimeAtom)
const delta = token.market?.pricePercentChange?.value
......
import { SupportedChainId } from 'constants/chains'
export const MAX_WIDTH_MEDIA_BREAKPOINT = '1200px'
export const XLARGE_MEDIA_BREAKPOINT = '960px'
export const LARGE_MEDIA_BREAKPOINT = '840px'
export const MEDIUM_MEDIA_BREAKPOINT = '720px'
export const SMALL_MEDIA_BREAKPOINT = '540px'
export const MOBILE_MEDIA_BREAKPOINT = '420px'
// includes chains that the backend does not current source off-chain metadata for
export const UNSUPPORTED_METADATA_CHAINS = [SupportedChainId.BNB]
import { loadingAnimation } from 'components/Loader/styled'
import { lighten } from 'polished'
import styled from 'styled-components/macro'
/* Loading state bubbles (animation style from: src/components/Loader/styled.tsx) */
export const LoadingBubble = styled.div`
export const LoadingBubble = styled.div<{
height?: string
width?: string
round?: boolean
delay?: string
margin?: string
}>`
border-radius: 12px;
height: 24px;
border-radius: ${({ round }) => (round ? '50%' : '12px')};
${({ margin }) => margin && `margin: ${margin}`};
height: ${({ height }) => height ?? '24px'};
width: 50%;
width: ${({ width }) => width ?? '50%'};
animation: ${loadingAnimation} 1.5s infinite;
${({ delay }) => delay && `animation-delay: ${delay};`}
animation-fill-mode: both;
background: linear-gradient(
to left,
${({ theme }) => theme.backgroundInteractive} 25%,
${({ theme }) => theme.backgroundOutline} 50%,
${({ theme }) => lighten(0.075, theme.backgroundInteractive)} 50%,
${({ theme }) => theme.backgroundInteractive} 75%
);
will-change: background-position;
......
......@@ -109,7 +109,7 @@ export function MouseoverTooltipContent({
}, [openCallback])
const close = useCallback(() => setShow(false), [setShow])
return (
<TooltipContent {...rest} show={show} content={disableHover ? null : content}>
<TooltipContent {...rest} show={!disableHover && show} content={disableHover ? null : content}>
<div
style={{ display: 'inline-block', lineHeight: 0, padding: '0.25rem' }}
onMouseEnter={open}
......
import { useWeb3React } from '@web3-react/core'
import UniswapWalletBanner from 'components/Banner/UniswapWalletBanner'
import AddressClaimModal from 'components/claim/AddressClaimModal'
import ConnectedAccountBlocked from 'components/ConnectedAccountBlocked'
import FiatOnrampModal from 'components/FiatOnrampModal'
import TaxServiceBanner from 'components/TaxServiceModal/TaxServiceBanner'
import UniwalletModal from 'components/WalletDropdown/UniwalletModal'
import { useTaxServiceBannerEnabled } from 'featureFlags/flags/taxServiceBanner'
import useAccountRiskCheck from 'hooks/useAccountRiskCheck'
import { useIsNftPage } from 'hooks/useIsNftPage'
......@@ -36,6 +38,8 @@ export default function TopLevelModals() {
<AddressClaimModal isOpen={addressClaimOpen} onDismiss={addressClaimToggle} />
<ConnectedAccountBlocked account={account} isOpen={accountBlocked} />
<Bag />
<UniwalletModal />
<UniswapWalletBanner />
<TransactionCompleteModal />
<AirdropModal />
<FiatOnrampModal />
......
import React, { memo, useMemo } from 'react'
import { useIsDarkMode } from 'state/user/hooks'
import { useIsDarkMode } from 'theme/components/ThemeToggle'
import { blurs, UniconAttributeData, UniconAttributes, UniconAttributesToIndices } from './types'
import { deriveUniconAttributeIndices, getUniconAttributeData, isEthAddress } from './utils'
......
import { Trans } from '@lingui/macro'
import { useWeb3React } from '@web3-react/core'
import { TransactionSummary } from 'components/AccountDetailsV2'
import { ButtonPrimary } from 'components/Button'
import { useActiveLocale } from 'hooks/useActiveLocale'
import { useMemo } from 'react'
import { ChevronRight, Moon, Sun } from 'react-feather'
import { useToggleWalletModal } from 'state/application/hooks'
import { useDarkModeManager } from 'state/user/hooks'
import Column from 'components/Column'
import WalletModal from 'components/WalletModal'
import { useCallback, useState } from 'react'
import styled from 'styled-components/macro'
import { useAllTransactions } from '../../state/transactions/hooks'
import AuthenticatedHeader from './AuthenticatedHeader'
import { MenuState } from './index'
import SettingsMenu from './SettingsMenu'
const ConnectButton = styled(ButtonPrimary)`
border-radius: 12px;
height: 44px;
width: 288px;
font-weight: 600;
font-size: 16px;
margin-left: auto;
margin-right: auto;
@media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) {
width: 100%;
}
`
const Divider = styled.div`
border-bottom: 1px solid ${({ theme }) => theme.backgroundOutline};
margin-top: 16px;
margin-bottom: 16px;
`
const ToggleMenuItem = styled.button`
background-color: transparent;
margin: 0;
border: none;
cursor: pointer;
display: flex;
flex: 1;
border-radius: 12px;
flex-direction: row;
align-items: center;
justify-content: space-between;
font-size: 14px;
font-weight: 400;
width: 100%;
padding: 12px 8px;
color: ${({ theme }) => theme.textSecondary};
:hover {
color: ${({ theme }) => theme.textPrimary};
background-color: ${({ theme }) => theme.backgroundModule};
transition: ${({
theme: {
transition: { duration, timing },
},
}) => `${duration.fast} all ${timing.in}`};
}
`
const FlexContainer = styled.div`
display: flex;
`
const LatestPendingTxnBox = styled(FlexContainer)`
display: flex;
border-radius: 12px;
background-color: ${({ theme }) => theme.backgroundModule};
align-items: center;
gap: 8px;
`
const PendingBadge = styled.span`
background-color: ${({ theme }) => theme.accentActionSoft};
color: ${({ theme }) => theme.accentAction};
font-weight: 600;
padding: 4px 8px;
border-radius: 4px;
`
const IconWrap = styled.span`
display: inline-block;
margin-top: auto;
margin-bottom: auto;
margin-left: 4px;
height: 16px;
`
const DefaultMenuWrap = styled.div`
const DefaultMenuWrap = styled(Column)`
width: 100%;
height: 100%;
`
const DefaultText = styled.span`
font-size: 14px;
font-weight: 400;
`
const CenterVertically = styled.div`
margin-top: auto;
margin-bottom: auto;
`
enum MenuState {
DEFAULT,
SETTINGS,
}
const WalletDropdown = ({ setMenu }: { setMenu: (state: MenuState) => void }) => {
function DefaultMenu() {
const { account } = useWeb3React()
const isAuthenticated = !!account
const [darkMode, toggleDarkMode] = useDarkModeManager()
const activeLocale = useActiveLocale()
const ISO = activeLocale.split('-')[0].toUpperCase()
const allTransactions = useAllTransactions()
const toggleWalletModal = useToggleWalletModal()
const pendingTransactions = useMemo(
() => Object.values(allTransactions).filter((tx) => !tx.receipt),
[allTransactions]
)
const latestPendingTransaction =
pendingTransactions.length > 0
? pendingTransactions.sort((tx1, tx2) => tx2.addedTime - tx1.addedTime)[0]
: undefined
const [menu, setMenu] = useState<MenuState>(MenuState.DEFAULT)
const openSettings = useCallback(() => setMenu(MenuState.SETTINGS), [])
const closeSettings = useCallback(() => setMenu(MenuState.DEFAULT), [])
return (
<DefaultMenuWrap>
{isAuthenticated ? (
<AuthenticatedHeader />
{menu === MenuState.DEFAULT &&
(isAuthenticated ? (
<AuthenticatedHeader account={account} openSettings={openSettings} />
) : (
<ConnectButton data-testid="wallet-connect-wallet" onClick={toggleWalletModal}>
Connect wallet
</ConnectButton>
)}
<Divider />
{isAuthenticated && (
<>
<ToggleMenuItem data-testid="wallet-transactions" onClick={() => setMenu(MenuState.TRANSACTIONS)}>
<DefaultText>
<Trans>Transactions</Trans>{' '}
{pendingTransactions.length > 0 && (
<PendingBadge>
{pendingTransactions.length} <Trans>Pending</Trans>
</PendingBadge>
)}
</DefaultText>
<IconWrap>
<ChevronRight size={16} strokeWidth={3} />
</IconWrap>
</ToggleMenuItem>
{!!latestPendingTransaction && (
<LatestPendingTxnBox>
<TransactionSummary
key={latestPendingTransaction.hash}
transactionDetails={latestPendingTransaction}
isLastTransactionInList={true}
/>
</LatestPendingTxnBox>
)}
</>
)}
<ToggleMenuItem data-testid="wallet-select-language" onClick={() => setMenu(MenuState.LANGUAGE)}>
<DefaultText>
<Trans>Language</Trans>
</DefaultText>
<FlexContainer>
<CenterVertically>
<DefaultText>{ISO}</DefaultText>
</CenterVertically>
<IconWrap>
<ChevronRight size={16} strokeWidth={3} />
</IconWrap>
</FlexContainer>
</ToggleMenuItem>
<ToggleMenuItem data-testid="wallet-select-theme" onClick={toggleDarkMode}>
<DefaultText>{darkMode ? <Trans> Light theme</Trans> : <Trans>Dark theme</Trans>}</DefaultText>
<IconWrap>{darkMode ? <Sun size={16} /> : <Moon size={16} />}</IconWrap>
</ToggleMenuItem>
<WalletModal openSettings={openSettings} />
))}
{menu === MenuState.SETTINGS && <SettingsMenu onClose={closeSettings} />}
</DefaultMenuWrap>
)
}
export default WalletDropdown
export default DefaultMenu
import { sendAnalyticsEvent } from '@uniswap/analytics'
import { useMGTMMicrositeEnabled } from 'featureFlags/flags/mgtm'
import { PropsWithChildren, useCallback } from 'react'
import { useNavigate } from 'react-router-dom'
import styled from 'styled-components/macro'
import { ClickableStyle } from 'theme'
import { isIOS } from 'utils/userAgent'
const StyledButton = styled.button<{ padded?: boolean; branded?: boolean }>`
${ClickableStyle}
width: 100%;
display: flex;
justify-content: center;
flex-direction: row;
gap: 6px;
padding: 8px 24px;
border: none;
white-space: nowrap;
background: ${({ theme, branded }) => (branded ? theme.promotionalGradient : theme.backgroundInteractive)};
border-radius: 12px;
font-weight: 600;
font-size: 14px;
line-height: 16px;
color: ${({ theme, branded }) => (branded ? theme.accentTextLightPrimary : theme.textPrimary)};
`
function BaseButton({ onClick, branded, children }: PropsWithChildren<{ onClick?: () => void; branded?: boolean }>) {
return (
<StyledButton branded={branded} onClick={onClick}>
{children}
</StyledButton>
)
}
export const APP_STORE_LINK = 'https://apps.apple.com/us/app/uniswap-wallet-defi-nfts/id6443944476'
// Launches App Store if on an iOS device, else navigates to Uniswap Wallet microsite
export function DownloadButton({ onClick, text = 'Download' }: { onClick?: () => void; text?: string }) {
const navigate = useNavigate()
const micrositeEnabled = useMGTMMicrositeEnabled()
const onButtonClick = useCallback(() => {
// handles any actions required by the parent, i.e. cancelling wallet connection attempt or dismissing an ad
onClick?.()
if (isIOS || !micrositeEnabled) {
sendAnalyticsEvent('Uniswap wallet download clicked')
window.open(APP_STORE_LINK)
} else navigate('/wallet')
}, [onClick, micrositeEnabled, navigate])
return (
<BaseButton branded onClick={onButtonClick}>
{text}
</BaseButton>
)
}
export function LearnMoreButton() {
const navigate = useNavigate()
return <BaseButton onClick={() => navigate('/wallet')}>Learn More</BaseButton>
}
import { Trans } from '@lingui/macro'
import Tooltip from 'components/Tooltip'
import useCopyClipboard from 'hooks/useCopyClipboard'
import styled from 'styled-components/macro'
import { ThemedText } from 'theme'
const Container = styled.div`
width: 100%;
cursor: pointer;
`
export function GitVersionRow() {
const [isCopied, staticCopy] = useCopyClipboard()
return process.env.REACT_APP_GIT_COMMIT_HASH ? (
<Container
onClick={() => {
staticCopy(process.env.REACT_APP_GIT_COMMIT_HASH as string)
}}
>
<Tooltip text="Copied" show={isCopied}>
<ThemedText.BodySmall color="textTertiary">
<Trans>Version: </Trans>
{' ' + process.env.REACT_APP_GIT_COMMIT_HASH.substring(0, 6)}
</ThemedText.BodySmall>
</Tooltip>
</Container>
) : null
}
......@@ -73,11 +73,10 @@ const IconBlock = (props: React.ComponentPropsWithoutRef<'a' | 'button'>) => {
return <IconBlockButton {...props} />
}
const IconButton = ({ Icon, children, ...rest }: IconButtonProps | IconLinkProps) => (
const IconButton = ({ Icon, ...rest }: IconButtonProps | IconLinkProps) => (
<IconBlock {...rest}>
<IconWrapper>
<Icon strokeWidth={1.5} size={16} />
<IconHoverText>{children}</IconHoverText>
</IconWrapper>
</IconBlock>
)
......
import { t } from '@lingui/macro'
import Column from 'components/Column'
import AlertTriangleFilled from 'components/Icons/AlertTriangleFilled'
import { LoaderV2 } from 'components/Icons/LoadingSpinner'
import { LoadingBubble } from 'components/Tokens/loading'
import { useWalletDrawer } from 'components/WalletDropdown'
import { getYear, isSameDay, isSameMonth, isSameWeek, isSameYear } from 'date-fns'
import { TransactionStatus, useTransactionListQuery } from 'graphql/data/__generated__/types-and-hooks'
import { PollingInterval } from 'graphql/data/util'
import useENSName from 'hooks/useENSName'
import { atom, useAtom } from 'jotai'
import { EmptyWalletModule } from 'nft/components/profile/view/EmptyWalletContent'
import { useEffect, useMemo } from 'react'
import styled from 'styled-components/macro'
import { EllipsisStyle, ThemedText } from 'theme'
import { ExplorerDataType, getExplorerLink } from 'utils/getExplorerLink'
import { PortfolioLogo } from '../PortfolioLogo'
import PortfolioRow, { PortfolioSkeleton, PortfolioTabWrapper } from '../PortfolioRow'
import { useLocalActivities } from './parseLocal'
import { parseRemoteActivities, useTimeSince } from './parseRemote'
import { Activity, ActivityMap } from './types'
interface ActivityGroup {
title: string
transactions: Array<Activity>
}
const sortActivities = (a: Activity, b: Activity) => b.timestamp - a.timestamp
const createGroups = (activities?: Array<Activity>) => {
if (!activities || !activities.length) return []
const now = Date.now()
const pending: Array<Activity> = []
const today: Array<Activity> = []
const currentWeek: Array<Activity> = []
const last30Days: Array<Activity> = []
const currentYear: Array<Activity> = []
const yearMap: { [key: string]: Array<Activity> } = {}
// TODO(cartcrom): create different time bucket system for activities to fall in based on design wants
activities.forEach((activity) => {
if (activity.status === TransactionStatus.Pending) {
pending.push(activity)
return
}
const addedTime = activity.timestamp * 1000
if (isSameDay(now, addedTime)) {
today.push(activity)
} else if (isSameWeek(addedTime, now)) {
currentWeek.push(activity)
} else if (isSameMonth(addedTime, now)) {
last30Days.push(activity)
} else if (isSameYear(addedTime, now)) {
currentYear.push(activity)
} else {
const year = getYear(addedTime)
if (!yearMap[year]) {
yearMap[year] = [activity]
} else {
yearMap[year].push(activity)
}
}
})
const sortedYears = Object.keys(yearMap)
.sort((a, b) => parseInt(b) - parseInt(a))
.map((year) => ({ title: year, transactions: yearMap[year] }))
const transactionGroups: Array<ActivityGroup> = [
{ title: t`Pending`, transactions: pending.sort(sortActivities) },
{ title: t`Today`, transactions: today.sort(sortActivities) },
{ title: t`This week`, transactions: currentWeek.sort(sortActivities) },
{ title: t`This month`, transactions: last30Days.sort(sortActivities) },
{ title: t`This year`, transactions: currentYear.sort(sortActivities) },
...sortedYears,
]
return transactionGroups.filter((transactionInformation) => transactionInformation.transactions.length > 0)
}
const ActivityGroupWrapper = styled(Column)`
margin-top: 16px;
gap: 8px;
`
function combineActivities(localMap: ActivityMap = {}, remoteMap: ActivityMap = {}): Array<Activity> {
const txHashes = [...new Set([...Object.keys(localMap), ...Object.keys(remoteMap)])]
// Merges local and remote activities w/ same hash, preferring remote data
return txHashes.reduce((acc: Array<Activity>, hash) => {
const localActivity = localMap?.[hash] ?? {}
const remoteActivity = remoteMap?.[hash] ?? {}
// TODO(cartcrom): determine best logic for which fields to prefer from which sources, i.e. prefer remote exact swap output instead of local estimated output
acc.push({ ...remoteActivity, ...localActivity } as Activity)
return acc
}, [])
}
const lastFetchedAtom = atom<number | undefined>(0)
export default function ActivityTab({ account }: { account: string }) {
const [drawerOpen, toggleWalletDrawer] = useWalletDrawer()
const [lastFetched, setLastFetched] = useAtom(lastFetchedAtom)
const localMap = useLocalActivities()
const { data, loading, refetch } = useTransactionListQuery({
variables: { account },
errorPolicy: 'all',
fetchPolicy: 'cache-first',
})
// We only refetch remote activity if the user renavigates to the activity tab by changing tabs or opening the drawer
useEffect(() => {
const currentTime = Date.now()
if (!lastFetched) {
setLastFetched(currentTime)
} else if (drawerOpen && lastFetched && currentTime - lastFetched > PollingInterval.Slow) {
refetch()
setLastFetched(currentTime)
}
}, [drawerOpen, lastFetched, refetch, setLastFetched])
const activityGroups = useMemo(() => {
const remoteMap = parseRemoteActivities(data?.portfolios?.[0].assetActivities)
const allActivities = combineActivities(localMap, remoteMap)
return createGroups(allActivities)
}, [data?.portfolios, localMap])
if (!data && loading)
return (
<>
<LoadingBubble height="16px" width="80px" margin="16px 16px 8px" />
<PortfolioSkeleton shrinkRight />
</>
)
else if (activityGroups.length === 0) {
return <EmptyWalletModule type="activity" onNavigateClick={toggleWalletDrawer} />
} else {
return (
<PortfolioTabWrapper>
{activityGroups.map((activityGroup) => (
<ActivityGroupWrapper key={activityGroup.title}>
<ThemedText.SubHeader color="textSecondary" fontWeight={500} marginLeft="16px">
{activityGroup.title}
</ThemedText.SubHeader>
<Column>
{activityGroup.transactions.map((activity) => (
<ActivityRow key={activity.hash} activity={activity} />
))}
</Column>
</ActivityGroupWrapper>
))}
</PortfolioTabWrapper>
)
}
}
const StyledDescriptor = styled(ThemedText.BodySmall)`
color: ${({ theme }) => theme.textSecondary};
${EllipsisStyle}
`
const StyledTimestamp = styled(ThemedText.Caption)`
color: ${({ theme }) => theme.textSecondary};
font-variant: small;
font-feature-settings: 'tnum' on, 'lnum' on, 'ss02' on;
`
function ActivityRow({ activity }: { activity: Activity }) {
const { chainId, status, title, descriptor, logos, otherAccount, currencies } = activity
const { ENSName } = useENSName(otherAccount)
const explorerUrl = getExplorerLink(activity.chainId, activity.hash, ExplorerDataType.TRANSACTION)
const timeSince = useTimeSince(activity.timestamp)
return (
<PortfolioRow
left={
<Column>
<PortfolioLogo chainId={chainId} currencies={currencies} images={logos} accountAddress={otherAccount} />
</Column>
}
title={<ThemedText.SubHeader fontWeight={500}>{title}</ThemedText.SubHeader>}
descriptor={
<StyledDescriptor color="textSecondary">
{descriptor}
{ENSName ?? otherAccount}
</StyledDescriptor>
}
right={
status === TransactionStatus.Pending ? (
<LoaderV2 />
) : status === TransactionStatus.Confirmed ? (
<StyledTimestamp>{timeSince}</StyledTimestamp>
) : (
<AlertTriangleFilled />
)
}
onClick={() => window.open(explorerUrl, '_blank')}
/>
)
}
import { t } from '@lingui/macro'
import { formatCurrencyAmount } from '@uniswap/conedison/format'
import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core'
import { nativeOnChain } from '@uniswap/smart-order-router'
import { useWeb3React } from '@web3-react/core'
import { SupportedChainId } from 'constants/chains'
import { TransactionPartsFragment, TransactionStatus } from 'graphql/data/__generated__/types-and-hooks'
import { useMemo } from 'react'
import { TokenAddressMap, useCombinedActiveList } from 'state/lists/hooks'
import { useMultichainTransactions } from 'state/transactions/hooks'
import {
AddLiquidityV2PoolTransactionInfo,
AddLiquidityV3PoolTransactionInfo,
ApproveTransactionInfo,
CollectFeesTransactionInfo,
CreateV3PoolTransactionInfo,
ExactInputSwapTransactionInfo,
ExactOutputSwapTransactionInfo,
MigrateV2LiquidityToV3TransactionInfo,
RemoveLiquidityV3TransactionInfo,
TransactionDetails,
TransactionType,
WrapTransactionInfo,
} from 'state/transactions/types'
import { getActivityTitle } from '../constants'
import { Activity, ActivityMap } from './types'
function getCurrency(currencyId: string, chainId: SupportedChainId, tokens: TokenAddressMap) {
return currencyId === 'ETH' ? nativeOnChain(chainId) : tokens[chainId][currencyId].token
}
function buildCurrencyDescriptor(
currencyA: Currency,
amtA: string,
currencyB: Currency,
amtB: string,
delimiter = t`for`
) {
const formattedA = formatCurrencyAmount(CurrencyAmount.fromRawAmount(currencyA, amtA))
const formattedB = formatCurrencyAmount(CurrencyAmount.fromRawAmount(currencyB, amtB))
return `${formattedA} ${currencyA.symbol} ${delimiter} ${formattedB} ${currencyB.symbol}`
}
function parseSwap(
swap: ExactInputSwapTransactionInfo | ExactOutputSwapTransactionInfo,
chainId: SupportedChainId,
tokens: TokenAddressMap
): Partial<Activity> {
const tokenIn = getCurrency(swap.inputCurrencyId, chainId, tokens)
const tokenOut = getCurrency(swap.outputCurrencyId, chainId, tokens)
const [inputRaw, outputRaw] =
swap.tradeType === TradeType.EXACT_INPUT
? [swap.inputCurrencyAmountRaw, swap.expectedOutputCurrencyAmountRaw]
: [swap.expectedInputCurrencyAmountRaw, swap.outputCurrencyAmountRaw]
return {
descriptor: buildCurrencyDescriptor(tokenIn, inputRaw, tokenOut, outputRaw),
currencies: [tokenIn, tokenOut],
}
}
function parseWrap(wrap: WrapTransactionInfo, chainId: SupportedChainId, status: TransactionStatus): Partial<Activity> {
const native = nativeOnChain(chainId)
const wrapped = native.wrapped
const [input, output] = wrap.unwrapped ? [wrapped, native] : [native, wrapped]
const descriptor = buildCurrencyDescriptor(input, wrap.currencyAmountRaw, output, wrap.currencyAmountRaw)
const title = getActivityTitle(TransactionType.WRAP, status, wrap.unwrapped)
const currencies = wrap.unwrapped ? [wrapped, native] : [native, wrapped]
return { title, descriptor, currencies }
}
function parseApproval(
approval: ApproveTransactionInfo,
chainId: SupportedChainId,
tokens: TokenAddressMap
): Partial<Activity> {
// TODO: Add 'amount' approved to ApproveTransactionInfo so we can distinguish between revoke and approve
const currency = getCurrency(approval.tokenAddress, chainId, tokens)
const descriptor = t`${currency.symbol ?? currency.name}`
return {
descriptor,
currencies: [currency],
}
}
type GenericLPInfo = Omit<
AddLiquidityV3PoolTransactionInfo | RemoveLiquidityV3TransactionInfo | AddLiquidityV2PoolTransactionInfo,
'type'
>
function parseLP(lp: GenericLPInfo, chainId: SupportedChainId, tokens: TokenAddressMap): Partial<Activity> {
const baseCurrency = getCurrency(lp.baseCurrencyId, chainId, tokens)
const quoteCurrency = getCurrency(lp.quoteCurrencyId, chainId, tokens)
const [baseRaw, quoteRaw] = [lp.expectedAmountBaseRaw, lp.expectedAmountQuoteRaw]
const descriptor = buildCurrencyDescriptor(baseCurrency, baseRaw, quoteCurrency, quoteRaw, t`and`)
return { descriptor, currencies: [baseCurrency, quoteCurrency] }
}
function parseCollectFees(
collect: CollectFeesTransactionInfo,
chainId: SupportedChainId,
tokens: TokenAddressMap
): Partial<Activity> {
// Adapts CollectFeesTransactionInfo to generic LP type
const {
currencyId0: baseCurrencyId,
currencyId1: quoteCurrencyId,
expectedCurrencyOwed0: expectedAmountBaseRaw,
expectedCurrencyOwed1: expectedAmountQuoteRaw,
} = collect
return parseLP({ baseCurrencyId, quoteCurrencyId, expectedAmountBaseRaw, expectedAmountQuoteRaw }, chainId, tokens)
}
function parseMigrateCreateV3(
lp: MigrateV2LiquidityToV3TransactionInfo | CreateV3PoolTransactionInfo,
chainId: SupportedChainId,
tokens: TokenAddressMap
): Partial<Activity> {
const baseCurrency = getCurrency(lp.baseCurrencyId, chainId, tokens)
const quoteCurrency = getCurrency(lp.baseCurrencyId, chainId, tokens)
const descriptor = t`${baseCurrency.symbol} and ${quoteCurrency.symbol}`
return { descriptor, currencies: [baseCurrency, quoteCurrency] }
}
function parseLocalActivity(
details: TransactionDetails,
chainId: SupportedChainId,
tokens: TokenAddressMap
): Activity | undefined {
const status = !details.receipt
? TransactionStatus.Pending
: details.receipt.status === 1 || details.receipt?.status === undefined
? TransactionStatus.Confirmed
: TransactionStatus.Failed
const receipt: TransactionPartsFragment | undefined = details.receipt
? {
id: details.receipt.transactionHash,
...details.receipt,
...details,
status,
}
: undefined
const defaultFields = {
hash: details.hash,
chainId,
title: getActivityTitle(details.info.type, status),
status,
timestamp: (details.confirmedTime ?? details.addedTime) / 1000,
receipt,
}
let additionalFields: Partial<Activity> = {}
const info = details.info
if (info.type === TransactionType.SWAP) {
additionalFields = parseSwap(info, chainId, tokens)
} else if (info.type === TransactionType.APPROVAL) {
additionalFields = parseApproval(info, chainId, tokens)
} else if (info.type === TransactionType.WRAP) {
additionalFields = parseWrap(info, chainId, status)
} else if (
info.type === TransactionType.ADD_LIQUIDITY_V3_POOL ||
info.type === TransactionType.REMOVE_LIQUIDITY_V3 ||
info.type === TransactionType.ADD_LIQUIDITY_V2_POOL
) {
additionalFields = parseLP(info, chainId, tokens)
} else if (info.type === TransactionType.COLLECT_FEES) {
additionalFields = parseCollectFees(info, chainId, tokens)
} else if (info.type === TransactionType.MIGRATE_LIQUIDITY_V3 || info.type === TransactionType.CREATE_V3_POOL) {
additionalFields = parseMigrateCreateV3(info, chainId, tokens)
}
return { ...defaultFields, ...additionalFields }
}
export function useLocalActivities(): ActivityMap | undefined {
const allTransactions = useMultichainTransactions()
const { chainId } = useWeb3React()
const tokens = useCombinedActiveList()
return useMemo(
() =>
chainId
? allTransactions.reduce((acc: { [hash: string]: Activity }, [transaction, chainId]) => {
try {
const localActivity = parseLocalActivity(transaction, chainId, tokens)
if (localActivity) acc[localActivity.hash] = localActivity
} catch (error) {
console.error('Failed to parse local activity', transaction)
}
return acc
}, {})
: undefined,
[allTransactions, chainId, tokens]
)
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment