Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb105b6ec7 | ||
|
|
267e7de2b6 | ||
|
|
ab9f2af054 | ||
|
|
67b405dd42 | ||
|
|
281dbf4305 | ||
|
|
369f2d7dfa | ||
|
|
803c749b13 | ||
|
|
8818dadf24 | ||
|
|
d179fc6b84 | ||
|
|
18ec675c52 | ||
|
|
1a79bac893 | ||
|
|
bbf49b0477 | ||
|
|
7ee41abd99 | ||
|
|
581c0982db | ||
|
|
478a05a34f | ||
|
|
5d6664019e | ||
|
|
0cc187a7cb | ||
|
|
f3e09a90e2 | ||
|
|
17db102484 | ||
|
|
e8ca84ee07 | ||
|
|
dae7314aa9 | ||
|
|
32e6237624 | ||
|
|
cee19aebe7 | ||
|
|
b2fbba13a2 | ||
|
|
a15a6108c1 | ||
|
|
46724cd8f6 | ||
|
|
09e6d38f25 | ||
|
|
236a4dc145 | ||
|
|
991d07ef39 | ||
|
|
7f5707e551 | ||
|
|
4050b1b241 | ||
|
|
a19aa303d9 | ||
|
|
c7f67437fd | ||
|
|
b5192a2b7d | ||
|
|
cd92e4cf6f |
2
.env
@@ -6,7 +6,7 @@ REACT_APP_AWS_API_ENDPOINT="https://beta.api.uniswap.org/v1/graphql"
|
||||
REACT_APP_BNB_RPC_URL="https://rough-sleek-hill.bsc.quiknode.pro/413cc98cbc776cda8fdf1d0f47003583ff73d9bf"
|
||||
REACT_APP_INFURA_KEY="4bf032f2d38a4ed6bb975b80d6340847"
|
||||
REACT_APP_MOONPAY_API="https://api.moonpay.com"
|
||||
REACT_APP_MOONPAY_LINK="https://us-central1-uniswap-mobile.cloudfunctions.net/signMoonpayLinkStaging?platform=web"
|
||||
REACT_APP_MOONPAY_LINK="https://us-central1-uniswap-mobile.cloudfunctions.net/signMoonpayLinkV2?platform=web&env=staging"
|
||||
REACT_APP_MOONPAY_PUBLISHABLE_KEY="pk_test_DycfESRid31UaSxhI5yWKe1r5E5kKSz"
|
||||
REACT_APP_SENTRY_DSN="https://a3c62e400b8748b5a8d007150e2f38b7@o1037921.ingest.sentry.io/4504255148851200"
|
||||
REACT_APP_STATSIG_PROXY_URL="https://api.uniswap.org/v1/statsig-proxy"
|
||||
|
||||
@@ -7,7 +7,7 @@ REACT_APP_FORTMATIC_KEY="pk_live_F937DF033A1666BF"
|
||||
REACT_APP_GOOGLE_ANALYTICS_ID="G-KDP9B6W4H8"
|
||||
REACT_APP_INFURA_KEY="099fc58e0de9451d80b18d7c74caa7c1"
|
||||
REACT_APP_MOONPAY_API="https://api.moonpay.com"
|
||||
REACT_APP_MOONPAY_LINK="https://us-central1-uniswap-mobile.cloudfunctions.net/signMoonpayLink?platform=web"
|
||||
REACT_APP_MOONPAY_LINK="https://us-central1-uniswap-mobile.cloudfunctions.net/signMoonpayLinkV2?platform=web&env=production"
|
||||
REACT_APP_MOONPAY_PUBLISHABLE_KEY="pk_live_uQG4BJC4w3cxnqpcSqAfohdBFDTsY6E"
|
||||
REACT_APP_SENTRY_ENABLED=true
|
||||
REACT_APP_SENTRY_TRACES_SAMPLE_RATE=0.00003
|
||||
|
||||
2
.github/CODEOWNERS
vendored
@@ -1 +1 @@
|
||||
@uniswap/web
|
||||
@uniswap/web-reviewers
|
||||
@@ -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')
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// 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')
|
||||
|
||||
@@ -48,4 +48,9 @@ describe('Testing nfts', () => {
|
||||
cy.get(getTestSelector('nft-details-toggle-bag')).eq(1).click()
|
||||
cy.get(getTestSelector('nft-bag')).should('exist')
|
||||
})
|
||||
|
||||
it('should navigate to the owned nfts page', () => {
|
||||
cy.get(getTestSelector('web3-status-connected')).click()
|
||||
cy.get(getTestSelector('nft-view-self-nfts')).click()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,6 +2,7 @@ import { FeatureFlag } from '../../src/featureFlags/flags/featureFlags'
|
||||
import { getClassContainsSelector, getTestSelector } from '../utils'
|
||||
|
||||
const UNI_GOERLI = '0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984'
|
||||
const WETH_GOERLI = '0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6'
|
||||
|
||||
describe('swap widget integration tests', () => {
|
||||
const verifyInputToken = (inputText: string) => {
|
||||
@@ -16,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) => {
|
||||
@@ -47,47 +48,47 @@ describe('swap widget integration tests', () => {
|
||||
verifyInputToken('ETH')
|
||||
verifyOutputToken('Select token')
|
||||
|
||||
selectOutputAndSwitch('UNI')
|
||||
selectOutputAndSwitch('WETH')
|
||||
|
||||
verifyInputToken('UNI')
|
||||
verifyInputToken('WETH')
|
||||
verifyOutputToken('ETH')
|
||||
})
|
||||
})
|
||||
|
||||
it('should have the correct default input from URL params ', () => {
|
||||
cy.visit(`/swap?inputCurrency=${UNI_GOERLI}`, {
|
||||
cy.visit(`/swap?inputCurrency=${WETH_GOERLI}`, {
|
||||
featureFlags: [FeatureFlag.swapWidget],
|
||||
}).then(() => {
|
||||
cy.wait('@eth_blockNumber')
|
||||
})
|
||||
|
||||
verifyInputToken('UNI')
|
||||
verifyInputToken('WETH')
|
||||
verifyOutputToken('Select token')
|
||||
|
||||
selectOutputAndSwitch('WETH')
|
||||
selectOutputAndSwitch('Ether')
|
||||
|
||||
verifyInputToken('WETH')
|
||||
verifyOutputToken('UNI')
|
||||
verifyInputToken('ETH')
|
||||
verifyOutputToken('WETH')
|
||||
})
|
||||
|
||||
it('should have the correct default output from URL params ', () => {
|
||||
cy.visit(`/swap?outputCurrency=${UNI_GOERLI}`, {
|
||||
cy.visit(`/swap?outputCurrency=${WETH_GOERLI}`, {
|
||||
featureFlags: [FeatureFlag.swapWidget],
|
||||
}).then(() => {
|
||||
cy.wait('@eth_blockNumber')
|
||||
})
|
||||
|
||||
verifyInputToken('Select token')
|
||||
verifyOutputToken('UNI')
|
||||
verifyOutputToken('WETH')
|
||||
|
||||
cy.get(getClassContainsSelector('ReverseButton')).first().click()
|
||||
verifyInputToken('UNI')
|
||||
verifyInputToken('WETH')
|
||||
verifyOutputToken('Select token')
|
||||
|
||||
selectOutputAndSwitch('WETH')
|
||||
selectOutputAndSwitch('Ether')
|
||||
|
||||
verifyInputToken('WETH')
|
||||
verifyOutputToken('UNI')
|
||||
verifyInputToken('ETH')
|
||||
verifyOutputToken('WETH')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -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')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
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)')
|
||||
})
|
||||
})
|
||||
|
||||
10
package.json
@@ -130,13 +130,12 @@
|
||||
"@popperjs/core": "^2.4.4",
|
||||
"@reach/dialog": "^0.10.3",
|
||||
"@reach/portal": "^0.10.3",
|
||||
"@react-hook/window-scroll": "^1.3.0",
|
||||
"@reduxjs/toolkit": "^1.6.1",
|
||||
"@sentry/react": "^7.40.0",
|
||||
"@sentry/tracing": "^7.40.0",
|
||||
"@types/react-window-infinite-loader": "^1.0.6",
|
||||
"@uniswap/analytics": "^1.3.1",
|
||||
"@uniswap/analytics-events": "^2.6.0",
|
||||
"@uniswap/analytics-events": "^2.8.0",
|
||||
"@uniswap/conedison": "^1.4.0",
|
||||
"@uniswap/governance": "^1.0.2",
|
||||
"@uniswap/liquidity-staker": "^1.0.2",
|
||||
@@ -154,7 +153,7 @@
|
||||
"@uniswap/v3-core": "1.0.0",
|
||||
"@uniswap/v3-periphery": "^1.1.1",
|
||||
"@uniswap/v3-sdk": "^3.9.0",
|
||||
"@uniswap/widgets": "^2.48.5",
|
||||
"@uniswap/widgets": "^2.49.0",
|
||||
"@vanilla-extract/css": "^1.7.2",
|
||||
"@vanilla-extract/css-utils": "^0.1.2",
|
||||
"@vanilla-extract/dynamic": "^2.0.2",
|
||||
@@ -184,7 +183,6 @@
|
||||
"copy-to-clipboard": "^3.2.0",
|
||||
"d3": "^7.6.1",
|
||||
"ethers": "^5.7.2",
|
||||
"firebase": "^9.1.3",
|
||||
"focus-visible": "^5.2.0",
|
||||
"get-graphql-schema": "^2.1.2",
|
||||
"graphql": "^16.5.0",
|
||||
@@ -200,7 +198,7 @@
|
||||
"numbro": "^2.3.6",
|
||||
"polished": "^3.3.2",
|
||||
"polyfill-object.fromentries": "^1.0.1",
|
||||
"popper-max-size-modifier": "^0.2.0",
|
||||
"qrcode.react": "^3.1.0",
|
||||
"qs": "^6.9.4",
|
||||
"rc-slider": "^10.0.1",
|
||||
"react": "^18.2.0",
|
||||
@@ -223,12 +221,10 @@
|
||||
"rebass": "^4.0.7",
|
||||
"redux": "^4.1.2",
|
||||
"redux-localstorage-simple": "^2.3.1",
|
||||
"setimmediate": "^1.0.5",
|
||||
"statsig-react": "^1.22.0",
|
||||
"styled-components": "^5.3.5",
|
||||
"tiny-invariant": "^1.2.0",
|
||||
"ua-parser-js": "^0.7.28",
|
||||
"use-count-up": "^2.2.5",
|
||||
"use-resize-observer": "^9.0.2",
|
||||
"uuid": "^8.3.2",
|
||||
"video-extensions": "^1.2.0",
|
||||
|
||||
@@ -1,22 +1,27 @@
|
||||
/* eslint-env node */
|
||||
|
||||
require('dotenv').config({ path: '.env.production' })
|
||||
|
||||
const { exec } = require('child_process')
|
||||
const child_process = require('child_process')
|
||||
const fs = require('fs/promises')
|
||||
const { promisify } = require('util')
|
||||
const dataConfig = require('../graphql.config')
|
||||
const thegraphConfig = require('../graphql_thegraph.config')
|
||||
|
||||
const exec = promisify(child_process.exec)
|
||||
|
||||
function fetchSchema(url, outputFile) {
|
||||
exec(
|
||||
`get-graphql-schema --h Origin=https://app.uniswap.org ${url} | tee ${outputFile}.temp`,
|
||||
(error, stdout, stderr) => {
|
||||
if (error || stderr) {
|
||||
console.log(`Failed to fetch schema from ${url}`)
|
||||
} else if (stdout) {
|
||||
exec(`mv ${outputFile}.temp ${outputFile}`)
|
||||
exec(`npx get-graphql-schema --h Origin=https://app.uniswap.org ${url}`)
|
||||
.then(({ stderr, stdout }) => {
|
||||
if (stderr) {
|
||||
throw new Error(stderr)
|
||||
} else {
|
||||
fs.writeFile(outputFile, stdout)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
console.error(`Failed to fetch schema from ${url}`)
|
||||
})
|
||||
}
|
||||
|
||||
fetchSchema(process.env.THE_GRAPH_SCHEMA_ENDPOINT, thegraphConfig.schema)
|
||||
|
||||
@@ -16,10 +16,10 @@ try {
|
||||
}
|
||||
|
||||
// The last recorded size for these assets, as reported by `yarn build`.
|
||||
const LAST_SIZE_MAIN_KB = 381
|
||||
const LAST_SIZE_MAIN_KB = 420
|
||||
|
||||
// This is the async-loaded js, called <number>.<hash>.js, with a matching css file.
|
||||
const LAST_SIZE_ENTRY_KB = 1432
|
||||
const LAST_SIZE_ENTRY_KB = 1442
|
||||
|
||||
const SIZE_TOLERANCE_KB = 10
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
20
src/assets/images/bnbCircle.svg
Normal file
@@ -0,0 +1,20 @@
|
||||
<?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>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/images/gnosis.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 112 KiB |
15
src/assets/images/metamask.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
BIN
src/assets/images/phantom.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
src/assets/images/rainbow.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
src/assets/images/uniswapWalletBannerDark.png
Normal file
|
After Width: | Height: | Size: 558 KiB |
BIN
src/assets/images/uniswapWalletBannerLight.png
Normal file
|
After Width: | Height: | Size: 306 KiB |
88
src/assets/images/uniwallet.svg
Normal file
@@ -0,0 +1,88 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_12510_20313)">
|
||||
<rect width="24" height="24" rx="8" fill="white"/>
|
||||
<g filter="url(#filter0_f_12510_20313)">
|
||||
<circle cx="13.0526" cy="14.3159" r="14.7368" fill="url(#paint0_radial_12510_20313)"/>
|
||||
</g>
|
||||
<g filter="url(#filter1_d_12510_20313)">
|
||||
<path d="M17.3968 5.20882C17.422 4.76813 17.482 4.47745 17.6027 4.21199C17.6505 4.10692 17.6952 4.02092 17.7021 4.02092C17.709 4.02092 17.6883 4.09848 17.656 4.19325C17.5682 4.45088 17.5538 4.80325 17.6143 5.21322C17.691 5.7334 17.7346 5.80845 18.2868 6.37036C18.5459 6.63392 18.8472 6.96633 18.9564 7.10904L19.155 7.36853L18.9564 7.18308C18.7135 6.9563 18.155 6.51402 18.0316 6.4508C17.9489 6.40839 17.9366 6.40913 17.8856 6.45969C17.8385 6.50629 17.8286 6.5763 17.8221 6.90727C17.812 7.4231 17.7413 7.7542 17.5709 8.08526C17.4787 8.26432 17.4642 8.2261 17.5476 8.02399C17.6099 7.87309 17.6162 7.80675 17.6157 7.30739C17.6148 6.30405 17.4951 6.06284 16.7935 5.64963C16.6157 5.54495 16.3229 5.39399 16.1427 5.31414C15.9625 5.23428 15.8193 5.16473 15.8245 5.15954C15.8444 5.13985 16.5287 5.33869 16.8041 5.44416C17.2138 5.60105 17.2814 5.62138 17.3311 5.60246C17.3645 5.58977 17.3806 5.49307 17.3968 5.20882Z" fill="url(#paint1_linear_12510_20313)"/>
|
||||
<path d="M8.74107 3.74916C8.49804 3.7117 8.48779 3.7073 8.60216 3.68984C8.82133 3.65634 9.33884 3.70199 9.69547 3.78626C10.528 3.98291 11.2856 4.48669 12.0943 5.38141L12.3091 5.6191L12.6165 5.57C13.9112 5.36324 15.2284 5.52756 16.3301 6.0333C16.6331 6.17243 17.111 6.44939 17.1707 6.52056C17.1897 6.54324 17.2246 6.68925 17.2483 6.84505C17.3301 7.38407 17.2892 7.79723 17.1233 8.10582C17.033 8.27375 17.0279 8.32696 17.0886 8.47068C17.1371 8.58536 17.2722 8.67024 17.4059 8.67005C17.6797 8.66969 17.9744 8.2299 18.1109 7.61798L18.1651 7.37491L18.2726 7.49585C18.8619 8.15934 19.3248 9.06419 19.4043 9.70827L19.425 9.87619L19.326 9.72353C19.1555 9.46083 18.9842 9.28201 18.7648 9.13777C18.3694 8.87779 17.9514 8.78931 16.8441 8.73133C15.8441 8.67897 15.2782 8.59409 14.717 8.41224C13.7621 8.10289 13.2808 7.69089 12.1466 6.21216C11.6428 5.55535 11.3314 5.19195 11.0217 4.8993C10.3179 4.23435 9.62632 3.88561 8.74107 3.74916Z" fill="url(#paint2_linear_12510_20313)"/>
|
||||
<path d="M9.21314 6.92984C8.71977 6.25487 8.41451 5.21998 8.4806 4.44636L8.50103 4.20696L8.61333 4.22732C8.82423 4.26555 9.18786 4.40004 9.35813 4.50281C9.82539 4.78481 10.0277 5.15608 10.2335 6.10945C10.2938 6.38869 10.3729 6.7047 10.4092 6.81168C10.4678 6.98388 10.6892 7.38612 10.8692 7.64735C10.9989 7.83549 10.9127 7.92464 10.6262 7.89893C10.1885 7.85967 9.59561 7.45307 9.21314 6.92984Z" fill="url(#paint3_linear_12510_20313)"/>
|
||||
<path d="M16.7978 11.9533C14.4921 11.0308 13.6801 10.2301 13.6801 8.87901C13.6801 8.68019 13.687 8.51751 13.6954 8.51751C13.7038 8.51751 13.793 8.58313 13.8936 8.66334C14.3611 9.03598 14.8847 9.19514 16.3341 9.40526C17.187 9.52892 17.667 9.62879 18.1097 9.7747C19.517 10.2385 20.3877 11.1797 20.5953 12.4616C20.6557 12.8341 20.6203 13.5327 20.5225 13.9008C20.4452 14.1916 20.2096 14.7157 20.1471 14.7358C20.1298 14.7414 20.1128 14.6754 20.1083 14.5856C20.0846 14.1043 19.8398 13.6356 19.4287 13.2846C18.9613 12.8854 18.3333 12.5677 16.7978 11.9533Z" fill="url(#paint4_linear_12510_20313)"/>
|
||||
<path d="M15.1791 12.3366C15.1503 12.1657 15.1002 11.9476 15.0678 11.8518L15.009 11.6776L15.1182 11.7995C15.2695 11.9681 15.389 12.184 15.4902 12.4714C15.5675 12.6908 15.5762 12.7561 15.5757 13.1126C15.5751 13.4626 15.5654 13.536 15.494 13.7335C15.3815 14.0449 15.2418 14.2657 15.0074 14.5027C14.5863 14.9286 14.0448 15.1645 13.2633 15.2623C13.1275 15.2793 12.7316 15.3079 12.3836 15.3259C11.5065 15.3712 10.9292 15.4649 10.4105 15.6459C10.3359 15.6719 10.2693 15.6877 10.2626 15.681C10.2416 15.6603 10.5947 15.4511 10.8864 15.3114C11.2978 15.1145 11.7072 15.0071 12.6246 14.8553C13.0778 14.7803 13.5458 14.6893 13.6646 14.6531C14.7869 14.3111 15.3638 13.4288 15.1791 12.3366Z" fill="url(#paint5_linear_12510_20313)"/>
|
||||
<path d="M16.2361 14.2019C15.9298 13.5475 15.8594 12.9156 16.0272 12.3264C16.0452 12.2634 16.0741 12.2119 16.0914 12.2119C16.1088 12.2119 16.1811 12.2507 16.2521 12.2982C16.3932 12.3926 16.6763 12.5517 17.4306 12.9603C18.3719 13.4703 18.9085 13.8652 19.2735 14.3163C19.5931 14.7114 19.7909 15.1614 19.8861 15.7101C19.94 16.0209 19.9084 16.7688 19.8281 17.0818C19.5751 18.0686 18.9869 18.8438 18.148 19.2961C18.0251 19.3624 17.9147 19.4168 17.9028 19.4171C17.8908 19.4173 17.9356 19.3041 18.0023 19.1656C18.2845 18.5794 18.3167 18.0092 18.1033 17.3745C17.9727 16.9858 17.7063 16.5116 17.1685 15.7102C16.5432 14.7784 16.3899 14.5304 16.2361 14.2019Z" fill="url(#paint6_linear_12510_20313)"/>
|
||||
<path d="M7.57574 17.7327C8.43136 17.0148 9.49596 16.5048 10.4657 16.3482C10.8836 16.2808 11.5799 16.3075 11.9669 16.406C12.5872 16.5637 13.1421 16.9171 13.4307 17.3381C13.7128 17.7495 13.8338 18.1081 13.9597 18.9058C14.0094 19.2205 14.0635 19.5365 14.0799 19.608C14.1745 20.0215 14.3586 20.3519 14.5867 20.5179C14.9491 20.7815 15.5731 20.7979 16.1869 20.5599C16.2911 20.5195 16.3815 20.4916 16.3879 20.4979C16.4101 20.5199 16.101 20.7255 15.883 20.8337C15.5896 20.9794 15.3563 21.0357 15.0463 21.0357C14.4841 21.0357 14.0173 20.7515 13.6279 20.172C13.5512 20.058 13.379 19.7164 13.2451 19.413C12.8339 18.481 12.6308 18.1971 12.1534 17.8863C11.7379 17.616 11.202 17.5675 10.7989 17.764C10.2694 18.0219 10.1216 18.6944 10.5009 19.1205C10.6516 19.2899 10.9327 19.436 11.1626 19.4644C11.5926 19.5175 11.9621 19.1925 11.9621 18.761C11.9621 18.4809 11.8538 18.3211 11.581 18.1988C11.2085 18.0317 10.808 18.227 10.81 18.5747C10.8108 18.723 10.8758 18.8162 11.0254 18.8834C11.1213 18.9266 11.1235 18.93 11.0453 18.9138C10.7035 18.8434 10.6234 18.4339 10.8982 18.1621C11.2282 17.8357 11.9105 17.9797 12.1448 18.4252C12.2432 18.6123 12.2546 18.9849 12.1688 19.2099C11.9768 19.7136 11.4167 19.9784 10.8486 19.8343C10.4618 19.7361 10.3044 19.6299 9.83802 19.1526C9.02768 18.3231 8.7131 18.1624 7.54487 17.9811L7.32101 17.9464L7.57574 17.7327Z" fill="url(#paint7_linear_12510_20313)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.77354 2.88677C6.47968 6.15059 8.34354 7.49719 8.55067 7.78169C8.72168 8.0166 8.65732 8.2278 8.36434 8.39331C8.20142 8.48533 7.86646 8.57858 7.69875 8.57858C7.50906 8.57858 7.44394 8.50586 7.44394 8.50586C7.33395 8.40221 7.27201 8.42033 6.70721 7.42408C5.92308 6.215 5.26687 5.21202 5.24896 5.19523C5.20757 5.15639 5.20828 5.15771 6.62725 7.68067C6.85652 8.20652 6.67286 8.39954 6.67286 8.47443C6.67286 8.62679 6.63103 8.70687 6.4419 8.91651C6.1266 9.26604 5.98566 9.65877 5.88391 10.4716C5.76985 11.3827 5.44913 12.0263 4.56029 13.1279C4.04 13.7727 3.95486 13.8909 3.82358 14.1508C3.65821 14.478 3.61274 14.6613 3.59431 15.0746C3.57484 15.5116 3.61277 15.7938 3.74711 16.2116C3.86472 16.5774 3.98749 16.8189 4.30133 17.3019C4.57217 17.7188 4.72812 18.0285 4.72812 18.1497C4.72812 18.2461 4.74665 18.2463 5.16634 18.1521C6.17072 17.9267 6.98627 17.5303 7.44495 17.0446C7.72882 16.7439 7.79546 16.5778 7.79762 16.1658C7.79904 15.8963 7.7895 15.8399 7.71619 15.6848C7.59685 15.4325 7.3796 15.2227 6.90076 14.8974C6.27336 14.4712 6.00538 14.1281 5.93136 13.6562C5.87064 13.269 5.94108 12.9958 6.28817 12.273C6.64742 11.5247 6.73645 11.2059 6.79667 10.4517C6.83557 9.9644 6.88943 9.77223 7.03032 9.61798C7.17725 9.45714 7.30953 9.40267 7.67316 9.3533C8.266 9.27282 8.64349 9.1204 8.95378 8.83627C9.22296 8.58978 9.3356 8.35227 9.35288 7.99474L9.366 7.72374L9.21558 7.54946C8.67084 6.9183 3.40873 2.39062 3.37521 2.39062C3.36805 2.39062 3.5473 2.61391 3.77354 2.88677ZM5.03391 15.569C5.15708 15.3523 5.09164 15.0736 4.88562 14.9375C4.69096 14.8089 4.38857 14.8694 4.38857 15.0371C4.38857 15.0882 4.41703 15.1254 4.48117 15.1583C4.58918 15.2135 4.59702 15.2756 4.51204 15.4025C4.42598 15.531 4.43293 15.6441 4.53164 15.7209C4.69074 15.8447 4.91596 15.7766 5.03391 15.569Z" fill="url(#paint8_linear_12510_20313)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.74013 9.49546C9.46182 9.5804 9.19129 9.87351 9.10754 10.1808C9.05645 10.3683 9.08544 10.6972 9.16196 10.7988C9.28559 10.9629 9.40515 11.0061 9.72887 11.0038C10.3627 10.9994 10.9136 10.7293 10.9777 10.3916C11.0302 10.1148 10.7882 9.73123 10.4549 9.56281C10.2829 9.47595 9.91711 9.44148 9.74013 9.49546ZM10.481 10.0712C10.5788 9.93317 10.536 9.78402 10.3698 9.68314C10.0532 9.49106 9.57452 9.65001 9.57452 9.94718C9.57452 10.0951 9.82416 10.2565 10.053 10.2565C10.2053 10.2565 10.4137 10.1662 10.481 10.0712Z" fill="url(#paint9_linear_12510_20313)"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_f_12510_20313" x="-72.277" y="-71.0137" width="170.659" height="170.659" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="35.2964" result="effect1_foregroundBlur_12510_20313"/>
|
||||
</filter>
|
||||
<filter id="filter1_d_12510_20313" x="-20.2828" y="-21.2671" width="64.5655" height="65.9605" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="11.8289"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 0 0 0 0 0 0.605405 0 0 0 0.15 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_12510_20313"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_12510_20313" result="shape"/>
|
||||
</filter>
|
||||
<radialGradient id="paint0_radial_12510_20313" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(13.0719 14.2967) rotate(42.4693) scale(16.2421)">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="#FFD8EF" stop-opacity="0.4"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="paint1_linear_12510_20313" x1="19.7578" y1="18.1172" x2="4.89844" y2="3.67969" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFCDF1"/>
|
||||
<stop offset="0.380969" stop-color="#F436DA"/>
|
||||
<stop offset="1" stop-color="#F356EF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_12510_20313" x1="19.7578" y1="18.1172" x2="4.89844" y2="3.67969" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFCDF1"/>
|
||||
<stop offset="0.380969" stop-color="#F436DA"/>
|
||||
<stop offset="1" stop-color="#F356EF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_12510_20313" x1="19.7578" y1="18.1172" x2="4.89844" y2="3.67969" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFCDF1"/>
|
||||
<stop offset="0.380969" stop-color="#F436DA"/>
|
||||
<stop offset="1" stop-color="#F356EF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint4_linear_12510_20313" x1="19.7578" y1="18.1172" x2="4.89844" y2="3.67969" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFCDF1"/>
|
||||
<stop offset="0.380969" stop-color="#F436DA"/>
|
||||
<stop offset="1" stop-color="#F356EF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint5_linear_12510_20313" x1="19.7578" y1="18.1172" x2="4.89844" y2="3.67969" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFCDF1"/>
|
||||
<stop offset="0.380969" stop-color="#F436DA"/>
|
||||
<stop offset="1" stop-color="#F356EF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint6_linear_12510_20313" x1="19.7578" y1="18.1172" x2="4.89844" y2="3.67969" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFCDF1"/>
|
||||
<stop offset="0.380969" stop-color="#F436DA"/>
|
||||
<stop offset="1" stop-color="#F356EF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint7_linear_12510_20313" x1="19.7578" y1="18.1172" x2="4.89844" y2="3.67969" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFCDF1"/>
|
||||
<stop offset="0.380969" stop-color="#F436DA"/>
|
||||
<stop offset="1" stop-color="#F356EF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint8_linear_12510_20313" x1="19.7578" y1="18.1172" x2="4.89844" y2="3.67969" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFCDF1"/>
|
||||
<stop offset="0.380969" stop-color="#F436DA"/>
|
||||
<stop offset="1" stop-color="#F356EF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint9_linear_12510_20313" x1="19.7578" y1="18.1172" x2="4.89844" y2="3.67969" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFCDF1"/>
|
||||
<stop offset="0.380969" stop-color="#F436DA"/>
|
||||
<stop offset="1" stop-color="#F356EF"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_12510_20313">
|
||||
<rect width="24" height="24" rx="8" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 1.3 KiB |
77
src/assets/svg/apple_logo.svg
Normal file
@@ -0,0 +1,77 @@
|
||||
<?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>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -1,181 +1,9 @@
|
||||
<?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>
|
||||
|
||||
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 2.3 KiB |
3
src/assets/svg/blank_token.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 216 B |
21
src/assets/svg/bnb_square_logo.svg
Normal file
@@ -0,0 +1,21 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 192 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 192 KiB After Width: | Height: | Size: 5.7 KiB |
5
src/assets/svg/celo_square_logo.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 398 B |
13
src/assets/svg/contract-interaction.svg
Normal file
@@ -0,0 +1,13 @@
|
||||
<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> -->
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
16
src/assets/svg/ethereum_square_logo.svg
Normal file
@@ -0,0 +1,16 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 5.4 KiB |
15
src/assets/svg/optimism_square_logo.svg
Normal file
|
After Width: | Height: | Size: 32 KiB |
10
src/assets/svg/polygon_square_logo.svg
Normal file
|
After Width: | Height: | Size: 26 KiB |
@@ -1,8 +1,8 @@
|
||||
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'
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
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',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
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'
|
||||
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
@@ -1,326 +0,0 @@
|
||||
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>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
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
|
||||
@@ -1,336 +0,0 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { Fraction, TradeType } from '@uniswap/sdk-core'
|
||||
import JSBI from 'jsbi'
|
||||
import {
|
||||
AddLiquidityV3PoolTransactionInfo,
|
||||
ApproveTransactionInfo,
|
||||
ClaimTransactionInfo,
|
||||
CollectFeesTransactionInfo,
|
||||
ExactInputSwapTransactionInfo,
|
||||
ExactOutputSwapTransactionInfo,
|
||||
RemoveLiquidityV3TransactionInfo,
|
||||
TransactionInfo,
|
||||
TransactionType,
|
||||
WrapTransactionInfo,
|
||||
} from 'state/transactions/types'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import { nativeOnChain } from '../../constants/tokens'
|
||||
import { useCurrency, useToken } from '../../hooks/Tokens'
|
||||
import useENSName from '../../hooks/useENSName'
|
||||
import { shortenAddress } from '../../utils'
|
||||
import { TransactionState } from './index'
|
||||
|
||||
const HighlightText = styled.span`
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
font-weight: 600;
|
||||
`
|
||||
|
||||
const BodyWrap = styled.div`
|
||||
line-height: 20px;
|
||||
`
|
||||
|
||||
interface ActionProps {
|
||||
pending: JSX.Element
|
||||
success: JSX.Element
|
||||
failed: JSX.Element
|
||||
transactionState: TransactionState
|
||||
}
|
||||
|
||||
const Action = ({ pending, success, failed, transactionState }: ActionProps) => {
|
||||
switch (transactionState) {
|
||||
case TransactionState.Failed:
|
||||
return failed
|
||||
case TransactionState.Success:
|
||||
return success
|
||||
default:
|
||||
return pending
|
||||
}
|
||||
}
|
||||
|
||||
const formatAmount = (amountRaw: string, decimals: number, sigFigs: number): string =>
|
||||
new Fraction(amountRaw, JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(decimals))).toSignificant(sigFigs)
|
||||
|
||||
const FailedText = ({ transactionState }: { transactionState: TransactionState }) =>
|
||||
transactionState === TransactionState.Failed ? <Trans>failed</Trans> : <span />
|
||||
|
||||
const FormattedCurrencyAmount = ({
|
||||
rawAmount,
|
||||
currencyId,
|
||||
}: {
|
||||
rawAmount: string
|
||||
currencyId: string
|
||||
sigFigs: number
|
||||
}) => {
|
||||
const currency = useCurrency(currencyId)
|
||||
|
||||
return currency ? (
|
||||
<HighlightText>
|
||||
{formatAmount(rawAmount, currency.decimals, /* sigFigs= */ 6)} {currency.symbol}
|
||||
</HighlightText>
|
||||
) : null
|
||||
}
|
||||
|
||||
const getRawAmounts = (
|
||||
info: ExactInputSwapTransactionInfo | ExactOutputSwapTransactionInfo
|
||||
): { rawAmountFrom: string; rawAmountTo: string } => {
|
||||
return info.tradeType === TradeType.EXACT_INPUT
|
||||
? { rawAmountFrom: info.inputCurrencyAmountRaw, rawAmountTo: info.expectedOutputCurrencyAmountRaw }
|
||||
: { rawAmountFrom: info.expectedInputCurrencyAmountRaw, rawAmountTo: info.outputCurrencyAmountRaw }
|
||||
}
|
||||
|
||||
const SwapSummary = ({
|
||||
info,
|
||||
transactionState,
|
||||
}: {
|
||||
info: ExactInputSwapTransactionInfo | ExactOutputSwapTransactionInfo
|
||||
transactionState: TransactionState
|
||||
}) => {
|
||||
const actionProps = {
|
||||
transactionState,
|
||||
pending: <Trans>Swapping</Trans>,
|
||||
success: <Trans>Swapped</Trans>,
|
||||
failed: <Trans>Swap</Trans>,
|
||||
}
|
||||
const { rawAmountFrom, rawAmountTo } = getRawAmounts(info)
|
||||
|
||||
return (
|
||||
<BodyWrap>
|
||||
<Action {...actionProps} />{' '}
|
||||
<FormattedCurrencyAmount rawAmount={rawAmountFrom} currencyId={info.inputCurrencyId} sigFigs={2} />{' '}
|
||||
<Trans>for </Trans>{' '}
|
||||
<FormattedCurrencyAmount rawAmount={rawAmountTo} currencyId={info.outputCurrencyId} sigFigs={2} />{' '}
|
||||
<FailedText transactionState={transactionState} />
|
||||
</BodyWrap>
|
||||
)
|
||||
}
|
||||
|
||||
const AddLiquidityV3PoolSummary = ({
|
||||
info,
|
||||
transactionState,
|
||||
}: {
|
||||
info: AddLiquidityV3PoolTransactionInfo
|
||||
transactionState: TransactionState
|
||||
}) => {
|
||||
const { createPool, quoteCurrencyId, baseCurrencyId } = info
|
||||
|
||||
const actionProps = {
|
||||
transactionState,
|
||||
pending: <Trans>Adding</Trans>,
|
||||
success: <Trans>Added</Trans>,
|
||||
failed: <Trans>Add</Trans>,
|
||||
}
|
||||
|
||||
return (
|
||||
<BodyWrap>
|
||||
{createPool ? (
|
||||
<CreateV3PoolSummary info={info} transactionState={transactionState} />
|
||||
) : (
|
||||
<>
|
||||
<Action {...actionProps} />{' '}
|
||||
<FormattedCurrencyAmount rawAmount={info.expectedAmountBaseRaw} currencyId={baseCurrencyId} sigFigs={2} />{' '}
|
||||
<Trans>and</Trans>{' '}
|
||||
<FormattedCurrencyAmount rawAmount={info.expectedAmountQuoteRaw} currencyId={quoteCurrencyId} sigFigs={2} />
|
||||
</>
|
||||
)}{' '}
|
||||
<FailedText transactionState={transactionState} />
|
||||
</BodyWrap>
|
||||
)
|
||||
}
|
||||
|
||||
const RemoveLiquidityV3Summary = ({
|
||||
info: { baseCurrencyId, quoteCurrencyId, expectedAmountBaseRaw, expectedAmountQuoteRaw },
|
||||
transactionState,
|
||||
}: {
|
||||
info: RemoveLiquidityV3TransactionInfo
|
||||
transactionState: TransactionState
|
||||
}) => {
|
||||
const actionProps = {
|
||||
transactionState,
|
||||
pending: <Trans>Removing</Trans>,
|
||||
success: <Trans>Removed</Trans>,
|
||||
failed: <Trans>Remove</Trans>,
|
||||
}
|
||||
|
||||
return (
|
||||
<BodyWrap>
|
||||
<Action {...actionProps} />{' '}
|
||||
<FormattedCurrencyAmount rawAmount={expectedAmountBaseRaw} currencyId={baseCurrencyId} sigFigs={2} />{' '}
|
||||
<Trans>and</Trans>{' '}
|
||||
<FormattedCurrencyAmount rawAmount={expectedAmountQuoteRaw} currencyId={quoteCurrencyId} sigFigs={2} />{' '}
|
||||
<FailedText transactionState={transactionState} />
|
||||
</BodyWrap>
|
||||
)
|
||||
}
|
||||
|
||||
const CreateV3PoolSummary = ({
|
||||
info: { baseCurrencyId, quoteCurrencyId },
|
||||
transactionState,
|
||||
}: {
|
||||
info: AddLiquidityV3PoolTransactionInfo
|
||||
transactionState: TransactionState
|
||||
}) => {
|
||||
const baseCurrency = useCurrency(baseCurrencyId)
|
||||
const quoteCurrency = useCurrency(quoteCurrencyId)
|
||||
const actionProps = {
|
||||
transactionState,
|
||||
pending: <Trans>Creating</Trans>,
|
||||
success: <Trans>Created</Trans>,
|
||||
failed: <Trans>Create</Trans>,
|
||||
}
|
||||
|
||||
return (
|
||||
<BodyWrap>
|
||||
<Action {...actionProps} />{' '}
|
||||
<HighlightText>
|
||||
{baseCurrency?.symbol}/{quoteCurrency?.symbol}{' '}
|
||||
</HighlightText>
|
||||
<Trans>Pool</Trans> <FailedText transactionState={transactionState} />
|
||||
</BodyWrap>
|
||||
)
|
||||
}
|
||||
|
||||
const CollectFeesSummary = ({
|
||||
info,
|
||||
transactionState,
|
||||
}: {
|
||||
info: CollectFeesTransactionInfo
|
||||
transactionState: TransactionState
|
||||
}) => {
|
||||
const { currencyId0, expectedCurrencyOwed0 = '0', expectedCurrencyOwed1 = '0', currencyId1 } = info
|
||||
const actionProps = {
|
||||
transactionState,
|
||||
pending: <Trans>Collecting</Trans>,
|
||||
success: <Trans>Collected</Trans>,
|
||||
failed: <Trans>Collect</Trans>,
|
||||
}
|
||||
|
||||
return (
|
||||
<BodyWrap>
|
||||
<Action {...actionProps} />{' '}
|
||||
<FormattedCurrencyAmount rawAmount={expectedCurrencyOwed0} currencyId={currencyId0} sigFigs={2} />{' '}
|
||||
<Trans>and</Trans>{' '}
|
||||
<FormattedCurrencyAmount rawAmount={expectedCurrencyOwed1} currencyId={currencyId1} sigFigs={2} />{' '}
|
||||
<Trans>fees</Trans> <FailedText transactionState={transactionState} />
|
||||
</BodyWrap>
|
||||
)
|
||||
}
|
||||
|
||||
const ApprovalSummary = ({
|
||||
info,
|
||||
transactionState,
|
||||
}: {
|
||||
info: ApproveTransactionInfo
|
||||
transactionState: TransactionState
|
||||
}) => {
|
||||
const token = useToken(info.tokenAddress)
|
||||
const actionProps = {
|
||||
transactionState,
|
||||
pending: <Trans>Approving</Trans>,
|
||||
success: <Trans>Approved</Trans>,
|
||||
failed: <Trans>Approve</Trans>,
|
||||
}
|
||||
|
||||
return (
|
||||
<BodyWrap>
|
||||
<Action {...actionProps} /> <HighlightText>{token?.symbol}</HighlightText>{' '}
|
||||
<FailedText transactionState={transactionState} />
|
||||
</BodyWrap>
|
||||
)
|
||||
}
|
||||
|
||||
const ClaimSummary = ({
|
||||
info: { recipient, uniAmountRaw },
|
||||
transactionState,
|
||||
}: {
|
||||
info: ClaimTransactionInfo
|
||||
transactionState: TransactionState
|
||||
}) => {
|
||||
const { ENSName } = useENSName()
|
||||
const actionProps = {
|
||||
transactionState,
|
||||
pending: <Trans>Claiming</Trans>,
|
||||
success: <Trans>Claimed</Trans>,
|
||||
failed: <Trans>Claim</Trans>,
|
||||
}
|
||||
|
||||
return (
|
||||
<BodyWrap>
|
||||
{uniAmountRaw && (
|
||||
<>
|
||||
<Action {...actionProps} />{' '}
|
||||
<HighlightText>
|
||||
{formatAmount(uniAmountRaw, 18, 4)}
|
||||
UNI{' '}
|
||||
</HighlightText>{' '}
|
||||
<Trans>for</Trans> <HighlightText>{ENSName ?? shortenAddress(recipient)}</HighlightText>
|
||||
</>
|
||||
)}{' '}
|
||||
<FailedText transactionState={transactionState} />
|
||||
</BodyWrap>
|
||||
)
|
||||
}
|
||||
|
||||
const WrapSummary = ({
|
||||
info: { chainId, currencyAmountRaw, unwrapped },
|
||||
transactionState,
|
||||
}: {
|
||||
info: WrapTransactionInfo
|
||||
transactionState: TransactionState
|
||||
}) => {
|
||||
const native = chainId ? nativeOnChain(chainId) : undefined
|
||||
const from = unwrapped ? native?.wrapped.symbol ?? 'WETH' : native?.symbol ?? 'ETH'
|
||||
const to = unwrapped ? native?.symbol ?? 'ETH' : native?.wrapped.symbol ?? 'WETH'
|
||||
|
||||
const amount = formatAmount(currencyAmountRaw, 18, 6)
|
||||
const actionProps = unwrapped
|
||||
? {
|
||||
transactionState,
|
||||
pending: <Trans>Unwrapping</Trans>,
|
||||
success: <Trans>Unwrapped</Trans>,
|
||||
failed: <Trans>Unwrap</Trans>,
|
||||
}
|
||||
: {
|
||||
transactionState,
|
||||
pending: <Trans>Wrapping</Trans>,
|
||||
success: <Trans>Wrapped</Trans>,
|
||||
failed: <Trans>Wrap</Trans>,
|
||||
}
|
||||
|
||||
return (
|
||||
<BodyWrap>
|
||||
<Action {...actionProps} />{' '}
|
||||
<HighlightText>
|
||||
{amount} {from}
|
||||
</HighlightText>{' '}
|
||||
<Trans>to</Trans>{' '}
|
||||
<HighlightText>
|
||||
{amount} {to}
|
||||
</HighlightText>{' '}
|
||||
<FailedText transactionState={transactionState} />
|
||||
</BodyWrap>
|
||||
)
|
||||
}
|
||||
|
||||
const TransactionBody = ({ info, transactionState }: { info: TransactionInfo; transactionState: TransactionState }) => {
|
||||
switch (info.type) {
|
||||
case TransactionType.SWAP:
|
||||
return <SwapSummary info={info} transactionState={transactionState} />
|
||||
case TransactionType.ADD_LIQUIDITY_V3_POOL:
|
||||
return <AddLiquidityV3PoolSummary info={info} transactionState={transactionState} />
|
||||
case TransactionType.REMOVE_LIQUIDITY_V3:
|
||||
return <RemoveLiquidityV3Summary info={info} transactionState={transactionState} />
|
||||
case TransactionType.WRAP:
|
||||
return <WrapSummary info={info} transactionState={transactionState} />
|
||||
case TransactionType.COLLECT_FEES:
|
||||
return <CollectFeesSummary info={info} transactionState={transactionState} />
|
||||
case TransactionType.APPROVAL:
|
||||
return <ApprovalSummary info={info} transactionState={transactionState} />
|
||||
case TransactionType.CLAIM:
|
||||
return <ClaimSummary info={info} transactionState={transactionState} />
|
||||
default:
|
||||
return <span />
|
||||
}
|
||||
}
|
||||
|
||||
export default TransactionBody
|
||||
@@ -1,98 +0,0 @@
|
||||
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'
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import Badge, { BadgeVariant } from 'components/Badge'
|
||||
import { AlertCircle } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
import { AlertTriangle, Slash } from 'react-feather'
|
||||
import styled, { useTheme } from 'styled-components/macro'
|
||||
|
||||
import { MouseoverTooltip } from '../../components/Tooltip'
|
||||
|
||||
@@ -13,7 +12,9 @@ const BadgeWrapper = styled.div`
|
||||
|
||||
const BadgeText = styled.div`
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
line-height: 14px;
|
||||
margin-right: 8px;
|
||||
`
|
||||
|
||||
const ActiveDot = styled.span`
|
||||
@@ -21,7 +22,14 @@ const ActiveDot = styled.span`
|
||||
border-radius: 50%;
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
margin-right: 4px;
|
||||
`
|
||||
|
||||
const LabelText = styled.div<{ color: string }>`
|
||||
align-items: center;
|
||||
color: ${({ color }) => color};
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
`
|
||||
|
||||
export default function RangeBadge({
|
||||
@@ -31,17 +39,17 @@ export default function RangeBadge({
|
||||
removed: boolean | undefined
|
||||
inRange: boolean | undefined
|
||||
}) {
|
||||
const theme = useTheme()
|
||||
return (
|
||||
<BadgeWrapper>
|
||||
{removed ? (
|
||||
<MouseoverTooltip text={<Trans>Your position has 0 liquidity, and is not earning fees.</Trans>}>
|
||||
<Badge variant={BadgeVariant.DEFAULT}>
|
||||
<AlertCircle width={14} height={14} />
|
||||
|
||||
<LabelText color={theme.textSecondary}>
|
||||
<BadgeText>
|
||||
<Trans>Closed</Trans>
|
||||
</BadgeText>
|
||||
</Badge>
|
||||
<Slash width={12} height={12} />
|
||||
</LabelText>
|
||||
</MouseoverTooltip>
|
||||
) : inRange ? (
|
||||
<MouseoverTooltip
|
||||
@@ -51,12 +59,12 @@ export default function RangeBadge({
|
||||
</Trans>
|
||||
}
|
||||
>
|
||||
<Badge variant={BadgeVariant.DEFAULT}>
|
||||
<ActiveDot />
|
||||
<LabelText color={theme.accentSuccess}>
|
||||
<BadgeText>
|
||||
<Trans>In range</Trans>
|
||||
<Trans>In Range</Trans>
|
||||
</BadgeText>
|
||||
</Badge>
|
||||
<ActiveDot />
|
||||
</LabelText>
|
||||
</MouseoverTooltip>
|
||||
) : (
|
||||
<MouseoverTooltip
|
||||
@@ -66,13 +74,12 @@ export default function RangeBadge({
|
||||
</Trans>
|
||||
}
|
||||
>
|
||||
<Badge variant={BadgeVariant.WARNING}>
|
||||
<AlertCircle width={14} height={14} />
|
||||
|
||||
<LabelText color={theme.accentWarning}>
|
||||
<BadgeText>
|
||||
<Trans>Out of range</Trans>
|
||||
</BadgeText>
|
||||
</Badge>
|
||||
<AlertTriangle width={12} height={12} />
|
||||
</LabelText>
|
||||
</MouseoverTooltip>
|
||||
)}
|
||||
</BadgeWrapper>
|
||||
|
||||
@@ -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;
|
||||
`
|
||||
|
||||
111
src/components/Banner/UniswapWalletBanner.tsx
Normal file
@@ -0,0 +1,111 @@
|
||||
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>
|
||||
)
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
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 +167,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 +183,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 +205,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()}
|
||||
@@ -223,12 +241,6 @@ export default function FeatureFlagModal() {
|
||||
featureFlag={FeatureFlag.nftGraphql}
|
||||
label="Migrate NFT read endpoints to GQL"
|
||||
/>
|
||||
<FeatureFlagOption
|
||||
variant={TaxServiceVariant}
|
||||
value={useTaxServiceBannerFlag()}
|
||||
featureFlag={FeatureFlag.taxService}
|
||||
label="Tax Service Banner"
|
||||
/>
|
||||
<FeatureFlagGroup name="Debug">
|
||||
<FeatureFlagOption
|
||||
variant={TraceJsonRpcVariant}
|
||||
|
||||
@@ -11,13 +11,14 @@ export const FEE_AMOUNT_DETAIL: Record<
|
||||
label: '0.01',
|
||||
description: <Trans>Best for very stable pairs.</Trans>,
|
||||
supportedChains: [
|
||||
SupportedChainId.MAINNET,
|
||||
SupportedChainId.POLYGON,
|
||||
SupportedChainId.POLYGON_MUMBAI,
|
||||
SupportedChainId.ARBITRUM_ONE,
|
||||
SupportedChainId.BNB,
|
||||
SupportedChainId.CELO,
|
||||
SupportedChainId.CELO_ALFAJORES,
|
||||
SupportedChainId.MAINNET,
|
||||
SupportedChainId.OPTIMISM,
|
||||
SupportedChainId.BNB,
|
||||
SupportedChainId.POLYGON,
|
||||
SupportedChainId.POLYGON_MUMBAI,
|
||||
],
|
||||
},
|
||||
[FeeAmount.LOW]: {
|
||||
|
||||
@@ -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'
|
||||
|
||||
12
src/components/Icons/AlertTriangleFilled.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
60
src/components/Icons/LoadingSpinner.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
28
src/components/Icons/shared.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
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}
|
||||
`
|
||||
@@ -1,17 +1,10 @@
|
||||
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 { 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,100 +25,75 @@ 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 Divider = styled.div`
|
||||
border-bottom: 1px solid ${({ theme }) => theme.backgroundOutline};
|
||||
margin: 12px 0;
|
||||
`
|
||||
|
||||
function UniconTooltip({ children, enabled }: PropsWithChildren<{ enabled?: boolean }>) {
|
||||
const MiniWalletIcon = ({ connection, side }: { connection: Connection; side: 'left' | 'right' }) => {
|
||||
return (
|
||||
<MouseoverTooltip
|
||||
timeout={ms`3s`}
|
||||
offsetY={8}
|
||||
disableHover={!enabled}
|
||||
text={
|
||||
// TODO(cartcrom): add Learn More link when unicon microsite is polished
|
||||
<>
|
||||
<ThemedText.SubHeaderSmall color="textPrimary" paddingTop="4px">
|
||||
This is your Unicon
|
||||
</ThemedText.SubHeaderSmall>
|
||||
<Divider />
|
||||
<ThemedText.Caption paddingBottom="4px">
|
||||
Unicons are avatars for your wallet, generated from your address.
|
||||
</ThemedText.Caption>
|
||||
</>
|
||||
}
|
||||
placement="bottom"
|
||||
>
|
||||
<div>{children}</div>
|
||||
</MouseoverTooltip>
|
||||
<MiniIconContainer side={side}>
|
||||
<MiniImg src={connection.icon} alt={`${connection.name} icon`} />
|
||||
</MiniIconContainer>
|
||||
)
|
||||
}
|
||||
|
||||
const useIcon = (connectionType: ConnectionType, size?: number, enableInfotips?: boolean) => {
|
||||
const { account, provider } = useWeb3React()
|
||||
const MainWalletIcon = ({ connection, size }: { connection: Connection; size: number }) => {
|
||||
const { account } = useWeb3React()
|
||||
const { avatar } = useENSAvatar(account ?? undefined)
|
||||
const isUniswapWallet = Boolean(provider && getWalletMeta(provider)?.name === 'Uniswap Wallet')
|
||||
|
||||
if (!account) return null
|
||||
|
||||
if (avatar || connectionType === ConnectionType.INJECTED) {
|
||||
return <Identicon />
|
||||
} else if (connectionType === ConnectionType.WALLET_CONNECT) {
|
||||
return isUniswapWallet ? (
|
||||
<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" />
|
||||
if (!account) {
|
||||
return null
|
||||
} else if (avatar || (connection.type === ConnectionType.INJECTED && connection.name === 'MetaMask')) {
|
||||
return <Identicon size={size} />
|
||||
} else {
|
||||
return <Unicon address={account} size={size} />
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
export default function StatusIcon({
|
||||
connectionType,
|
||||
size,
|
||||
enableInfotips,
|
||||
connection,
|
||||
size = 16,
|
||||
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} />
|
||||
{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'
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
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%;
|
||||
box-shadow: 0 0 1px white;
|
||||
|
||||
${({ 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}
|
||||
|
||||
35
src/components/NavBar/Blur.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
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}>
|
||||
<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>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<img src={info.logoUrl} alt={info.label} className={styles.Image} />
|
||||
<Box as="span" className={subhead} display={{ sm: 'none', xxl: 'flex' }} style={{ lineHeight: '20px' }}>
|
||||
{info.label}
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
{isOpen ? <ChevronUp {...chevronProps} /> : <ChevronDown {...chevronProps} />}
|
||||
</Row>
|
||||
<MouseoverTooltip text={t`Your wallet's current network is unsupported.`} disableHover={isSupported}>
|
||||
<Row
|
||||
as="button"
|
||||
gap="8"
|
||||
className={styles.ChainSelector}
|
||||
background={isOpen ? 'accentActiveSoft' : 'none'}
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
>
|
||||
{!isSupported ? (
|
||||
<AlertTriangle size={20} color={theme.textSecondary} />
|
||||
) : (
|
||||
<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>
|
||||
)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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} />}
|
||||
|
||||
@@ -1,22 +1,19 @@
|
||||
import { t, Trans } from '@lingui/macro'
|
||||
import { ReactComponent as AppleLogo } from 'assets/svg/apple_logo.svg'
|
||||
import FeatureFlagModal from 'components/FeatureFlagModal/FeatureFlagModal'
|
||||
import { PrivacyPolicyModal } from 'components/PrivacyPolicy'
|
||||
import { APP_STORE_LINK } from 'components/WalletDropdown/DownloadButton'
|
||||
import NewBadge from 'components/WalletModal/NewBadge'
|
||||
import { useMgtmEnabled, useMGTMMicrositeEnabled } from 'featureFlags/flags/mgtm'
|
||||
import { useOnClickOutside } from 'hooks/useOnClickOutside'
|
||||
import { Box } from 'nft/components/Box'
|
||||
import { Column, Row } from 'nft/components/Flex'
|
||||
import {
|
||||
BarChartIcon,
|
||||
DiscordIconMenu,
|
||||
EllipsisIcon,
|
||||
GithubIconMenu,
|
||||
GovernanceIcon,
|
||||
TwitterIconMenu,
|
||||
} from 'nft/components/icons'
|
||||
import { BarChartIcon, EllipsisIcon, GovernanceIcon, PoolIcon } from 'nft/components/icons'
|
||||
import { body, bodySmall } from 'nft/css/common.css'
|
||||
import { themeVars } from 'nft/css/sprinkles.css'
|
||||
import { ReactNode, useReducer, useRef } from 'react'
|
||||
import { HelpCircle, Shield, Terminal } from 'react-feather'
|
||||
import { NavLink, NavLinkProps } from 'react-router-dom'
|
||||
import styled from 'styled-components/macro'
|
||||
import styled, { useTheme } from 'styled-components/macro'
|
||||
import { isDevelopmentEnv, isStagingEnv } from 'utils/env'
|
||||
|
||||
import { useToggleModal } from '../../state/application/hooks'
|
||||
@@ -30,20 +27,30 @@ const PrimaryMenuRow = ({
|
||||
href,
|
||||
close,
|
||||
children,
|
||||
onClick,
|
||||
}: {
|
||||
to?: NavLinkProps['to']
|
||||
href?: string
|
||||
close?: () => void
|
||||
children: ReactNode
|
||||
onClick?: () => void
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
{to ? (
|
||||
<NavLink to={to} className={styles.MenuRow}>
|
||||
<NavLink to={to} className={styles.MenuRow} onClick={onClick}>
|
||||
<Row onClick={close}>{children}</Row>
|
||||
</NavLink>
|
||||
) : (
|
||||
<Row as="a" href={href} target="_blank" rel="noopener noreferrer" className={styles.MenuRow}>
|
||||
<Row
|
||||
as="a"
|
||||
href={href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={styles.MenuRow}
|
||||
onClick={onClick}
|
||||
cursor="pointer"
|
||||
>
|
||||
{children}
|
||||
</Row>
|
||||
)}
|
||||
@@ -90,10 +97,6 @@ const Separator = () => {
|
||||
return <Box className={styles.Separator} />
|
||||
}
|
||||
|
||||
const IconRow = ({ children }: { children: ReactNode }) => {
|
||||
return <Row className={styles.IconRow}>{children}</Row>
|
||||
}
|
||||
|
||||
const Icon = ({ href, children }: { href?: string; children: ReactNode }) => {
|
||||
return (
|
||||
<>
|
||||
@@ -104,7 +107,7 @@ const Icon = ({ href, children }: { href?: string; children: ReactNode }) => {
|
||||
rel={href ? 'noopener noreferrer' : undefined}
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
color="textPrimary"
|
||||
color="textSecondary"
|
||||
background="none"
|
||||
border="none"
|
||||
justifyContent="center"
|
||||
@@ -117,30 +120,68 @@ const Icon = ({ href, children }: { href?: string; children: ReactNode }) => {
|
||||
)
|
||||
}
|
||||
|
||||
const StyledAppleLogo = styled(AppleLogo)`
|
||||
fill: ${({ theme }) => theme.textSecondary};
|
||||
padding: 2px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
`
|
||||
|
||||
const BadgeWrapper = styled.div`
|
||||
margin-left: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
`
|
||||
|
||||
export const MenuDropdown = () => {
|
||||
const [isOpen, toggleOpen] = useReducer((s) => !s, false)
|
||||
const togglePrivacyPolicy = useToggleModal(ApplicationModal.PRIVACY_POLICY)
|
||||
const openFeatureFlagsModal = useToggleModal(ApplicationModal.FEATURE_FLAGS)
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
useOnClickOutside(ref, isOpen ? toggleOpen : undefined)
|
||||
const theme = useTheme()
|
||||
|
||||
const mgtmEnabled = useMgtmEnabled()
|
||||
const micrositeEnabled = useMGTMMicrositeEnabled()
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box position="relative" ref={ref}>
|
||||
<NavIcon isActive={isOpen} onClick={toggleOpen} label={isOpen ? t`Show resources` : t`Hide resources`}>
|
||||
<EllipsisIcon viewBox="0 0 20 20" width={24} height={24} />
|
||||
<NavIcon
|
||||
isActive={isOpen}
|
||||
onClick={toggleOpen}
|
||||
label={isOpen ? t`Show resources` : t`Hide resources`}
|
||||
activeBackground={isOpen}
|
||||
>
|
||||
<EllipsisIcon
|
||||
viewBox="0 0 20 20"
|
||||
width={24}
|
||||
height={24}
|
||||
color={isOpen ? theme.accentActive : theme.textSecondary}
|
||||
/>
|
||||
</NavIcon>
|
||||
|
||||
{isOpen && (
|
||||
<NavDropdown top={{ sm: 'unset', lg: '56' }} bottom={{ sm: '56', lg: 'unset' }} right="0">
|
||||
<Column gap="16">
|
||||
<Column paddingX="8" gap="4">
|
||||
<Box display={{ sm: 'none', lg: 'flex', xxl: 'none' }}>
|
||||
<PrimaryMenuRow to="/pool" close={toggleOpen}>
|
||||
<Icon>
|
||||
<PoolIcon width={24} height={24} color={theme.textSecondary} />
|
||||
</Icon>
|
||||
<PrimaryMenuRow.Text>
|
||||
<Trans>Pool</Trans>
|
||||
</PrimaryMenuRow.Text>
|
||||
</PrimaryMenuRow>
|
||||
</Box>
|
||||
<PrimaryMenuRow to="/vote" close={toggleOpen}>
|
||||
<Icon>
|
||||
<GovernanceIcon width={24} height={24} />
|
||||
</Icon>
|
||||
<PrimaryMenuRow.Text>
|
||||
<Trans>Vote in governance</Trans>
|
||||
<Trans>Governance</Trans>
|
||||
</PrimaryMenuRow.Text>
|
||||
</PrimaryMenuRow>
|
||||
<PrimaryMenuRow href="https://info.uniswap.org/#/">
|
||||
@@ -148,67 +189,66 @@ export const MenuDropdown = () => {
|
||||
<BarChartIcon width={24} height={24} />
|
||||
</Icon>
|
||||
<PrimaryMenuRow.Text>
|
||||
<Trans>View more analytics</Trans>
|
||||
<Trans>Token analytics</Trans>
|
||||
</PrimaryMenuRow.Text>
|
||||
</PrimaryMenuRow>
|
||||
</Column>
|
||||
<Separator />
|
||||
<Box
|
||||
display="flex"
|
||||
flexDirection={{ sm: 'row', md: 'column' }}
|
||||
flexWrap="wrap"
|
||||
alignItems={{ sm: 'center', md: 'flex-start' }}
|
||||
paddingX="8"
|
||||
>
|
||||
<SecondaryLinkedText href="https://help.uniswap.org/en/">
|
||||
<Trans>Help center</Trans> ↗
|
||||
</SecondaryLinkedText>
|
||||
<SecondaryLinkedText href="https://docs.uniswap.org/">
|
||||
<Trans>Documentation</Trans> ↗
|
||||
</SecondaryLinkedText>
|
||||
<SecondaryLinkedText href="https://uniswap.canny.io/feature-requests">
|
||||
<Trans>Feedback</Trans> ↗
|
||||
</SecondaryLinkedText>
|
||||
<SecondaryLinkedText
|
||||
<PrimaryMenuRow href="https://help.uniswap.org/en/">
|
||||
<Icon>
|
||||
<HelpCircle color={theme.textSecondary} />
|
||||
</Icon>
|
||||
<PrimaryMenuRow.Text>
|
||||
<Trans>Help center</Trans>
|
||||
</PrimaryMenuRow.Text>
|
||||
</PrimaryMenuRow>
|
||||
<PrimaryMenuRow href="https://docs.uniswap.org/">
|
||||
<Icon>
|
||||
<Terminal color={theme.textSecondary} />
|
||||
</Icon>
|
||||
<PrimaryMenuRow.Text>
|
||||
<Trans>Documentation</Trans>
|
||||
</PrimaryMenuRow.Text>
|
||||
</PrimaryMenuRow>
|
||||
<PrimaryMenuRow
|
||||
onClick={() => {
|
||||
toggleOpen()
|
||||
togglePrivacyPolicy()
|
||||
}}
|
||||
>
|
||||
<Trans>Legal & Privacy</Trans> ↗
|
||||
</SecondaryLinkedText>
|
||||
{(isDevelopmentEnv() || isStagingEnv()) && (
|
||||
<SecondaryLinkedText onClick={openFeatureFlagsModal}>
|
||||
<Trans>Feature Flags</Trans>
|
||||
</SecondaryLinkedText>
|
||||
<Icon>
|
||||
<Shield color={theme.textSecondary} />
|
||||
</Icon>
|
||||
<PrimaryMenuRow.Text>
|
||||
<Trans>Legal & Privacy</Trans>
|
||||
</PrimaryMenuRow.Text>
|
||||
</PrimaryMenuRow>
|
||||
{mgtmEnabled && (
|
||||
<Box display={micrositeEnabled ? { xxl: 'flex', xxxl: 'none' } : 'flex'}>
|
||||
<PrimaryMenuRow
|
||||
to={micrositeEnabled ? '/wallet' : undefined}
|
||||
href={micrositeEnabled ? undefined : APP_STORE_LINK}
|
||||
close={toggleOpen}
|
||||
>
|
||||
<Icon>
|
||||
<StyledAppleLogo />
|
||||
</Icon>
|
||||
<PrimaryMenuRow.Text>
|
||||
<Trans>Uniswap Wallet</Trans>
|
||||
</PrimaryMenuRow.Text>
|
||||
<BadgeWrapper>
|
||||
<NewBadge />
|
||||
</BadgeWrapper>
|
||||
</PrimaryMenuRow>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
<IconRow>
|
||||
<Icon href="https://discord.com/invite/FCfyBSbCU5">
|
||||
<DiscordIconMenu
|
||||
className={styles.hover}
|
||||
width={24}
|
||||
height={24}
|
||||
color={themeVars.colors.textSecondary}
|
||||
/>
|
||||
</Icon>
|
||||
<Icon href="https://twitter.com/Uniswap">
|
||||
<TwitterIconMenu
|
||||
className={styles.hover}
|
||||
width={24}
|
||||
height={24}
|
||||
color={themeVars.colors.textSecondary}
|
||||
/>
|
||||
</Icon>
|
||||
<Icon href="https://github.com/Uniswap">
|
||||
<GithubIconMenu
|
||||
className={styles.hover}
|
||||
width={24}
|
||||
height={24}
|
||||
color={themeVars.colors.textSecondary}
|
||||
/>
|
||||
</Icon>
|
||||
</IconRow>
|
||||
{(isDevelopmentEnv() || isStagingEnv()) && (
|
||||
<>
|
||||
<Separator />
|
||||
<SecondaryLinkedText onClick={openFeatureFlagsModal}>
|
||||
<Trans>Feature Flags</Trans>
|
||||
</SecondaryLinkedText>
|
||||
</>
|
||||
)}
|
||||
</Column>
|
||||
</Column>
|
||||
</NavDropdown>
|
||||
)}
|
||||
|
||||
@@ -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 })
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ import { Trans } from '@lingui/macro'
|
||||
import { useTrace } from '@uniswap/analytics'
|
||||
import { InterfaceSectionName, NavBarSearchTypes } from '@uniswap/analytics-events'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import Badge from 'components/Badge'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import { useNftGraphqlEnabled } from 'featureFlags/flags/nftlGraphql'
|
||||
import { HistoryDuration, SafetyLevel } from 'graphql/data/__generated__/types-and-hooks'
|
||||
import { useTrendingCollections } from 'graphql/data/nft/TrendingCollections'
|
||||
@@ -17,7 +19,10 @@ import { formatEthPrice } from 'nft/utils/currency'
|
||||
import { ReactNode, useEffect, useMemo, useState } from 'react'
|
||||
import { useQuery } from 'react-query'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ThemedText } from 'theme'
|
||||
|
||||
import BnbLogoURI from '../../assets/svg/bnb-logo.svg'
|
||||
import { ClockIcon, TrendingArrow } from '../../nft/components/icons'
|
||||
import { useRecentlySearchedAssets } from './RecentlySearchedAssets'
|
||||
import * as styles from './SearchBar.css'
|
||||
@@ -101,6 +106,24 @@ function isKnownToken(token: SearchToken) {
|
||||
return token.project?.safetyLevel == SafetyLevel.Verified || token.project?.safetyLevel == SafetyLevel.MediumWarning
|
||||
}
|
||||
|
||||
const BNBLogo = styled.img`
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
margin-right: 8px;
|
||||
`
|
||||
const BNBComingSoonBadge = styled(Badge)`
|
||||
align-items: center;
|
||||
background-color: ${({ theme }) => theme.backgroundModule};
|
||||
color: ${({ theme }) => theme.textSecondary};
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
opacity: 1;
|
||||
padding: 8px;
|
||||
margin: 16px 16px 4px;
|
||||
width: calc(100% - 32px);
|
||||
`
|
||||
|
||||
interface SearchBarDropdownProps {
|
||||
toggleOpen: () => void
|
||||
tokens: SearchToken[]
|
||||
@@ -124,6 +147,7 @@ export const SearchBarDropdown = ({
|
||||
const shortenedHistory = useMemo(() => searchHistory?.slice(0, 2) ?? [...Array<SearchToken>(2)], [searchHistory])
|
||||
|
||||
const { pathname } = useLocation()
|
||||
const { chainId } = useWeb3React()
|
||||
const isNFTPage = useIsNftPage()
|
||||
const isNftGraphqlEnabled = useNftGraphqlEnabled()
|
||||
const isTokenPage = pathname.includes('/tokens')
|
||||
@@ -336,10 +360,20 @@ export const SearchBarDropdown = ({
|
||||
searchHistory,
|
||||
])
|
||||
|
||||
const showBNBComingSoonBadge = chainId === SupportedChainId.BNB && !isLoading
|
||||
|
||||
return (
|
||||
<Box className={styles.searchBarDropdownNft}>
|
||||
<Box opacity={isLoading ? '0.3' : '1'} transition="125">
|
||||
{resultsState}
|
||||
{showBNBComingSoonBadge && (
|
||||
<BNBComingSoonBadge>
|
||||
<BNBLogo src={BnbLogoURI} />
|
||||
<ThemedText.BodySmall color="textSecondary" fontSize="14px" fontWeight="400" lineHeight="20px">
|
||||
<Trans>Coming soon: search and explore tokens on BNB Chain</Trans>
|
||||
</ThemedText.BodySmall>
|
||||
</BNBComingSoonBadge>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
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>
|
||||
<MenuItem href="/pools" dataTestId="pool-nav-link" isActive={isPoolActive}>
|
||||
<Trans>Pools</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'
|
||||
|
||||
@@ -141,7 +141,7 @@ const TEXT_COLORS: { [chainId in NetworkAlertChains]: string } = {
|
||||
[SupportedChainId.OPTIMISM]: '#ff3856',
|
||||
[SupportedChainId.OPTIMISM_GOERLI]: '#ff3856',
|
||||
[SupportedChainId.ARBITRUM_ONE]: '#0490ed',
|
||||
[SupportedChainId.BNB]: colors.gold200,
|
||||
[SupportedChainId.BNB]: colors.gold400,
|
||||
[SupportedChainId.ARBITRUM_GOERLI]: '#0490ed',
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,10 @@ import { BigNumber } from '@ethersproject/bignumber'
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { Percent, Price, Token } from '@uniswap/sdk-core'
|
||||
import { Position } from '@uniswap/v3-sdk'
|
||||
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'
|
||||
@@ -15,7 +14,7 @@ import { useMemo } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Bound } from 'state/mint/v3/actions'
|
||||
import styled from 'styled-components/macro'
|
||||
import { HideSmall, MEDIA_WIDTHS, SmallOnly } from 'theme'
|
||||
import { HideSmall, MEDIA_WIDTHS, SmallOnly, ThemedText } from 'theme'
|
||||
import { formatTickPrice } from 'utils/formatTickPrice'
|
||||
import { unwrappedToken } from 'utils/unwrappedToken'
|
||||
import { hasURL } from 'utils/urlChecks'
|
||||
@@ -52,14 +51,6 @@ const LinkRow = styled(Link)`
|
||||
`};
|
||||
`
|
||||
|
||||
const BadgeText = styled.div`
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
|
||||
font-size: 12px;
|
||||
`};
|
||||
`
|
||||
|
||||
const DataLineItem = styled.div`
|
||||
font-size: 14px;
|
||||
`
|
||||
@@ -73,20 +64,27 @@ const RangeLineItem = styled(DataLineItem)`
|
||||
`
|
||||
|
||||
const DoubleArrow = styled.span`
|
||||
font-size: 12px;
|
||||
margin: 0 2px;
|
||||
color: ${({ theme }) => theme.textTertiary};
|
||||
`
|
||||
|
||||
const RangeText = styled.div`
|
||||
const RangeText = styled(ThemedText.Caption)`
|
||||
font-size: 12px !important;
|
||||
word-break: break-word;
|
||||
padding: 0.25rem 0.25rem;
|
||||
border-radius: 8px;
|
||||
`
|
||||
|
||||
const ExtentsText = styled.span`
|
||||
const FeeTierText = styled(ThemedText.UtilityBadge)`
|
||||
font-size: 10px !important;
|
||||
margin-left: 14px !important;
|
||||
`
|
||||
const ExtentsText = styled(ThemedText.Caption)`
|
||||
color: ${({ theme }) => theme.textTertiary};
|
||||
font-size: 14px;
|
||||
margin-right: 4px;
|
||||
display: inline-block;
|
||||
line-height: 16px;
|
||||
margin-right: 4px !important;
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
|
||||
display: none;
|
||||
`};
|
||||
@@ -101,15 +99,6 @@ const PrimaryPositionIdData = styled.div`
|
||||
}
|
||||
`
|
||||
|
||||
const DataText = styled.div`
|
||||
font-weight: 600;
|
||||
font-size: 18px;
|
||||
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
|
||||
font-size: 18px;
|
||||
`};
|
||||
`
|
||||
|
||||
interface PositionListItemProps {
|
||||
token0: string
|
||||
token1: string
|
||||
@@ -215,11 +204,7 @@ export default function PositionListItem({
|
||||
const removed = liquidity?.eq(0)
|
||||
|
||||
const containsURL = useMemo(
|
||||
() =>
|
||||
[token0?.name, token0?.symbol, token1?.name, token1?.symbol].reduce(
|
||||
(acc, testString) => acc || Boolean(testString && hasURL(testString)),
|
||||
false
|
||||
),
|
||||
() => [token0?.name, token0?.symbol, token1?.name, token1?.symbol].some((testString) => hasURL(testString)),
|
||||
[token0?.name, token0?.symbol, token1?.name, token1?.symbol]
|
||||
)
|
||||
|
||||
@@ -232,15 +217,13 @@ export default function PositionListItem({
|
||||
<RowBetween>
|
||||
<PrimaryPositionIdData>
|
||||
<DoubleCurrencyLogo currency0={currencyBase} currency1={currencyQuote} size={18} margin />
|
||||
<DataText>
|
||||
<ThemedText.SubHeader>
|
||||
{currencyQuote?.symbol} / {currencyBase?.symbol}
|
||||
</DataText>
|
||||
|
||||
<Badge>
|
||||
<BadgeText>
|
||||
<Trans>{new Percent(feeAmount, 1_000_000).toSignificant()}%</Trans>
|
||||
</BadgeText>
|
||||
</Badge>
|
||||
</ThemedText.SubHeader>
|
||||
|
||||
<FeeTierText>
|
||||
<Trans>{new Percent(feeAmount, 1_000_000).toSignificant()}%</Trans>
|
||||
</FeeTierText>
|
||||
</PrimaryPositionIdData>
|
||||
<RangeBadge removed={removed} inRange={!outOfRange} />
|
||||
</RowBetween>
|
||||
@@ -263,10 +246,10 @@ export default function PositionListItem({
|
||||
</Trans>
|
||||
</RangeText>{' '}
|
||||
<HideSmall>
|
||||
<DoubleArrow>⟷</DoubleArrow>{' '}
|
||||
<DoubleArrow>↔</DoubleArrow>{' '}
|
||||
</HideSmall>
|
||||
<SmallOnly>
|
||||
<DoubleArrow>⟷</DoubleArrow>{' '}
|
||||
<DoubleArrow>↔</DoubleArrow>{' '}
|
||||
</SmallOnly>
|
||||
<RangeText>
|
||||
<ExtentsText>
|
||||
|
||||
@@ -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])
|
||||
|
||||
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
@@ -1,179 +0,0 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { TraceEvent } from '@uniswap/analytics'
|
||||
import { BrowserEvent, InterfaceElementName, SharedEventName } from '@uniswap/analytics-events'
|
||||
import { ButtonEmphasis, ButtonSize, ThemeButton } from 'components/Button'
|
||||
import { bodySmall, subhead } from 'nft/css/common.css'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { X } from 'react-feather'
|
||||
import { useTaxServiceDismissal } from 'state/user/hooks'
|
||||
import { useIsDarkMode } from 'state/user/hooks'
|
||||
import styled from 'styled-components/macro'
|
||||
import { opacify } from 'theme/utils'
|
||||
import { Z_INDEX } from 'theme/zIndex'
|
||||
|
||||
import TaxServiceModal from '.'
|
||||
import CointrackerLogo from './CointrackerLogo.png'
|
||||
import TokenTaxLogo from './TokenTaxLogo.png'
|
||||
|
||||
const PopupContainer = styled.div<{ show: boolean; isDarkMode: boolean }>`
|
||||
box-shadow: ${({ theme }) => theme.deepShadow};
|
||||
border: 1px solid ${({ theme }) => theme.backgroundOutline};
|
||||
background-color: ${({ theme }) => theme.backgroundSurface};
|
||||
border-radius: 13px;
|
||||
cursor: pointer;
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
display: ${({ show }) => (show ? 'flex' : 'none')};
|
||||
flex-direction: column;
|
||||
position: fixed;
|
||||
right: clamp(0px, 1vw, 16px);
|
||||
z-index: ${Z_INDEX.sticky};
|
||||
transition: ${({
|
||||
theme: {
|
||||
transition: { duration, timing },
|
||||
},
|
||||
}) => `${duration.slow} opacity ${timing.in}`};
|
||||
width: 320px;
|
||||
height: 156px;
|
||||
bottom: 50px;
|
||||
@media screen and (max-width: ${({ theme }) => theme.breakpoint.sm}px) {
|
||||
border-style: solid none;
|
||||
width: 100%;
|
||||
border-radius: 0;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
|
||||
background-image: url(${CointrackerLogo}), url(${TokenTaxLogo});
|
||||
background-size: 15%, 20%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: top right 75px, bottom 5px right 7px;
|
||||
@media screen and (max-width: ${({ theme }) => theme.breakpoint.sm}px) {
|
||||
background-size: 48px, 64px;
|
||||
background-position: top right 75px, bottom 20px right 7px;
|
||||
}
|
||||
|
||||
opacity: ${({ isDarkMode }) => (isDarkMode ? '0.9' : '0.25')};
|
||||
}
|
||||
`
|
||||
|
||||
const InnerContainer = styled.div<{ isDarkMode: boolean }>`
|
||||
border-radius: 12px;
|
||||
cursor: auto;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
gap: 8px;
|
||||
padding: 16px;
|
||||
background-color: ${({ isDarkMode, theme }) =>
|
||||
isDarkMode ? opacify(10, theme.accentAction) : opacify(4, theme.accentAction)};
|
||||
@media screen and (max-width: ${({ theme }) => theme.breakpoint.sm}px) {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border-radius: 0;
|
||||
}
|
||||
`
|
||||
|
||||
const Button = styled(ThemeButton)`
|
||||
margin-top: auto;
|
||||
margin-right: auto;
|
||||
padding: 8px 24px;
|
||||
gap: 8px;
|
||||
border-radius: 12px;
|
||||
`
|
||||
|
||||
const TextContainer = styled.div`
|
||||
user-select: none;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 90%;
|
||||
justify-content: center;
|
||||
`
|
||||
|
||||
export const StyledXButton = styled(X)`
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
opacity: ${({ theme }) => theme.opacity.hover};
|
||||
}
|
||||
&:active {
|
||||
opacity: ${({ theme }) => theme.opacity.click};
|
||||
}
|
||||
`
|
||||
|
||||
const TAX_SERVICE_DISMISSED = 'TaxServiceToast-dismissed'
|
||||
|
||||
const MAX_RENDER_COUNT = 3
|
||||
|
||||
export default function TaxServiceBanner() {
|
||||
const isDarkMode = useIsDarkMode()
|
||||
const [dismissals, addTaxServiceDismissal] = useTaxServiceDismissal()
|
||||
const [modalOpen, setModalOpen] = useState(false)
|
||||
const sessionStorageTaxServiceDismissed = sessionStorage.getItem(TAX_SERVICE_DISMISSED)
|
||||
|
||||
if (!sessionStorageTaxServiceDismissed) {
|
||||
sessionStorage.setItem(TAX_SERVICE_DISMISSED, 'false')
|
||||
}
|
||||
const [bannerOpen, setBannerOpen] = useState(
|
||||
sessionStorageTaxServiceDismissed !== 'true' && (dismissals === undefined || dismissals < MAX_RENDER_COUNT)
|
||||
)
|
||||
const onDismiss = useCallback(() => {
|
||||
setModalOpen(false)
|
||||
}, [])
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
sessionStorage.setItem(TAX_SERVICE_DISMISSED, 'true')
|
||||
setBannerOpen(false)
|
||||
dismissals === undefined ? addTaxServiceDismissal(1) : addTaxServiceDismissal(dismissals + 1)
|
||||
}, [addTaxServiceDismissal, dismissals])
|
||||
|
||||
const handleLearnMoreClick = useCallback((e: any) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
setModalOpen(true)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<PopupContainer show={bannerOpen} isDarkMode={isDarkMode}>
|
||||
<InnerContainer isDarkMode={isDarkMode} tabIndex={0}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<TextContainer data-testid="tax-service-description">
|
||||
<div className={subhead} style={{ paddingBottom: '12px' }}>
|
||||
<Trans>Save on your crypto taxes</Trans>
|
||||
</div>
|
||||
<div className={bodySmall} style={{ paddingBottom: '12px' }}>
|
||||
<Trans>Uniswap Labs can save you up to 20% on CoinTracker and TokenTax</Trans>{' '}
|
||||
</div>
|
||||
</TextContainer>
|
||||
<StyledXButton size={20} onClick={handleClose} />
|
||||
</div>
|
||||
|
||||
<TraceEvent
|
||||
events={[BrowserEvent.onClick]}
|
||||
name={SharedEventName.ELEMENT_CLICKED}
|
||||
element={InterfaceElementName.TAX_SERVICE_BANNER_CTA_BUTTON}
|
||||
>
|
||||
<Button
|
||||
size={ButtonSize.small}
|
||||
emphasis={ButtonEmphasis.promotional}
|
||||
onMouseDown={(e) => {
|
||||
e.preventDefault()
|
||||
}}
|
||||
onClick={handleLearnMoreClick}
|
||||
data-testid="learn-more-button"
|
||||
>
|
||||
<Trans>Learn more</Trans>
|
||||
</Button>
|
||||
</TraceEvent>
|
||||
</InnerContainer>
|
||||
<TaxServiceModal isOpen={modalOpen} onDismiss={onDismiss} />
|
||||
</PopupContainer>
|
||||
)
|
||||
}
|
||||
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 11 KiB |
@@ -1,17 +0,0 @@
|
||||
import { render, screen } from '../../test-utils'
|
||||
import TaxServiceModal from './'
|
||||
import TaxServiceBanner from './TaxServiceBanner'
|
||||
|
||||
it('renders Tax Service Modal content', async () => {
|
||||
render(<TaxServiceModal isOpen={true} onDismiss={() => null} />)
|
||||
expect(screen.getByText('Save 10% on all plans')).toBeInTheDocument()
|
||||
expect(screen.getByText('New and existing users save up to 20%')).toBeInTheDocument()
|
||||
expect(screen.getAllByTestId('tax-service-option-button')).toHaveLength(2)
|
||||
})
|
||||
|
||||
it('renders Tax Service Banner', async () => {
|
||||
render(<TaxServiceBanner />)
|
||||
expect(screen.getByText('Save on your crypto taxes')).toBeInTheDocument()
|
||||
expect(screen.getAllByTestId('learn-more-button')).toHaveLength(1)
|
||||
expect(screen.getByText('Uniswap Labs can save you up to 20% on CoinTracker and TokenTax')).toBeInTheDocument()
|
||||
})
|
||||
@@ -1,141 +0,0 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { TraceEvent } from '@uniswap/analytics'
|
||||
import { BrowserEvent, InterfaceElementName, SharedEventName } from '@uniswap/analytics-events'
|
||||
import { ButtonEmphasis } from 'components/Button'
|
||||
import { ButtonSize, ThemeButton } from 'components/Button'
|
||||
import { Box } from 'nft/components/Box'
|
||||
import { bodySmall, subhead } from 'nft/css/common.css'
|
||||
import { memo } from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import Modal from '../Modal'
|
||||
import CointrackerFullLogo from './CointrackerFullLogo.png'
|
||||
import { StyledXButton } from './TaxServiceBanner'
|
||||
import TokenTaxFullLogo from './TokenTaxFullLogo.png'
|
||||
|
||||
interface TaxServiceModalProps {
|
||||
isOpen: boolean
|
||||
onDismiss: () => void
|
||||
}
|
||||
|
||||
interface TaxServiceOptionProps {
|
||||
logo: any
|
||||
description: string
|
||||
url: string
|
||||
}
|
||||
|
||||
const InnerContainer = styled.div`
|
||||
background-color: ${({ theme }) => theme.backgroundSurface};
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
width: 420px;
|
||||
height: 268px;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
gap: 20px;
|
||||
padding: 16px;
|
||||
`
|
||||
|
||||
const TaxOptionContainer = styled.div`
|
||||
display: flex;
|
||||
flex: 1;
|
||||
gap: 16px;
|
||||
justify-content: center;
|
||||
`
|
||||
|
||||
const TaxOptionDescription = styled.div`
|
||||
display: flex;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
user-select: none;
|
||||
text-align: center;
|
||||
`
|
||||
|
||||
const TaxOption = styled.div`
|
||||
align-items: center;
|
||||
background-color: ${({ theme }) => theme.backgroundModule};
|
||||
border-radius: 12px;
|
||||
cursor: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
justify-content: space-between;
|
||||
padding: 12px;
|
||||
gap: 16px;
|
||||
`
|
||||
|
||||
const StyledImageContainer = styled(Box)`
|
||||
width: 75%;
|
||||
height: 80%;
|
||||
cursor: auto;
|
||||
object-fit: contain;
|
||||
`
|
||||
|
||||
const Button = styled(ThemeButton)`
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
margin-right: auto;
|
||||
`
|
||||
|
||||
const TOKEN_TAX_URL = 'https://tokentax.co/uniswap?via=uniswap'
|
||||
const COINTRACKER_URL = 'https://www.cointracker.io/partner/uniswap?utm_source=uniswap'
|
||||
|
||||
const TOKEN_TAX_DESCRIPTION = 'Save 10% on all plans'
|
||||
const COINTRACKER_DESCRIPTION = 'New and existing users save up to 20%'
|
||||
|
||||
function TaxServiceOption({ description, logo, url }: TaxServiceOptionProps) {
|
||||
return (
|
||||
<TaxOption tabIndex={0}>
|
||||
<StyledImageContainer as="img" src={logo} draggable={false} />
|
||||
<TaxOptionDescription className={bodySmall}>{description}</TaxOptionDescription>
|
||||
<TraceEvent
|
||||
events={[BrowserEvent.onClick]}
|
||||
name={SharedEventName.ELEMENT_CLICKED}
|
||||
element={
|
||||
url.includes('tokentax')
|
||||
? InterfaceElementName.TAX_SERVICE_TOKENTAX_BUTTON
|
||||
: InterfaceElementName.TAX_SERVICE_COINTRACKER_BUTTON
|
||||
}
|
||||
>
|
||||
<a href={url} target="_blank" rel="noreferrer" style={{ textDecoration: 'none' }}>
|
||||
<Button
|
||||
onMouseDown={(e) => {
|
||||
e.preventDefault()
|
||||
}}
|
||||
size={ButtonSize.medium}
|
||||
emphasis={ButtonEmphasis.medium}
|
||||
data-testid="tax-service-option-button"
|
||||
>
|
||||
Get started
|
||||
</Button>
|
||||
</a>
|
||||
</TraceEvent>
|
||||
</TaxOption>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(function TaxServiceModal({ isOpen, onDismiss }: TaxServiceModalProps) {
|
||||
return (
|
||||
<Modal isOpen={isOpen} onDismiss={onDismiss} maxHeight={90} minHeight={false}>
|
||||
<InnerContainer>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', userSelect: 'none' }}>
|
||||
<div className={subhead}>
|
||||
<Trans>Save on your crypto taxes</Trans>
|
||||
</div>
|
||||
<StyledXButton
|
||||
size={20}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
onDismiss()
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<TaxOptionContainer>
|
||||
<TaxServiceOption description={COINTRACKER_DESCRIPTION} logo={CointrackerFullLogo} url={COINTRACKER_URL} />
|
||||
<TaxServiceOption description={TOKEN_TAX_DESCRIPTION} logo={TokenTaxFullLogo} url={TOKEN_TAX_URL} />
|
||||
</TaxOptionContainer>
|
||||
</InnerContainer>
|
||||
</Modal>
|
||||
)
|
||||
})
|
||||
@@ -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>
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
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 />
|
||||
<InvalidDetailsText>
|
||||
{chainName ? (
|
||||
<Trans>{`This token doesn't exist on ${chainName}`}</Trans>
|
||||
) : (
|
||||
<Trans>This token doesn't exist</Trans>
|
||||
)}
|
||||
</InvalidDetailsText>
|
||||
<TokenExploreButton onClick={() => navigate('/tokens')}>
|
||||
<Trans>Explore tokens</Trans>
|
||||
</TokenExploreButton>
|
||||
{isInvalidAddress || isNonExistantToken ? (
|
||||
<>
|
||||
<InvalidDetailsText>
|
||||
<Trans>This token doesn't exist</Trans>
|
||||
</InvalidDetailsText>
|
||||
<TokenExploreButton onClick={() => navigate('/tokens')}>
|
||||
<ThemedText.SubHeader>
|
||||
<Trans>Explore tokens</Trans>
|
||||
</ThemedText.SubHeader>
|
||||
</TokenExploreButton>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<InvalidDetailsText>
|
||||
<Trans>This token doesn'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}
|
||||
>
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { Trace } from '@uniswap/analytics'
|
||||
import { InterfacePageName } from '@uniswap/analytics-events'
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import { Currency, Field } from '@uniswap/widgets'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import CurrencyLogo from 'components/Logo/CurrencyLogo'
|
||||
import { AboutSection } from 'components/Tokens/TokenDetails/About'
|
||||
@@ -23,7 +23,7 @@ import StatsSection from 'components/Tokens/TokenDetails/StatsSection'
|
||||
import TokenSafetyMessage from 'components/TokenSafety/TokenSafetyMessage'
|
||||
import TokenSafetyModal from 'components/TokenSafety/TokenSafetyModal'
|
||||
import Widget from 'components/Widget'
|
||||
import { getChainInfo } from 'constants/chainInfo'
|
||||
import { SwapTokens } from 'components/Widget/inputs'
|
||||
import { NATIVE_CHAIN_ID, nativeOnChain } from 'constants/tokens'
|
||||
import { checkWarning } from 'constants/tokenSafety'
|
||||
import { TokenPriceQuery } from 'graphql/data/__generated__/types-and-hooks'
|
||||
@@ -33,6 +33,7 @@ import { CHAIN_NAME_TO_CHAIN_ID, getTokenDetailsURL } from 'graphql/data/util'
|
||||
import { useIsUserAddedTokenOnChain } from 'hooks/Tokens'
|
||||
import { useOnGlobalChainSwitch } from 'hooks/useGlobalChainSwitch'
|
||||
import { UNKNOWN_TOKEN_SYMBOL, useTokenFromActiveNetwork } from 'lib/hooks/useCurrency'
|
||||
import { getTokenAddress } from 'lib/utils/analytics'
|
||||
import { useCallback, useMemo, useState, useTransition } from 'react'
|
||||
import { ArrowLeft } from 'react-feather'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
@@ -88,6 +89,7 @@ function useRelevantToken(
|
||||
|
||||
type TokenDetailsProps = {
|
||||
urlAddress: string | undefined
|
||||
inputTokenAddress?: string
|
||||
chain: Chain
|
||||
tokenQuery: TokenQuery
|
||||
tokenPriceQuery: TokenPriceQuery | undefined
|
||||
@@ -95,6 +97,7 @@ type TokenDetailsProps = {
|
||||
}
|
||||
export default function TokenDetails({
|
||||
urlAddress,
|
||||
inputTokenAddress,
|
||||
chain,
|
||||
tokenQuery,
|
||||
tokenPriceQuery,
|
||||
@@ -120,7 +123,8 @@ export default function TokenDetails({
|
||||
[tokenQueryData]
|
||||
)
|
||||
|
||||
const { token, didFetchFromChain } = useRelevantToken(address, pageChainId, tokenQueryData)
|
||||
const { token: detailedToken, didFetchFromChain } = useRelevantToken(address, pageChainId, tokenQueryData)
|
||||
const { token: inputToken } = useRelevantToken(inputTokenAddress, pageChainId, undefined)
|
||||
|
||||
const tokenWarning = address ? checkWarning(address) : null
|
||||
const isBlockedToken = tokenWarning?.canProceed === false
|
||||
@@ -133,19 +137,28 @@ export default function TokenDetails({
|
||||
if (!address) return
|
||||
const bridgedAddress = crossChainMap[update]
|
||||
if (bridgedAddress) {
|
||||
startTokenTransition(() => navigate(getTokenDetailsURL({ address: bridgedAddress, chain })))
|
||||
} else if (didFetchFromChain || token?.isNative) {
|
||||
startTokenTransition(() => navigate(getTokenDetailsURL({ address, chain })))
|
||||
startTokenTransition(() => navigate(getTokenDetailsURL({ address: bridgedAddress, chain: update })))
|
||||
} else if (didFetchFromChain || detailedToken?.isNative) {
|
||||
startTokenTransition(() => navigate(getTokenDetailsURL({ address, chain: update })))
|
||||
}
|
||||
},
|
||||
[address, chain, crossChainMap, didFetchFromChain, navigate, token?.isNative]
|
||||
[address, crossChainMap, didFetchFromChain, navigate, detailedToken?.isNative]
|
||||
)
|
||||
useOnGlobalChainSwitch(navigateToTokenForChain)
|
||||
|
||||
const navigateToWidgetSelectedToken = useCallback(
|
||||
(token: Currency) => {
|
||||
const address = token.isNative ? NATIVE_CHAIN_ID : token.address
|
||||
startTokenTransition(() => navigate(getTokenDetailsURL({ address, chain })))
|
||||
(tokens: SwapTokens) => {
|
||||
const newDefaultToken = tokens[Field.OUTPUT] ?? tokens.default
|
||||
const address = newDefaultToken?.isNative ? NATIVE_CHAIN_ID : newDefaultToken?.address
|
||||
startTokenTransition(() =>
|
||||
navigate(
|
||||
getTokenDetailsURL({
|
||||
address,
|
||||
chain,
|
||||
inputAddress: tokens[Field.INPUT] ? getTokenAddress(tokens[Field.INPUT] as Currency) : null,
|
||||
})
|
||||
)
|
||||
)
|
||||
},
|
||||
[chain, navigate]
|
||||
)
|
||||
@@ -168,36 +181,38 @@ export default function TokenDetails({
|
||||
},
|
||||
[continueSwap, setContinueSwap]
|
||||
)
|
||||
|
||||
// address will never be undefined if token is defined; address is checked here to appease typechecker
|
||||
if (token === undefined || !address) {
|
||||
return <InvalidTokenDetails chainName={address && getChainInfo(pageChainId)?.label} />
|
||||
if (detailedToken === undefined || !address) {
|
||||
return <InvalidTokenDetails pageChainId={pageChainId} isInvalidAddress={!address} />
|
||||
}
|
||||
return (
|
||||
<Trace
|
||||
page={InterfacePageName.TOKEN_DETAILS_PAGE}
|
||||
properties={{ tokenAddress: address, tokenName: token?.name }}
|
||||
properties={{ tokenAddress: address, tokenName: detailedToken?.name }}
|
||||
shouldLogImpression
|
||||
>
|
||||
<TokenDetailsLayout>
|
||||
{token && !isPending ? (
|
||||
{detailedToken && !isPending ? (
|
||||
<LeftPanel>
|
||||
<BreadcrumbNavLink to={`/tokens/${chain.toLowerCase()}`}>
|
||||
<ArrowLeft data-testid="token-details-return-button" size={14} /> Tokens
|
||||
</BreadcrumbNavLink>
|
||||
<TokenInfoContainer data-testid="token-info-container">
|
||||
<TokenNameCell>
|
||||
<CurrencyLogo currency={token} size="32px" hideL2Icon={false} />
|
||||
<CurrencyLogo currency={detailedToken} size="32px" hideL2Icon={false} />
|
||||
|
||||
{token.name ?? <Trans>Name not found</Trans>}
|
||||
<TokenSymbol>{token.symbol ?? <Trans>Symbol not found</Trans>}</TokenSymbol>
|
||||
{detailedToken.name ?? <Trans>Name not found</Trans>}
|
||||
<TokenSymbol>{detailedToken.symbol ?? <Trans>Symbol not found</Trans>}</TokenSymbol>
|
||||
</TokenNameCell>
|
||||
<TokenActions>
|
||||
<ShareButton currency={token} />
|
||||
<ShareButton currency={detailedToken} />
|
||||
</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}
|
||||
@@ -211,7 +226,7 @@ export default function TokenDetails({
|
||||
homepageUrl={tokenQueryData?.project?.homepageUrl}
|
||||
twitterName={tokenQueryData?.project?.twitterName}
|
||||
/>
|
||||
{!token.isNative && <AddressSection address={address} />}
|
||||
{!detailedToken.isNative && <AddressSection address={address} />}
|
||||
</LeftPanel>
|
||||
) : (
|
||||
<TokenDetailsSkeleton />
|
||||
@@ -221,16 +236,17 @@ export default function TokenDetails({
|
||||
<div style={{ pointerEvents: isBlockedToken ? 'none' : 'auto' }}>
|
||||
<Widget
|
||||
defaultTokens={{
|
||||
default: token ?? undefined,
|
||||
[Field.INPUT]: inputToken ?? undefined,
|
||||
default: detailedToken ?? undefined,
|
||||
}}
|
||||
onDefaultTokenChange={navigateToWidgetSelectedToken}
|
||||
onReviewSwapClick={onReviewSwapClick}
|
||||
/>
|
||||
</div>
|
||||
{tokenWarning && <TokenSafetyMessage tokenAddress={address} warning={tokenWarning} />}
|
||||
{token && <BalanceSummary token={token} />}
|
||||
{detailedToken && <BalanceSummary token={detailedToken} />}
|
||||
</RightPanel>
|
||||
{token && <MobileBalanceSummaryFooter token={token} />}
|
||||
{detailedToken && <MobileBalanceSummaryFooter token={detailedToken} />}
|
||||
|
||||
<TokenSafetyModal
|
||||
isOpen={openTokenSafetyModal || !!continueSwap}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import Badge from 'components/Badge'
|
||||
import { getChainInfo } from 'constants/chainInfo'
|
||||
import { BACKEND_CHAIN_NAMES, CHAIN_NAME_TO_CHAIN_ID, validateUrlChainParam } from 'graphql/data/util'
|
||||
import { useOnClickOutside } from 'hooks/useOnClickOutside'
|
||||
@@ -6,7 +7,8 @@ import { Check, ChevronDown, ChevronUp } from 'react-feather'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
import { useModalIsOpen, useToggleModal } from 'state/application/hooks'
|
||||
import { ApplicationModal } from 'state/application/reducer'
|
||||
import styled, { useTheme } from 'styled-components/macro'
|
||||
import styled, { css, useTheme } from 'styled-components/macro'
|
||||
import { EllipsisStyle } from 'theme'
|
||||
|
||||
import FilterOption from './FilterOption'
|
||||
|
||||
@@ -20,7 +22,7 @@ const InternalMenuItem = styled.div`
|
||||
text-decoration: none;
|
||||
}
|
||||
`
|
||||
const InternalLinkMenuItem = styled(InternalMenuItem)`
|
||||
const InternalLinkMenuItem = styled(InternalMenuItem)<{ disabled?: boolean }>`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
@@ -32,6 +34,13 @@ const InternalLinkMenuItem = styled(InternalMenuItem)`
|
||||
background-color: ${({ theme }) => theme.hoverState};
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
${({ disabled }) =>
|
||||
disabled &&
|
||||
css`
|
||||
opacity: 60%;
|
||||
pointer-events: none;
|
||||
`}
|
||||
`
|
||||
const MenuTimeFlyout = styled.span`
|
||||
min-width: 240px;
|
||||
@@ -72,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;
|
||||
@@ -85,7 +95,14 @@ 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};
|
||||
color: ${({ theme }) => theme.textSecondary};
|
||||
font-size: 10px;
|
||||
opacity: 1;
|
||||
padding: 4px 6px;
|
||||
`
|
||||
|
||||
export default function NetworkFilter() {
|
||||
@@ -100,6 +117,7 @@ export default function NetworkFilter() {
|
||||
const currentChainName = validateUrlChainParam(chainName)
|
||||
|
||||
const chainInfo = getChainInfo(CHAIN_NAME_TO_CHAIN_ID[currentChainName])
|
||||
const BNBChainInfo = getChainInfo(CHAIN_NAME_TO_CHAIN_ID.BNB)
|
||||
|
||||
return (
|
||||
<StyledMenu ref={node}>
|
||||
@@ -148,6 +166,13 @@ export default function NetworkFilter() {
|
||||
</InternalLinkMenuItem>
|
||||
)
|
||||
})}
|
||||
<InternalLinkMenuItem data-testid="tokens-network-filter-option-bnb-chain" disabled>
|
||||
<NetworkLabel>
|
||||
<Logo src={BNBChainInfo.logoUrl} />
|
||||
{BNBChainInfo.label}
|
||||
</NetworkLabel>
|
||||
<Tag>Coming soon</Tag>
|
||||
</InternalLinkMenuItem>
|
||||
</MenuTimeFlyout>
|
||||
)}
|
||||
</StyledMenu>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
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]
|
||||
|
||||
@@ -1,17 +1,28 @@
|
||||
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}
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
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 { useTaxServiceBannerEnabled } from 'featureFlags/flags/taxServiceBanner'
|
||||
import UniwalletModal from 'components/WalletDropdown/UniwalletModal'
|
||||
import useAccountRiskCheck from 'hooks/useAccountRiskCheck'
|
||||
import { useIsNftPage } from 'hooks/useIsNftPage'
|
||||
import { useIsPoolsPage } from 'hooks/useIsPoolsPage'
|
||||
import { lazy } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import { useModalIsOpen, useToggleModal } from 'state/application/hooks'
|
||||
import { ApplicationModal } from 'state/application/reducer'
|
||||
|
||||
@@ -23,23 +20,17 @@ export default function TopLevelModals() {
|
||||
const { account } = useWeb3React()
|
||||
useAccountRiskCheck(account)
|
||||
const accountBlocked = Boolean(blockedAccountModalOpen && account)
|
||||
const taxServiceEnabled = useTaxServiceBannerEnabled()
|
||||
|
||||
const { pathname } = useLocation()
|
||||
const isNftPage = useIsNftPage()
|
||||
const isPoolPage = useIsPoolsPage()
|
||||
|
||||
const isTaxModalServicePage = isNftPage || isPoolPage || pathname.startsWith('/swap')
|
||||
|
||||
return (
|
||||
<>
|
||||
<AddressClaimModal isOpen={addressClaimOpen} onDismiss={addressClaimToggle} />
|
||||
<ConnectedAccountBlocked account={account} isOpen={accountBlocked} />
|
||||
<Bag />
|
||||
<UniwalletModal />
|
||||
<UniswapWalletBanner />
|
||||
<TransactionCompleteModal />
|
||||
<AirdropModal />
|
||||
<FiatOnrampModal />
|
||||
{taxServiceEnabled && isTaxModalServicePage && <TaxServiceBanner />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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'
|
||||
|
||||
@@ -1,37 +1,46 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { sendAnalyticsEvent } from '@uniswap/analytics'
|
||||
import { InterfaceEventName } from '@uniswap/analytics-events'
|
||||
import { formatUSDPrice } from '@uniswap/conedison/format'
|
||||
import { sendAnalyticsEvent, TraceEvent } from '@uniswap/analytics'
|
||||
import { BrowserEvent, InterfaceElementName, InterfaceEventName, SharedEventName } from '@uniswap/analytics-events'
|
||||
import { formatNumber, NumberType } from '@uniswap/conedison/format'
|
||||
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { ButtonEmphasis, ButtonSize, LoadingButtonSpinner, ThemeButton } from 'components/Button'
|
||||
import Column from 'components/Column'
|
||||
import { AutoRow } from 'components/Row'
|
||||
import { LoadingBubble } from 'components/Tokens/loading'
|
||||
import { formatDelta } from 'components/Tokens/TokenDetails/PriceChart'
|
||||
import Tooltip from 'components/Tooltip'
|
||||
import { getConnection } from 'connection/utils'
|
||||
import { getChainInfoOrDefault } from 'constants/chainInfo'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import useCopyClipboard from 'hooks/useCopyClipboard'
|
||||
import useStablecoinPrice from 'hooks/useStablecoinPrice'
|
||||
import useNativeCurrency from 'lib/hooks/useNativeCurrency'
|
||||
import { useGetConnection } from 'connection'
|
||||
import { usePortfolioBalancesQuery } from 'graphql/data/__generated__/types-and-hooks'
|
||||
import { useProfilePageState, useSellAsset, useWalletCollections } from 'nft/hooks'
|
||||
import { useIsNftClaimAvailable } from 'nft/hooks/useIsNftClaimAvailable'
|
||||
import { ProfilePageStateType } from 'nft/types'
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
import { Copy, CreditCard, ExternalLink as ExternalLinkIcon, Info, Power } from 'react-feather'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { ArrowDownRight, ArrowUpRight, Copy, CreditCard, IconProps, Info, Power, Settings } from 'react-feather'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { useCurrencyBalanceString } from 'state/connection/hooks'
|
||||
import { useAppDispatch } from 'state/hooks'
|
||||
import { updateSelectedWallet } from 'state/user/reducer'
|
||||
import styled, { css } from 'styled-components/macro'
|
||||
import { ExternalLink, ThemedText } from 'theme'
|
||||
import styled, { useTheme } from 'styled-components/macro'
|
||||
import { CopyHelper, ExternalLink, ThemedText } from 'theme'
|
||||
|
||||
import { shortenAddress } from '../../nft/utils/address'
|
||||
import { useCloseModal, useFiatOnrampAvailability, useOpenModal, useToggleModal } from '../../state/application/hooks'
|
||||
import { ApplicationModal } from '../../state/application/reducer'
|
||||
import { useUserHasAvailableClaim, useUserUnclaimedAmount } from '../../state/claim/hooks'
|
||||
import StatusIcon from '../Identicon/StatusIcon'
|
||||
import { useToggleWalletDrawer } from '.'
|
||||
import IconButton, { IconHoverText } from './IconButton'
|
||||
import MiniPortfolio from './MiniPortfolio'
|
||||
import { portfolioFadeInAnimation } from './MiniPortfolio/PortfolioRow'
|
||||
|
||||
const BuyCryptoButton = styled(ThemeButton)`
|
||||
const AuthenticatedHeaderWrapper = styled.div`
|
||||
padding: 14px 12px 16px 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
`
|
||||
|
||||
const HeaderButton = styled(ThemeButton)`
|
||||
border-color: transparent;
|
||||
border-radius: 12px;
|
||||
border-style: solid;
|
||||
@@ -39,6 +48,7 @@ const BuyCryptoButton = styled(ThemeButton)`
|
||||
height: 40px;
|
||||
margin-top: 8px;
|
||||
`
|
||||
|
||||
const WalletButton = styled(ThemeButton)`
|
||||
border-radius: 12px;
|
||||
padding-top: 10px;
|
||||
@@ -48,22 +58,16 @@ const WalletButton = styled(ThemeButton)`
|
||||
border: none;
|
||||
`
|
||||
|
||||
const ProfileButton = styled(WalletButton)`
|
||||
background: ${({ theme }) => theme.accentAction};
|
||||
transition: ${({ theme }) => theme.transition.duration.fast} ${({ theme }) => theme.transition.timing.ease}
|
||||
background-color;
|
||||
`
|
||||
|
||||
const UNIButton = styled(WalletButton)`
|
||||
border-radius: 12px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
margin-top: 4px;
|
||||
color: white;
|
||||
border: none;
|
||||
background: linear-gradient(to right, #9139b0 0%, #4261d6 100%);
|
||||
`
|
||||
|
||||
const Column = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
`
|
||||
|
||||
const IconContainer = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -79,13 +83,6 @@ const IconContainer = styled.div`
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const USDText = styled.div`
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: ${({ theme }) => theme.textSecondary};
|
||||
margin-top: 8px;
|
||||
`
|
||||
const FiatOnrampNotAvailableText = styled(ThemedText.Caption)`
|
||||
align-items: center;
|
||||
color: ${({ theme }) => theme.textSecondary};
|
||||
@@ -101,34 +98,23 @@ const FiatOnrampAvailabilityExternalLink = styled(ExternalLink)`
|
||||
width: 14px;
|
||||
`
|
||||
|
||||
const TruncatedTextStyle = css`
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
`
|
||||
|
||||
const FlexContainer = styled.div`
|
||||
${TruncatedTextStyle}
|
||||
const StatusWrapper = styled.div`
|
||||
display: inline-block;
|
||||
width: 70%;
|
||||
padding-right: 4px;
|
||||
display: inline-flex;
|
||||
`
|
||||
|
||||
const AccountNamesWrapper = styled.div`
|
||||
min-width: 0;
|
||||
margin-right: 8px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
`
|
||||
|
||||
const ENSNameContainer = styled(ThemedText.SubHeader)`
|
||||
${TruncatedTextStyle}
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
margin-top: 2.5px;
|
||||
`
|
||||
|
||||
const AccountContainer = styled(ThemedText.BodySmall)`
|
||||
${TruncatedTextStyle}
|
||||
color: ${({ theme }) => theme.textSecondary};
|
||||
margin-top: 2.5px;
|
||||
`
|
||||
const StyledInfoIcon = styled(Info)`
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
@@ -137,31 +123,42 @@ const StyledInfoIcon = styled(Info)`
|
||||
const StyledLoadingButtonSpinner = styled(LoadingButtonSpinner)`
|
||||
fill: ${({ theme }) => theme.accentAction};
|
||||
`
|
||||
const BalanceWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 16px 0;
|
||||
`
|
||||
|
||||
const HeaderWrapper = styled.div`
|
||||
margin-bottom: 12px;
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
`
|
||||
|
||||
const AuthenticatedHeader = () => {
|
||||
const { account, chainId, connector, ENSName } = useWeb3React()
|
||||
const [isCopied, setCopied] = useCopyClipboard()
|
||||
const copy = useCallback(() => {
|
||||
setCopied(account || '')
|
||||
}, [account, setCopied])
|
||||
const CopyText = styled(CopyHelper).attrs({
|
||||
InitialIcon: Copy,
|
||||
CopiedIcon: Copy,
|
||||
gap: 4,
|
||||
iconSize: 14,
|
||||
iconPosition: 'right',
|
||||
})``
|
||||
|
||||
const FadeInColumn = styled(Column)`
|
||||
${portfolioFadeInAnimation}
|
||||
`
|
||||
|
||||
const PortfolioDrawerContainer = styled(Column)`
|
||||
flex: 1;
|
||||
`
|
||||
|
||||
export function PortfolioArrow({ change, ...rest }: { change: number } & IconProps) {
|
||||
const theme = useTheme()
|
||||
return change < 0 ? (
|
||||
<ArrowDownRight color={theme.accentCritical} size={20} {...rest} />
|
||||
) : (
|
||||
<ArrowUpRight color={theme.accentSuccess} size={20} {...rest} />
|
||||
)
|
||||
}
|
||||
|
||||
export default function AuthenticatedHeader({ account, openSettings }: { account: string; openSettings: () => void }) {
|
||||
const { connector, ENSName } = useWeb3React()
|
||||
const dispatch = useAppDispatch()
|
||||
const balanceString = useCurrencyBalanceString(account ?? '')
|
||||
const {
|
||||
nativeCurrency: { symbol: nativeCurrencySymbol },
|
||||
explorer,
|
||||
} = getChainInfoOrDefault(chainId ? chainId : SupportedChainId.MAINNET)
|
||||
const navigate = useNavigate()
|
||||
const closeModal = useCloseModal()
|
||||
const setSellPageState = useProfilePageState((state) => state.setProfilePageState)
|
||||
@@ -171,9 +168,8 @@ const AuthenticatedHeader = () => {
|
||||
|
||||
const unclaimedAmount: CurrencyAmount<Token> | undefined = useUserUnclaimedAmount(account)
|
||||
const isUnclaimed = useUserHasAvailableClaim(account)
|
||||
const connectionType = getConnection(connector).type
|
||||
const nativeCurrency = useNativeCurrency()
|
||||
const nativeCurrencyPrice = useStablecoinPrice(nativeCurrency ?? undefined)
|
||||
const getConnection = useGetConnection()
|
||||
const connection = getConnection(connector)
|
||||
const openClaimModal = useToggleModal(ApplicationModal.ADDRESS_CLAIM)
|
||||
const openNftModal = useToggleModal(ApplicationModal.UNISWAP_NFT_AIRDROP_CLAIM)
|
||||
const disconnect = useCallback(() => {
|
||||
@@ -184,20 +180,16 @@ const AuthenticatedHeader = () => {
|
||||
dispatch(updateSelectedWallet({ wallet: undefined }))
|
||||
}, [connector, dispatch])
|
||||
|
||||
const amountUSD = useMemo(() => {
|
||||
if (!nativeCurrencyPrice || !balanceString) return undefined
|
||||
const price = parseFloat(nativeCurrencyPrice.toFixed(5))
|
||||
const balance = parseFloat(balanceString)
|
||||
return price * balance
|
||||
}, [balanceString, nativeCurrencyPrice])
|
||||
const toggleWalletDrawer = useToggleWalletDrawer()
|
||||
|
||||
const navigateToProfile = useCallback(() => {
|
||||
toggleWalletDrawer()
|
||||
resetSellAssets()
|
||||
setSellPageState(ProfilePageStateType.VIEWING)
|
||||
clearCollectionFilters()
|
||||
navigate('/nfts/profile')
|
||||
closeModal()
|
||||
}, [clearCollectionFilters, closeModal, navigate, resetSellAssets, setSellPageState])
|
||||
}, [clearCollectionFilters, closeModal, navigate, resetSellAssets, setSellPageState, toggleWalletDrawer])
|
||||
|
||||
const openFiatOnrampModal = useOpenModal(ApplicationModal.FIAT_ONRAMP)
|
||||
const openFoRModalWithAnalytics = useCallback(() => {
|
||||
@@ -227,49 +219,71 @@ const AuthenticatedHeader = () => {
|
||||
const openFiatOnrampUnavailableTooltip = useCallback(() => setShow(true), [setShow])
|
||||
const closeFiatOnrampUnavailableTooltip = useCallback(() => setShow(false), [setShow])
|
||||
|
||||
const { data: portfolioBalances } = usePortfolioBalancesQuery({
|
||||
variables: { ownerAddress: account ?? '' },
|
||||
fetchPolicy: 'cache-only', // PrefetchBalancesWrapper handles balance fetching/staleness; this component only reads from cache
|
||||
})
|
||||
const portfolio = portfolioBalances?.portfolios?.[0]
|
||||
const totalBalance = portfolio?.tokensTotalDenominatedValue?.value
|
||||
const absoluteChange = portfolio?.tokensTotalDenominatedValueChange?.absolute?.value
|
||||
const percentChange = portfolio?.tokensTotalDenominatedValueChange?.percentage?.value
|
||||
|
||||
return (
|
||||
<>
|
||||
<AuthenticatedHeaderWrapper>
|
||||
<HeaderWrapper>
|
||||
<FlexContainer>
|
||||
<StatusIcon connectionType={connectionType} size={24} />
|
||||
{ENSName ? (
|
||||
<StatusWrapper>
|
||||
<StatusIcon connection={connection} size={40} />
|
||||
{account && (
|
||||
<AccountNamesWrapper>
|
||||
<ENSNameContainer>{ENSName}</ENSNameContainer>
|
||||
<AccountContainer>{account && shortenAddress(account, 2, 4)}</AccountContainer>
|
||||
<ThemedText.SubHeader color="textPrimary" fontWeight={500}>
|
||||
<CopyText toCopy={ENSName ?? account}>{ENSName ?? shortenAddress(account, 4, 4)}</CopyText>
|
||||
</ThemedText.SubHeader>
|
||||
{/* Displays smaller view of account if ENS name was rendered above */}
|
||||
{ENSName && (
|
||||
<ThemedText.BodySmall color="textTertiary">
|
||||
<CopyText toCopy={account}>{shortenAddress(account, 4, 4)}</CopyText>
|
||||
</ThemedText.BodySmall>
|
||||
)}
|
||||
</AccountNamesWrapper>
|
||||
) : (
|
||||
<ThemedText.SubHeader marginTop="2.5px">{account && shortenAddress(account, 2, 4)}</ThemedText.SubHeader>
|
||||
)}
|
||||
</FlexContainer>
|
||||
</StatusWrapper>
|
||||
<IconContainer>
|
||||
<IconButton onClick={copy} Icon={Copy}>
|
||||
{isCopied ? <Trans>Copied!</Trans> : <Trans>Copy</Trans>}
|
||||
</IconButton>
|
||||
<IconButton href={`${explorer}address/${account}`} target="_blank" Icon={ExternalLinkIcon}>
|
||||
<Trans>Explore</Trans>
|
||||
</IconButton>
|
||||
<IconButton data-testid="wallet-disconnect" onClick={disconnect} Icon={Power}>
|
||||
<Trans>Disconnect</Trans>
|
||||
</IconButton>
|
||||
<IconButton data-testid="wallet-settings" onClick={openSettings} Icon={Settings} />
|
||||
<TraceEvent
|
||||
events={[BrowserEvent.onClick]}
|
||||
name={SharedEventName.ELEMENT_CLICKED}
|
||||
element={InterfaceElementName.DISCONNECT_WALLET_BUTTON}
|
||||
>
|
||||
<IconButton data-testid="wallet-disconnect" onClick={disconnect} Icon={Power} />
|
||||
</TraceEvent>
|
||||
</IconContainer>
|
||||
</HeaderWrapper>
|
||||
<Column>
|
||||
<BalanceWrapper>
|
||||
<ThemedText.SubHeaderSmall>ETH Balance</ThemedText.SubHeaderSmall>
|
||||
<ThemedText.HeadlineLarge fontSize={36} fontWeight={400}>
|
||||
{balanceString} {nativeCurrencySymbol}
|
||||
</ThemedText.HeadlineLarge>
|
||||
{amountUSD !== undefined && <USDText>{formatUSDPrice(amountUSD)} USD</USDText>}
|
||||
</BalanceWrapper>
|
||||
<ProfileButton
|
||||
data-testid="nft-view-self-nfts"
|
||||
onClick={navigateToProfile}
|
||||
size={ButtonSize.medium}
|
||||
emphasis={ButtonEmphasis.medium}
|
||||
>
|
||||
<Trans>View and sell NFTs</Trans>
|
||||
</ProfileButton>
|
||||
<BuyCryptoButton
|
||||
<PortfolioDrawerContainer>
|
||||
{totalBalance !== undefined ? (
|
||||
<FadeInColumn gap="xs">
|
||||
<ThemedText.HeadlineLarge fontWeight={500}>
|
||||
{formatNumber(totalBalance, NumberType.PortfolioBalance)}
|
||||
</ThemedText.HeadlineLarge>
|
||||
<AutoRow marginBottom="20px">
|
||||
{absoluteChange !== 0 && percentChange && (
|
||||
<>
|
||||
<PortfolioArrow change={absoluteChange as number} />
|
||||
<ThemedText.BodySecondary>
|
||||
{`${formatNumber(Math.abs(absoluteChange as number), NumberType.PortfolioBalance)} (${formatDelta(
|
||||
percentChange
|
||||
)})`}
|
||||
</ThemedText.BodySecondary>
|
||||
</>
|
||||
)}
|
||||
</AutoRow>
|
||||
</FadeInColumn>
|
||||
) : (
|
||||
<Column gap="xs">
|
||||
<LoadingBubble height="44px" width="170px" />
|
||||
<LoadingBubble height="16px" width="100px" margin="4px 0 20px 0" />
|
||||
</Column>
|
||||
)}
|
||||
<HeaderButton
|
||||
size={ButtonSize.medium}
|
||||
emphasis={ButtonEmphasis.medium}
|
||||
onClick={handleBuyCryptoClick}
|
||||
@@ -287,7 +301,15 @@ const AuthenticatedHeader = () => {
|
||||
<Trans>Buy crypto</Trans>
|
||||
</>
|
||||
)}
|
||||
</BuyCryptoButton>
|
||||
</HeaderButton>
|
||||
<HeaderButton
|
||||
data-testid="nft-view-self-nfts"
|
||||
onClick={navigateToProfile}
|
||||
size={ButtonSize.medium}
|
||||
emphasis={ButtonEmphasis.medium}
|
||||
>
|
||||
<Trans>View and sell NFTs</Trans>
|
||||
</HeaderButton>
|
||||
{Boolean(!fiatOnrampAvailable && fiatOnrampAvailabilityChecked) && (
|
||||
<FiatOnrampNotAvailableText marginTop="8px">
|
||||
<Trans>Not available in your region</Trans>
|
||||
@@ -306,6 +328,7 @@ const AuthenticatedHeader = () => {
|
||||
</Tooltip>
|
||||
</FiatOnrampNotAvailableText>
|
||||
)}
|
||||
<MiniPortfolio account={account} />
|
||||
{isUnclaimed && (
|
||||
<UNIButton onClick={openClaimModal} size={ButtonSize.medium} emphasis={ButtonEmphasis.medium}>
|
||||
<Trans>Claim</Trans> {unclaimedAmount?.toFixed(0, { groupSeparator: ',' } ?? '-')} <Trans>reward</Trans>
|
||||
@@ -316,9 +339,7 @@ const AuthenticatedHeader = () => {
|
||||
<Trans>Claim Uniswap NFT Airdrop</Trans>
|
||||
</UNIButton>
|
||||
)}
|
||||
</Column>
|
||||
</>
|
||||
</PortfolioDrawerContainer>
|
||||
</AuthenticatedHeaderWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
export default AuthenticatedHeader
|
||||
|
||||
@@ -1,181 +1,41 @@
|
||||
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;
|
||||
`
|
||||
enum MenuState {
|
||||
DEFAULT,
|
||||
SETTINGS,
|
||||
}
|
||||
|
||||
const CenterVertically = styled.div`
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
`
|
||||
|
||||
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 />
|
||||
) : (
|
||||
<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>
|
||||
{menu === MenuState.DEFAULT &&
|
||||
(isAuthenticated ? (
|
||||
<AuthenticatedHeader account={account} openSettings={openSettings} />
|
||||
) : (
|
||||
<WalletModal openSettings={openSettings} />
|
||||
))}
|
||||
{menu === MenuState.SETTINGS && <SettingsMenu onClose={closeSettings} />}
|
||||
</DefaultMenuWrap>
|
||||
)
|
||||
}
|
||||
|
||||
export default WalletDropdown
|
||||
export default DefaultMenu
|
||||
|
||||
63
src/components/WalletDropdown/DownloadButton.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
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>
|
||||
}
|
||||
28
src/components/WalletDropdown/GitVersionRow.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
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
|
||||
}
|
||||