Compare commits
175 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4529e3cc88 | ||
|
|
4d47470f33 | ||
|
|
aedc020646 | ||
|
|
fd8085722e | ||
|
|
a60a85db54 | ||
|
|
ad2472eac6 | ||
|
|
f4d4acacae | ||
|
|
a5d7af192c | ||
|
|
21a2863ae3 | ||
|
|
1f871d4e73 | ||
|
|
3690936aff | ||
|
|
e95e2321b4 | ||
|
|
8b1bf09ff1 | ||
|
|
6383e9e4bf | ||
|
|
515ce9253d | ||
|
|
23ed384802 | ||
|
|
9ae31aa26e | ||
|
|
642c489240 | ||
|
|
778ea8ee42 | ||
|
|
36900fe6c6 | ||
|
|
40eb28f1e1 | ||
|
|
7c3ee78715 | ||
|
|
ccac51ec1f | ||
|
|
77747f9f6f | ||
|
|
0622ff30f6 | ||
|
|
982c99b07f | ||
|
|
484a7d49f6 | ||
|
|
082591d5dd | ||
|
|
aa03f97890 | ||
|
|
fd12a0d6e7 | ||
|
|
0176c74430 | ||
|
|
35613cc979 | ||
|
|
b0d71f10e9 | ||
|
|
1cfb3d8034 | ||
|
|
a650807c96 | ||
|
|
6efd7ca779 | ||
|
|
d658720505 | ||
|
|
bba40846e1 | ||
|
|
8fa6c87015 | ||
|
|
933d02b275 | ||
|
|
7738a6b9e0 | ||
|
|
2db4b1da3d | ||
|
|
06c85b744f | ||
|
|
aa29ea80a9 | ||
|
|
3613dc2d4a | ||
|
|
e22554b4c4 | ||
|
|
5f1625f5dc | ||
|
|
c1e6dd7335 | ||
|
|
45419c2739 | ||
|
|
c2342a86d6 | ||
|
|
504cd5b848 | ||
|
|
1faf13639c | ||
|
|
69c084ebe7 | ||
|
|
e5ac7e77da | ||
|
|
0614358a5e | ||
|
|
42784e6121 | ||
|
|
830500dc3b | ||
|
|
5af32592aa | ||
|
|
0f36a99e98 | ||
|
|
5fe9f3f6e8 | ||
|
|
167fff16a0 | ||
|
|
a6eff7823a | ||
|
|
1aba4fbcd7 | ||
|
|
714d215cda | ||
|
|
0f32ed34f7 | ||
|
|
3703e843f8 | ||
|
|
3c3158f443 | ||
|
|
f933e538e6 | ||
|
|
02b678b4a9 | ||
|
|
c25a2cfc00 | ||
|
|
a5d75cad5b | ||
|
|
40784963a5 | ||
|
|
6ca8e4f664 | ||
|
|
91c0580825 | ||
|
|
c518501e7b | ||
|
|
d59e4f334e | ||
|
|
4c3528a03d | ||
|
|
ed82f9ff8a | ||
|
|
dd5a22ce83 | ||
|
|
185c1f6772 | ||
|
|
93e6b65cb3 | ||
|
|
965a745d5e | ||
|
|
6d97590c0f | ||
|
|
8e7ab6f8c3 | ||
|
|
61729610c2 | ||
|
|
53860dd8e4 | ||
|
|
19028c1d82 | ||
|
|
6676d80707 | ||
|
|
f9f804c381 | ||
|
|
0a0b56b13d | ||
|
|
480f3f29f3 | ||
|
|
5f2072f449 | ||
|
|
4e144c7fb5 | ||
|
|
cfbb6a7129 | ||
|
|
61f03af20a | ||
|
|
5eb1274f97 | ||
|
|
9eb7d45aea | ||
|
|
2e1d4fdda1 | ||
|
|
e85b6e4cc6 | ||
|
|
9c334bc865 | ||
|
|
6a833fc740 | ||
|
|
80ed8eb6c2 | ||
|
|
a14d2df8e6 | ||
|
|
09511b06f2 | ||
|
|
9def686344 | ||
|
|
a88c083758 | ||
|
|
1a6cad4a8f | ||
|
|
505b3f2a20 | ||
|
|
1b5a145738 | ||
|
|
dbdd3a8e16 | ||
|
|
78e438294f | ||
|
|
2c014c6f38 | ||
|
|
7b3b7864ad | ||
|
|
e35eefbeb3 | ||
|
|
049a7d1d6a | ||
|
|
28d6c6454e | ||
|
|
f96ecb59eb | ||
|
|
a4c54ff953 | ||
|
|
d4cb32c4c3 | ||
|
|
efb76200ce | ||
|
|
a96bdaad04 | ||
|
|
b89860dc53 | ||
|
|
e640dccebd | ||
|
|
4710b832fa | ||
|
|
fb07666e23 | ||
|
|
22f64a98a1 | ||
|
|
50f6401a8a | ||
|
|
6480b947ef | ||
|
|
f41cbbb58f | ||
|
|
0cb098b9d4 | ||
|
|
76f5638583 | ||
|
|
c840de73db | ||
|
|
ab4271b2ff | ||
|
|
6f0586c596 | ||
|
|
9f33ed06dd | ||
|
|
85f4cec829 | ||
|
|
5e23501d58 | ||
|
|
6bc98363cc | ||
|
|
e15ccc3c79 | ||
|
|
e8880be1d9 | ||
|
|
0e9b05405d | ||
|
|
55d85d2623 | ||
|
|
7fb7517a1a | ||
|
|
1a6fe3c1a8 | ||
|
|
a49ff49185 | ||
|
|
ca1dc593d9 | ||
|
|
afacc4a348 | ||
|
|
91157b7a43 | ||
|
|
d5e676efb5 | ||
|
|
9ac83bea7e | ||
|
|
53b9a847ca | ||
|
|
04f9127961 | ||
|
|
5364eb5715 | ||
|
|
5126e24d19 | ||
|
|
a06bb79039 | ||
|
|
a446dc7f10 | ||
|
|
e903a335d6 | ||
|
|
3be5e9b5fe | ||
|
|
818e98328e | ||
|
|
aa3225c21c | ||
|
|
35d66f1e09 | ||
|
|
e156635f77 | ||
|
|
d2a97c62ed | ||
|
|
6aa999f713 | ||
|
|
0a3f8636e7 | ||
|
|
f5de7178d9 | ||
|
|
6c203cc990 | ||
|
|
3a40159147 | ||
|
|
c6f6bd446b | ||
|
|
416212be3b | ||
|
|
ce9f4525a3 | ||
|
|
23a250aae0 | ||
|
|
5d5e0f4596 | ||
|
|
d6199e0f61 | ||
|
|
a611cd03d8 |
4
.env
4
.env
@@ -2,4 +2,6 @@ REACT_APP_INFURA_KEY="4bf032f2d38a4ed6bb975b80d6340847"
|
||||
REACT_APP_AMPLITUDE_PROXY_URL="https://api.uniswap.org/v1/amplitude-proxy"
|
||||
REACT_APP_AWS_API_REGION="us-east-2"
|
||||
REACT_APP_AWS_API_ENDPOINT="https://beta.api.uniswap.org/v1/graphql"
|
||||
REACT_APP_TEMP_API_URL="https://temp.api.uniswap.org/v1"
|
||||
REACT_APP_TEMP_API_URL="https://temp.api.uniswap.org/v1"
|
||||
REACT_APP_SENTRY_DSN="https://a3c62e400b8748b5a8d007150e2f38b7@o1037921.ingest.sentry.io/4504255148851200"
|
||||
ESLINT_NO_DEV_ERRORS=true
|
||||
|
||||
2
.eslintignore
Normal file
2
.eslintignore
Normal file
@@ -0,0 +1,2 @@
|
||||
*.config.ts
|
||||
*.d.ts
|
||||
@@ -11,6 +11,14 @@
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
},
|
||||
"import/parsers": {
|
||||
"@typescript-eslint/parser": [".ts", ".tsx"]
|
||||
},
|
||||
"import/resolver": {
|
||||
"typescript": {
|
||||
"alwaysTryTypes": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"ignorePatterns": [
|
||||
@@ -38,10 +46,12 @@
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:react-hooks/recommended",
|
||||
"prettier/@typescript-eslint",
|
||||
"plugin:prettier/recommended"
|
||||
"plugin:prettier/recommended",
|
||||
"plugin:import/typescript"
|
||||
],
|
||||
"plugins": ["simple-import-sort", "unused-imports"],
|
||||
"plugins": ["import", "simple-import-sort", "unused-imports"],
|
||||
"rules": {
|
||||
"import/no-unused-modules": [2, { "unusedExports": true }],
|
||||
"unused-imports/no-unused-imports": "error",
|
||||
"simple-import-sort/imports": "error",
|
||||
"simple-import-sort/exports": "error",
|
||||
@@ -52,7 +62,7 @@
|
||||
"@typescript-eslint/ban-ts-ignore": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"react/react-in-jsx-scope": "off",
|
||||
"react/jsx-curly-brace-presence": ["error", {"props": "never", "children": "never" }],
|
||||
"react/jsx-curly-brace-presence": ["error", { "props": "never", "children": "never" }],
|
||||
"object-shorthand": ["error", "always"],
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
|
||||
48
.github/workflows/release.yaml
vendored
48
.github/workflows/release.yaml
vendored
@@ -71,29 +71,6 @@ jobs:
|
||||
with:
|
||||
cidv0: ${{ steps.pinata.outputs.hash }}
|
||||
|
||||
- uses: actions/cache@v3
|
||||
id: cypress-cache
|
||||
with:
|
||||
path: /home/runner/.cache/Cypress
|
||||
key: ${{ runner.os }}-cypress-${{ hashFiles('node_modules/cypress') }}
|
||||
- if: steps.cypress-cache.outputs.cache-hit != 'true'
|
||||
run: yarn cypress install
|
||||
- uses: cypress-io/github-action@v4
|
||||
with:
|
||||
install: false
|
||||
browser: chrome
|
||||
config-file: cypress.release.config.ts
|
||||
config: baseUrl=https://cloudflare-ipfs.com/ipfs/${{ steps.pinata.outputs.hash }}
|
||||
|
||||
- name: Update DNS with new IPFS hash
|
||||
env:
|
||||
CLOUDFLARE_TOKEN: ${{ secrets.CLOUDFLARE_TOKEN }}
|
||||
CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }}
|
||||
CLOUDFLARE_GATEWAY_ID: ${{ secrets.CLOUDFLARE_GATEWAY_ID }}
|
||||
uses: Uniswap/cloudflare-update-web3-gateway@b3205288b1c6d0acb63fa3bd8fb686c72a9e3f3e
|
||||
with:
|
||||
cid: ${{ steps.pinata.outputs.hash }}
|
||||
|
||||
- name: Release
|
||||
uses: actions/create-release@v1.1.0
|
||||
env:
|
||||
@@ -119,3 +96,28 @@ jobs:
|
||||
- [ipfs://${{ steps.upload.outputs.hash }}/](ipfs://${{ steps.pinata.outputs.hash }}/)
|
||||
|
||||
${{ needs.tag.outputs.changelog }}
|
||||
|
||||
- name: Setup node@16 (required by Cloudflare Pages)
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
- name: Update Cloudflare Pages deployment
|
||||
uses: cloudflare/pages-action@364c7ca09a4b57837c5967871d64a2c31adb8c0d
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
projectName: ${{ secrets.CLOUDFLARE_PROJECT_NAME }}
|
||||
directory: build
|
||||
githubToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Upload source maps to Sentry
|
||||
uses: getsentry/action-release@bd5f874fcda966ba48139b0140fb3ec0cb3aabdd
|
||||
env:
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
||||
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
|
||||
with:
|
||||
environment: production
|
||||
sourcemaps: './build/static/js'
|
||||
url_prefix: '/static/js'
|
||||
27
.github/workflows/revert.yaml
vendored
Normal file
27
.github/workflows/revert.yaml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: Revert
|
||||
on:
|
||||
# manual trigger
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
- run: yarn prepare
|
||||
- run: yarn build
|
||||
|
||||
- name: Setup node@16 (required by Cloudflare Pages)
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
- name: Update Cloudflare Pages deployment
|
||||
uses: cloudflare/pages-action@364c7ca09a4b57837c5967871d64a2c31adb8c0d
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
projectName: ${{ secrets.CLOUDFLARE_PROJECT_NAME }}
|
||||
directory: build
|
||||
githubToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -59,7 +59,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
containers: [1, 2, 3, 4, 5]
|
||||
containers: [1, 2, 3, 4]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
|
||||
25
.snyk
Normal file
25
.snyk
Normal file
@@ -0,0 +1,25 @@
|
||||
# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
|
||||
version: v1.25.0
|
||||
# ignores vulnerabilities until expiry date; change duration by modifying expiry date
|
||||
ignore:
|
||||
SNYK-JS-OPENZEPPELINCONTRACTS-2964946:
|
||||
- '*':
|
||||
reason: None Given
|
||||
expires: 2099-01-01T00:00:00.000Z
|
||||
created: 2022-12-08T16:25:57.347Z
|
||||
SNYK-JS-OPENZEPPELINCONTRACTS-2958047:
|
||||
- '*':
|
||||
reason: None Given
|
||||
expires: 2099-01-01T00:00:00.000Z
|
||||
created: 2022-12-08T16:26:09.720Z
|
||||
SNYK-JS-OPENZEPPELINCONTRACTS-2958050:
|
||||
- '*':
|
||||
reason: None Given
|
||||
expires: 2099-01-01T00:00:00.000Z
|
||||
created: 2022-12-08T16:26:17.702Z
|
||||
SNYK-JS-OPENZEPPELINCONTRACTS-2965580:
|
||||
- '*':
|
||||
reason: None Given
|
||||
expires: 2099-01-01T00:00:00.000Z
|
||||
created: 2022-12-08T16:26:34.283Z
|
||||
patch: {}
|
||||
@@ -5,10 +5,6 @@ describe('Landing Page', () => {
|
||||
cy.screenshot()
|
||||
})
|
||||
|
||||
it('redirects to url /swap', () => {
|
||||
cy.url().should('include', '/swap')
|
||||
})
|
||||
|
||||
it('allows navigation to pool', () => {
|
||||
cy.get('#pool-nav-link').click()
|
||||
cy.url().should('include', '/pool')
|
||||
|
||||
64
cypress/e2e/nfts.test.ts
Normal file
64
cypress/e2e/nfts.test.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { getTestSelector } from '../utils'
|
||||
|
||||
const COLLECTION_ADDRESS = '0xbd3531da5cf5857e7cfaa92426877b022e612cf8'
|
||||
|
||||
describe('Testing nfts', () => {
|
||||
before(() => {
|
||||
cy.visit('/')
|
||||
})
|
||||
|
||||
it('should load nft leaderboard', () => {
|
||||
cy.get(getTestSelector('nft-nav')).first().click()
|
||||
cy.get(getTestSelector('nft-nav')).first().should('exist')
|
||||
cy.get(getTestSelector('nft-nav')).first().click()
|
||||
cy.get(getTestSelector('nft-trending-collection')).its('length').should('be.gte', 25)
|
||||
})
|
||||
|
||||
it('should load pudgy penguin collection page', () => {
|
||||
cy.visit(`/#/nfts/collection/${COLLECTION_ADDRESS}`)
|
||||
cy.get(getTestSelector('nft-collection-asset')).should('exist')
|
||||
cy.get(getTestSelector('nft-collection-filter-buy-now')).should('not.exist')
|
||||
cy.get(getTestSelector('nft-filter')).first().click()
|
||||
cy.get(getTestSelector('nft-collection-filter-buy-now')).should('exist')
|
||||
})
|
||||
|
||||
it('should be able to open bag and open sweep', () => {
|
||||
cy.get(getTestSelector('nft-sweep-button')).first().click()
|
||||
cy.get(getTestSelector('nft-empty-bag')).should('exist')
|
||||
cy.get(getTestSelector('nft-sweep-slider')).should('exist')
|
||||
})
|
||||
|
||||
it('should be able to navigate to activity', () => {
|
||||
cy.get(getTestSelector('nft-activity')).first().click()
|
||||
cy.get(getTestSelector('nft-activity-row')).should('exist')
|
||||
})
|
||||
|
||||
it('should go to the details page', () => {
|
||||
cy.visit(`/#/nfts/collection/${COLLECTION_ADDRESS}`)
|
||||
cy.get(getTestSelector('nft-filter')).first().click()
|
||||
cy.get(getTestSelector('nft-collection-filter-buy-now')).click()
|
||||
cy.get(getTestSelector('nft-details-link')).first().click()
|
||||
cy.get(getTestSelector('nft-details-traits')).should('exist')
|
||||
cy.get(getTestSelector('nft-details-activity')).should('exist')
|
||||
cy.get(getTestSelector('nft-details-description')).should('exist')
|
||||
cy.get(getTestSelector('nft-details-asset-details')).should('exist')
|
||||
})
|
||||
|
||||
it('should toggle buy now on details page', () => {
|
||||
cy.get(getTestSelector('nft-details-description-text')).should('exist')
|
||||
cy.get(getTestSelector('nft-details-description')).click()
|
||||
cy.get(getTestSelector('nft-details-description-text')).should('not.exist')
|
||||
cy.get(getTestSelector('nft-details-toggle-bag')).eq(1).click()
|
||||
cy.get(getTestSelector('nft-bag')).should('exist')
|
||||
})
|
||||
|
||||
it('should go view my nfts', () => {
|
||||
cy.get(getTestSelector('web3-status-connected')).click()
|
||||
cy.get(getTestSelector('nft-view-self-nfts')).click()
|
||||
cy.get(getTestSelector('nft-explore-nfts-button')).should('exist')
|
||||
cy.get(getTestSelector('nft-no-nfts-selected')).should('exist')
|
||||
cy.get(getTestSelector('nft-bag-close-icon')).click()
|
||||
cy.get(getTestSelector('nft-explore-nfts-button')).click()
|
||||
cy.get(getTestSelector('nft-welcome-modal')).should('exist')
|
||||
})
|
||||
})
|
||||
@@ -3,8 +3,8 @@ describe('Redirect', () => {
|
||||
cy.visit('/create-proposal')
|
||||
cy.url().should('match', /\/vote\/create-proposal/)
|
||||
})
|
||||
it('should redirect to /swap when visiting nonexist url', () => {
|
||||
it('should redirect to /not-found when visiting nonexist url', () => {
|
||||
cy.visit('/none-exist-url')
|
||||
cy.url().should('match', /\/swap/)
|
||||
cy.url().should('match', /\/not-found/)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -22,10 +22,4 @@ describe('Remove Liquidity', () => {
|
||||
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'WETH')
|
||||
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'WETH')
|
||||
})
|
||||
|
||||
it.skip('token not in storage is loaded', () => {
|
||||
cy.visit('/remove/v2/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
||||
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'SKL')
|
||||
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'MKR')
|
||||
})
|
||||
})
|
||||
|
||||
20
cypress/e2e/token.test.ts
Normal file
20
cypress/e2e/token.test.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { getTestSelector, getTestSelectorStartsWith } from '../utils'
|
||||
|
||||
describe('Testing tokens on uniswap page', () => {
|
||||
before(() => {
|
||||
cy.visit('/')
|
||||
})
|
||||
|
||||
it('should load token leaderboard', () => {
|
||||
cy.visit('/tokens/ethereum')
|
||||
cy.get(getTestSelectorStartsWith('token-table')).its('length').should('be.gte', 25)
|
||||
})
|
||||
|
||||
it('should load go to ethereum token and return to token list page', () => {
|
||||
cy.visit('/tokens/ethereum')
|
||||
cy.get(getTestSelector('token-table-row-Ether')).click()
|
||||
cy.get(getTestSelector('token-details-stats')).should('exist')
|
||||
cy.get(getTestSelector('token-details-return-button')).click()
|
||||
cy.get(getTestSelectorStartsWith('token-table')).its('length').should('be.gte', 25)
|
||||
})
|
||||
})
|
||||
@@ -1,9 +1,8 @@
|
||||
import { FeatureFlag } from '../../src/featureFlags/flags/featureFlags'
|
||||
import { getTestSelector } from '../utils'
|
||||
|
||||
describe('Wallet Dropdown', () => {
|
||||
before(() => {
|
||||
cy.visit('/', { featureFlags: [FeatureFlag.navBar, FeatureFlag.tokenSafety] })
|
||||
cy.visit('/pool')
|
||||
})
|
||||
|
||||
it('should change the theme', () => {
|
||||
@@ -31,6 +30,8 @@ describe('Wallet Dropdown', () => {
|
||||
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')
|
||||
})
|
||||
|
||||
it('should select a language when not connected', () => {
|
||||
|
||||
@@ -65,6 +65,17 @@ beforeEach(() => {
|
||||
res.headers['origin'] = 'http://localhost:3000'
|
||||
res.continue()
|
||||
})
|
||||
|
||||
// Graphql security policies are based on Origin headers.
|
||||
// These are stripped by cypress because chromeWebSecurity === false; this adds them back in.
|
||||
cy.intercept('https://api.uniswap.org/v1/graphql', (res) => {
|
||||
res.headers['origin'] = 'https://app.uniswap.org'
|
||||
res.continue()
|
||||
})
|
||||
cy.intercept('https://beta.api.uniswap.org/v1/graphql', (res) => {
|
||||
res.headers['origin'] = 'https://app.uniswap.org'
|
||||
res.continue()
|
||||
})
|
||||
})
|
||||
|
||||
Cypress.on('uncaught:exception', (_err, _runnable) => {
|
||||
|
||||
@@ -12,12 +12,7 @@ import { Wallet } from '@ethersproject/wallet'
|
||||
const TEST_PRIVATE_KEY = '0xe580410d7c37d26c6ad1a837bbae46bc27f9066a466fb3a66e770523b4666d19'
|
||||
|
||||
// address of the above key
|
||||
export const TEST_ADDRESS_NEVER_USE = new Wallet(TEST_PRIVATE_KEY).address
|
||||
|
||||
export const TEST_ADDRESS_NEVER_USE_SHORTENED = `${TEST_ADDRESS_NEVER_USE.substr(
|
||||
0,
|
||||
6
|
||||
)}...${TEST_ADDRESS_NEVER_USE.substr(-4, 4)}`
|
||||
const TEST_ADDRESS_NEVER_USE = new Wallet(TEST_PRIVATE_KEY).address
|
||||
|
||||
const provider = new JsonRpcProvider('https://goerli.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847', 4)
|
||||
const signer = new Wallet(TEST_PRIVATE_KEY, provider)
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
export const getTestSelector = (selectorId: string) => `[data-testid=${selectorId}]`
|
||||
|
||||
export const getTestSelectorStartsWith = (selectorId: string) => `[data-testid^=${selectorId}]`
|
||||
|
||||
13
package.json
13
package.json
@@ -99,7 +99,9 @@
|
||||
"env-cmd": "^10.1.0",
|
||||
"eslint": "^7.11.0",
|
||||
"eslint-config-prettier": "^6.11.0",
|
||||
"eslint-import-resolver-typescript": "^3.5.2",
|
||||
"eslint-plugin-better-styled-components": "^1.1.2",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-prettier": "^3.1.3",
|
||||
"eslint-plugin-react": "^7.21.5",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
@@ -132,26 +134,29 @@
|
||||
"@reach/portal": "^0.10.3",
|
||||
"@react-hook/window-scroll": "^1.3.0",
|
||||
"@reduxjs/toolkit": "^1.6.1",
|
||||
"@sentry/react": "7.20.1",
|
||||
"@types/react-relay": "^13.0.2",
|
||||
"@types/react-window-infinite-loader": "^1.0.6",
|
||||
"@uniswap/analytics": "1.1.1",
|
||||
"@uniswap/analytics-events": "1.1.0",
|
||||
"@uniswap/analytics": "1.2.0",
|
||||
"@uniswap/analytics-events": "1.3.1",
|
||||
"@uniswap/conedison": "^1.1.0",
|
||||
"@uniswap/governance": "^1.0.2",
|
||||
"@uniswap/liquidity-staker": "^1.0.2",
|
||||
"@uniswap/merkle-distributor": "1.0.1",
|
||||
"@uniswap/permit2-sdk": "1.2.0",
|
||||
"@uniswap/redux-multicall": "^1.1.8",
|
||||
"@uniswap/router-sdk": "^1.3.0",
|
||||
"@uniswap/sdk-core": "^3.0.1",
|
||||
"@uniswap/smart-order-router": "^2.10.0",
|
||||
"@uniswap/token-lists": "^1.0.0-beta.30",
|
||||
"@uniswap/universal-router-sdk": "1.3.0",
|
||||
"@uniswap/v2-core": "1.0.0",
|
||||
"@uniswap/v2-periphery": "^1.1.0-beta.0",
|
||||
"@uniswap/v2-sdk": "^3.0.1",
|
||||
"@uniswap/v3-core": "1.0.0",
|
||||
"@uniswap/v3-periphery": "^1.1.1",
|
||||
"@uniswap/v3-sdk": "^3.9.0",
|
||||
"@uniswap/widgets": "2.20.0",
|
||||
"@uniswap/widgets": "2.22.11",
|
||||
"@vanilla-extract/css": "^1.7.2",
|
||||
"@vanilla-extract/css-utils": "^0.1.2",
|
||||
"@vanilla-extract/dynamic": "^2.0.2",
|
||||
@@ -201,7 +206,6 @@
|
||||
"qs": "^6.9.4",
|
||||
"rc-slider": "^10.0.1",
|
||||
"react": "^18.2.0",
|
||||
"react-confetti": "^6.0.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-feather": "^2.0.8",
|
||||
"react-ga4": "^1.4.1",
|
||||
@@ -212,6 +216,7 @@
|
||||
"react-query": "^3.39.1",
|
||||
"react-redux": "^8.0.2",
|
||||
"react-relay": "^14.1.0",
|
||||
"react-relay-network-modern": "^6.2.1",
|
||||
"react-router-dom": "^6.3.0",
|
||||
"react-spring": "^9.5.5",
|
||||
"react-table": "^7.8.0",
|
||||
|
||||
@@ -89,7 +89,9 @@
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
html {
|
||||
html,
|
||||
body,
|
||||
#root {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
|
||||
BIN
src/assets/images/404-page-dark.png
Normal file
BIN
src/assets/images/404-page-dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 83 KiB |
BIN
src/assets/images/404-page-light.png
Normal file
BIN
src/assets/images/404-page-light.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 83 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.0 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 19 KiB |
@@ -27,12 +27,12 @@ const TransactionState = styled(ExternalLink)<{ pending: boolean; success?: bool
|
||||
padding: 0.25rem 0rem;
|
||||
font-weight: 500;
|
||||
font-size: 0.825rem;
|
||||
color: ${({ theme }) => theme.deprecated_primary1};
|
||||
color: ${({ theme }) => theme.accentAction};
|
||||
`
|
||||
|
||||
const IconWrapper = styled.div<{ pending: boolean; success?: boolean }>`
|
||||
color: ${({ pending, success, theme }) =>
|
||||
pending ? theme.deprecated_primary1 : success ? theme.deprecated_green1 : theme.deprecated_red1};
|
||||
pending ? theme.accentAction : success ? theme.accentSuccess : theme.accentFailure};
|
||||
`
|
||||
|
||||
export default function Transaction({ hash }: { hash: string }) {
|
||||
|
||||
@@ -24,7 +24,7 @@ const HeaderRow = styled.div`
|
||||
${flexRowNoWrap};
|
||||
padding: 1rem 1rem;
|
||||
font-weight: 500;
|
||||
color: ${(props) => (props.color === 'blue' ? ({ theme }) => theme.deprecated_primary1 : 'inherit')};
|
||||
color: ${(props) => (props.color === 'blue' ? ({ theme }) => theme.accentAction : 'inherit')};
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
|
||||
padding: 1rem;
|
||||
`};
|
||||
@@ -65,7 +65,7 @@ const AccountGroupingRow = styled.div`
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-weight: 400;
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
|
||||
div {
|
||||
${flexColumnNoWrap};
|
||||
@@ -95,14 +95,14 @@ const LowerSection = styled.div`
|
||||
padding: 1.5rem;
|
||||
flex-grow: 1;
|
||||
overflow: auto;
|
||||
background-color: ${({ theme }) => theme.deprecated_bg2};
|
||||
background-color: ${({ theme }) => theme.backgroundInteractive};
|
||||
border-bottom-left-radius: 20px;
|
||||
border-bottom-right-radius: 20px;
|
||||
|
||||
h5 {
|
||||
margin: 0;
|
||||
font-weight: 400;
|
||||
color: ${({ theme }) => theme.deprecated_text3};
|
||||
color: ${({ theme }) => theme.textTertiary};
|
||||
}
|
||||
`
|
||||
|
||||
@@ -129,14 +129,14 @@ const AccountControl = styled.div`
|
||||
`
|
||||
|
||||
const AddressLink = styled(ExternalLink)`
|
||||
color: ${({ theme }) => theme.deprecated_text3};
|
||||
color: ${({ theme }) => theme.textTertiary};
|
||||
margin-left: 1rem;
|
||||
font-size: 0.825rem;
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
text-decoration: none !important;
|
||||
:hover {
|
||||
color: ${({ theme }) => theme.deprecated_text2};
|
||||
color: ${({ theme }) => theme.textSecondary};
|
||||
}
|
||||
`
|
||||
|
||||
@@ -160,7 +160,7 @@ const WalletName = styled.div`
|
||||
width: initial;
|
||||
font-size: 0.825rem;
|
||||
font-weight: 500;
|
||||
color: ${({ theme }) => theme.deprecated_text3};
|
||||
color: ${({ theme }) => theme.textTertiary};
|
||||
`
|
||||
|
||||
const TransactionListWrapper = styled.div`
|
||||
@@ -316,7 +316,7 @@ export default function AccountDetails({
|
||||
</LowerSection>
|
||||
) : (
|
||||
<LowerSection>
|
||||
<ThemedText.DeprecatedBody color={theme.deprecated_text1}>
|
||||
<ThemedText.DeprecatedBody color={theme.textPrimary}>
|
||||
<Trans>Your transactions will appear here...</Trans>
|
||||
</ThemedText.DeprecatedBody>
|
||||
</LowerSection>
|
||||
|
||||
@@ -4,6 +4,7 @@ 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'
|
||||
@@ -17,13 +18,14 @@ export enum TransactionState {
|
||||
Failed,
|
||||
}
|
||||
|
||||
const Grid = styled.a`
|
||||
const Grid = styled(ExternalLink)<{ isLastTransactionInList?: boolean }>`
|
||||
cursor: pointer;
|
||||
display: grid;
|
||||
grid-template-columns: 44px auto 24px;
|
||||
width: 100%;
|
||||
text-decoration: none;
|
||||
border-bottom: ${({ theme }) => `1px solid ${theme.backgroundOutline}`};
|
||||
border-bottom: ${({ theme, isLastTransactionInList }) =>
|
||||
isLastTransactionInList ? 'none' : `1px solid ${theme.backgroundOutline}`};
|
||||
padding: 12px;
|
||||
|
||||
&:hover {
|
||||
@@ -46,7 +48,13 @@ const IconStyleWrap = styled.span`
|
||||
height: 16px;
|
||||
`
|
||||
|
||||
export const TransactionSummary = ({ transactionDetails }: { transactionDetails: TransactionDetails }) => {
|
||||
export const TransactionSummary = ({
|
||||
transactionDetails,
|
||||
isLastTransactionInList = false,
|
||||
}: {
|
||||
transactionDetails: TransactionDetails
|
||||
isLastTransactionInList?: boolean
|
||||
}) => {
|
||||
const { chainId = 1 } = useWeb3React()
|
||||
const tx = transactionDetails
|
||||
const { explorer } = getChainInfoOrDefault(chainId ? chainId : SupportedChainId.MAINNET)
|
||||
@@ -67,7 +75,7 @@ export const TransactionSummary = ({ transactionDetails }: { transactionDetails:
|
||||
const link = `${explorer}tx/${hash}`
|
||||
|
||||
return chainId ? (
|
||||
<Grid href={link} target="_blank">
|
||||
<Grid href={link} target="_blank" isLastTransactionInList={isLastTransactionInList}>
|
||||
<LogoView info={info} />
|
||||
<TextContainer as="span">
|
||||
<TransactionBody info={info} transactionState={transactionState} />
|
||||
|
||||
@@ -26,7 +26,7 @@ const ContainerRow = styled.div<{ error: boolean }>`
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 1.25rem;
|
||||
border: 1px solid ${({ error, theme }) => (error ? theme.deprecated_red1 : theme.deprecated_bg2)};
|
||||
border: 1px solid ${({ error, theme }) => (error ? theme.accentFailure : theme.backgroundInteractive)};
|
||||
transition: border-color 300ms ${({ error }) => (error ? 'step-end' : 'step-start')},
|
||||
color 500ms ${({ error }) => (error ? 'step-end' : 'step-start')};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg1};
|
||||
@@ -45,7 +45,7 @@ const Input = styled.input<{ error?: boolean }>`
|
||||
width: 0;
|
||||
background-color: ${({ theme }) => theme.deprecated_bg1};
|
||||
transition: color 300ms ${({ error }) => (error ? 'step-end' : 'step-start')};
|
||||
color: ${({ error, theme }) => (error ? theme.deprecated_red1 : theme.deprecated_text1)};
|
||||
color: ${({ error, theme }) => (error ? theme.accentFailure : theme.textPrimary)};
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-weight: 500;
|
||||
@@ -109,7 +109,7 @@ export default function AddressInputPanel({
|
||||
<InputContainer>
|
||||
<AutoColumn gap="md">
|
||||
<RowBetween>
|
||||
<ThemedText.DeprecatedBlack color={theme.deprecated_text2} fontWeight={500} fontSize={14}>
|
||||
<ThemedText.DeprecatedBlack color={theme.textSecondary} fontWeight={500} fontSize={14}>
|
||||
{label ?? <Trans>Recipient</Trans>}
|
||||
</ThemedText.DeprecatedBlack>
|
||||
{address && chainId && (
|
||||
|
||||
@@ -17,7 +17,7 @@ const BadgeText = styled.div`
|
||||
`
|
||||
|
||||
const ActiveDot = styled.span`
|
||||
background-color: ${({ theme }) => theme.deprecated_success};
|
||||
background-color: ${({ theme }) => theme.accentSuccess};
|
||||
border-radius: 50%;
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
|
||||
@@ -19,24 +19,24 @@ interface BadgeProps {
|
||||
function pickBackgroundColor(variant: BadgeVariant | undefined, theme: DefaultTheme): string {
|
||||
switch (variant) {
|
||||
case BadgeVariant.NEGATIVE:
|
||||
return theme.deprecated_error
|
||||
return theme.accentFailure
|
||||
case BadgeVariant.POSITIVE:
|
||||
return theme.deprecated_success
|
||||
return theme.accentSuccess
|
||||
case BadgeVariant.PRIMARY:
|
||||
return theme.deprecated_primary1
|
||||
return theme.accentAction
|
||||
case BadgeVariant.WARNING:
|
||||
return theme.deprecated_warning
|
||||
return theme.accentWarning
|
||||
case BadgeVariant.WARNING_OUTLINE:
|
||||
return 'transparent'
|
||||
default:
|
||||
return theme.deprecated_bg2
|
||||
return theme.backgroundInteractive
|
||||
}
|
||||
}
|
||||
|
||||
function pickBorder(variant: BadgeVariant | undefined, theme: DefaultTheme): string {
|
||||
switch (variant) {
|
||||
case BadgeVariant.WARNING_OUTLINE:
|
||||
return `1px solid ${theme.deprecated_warning}`
|
||||
return `1px solid ${theme.accentWarning}`
|
||||
default:
|
||||
return 'unset'
|
||||
}
|
||||
@@ -45,15 +45,15 @@ function pickBorder(variant: BadgeVariant | undefined, theme: DefaultTheme): str
|
||||
function pickFontColor(variant: BadgeVariant | undefined, theme: DefaultTheme): string {
|
||||
switch (variant) {
|
||||
case BadgeVariant.NEGATIVE:
|
||||
return readableColor(theme.deprecated_error)
|
||||
return readableColor(theme.accentFailure)
|
||||
case BadgeVariant.POSITIVE:
|
||||
return readableColor(theme.deprecated_success)
|
||||
return readableColor(theme.accentSuccess)
|
||||
case BadgeVariant.WARNING:
|
||||
return readableColor(theme.deprecated_warning)
|
||||
return readableColor(theme.accentWarning)
|
||||
case BadgeVariant.WARNING_OUTLINE:
|
||||
return theme.deprecated_warning
|
||||
return theme.accentWarning
|
||||
default:
|
||||
return readableColor(theme.deprecated_bg2)
|
||||
return readableColor(theme.backgroundInteractive)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ export const BaseButton = styled(RebassButton)<
|
||||
border-radius: ${({ $borderRadius }) => $borderRadius ?? '20px'};
|
||||
outline: none;
|
||||
border: 1px solid transparent;
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@@ -57,21 +57,21 @@ export const ButtonPrimary = styled(BaseButton)`
|
||||
padding: 16px;
|
||||
color: ${({ theme }) => theme.accentTextLightPrimary};
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, theme.deprecated_primary1)};
|
||||
background-color: ${({ theme }) => darken(0.05, theme.deprecated_primary1)};
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, theme.accentAction)};
|
||||
background-color: ${({ theme }) => darken(0.05, theme.accentAction)};
|
||||
}
|
||||
&:hover {
|
||||
background-color: ${({ theme }) => darken(0.05, theme.deprecated_primary1)};
|
||||
background-color: ${({ theme }) => darken(0.05, theme.accentAction)};
|
||||
}
|
||||
&:active {
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, theme.deprecated_primary1)};
|
||||
background-color: ${({ theme }) => darken(0.1, theme.deprecated_primary1)};
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, theme.accentAction)};
|
||||
background-color: ${({ theme }) => darken(0.1, theme.accentAction)};
|
||||
}
|
||||
&:disabled {
|
||||
background-color: ${({ theme, altDisabledStyle, disabled }) =>
|
||||
altDisabledStyle ? (disabled ? theme.deprecated_primary1 : theme.deprecated_bg2) : theme.deprecated_bg2};
|
||||
altDisabledStyle ? (disabled ? theme.accentAction : theme.backgroundInteractive) : theme.backgroundInteractive};
|
||||
color: ${({ altDisabledStyle, disabled, theme }) =>
|
||||
altDisabledStyle ? (disabled ? theme.deprecated_white : theme.deprecated_text2) : theme.deprecated_text2};
|
||||
altDisabledStyle ? (disabled ? theme.white : theme.textSecondary) : theme.textSecondary};
|
||||
cursor: auto;
|
||||
box-shadow: none;
|
||||
border: 1px solid transparent;
|
||||
@@ -79,6 +79,13 @@ export const ButtonPrimary = styled(BaseButton)`
|
||||
}
|
||||
`
|
||||
|
||||
export const SmallButtonPrimary = styled(ButtonPrimary)`
|
||||
width: auto;
|
||||
font-size: 16px;
|
||||
padding: 10px 16px;
|
||||
border-radius: 12px;
|
||||
`
|
||||
|
||||
export const ButtonLight = styled(BaseButton)`
|
||||
background-color: ${({ theme }) => theme.accentActionSoft};
|
||||
color: ${({ theme }) => theme.accentAction};
|
||||
@@ -110,21 +117,21 @@ export const ButtonLight = styled(BaseButton)`
|
||||
|
||||
export const ButtonGray = styled(BaseButton)`
|
||||
background-color: ${({ theme }) => theme.deprecated_bg1};
|
||||
color: ${({ theme }) => theme.deprecated_text2};
|
||||
color: ${({ theme }) => theme.textSecondary};
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
|
||||
&:hover {
|
||||
background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.deprecated_bg2)};
|
||||
background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.backgroundInteractive)};
|
||||
}
|
||||
&:active {
|
||||
background-color: ${({ theme, disabled }) => !disabled && darken(0.1, theme.deprecated_bg2)};
|
||||
background-color: ${({ theme, disabled }) => !disabled && darken(0.1, theme.backgroundInteractive)};
|
||||
}
|
||||
`
|
||||
|
||||
export const ButtonSecondary = styled(BaseButton)`
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_primary4};
|
||||
color: ${({ theme }) => theme.deprecated_primary1};
|
||||
color: ${({ theme }) => theme.accentAction};
|
||||
background-color: transparent;
|
||||
font-size: 16px;
|
||||
border-radius: 12px;
|
||||
@@ -151,14 +158,14 @@ export const ButtonSecondary = styled(BaseButton)`
|
||||
`
|
||||
|
||||
export const ButtonOutlined = styled(BaseButton)`
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_bg2};
|
||||
border: 1px solid ${({ theme }) => theme.backgroundOutline};
|
||||
background-color: transparent;
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 1px ${({ theme }) => theme.deprecated_bg4};
|
||||
}
|
||||
&:hover {
|
||||
box-shadow: 0 0 0 1px ${({ theme }) => theme.deprecated_bg4};
|
||||
box-shadow: 0 0 0 1px ${({ theme }) => theme.textTertiary};
|
||||
}
|
||||
&:active {
|
||||
box-shadow: 0 0 0 1px ${({ theme }) => theme.deprecated_bg4};
|
||||
@@ -191,7 +198,7 @@ export const ButtonYellow = styled(BaseButton)`
|
||||
|
||||
export const ButtonEmpty = styled(BaseButton)`
|
||||
background-color: transparent;
|
||||
color: ${({ theme }) => theme.deprecated_primary1};
|
||||
color: ${({ theme }) => theme.accentAction};
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@@ -217,11 +224,9 @@ export const ButtonText = styled(BaseButton)`
|
||||
background: none;
|
||||
text-decoration: none;
|
||||
&:focus {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
text-decoration: underline;
|
||||
}
|
||||
&:hover {
|
||||
// text-decoration: underline;
|
||||
opacity: 0.9;
|
||||
}
|
||||
&:active {
|
||||
@@ -235,38 +240,38 @@ export const ButtonText = styled(BaseButton)`
|
||||
|
||||
const ButtonConfirmedStyle = styled(BaseButton)`
|
||||
background-color: ${({ theme }) => theme.deprecated_bg3};
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
/* border: 1px solid ${({ theme }) => theme.deprecated_green1}; */
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
/* border: 1px solid ${({ theme }) => theme.accentSuccess}; */
|
||||
|
||||
&:disabled {
|
||||
opacity: 50%;
|
||||
background-color: ${({ theme }) => theme.deprecated_bg2};
|
||||
color: ${({ theme }) => theme.deprecated_text2};
|
||||
background-color: ${({ theme }) => theme.backgroundInteractive};
|
||||
color: ${({ theme }) => theme.textSecondary};
|
||||
cursor: auto;
|
||||
}
|
||||
`
|
||||
|
||||
const ButtonErrorStyle = styled(BaseButton)`
|
||||
background-color: ${({ theme }) => theme.deprecated_red1};
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_red1};
|
||||
background-color: ${({ theme }) => theme.accentFailure};
|
||||
border: 1px solid ${({ theme }) => theme.accentFailure};
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, theme.deprecated_red1)};
|
||||
background-color: ${({ theme }) => darken(0.05, theme.deprecated_red1)};
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, theme.accentFailure)};
|
||||
background-color: ${({ theme }) => darken(0.05, theme.accentFailure)};
|
||||
}
|
||||
&:hover {
|
||||
background-color: ${({ theme }) => darken(0.05, theme.deprecated_red1)};
|
||||
background-color: ${({ theme }) => darken(0.05, theme.accentFailure)};
|
||||
}
|
||||
&:active {
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, theme.deprecated_red1)};
|
||||
background-color: ${({ theme }) => darken(0.1, theme.deprecated_red1)};
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, theme.accentFailure)};
|
||||
background-color: ${({ theme }) => darken(0.1, theme.accentFailure)};
|
||||
}
|
||||
&:disabled {
|
||||
opacity: 50%;
|
||||
cursor: auto;
|
||||
box-shadow: none;
|
||||
background-color: ${({ theme }) => theme.deprecated_red1};
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_red1};
|
||||
background-color: ${({ theme }) => theme.accentFailure};
|
||||
border: 1px solid ${({ theme }) => theme.accentFailure};
|
||||
}
|
||||
`
|
||||
|
||||
@@ -314,14 +319,14 @@ export function ButtonDropdownLight({ disabled = false, children, ...rest }: { d
|
||||
|
||||
const ActiveOutlined = styled(ButtonOutlined)`
|
||||
border: 1px solid;
|
||||
border-color: ${({ theme }) => theme.deprecated_primary1};
|
||||
border-color: ${({ theme }) => theme.accentAction};
|
||||
`
|
||||
|
||||
const Circle = styled.div`
|
||||
height: 17px;
|
||||
width: 17px;
|
||||
border-radius: 50%;
|
||||
background-color: ${({ theme }) => theme.deprecated_primary1};
|
||||
background-color: ${({ theme }) => theme.accentAction};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -355,7 +360,7 @@ export function ButtonRadioChecked({ active = false, children, ...rest }: { acti
|
||||
{children}
|
||||
<CheckboxWrapper>
|
||||
<Circle>
|
||||
<ResponsiveCheck size={13} stroke={theme.deprecated_white} />
|
||||
<ResponsiveCheck size={13} stroke={theme.white} />
|
||||
</Circle>
|
||||
</CheckboxWrapper>
|
||||
</RowBetween>
|
||||
|
||||
@@ -10,24 +10,20 @@ const Card = styled(Box)<{ width?: string; padding?: string; border?: string; $b
|
||||
export default Card
|
||||
|
||||
export const LightCard = styled(Card)`
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_bg2};
|
||||
border: 1px solid ${({ theme }) => theme.backgroundInteractive};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg1};
|
||||
`
|
||||
|
||||
export const LightGrayCard = styled(Card)`
|
||||
background-color: ${({ theme }) => theme.deprecated_bg2};
|
||||
`
|
||||
|
||||
export const GrayCard = styled(Card)`
|
||||
background-color: ${({ theme }) => theme.deprecated_bg3};
|
||||
`
|
||||
|
||||
export const DarkGrayCard = styled(Card)`
|
||||
background-color: ${({ theme }) => theme.deprecated_bg2};
|
||||
background-color: ${({ theme }) => theme.backgroundInteractive};
|
||||
`
|
||||
|
||||
export const DarkCard = styled(Card)`
|
||||
background-color: ${({ theme }) => theme.deprecated_bg0};
|
||||
background-color: ${({ theme }) => theme.backgroundSurface};
|
||||
`
|
||||
|
||||
export const OutlineCard = styled(Card)`
|
||||
@@ -42,6 +38,6 @@ export const YellowCard = styled(Card)`
|
||||
|
||||
export const BlueCard = styled(Card)`
|
||||
background-color: ${({ theme }) => theme.deprecated_primary5};
|
||||
color: ${({ theme }) => theme.deprecated_blue2};
|
||||
color: ${({ theme }) => theme.accentAction};
|
||||
border-radius: 12px;
|
||||
`
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Group } from '@visx/group'
|
||||
import { LinePath } from '@visx/shape'
|
||||
import { easeCubicInOut } from 'd3'
|
||||
import { easeSinOut } from 'd3'
|
||||
import ms from 'ms.macro'
|
||||
import React from 'react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { animated, useSpring } from 'react-spring'
|
||||
@@ -11,8 +12,8 @@ import { LineChartProps } from './LineChart'
|
||||
type AnimatedInLineChartProps<T> = Omit<LineChartProps<T>, 'height' | 'width' | 'children'>
|
||||
|
||||
const config = {
|
||||
duration: 800,
|
||||
easing: easeCubicInOut,
|
||||
duration: ms`0.8s`,
|
||||
easing: easeSinOut,
|
||||
}
|
||||
|
||||
// code reference: https://airbnb.io/visx/lineradial
|
||||
@@ -91,4 +92,4 @@ function AnimatedInLineChart<T>({
|
||||
)
|
||||
}
|
||||
|
||||
export default AnimatedInLineChart
|
||||
export default React.memo(AnimatedInLineChart) as typeof AnimatedInLineChart
|
||||
|
||||
86
src/components/Charts/FadeInLineChart.tsx
Normal file
86
src/components/Charts/FadeInLineChart.tsx
Normal file
@@ -0,0 +1,86 @@
|
||||
import { Group } from '@visx/group'
|
||||
import { LinePath } from '@visx/shape'
|
||||
import { easeCubicInOut } from 'd3'
|
||||
import React from 'react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { animated, useSpring } from 'react-spring'
|
||||
import { useTheme } from 'styled-components/macro'
|
||||
|
||||
import { LineChartProps } from './LineChart'
|
||||
|
||||
type FadedInLineChartProps<T> = Omit<LineChartProps<T>, 'height' | 'width' | 'children'> & { dashed?: boolean }
|
||||
|
||||
const config = {
|
||||
duration: 3000,
|
||||
easing: easeCubicInOut,
|
||||
}
|
||||
|
||||
// code reference: https://airbnb.io/visx/lineradial
|
||||
|
||||
function FadedInLineChart<T>({
|
||||
data,
|
||||
getX,
|
||||
getY,
|
||||
marginTop,
|
||||
curve,
|
||||
color,
|
||||
strokeWidth,
|
||||
dashed,
|
||||
}: FadedInLineChartProps<T>) {
|
||||
const lineRef = useRef<SVGPathElement>(null)
|
||||
const [lineLength, setLineLength] = useState(0)
|
||||
const [shouldAnimate, setShouldAnimate] = useState(false)
|
||||
const [hasAnimatedIn, setHasAnimatedIn] = useState(false)
|
||||
|
||||
const spring = useSpring({
|
||||
frame: shouldAnimate ? 0 : 1,
|
||||
config,
|
||||
onRest: () => {
|
||||
setShouldAnimate(false)
|
||||
setHasAnimatedIn(true)
|
||||
},
|
||||
})
|
||||
|
||||
// We need to check to see after the "invisble" line has been drawn
|
||||
// what the length is to be able to animate in the line for the first time
|
||||
// This will run on each render to see if there is a new line length
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
useEffect(() => {
|
||||
if (lineRef.current) {
|
||||
const length = lineRef.current.getTotalLength()
|
||||
if (length !== lineLength) {
|
||||
setLineLength(length)
|
||||
}
|
||||
if (length > 0 && !shouldAnimate && !hasAnimatedIn) {
|
||||
setShouldAnimate(true)
|
||||
}
|
||||
}
|
||||
})
|
||||
const theme = useTheme()
|
||||
const lineColor = color ?? theme.accentAction
|
||||
|
||||
return (
|
||||
<Group top={marginTop}>
|
||||
<LinePath curve={curve} x={getX} y={getY}>
|
||||
{({ path }) => {
|
||||
const d = path(data) || ''
|
||||
return (
|
||||
<>
|
||||
<animated.path
|
||||
d={d}
|
||||
ref={lineRef}
|
||||
strokeWidth={strokeWidth}
|
||||
strokeOpacity={hasAnimatedIn ? 1 : spring.frame.to((v) => 1 - v)}
|
||||
fill="none"
|
||||
stroke={lineColor}
|
||||
strokeDasharray={dashed ? '4,4' : undefined}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}}
|
||||
</LinePath>
|
||||
</Group>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(FadedInLineChart) as typeof FadedInLineChart
|
||||
@@ -1,9 +1,14 @@
|
||||
import styled from 'styled-components/macro'
|
||||
import styled, { DefaultTheme } from 'styled-components/macro'
|
||||
|
||||
const Column = styled.div`
|
||||
type Gap = keyof DefaultTheme['grids']
|
||||
|
||||
export const Column = styled.div<{
|
||||
gap?: Gap
|
||||
}>`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
gap: ${({ gap, theme }) => gap && theme.grids[gap]};
|
||||
`
|
||||
export const ColumnCenter = styled(Column)`
|
||||
width: 100%;
|
||||
@@ -11,12 +16,12 @@ export const ColumnCenter = styled(Column)`
|
||||
`
|
||||
|
||||
export const AutoColumn = styled.div<{
|
||||
gap?: 'sm' | 'md' | 'lg' | string
|
||||
gap?: Gap | string
|
||||
justify?: 'stretch' | 'center' | 'start' | 'end' | 'flex-start' | 'flex-end' | 'space-between'
|
||||
}>`
|
||||
display: grid;
|
||||
grid-auto-rows: auto;
|
||||
grid-row-gap: ${({ gap }) => (gap === 'sm' && '8px') || (gap === 'md' && '12px') || (gap === 'lg' && '24px') || gap};
|
||||
grid-row-gap: ${({ gap, theme }) => (gap && theme.grids[gap as Gap]) || gap};
|
||||
justify-items: ${({ justify }) => justify && justify};
|
||||
`
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import ReactConfetti from 'react-confetti'
|
||||
|
||||
import { useWindowSize } from '../../hooks/useWindowSize'
|
||||
|
||||
// eslint-disable-next-line react/prop-types
|
||||
export default function Confetti({ start, variant }: { start: boolean; variant?: string }) {
|
||||
const { width, height } = useWindowSize()
|
||||
|
||||
const _variant = variant ? variant : height && width && height > 1.5 * width ? 'bottom' : variant
|
||||
|
||||
return start && width && height ? (
|
||||
<ReactConfetti
|
||||
style={{ zIndex: 1401 }}
|
||||
numberOfPieces={400}
|
||||
recycle={false}
|
||||
run={true}
|
||||
width={width}
|
||||
height={height}
|
||||
confettiSource={{
|
||||
h: height,
|
||||
w: width,
|
||||
x: 0,
|
||||
y: _variant === 'top' ? height * 0.25 : _variant === 'bottom' ? height * 0.75 : height * 0.5,
|
||||
}}
|
||||
initialVelocityX={15}
|
||||
initialVelocityY={30}
|
||||
gravity={0.45}
|
||||
tweenDuration={100}
|
||||
wind={0.05}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
@@ -49,7 +49,7 @@ export default function ConnectedAccountBlocked(props: ConnectedAccountBlockedPr
|
||||
fontSize={14}
|
||||
iconSize={16}
|
||||
gap={6}
|
||||
color={theme.deprecated_primary1}
|
||||
color={theme.accentAction}
|
||||
iconPosition="right"
|
||||
>
|
||||
compliance@uniswap.org
|
||||
|
||||
@@ -1,45 +1,72 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { t } from '@lingui/macro'
|
||||
import { formatCurrencyAmount, formatPriceImpact, NumberType } from '@uniswap/conedison/format'
|
||||
import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core'
|
||||
import { useMemo } from 'react'
|
||||
import { useTheme } from 'styled-components/macro'
|
||||
import { LoadingBubble } from 'components/Tokens/loading'
|
||||
import { MouseoverTooltip } from 'components/Tooltip'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import styled, { useTheme } from 'styled-components/macro'
|
||||
|
||||
import { ThemedText } from '../../theme'
|
||||
import { warningSeverity } from '../../utils/prices'
|
||||
import { MouseoverTooltip } from '../Tooltip'
|
||||
|
||||
const FiatLoadingBubble = styled(LoadingBubble)`
|
||||
border-radius: 4px;
|
||||
width: 4rem;
|
||||
height: 1rem;
|
||||
`
|
||||
|
||||
export function FiatValue({
|
||||
fiatValue,
|
||||
priceImpact,
|
||||
isLoading = false,
|
||||
}: {
|
||||
fiatValue: CurrencyAmount<Currency> | null | undefined
|
||||
priceImpact?: Percent
|
||||
isLoading?: boolean
|
||||
}) {
|
||||
const theme = useTheme()
|
||||
const [showLoadingPlaceholder, setShowLoadingPlaceholder] = useState(false)
|
||||
const priceImpactColor = useMemo(() => {
|
||||
if (!priceImpact) return undefined
|
||||
if (priceImpact.lessThan('0')) return theme.deprecated_green1
|
||||
if (priceImpact.lessThan('0')) return theme.accentSuccess
|
||||
const severity = warningSeverity(priceImpact)
|
||||
if (severity < 1) return theme.deprecated_text3
|
||||
if (severity < 1) return theme.textTertiary
|
||||
if (severity < 3) return theme.deprecated_yellow1
|
||||
return theme.deprecated_red1
|
||||
}, [priceImpact, theme.deprecated_green1, theme.deprecated_red1, theme.deprecated_text3, theme.deprecated_yellow1])
|
||||
return theme.accentFailure
|
||||
}, [priceImpact, theme.accentSuccess, theme.accentFailure, theme.textTertiary, theme.deprecated_yellow1])
|
||||
|
||||
const p = Number(fiatValue?.toFixed())
|
||||
const visibleDecimalPlaces = p < 1.05 ? 4 : 2
|
||||
useEffect(() => {
|
||||
const stale = false
|
||||
let timeoutId = 0
|
||||
if (isLoading && !fiatValue) {
|
||||
timeoutId = setTimeout(() => {
|
||||
if (!stale) setShowLoadingPlaceholder(true)
|
||||
}, 200) as unknown as number
|
||||
} else {
|
||||
setShowLoadingPlaceholder(false)
|
||||
}
|
||||
return () => clearTimeout(timeoutId)
|
||||
}, [isLoading, fiatValue])
|
||||
|
||||
return (
|
||||
<ThemedText.DeprecatedBody fontSize={14} color={theme.textSecondary}>
|
||||
{fiatValue && <>${fiatValue?.toFixed(visibleDecimalPlaces, { groupSeparator: ',' })}</>}
|
||||
{priceImpact ? (
|
||||
<span style={{ color: priceImpactColor }}>
|
||||
{' '}
|
||||
<MouseoverTooltip text={t`The estimated difference between the USD values of input and output amounts.`}>
|
||||
(<Trans>{priceImpact.multiply(-1).toSignificant(3)}%</Trans>)
|
||||
</MouseoverTooltip>
|
||||
</span>
|
||||
) : null}
|
||||
{showLoadingPlaceholder ? (
|
||||
<FiatLoadingBubble />
|
||||
) : (
|
||||
<div>
|
||||
{fiatValue && <>{formatCurrencyAmount(fiatValue, NumberType.FiatTokenPrice)}</>}
|
||||
{priceImpact && (
|
||||
<span style={{ color: priceImpactColor }}>
|
||||
{' '}
|
||||
<MouseoverTooltip text={t`The estimated difference between the USD values of input and output amounts.`}>
|
||||
(<Trans>{formatPriceImpact(priceImpact)}</Trans>)
|
||||
</MouseoverTooltip>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</ThemedText.DeprecatedBody>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { LoadingOpacityContainer, loadingOpacityMixin } from 'components/Loader/
|
||||
import CurrencyLogo from 'components/Logo/CurrencyLogo'
|
||||
import { isSupportedChain } from 'constants/chains'
|
||||
import { darken } from 'polished'
|
||||
import { ReactNode, useCallback, useState } from 'react'
|
||||
import { ReactNode, useCallback, useEffect, useState } from 'react'
|
||||
import { Lock } from 'react-feather'
|
||||
import styled, { useTheme } from 'styled-components/macro'
|
||||
import { flexColumnNoWrap, flexRowNoWrap } from 'theme/styles'
|
||||
@@ -62,7 +62,7 @@ const CurrencySelect = styled(ButtonGray)<{
|
||||
background-color: ${({ selected, theme }) => (selected ? theme.backgroundInteractive : theme.accentAction)};
|
||||
opacity: ${({ disabled }) => (!disabled ? 1 : 0.4)};
|
||||
box-shadow: ${({ selected }) => (selected ? 'none' : '0px 6px 10px rgba(0, 0, 0, 0.075)')};
|
||||
color: ${({ selected, theme }) => (selected ? theme.deprecated_text1 : theme.deprecated_white)};
|
||||
color: ${({ selected, theme }) => (selected ? theme.textPrimary : theme.white)};
|
||||
cursor: pointer;
|
||||
height: unset;
|
||||
border-radius: 16px;
|
||||
@@ -121,7 +121,7 @@ const LabelRow = styled.div`
|
||||
|
||||
span:hover {
|
||||
cursor: pointer;
|
||||
color: ${({ theme }) => darken(0.2, theme.deprecated_text2)};
|
||||
color: ${({ theme }) => darken(0.2, theme.textSecondary)};
|
||||
}
|
||||
`
|
||||
|
||||
@@ -144,7 +144,7 @@ const StyledDropDown = styled(DropDown)<{ selected: boolean }>`
|
||||
margin-left: 8px;
|
||||
|
||||
path {
|
||||
stroke: ${({ selected, theme }) => (selected ? theme.deprecated_text1 : theme.deprecated_white)};
|
||||
stroke: ${({ selected, theme }) => (selected ? theme.textPrimary : theme.white)};
|
||||
stroke-width: 2px;
|
||||
}
|
||||
`
|
||||
@@ -229,6 +229,7 @@ export default function SwapCurrencyInputPanel({
|
||||
...rest
|
||||
}: SwapCurrencyInputPanelProps) {
|
||||
const [modalOpen, setModalOpen] = useState(false)
|
||||
const [fiatValueIsLoading, setFiatValueIsLoading] = useState(false)
|
||||
const { account, chainId } = useWeb3React()
|
||||
const selectedCurrencyBalance = useCurrencyBalance(account ?? undefined, currency ?? undefined)
|
||||
const theme = useTheme()
|
||||
@@ -239,6 +240,10 @@ export default function SwapCurrencyInputPanel({
|
||||
|
||||
const chainAllowed = isSupportedChain(chainId)
|
||||
|
||||
useEffect(() => {
|
||||
!!value && !fiatValue ? setFiatValueIsLoading(true) : setFiatValueIsLoading(false)
|
||||
}, [fiatValueIsLoading, value, fiatValue])
|
||||
|
||||
return (
|
||||
<InputPanel id={id} hideInput={hideInput} {...rest}>
|
||||
{locked && (
|
||||
@@ -306,7 +311,7 @@ export default function SwapCurrencyInputPanel({
|
||||
<FiatRow>
|
||||
<RowBetween>
|
||||
<LoadingOpacityContainer $loading={loading}>
|
||||
<FiatValue fiatValue={fiatValue} priceImpact={priceImpact} />
|
||||
<FiatValue fiatValue={fiatValue} priceImpact={priceImpact} isLoading={fiatValueIsLoading} />
|
||||
</LoadingOpacityContainer>
|
||||
{account ? (
|
||||
<RowFixed style={{ height: '17px' }}>
|
||||
|
||||
@@ -29,7 +29,7 @@ const InputPanel = styled.div<{ hideInput?: boolean }>`
|
||||
${flexColumnNoWrap};
|
||||
position: relative;
|
||||
border-radius: ${({ hideInput }) => (hideInput ? '16px' : '20px')};
|
||||
background-color: ${({ theme, hideInput }) => (hideInput ? 'transparent' : theme.deprecated_bg2)};
|
||||
background-color: ${({ theme, hideInput }) => (hideInput ? 'transparent' : theme.backgroundInteractive)};
|
||||
z-index: 1;
|
||||
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
|
||||
transition: height 1s ease;
|
||||
@@ -41,7 +41,7 @@ const FixedContainer = styled.div`
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
border-radius: 20px;
|
||||
background-color: ${({ theme }) => theme.deprecated_bg2};
|
||||
background-color: ${({ theme }) => theme.backgroundInteractive};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -50,7 +50,7 @@ const FixedContainer = styled.div`
|
||||
|
||||
const Container = styled.div<{ hideInput: boolean; disabled: boolean }>`
|
||||
border-radius: ${({ hideInput }) => (hideInput ? '16px' : '20px')};
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_bg0};
|
||||
border: 1px solid ${({ theme }) => theme.backgroundSurface};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg1};
|
||||
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
|
||||
${({ theme, hideInput, disabled }) =>
|
||||
@@ -70,11 +70,11 @@ const CurrencySelect = styled(ButtonGray)<{
|
||||
disabled?: boolean
|
||||
}>`
|
||||
align-items: center;
|
||||
background-color: ${({ selected, theme }) => (selected ? theme.deprecated_bg2 : theme.deprecated_primary1)};
|
||||
background-color: ${({ selected, theme }) => (selected ? theme.backgroundInteractive : theme.accentAction)};
|
||||
opacity: ${({ disabled }) => (!disabled ? 1 : 0.4)};
|
||||
box-shadow: ${({ selected }) => (selected ? 'none' : '0px 6px 10px rgba(0, 0, 0, 0.075)')};
|
||||
box-shadow: 0px 6px 10px rgba(0, 0, 0, 0.075);
|
||||
color: ${({ selected, theme }) => (selected ? theme.deprecated_text1 : theme.deprecated_white)};
|
||||
color: ${({ selected, theme }) => (selected ? theme.textPrimary : theme.white)};
|
||||
cursor: pointer;
|
||||
border-radius: 16px;
|
||||
outline: none;
|
||||
@@ -89,8 +89,7 @@ const CurrencySelect = styled(ButtonGray)<{
|
||||
margin-left: ${({ hideInput }) => (hideInput ? '0' : '12px')};
|
||||
:focus,
|
||||
:hover {
|
||||
background-color: ${({ selected, theme }) =>
|
||||
selected ? theme.deprecated_bg3 : darken(0.05, theme.deprecated_primary1)};
|
||||
background-color: ${({ selected, theme }) => (selected ? theme.deprecated_bg3 : darken(0.05, theme.accentAction))};
|
||||
}
|
||||
visibility: ${({ visible }) => (visible ? 'visible' : 'hidden')};
|
||||
`
|
||||
@@ -105,13 +104,13 @@ const InputRow = styled.div<{ selected: boolean }>`
|
||||
const LabelRow = styled.div`
|
||||
${flexRowNoWrap};
|
||||
align-items: center;
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
font-size: 0.75rem;
|
||||
line-height: 1rem;
|
||||
padding: 0 1rem 1rem;
|
||||
span:hover {
|
||||
cursor: pointer;
|
||||
color: ${({ theme }) => darken(0.2, theme.deprecated_text2)};
|
||||
color: ${({ theme }) => darken(0.2, theme.textSecondary)};
|
||||
}
|
||||
`
|
||||
|
||||
@@ -133,7 +132,7 @@ const StyledDropDown = styled(DropDown)<{ selected: boolean }>`
|
||||
height: 35%;
|
||||
|
||||
path {
|
||||
stroke: ${({ selected, theme }) => (selected ? theme.deprecated_text1 : theme.deprecated_white)};
|
||||
stroke: ${({ selected, theme }) => (selected ? theme.textPrimary : theme.white)};
|
||||
stroke-width: 1.5px;
|
||||
}
|
||||
`
|
||||
@@ -148,7 +147,7 @@ const StyledBalanceMax = styled.button<{ disabled?: boolean }>`
|
||||
background-color: ${({ theme }) => theme.deprecated_primary5};
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
color: ${({ theme }) => theme.deprecated_primary1};
|
||||
color: ${({ theme }) => theme.accentAction};
|
||||
cursor: pointer;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
@@ -300,7 +299,7 @@ export default function CurrencyInputPanel({
|
||||
<RowFixed style={{ height: '17px' }}>
|
||||
<ThemedText.DeprecatedBody
|
||||
onClick={onMax}
|
||||
color={theme.deprecated_text3}
|
||||
color={theme.textTertiary}
|
||||
fontWeight={500}
|
||||
fontSize={14}
|
||||
style={{ display: 'inline', cursor: 'pointer' }}
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import { AlertOctagon } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ExternalLink } from 'theme'
|
||||
|
||||
import { isL2ChainId } from '../../utils/chains'
|
||||
|
||||
const Root = styled.div`
|
||||
background-color: ${({ theme }) => (theme.darkMode ? '#888D9B' : '#CED0D9')};
|
||||
border-radius: 18px;
|
||||
color: black;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-size: 14px;
|
||||
margin: 12px auto;
|
||||
padding: 16px;
|
||||
width: 100%;
|
||||
max-width: 880px;
|
||||
`
|
||||
const WarningIcon = styled(AlertOctagon)`
|
||||
margin: auto 16px auto 0;
|
||||
min-height: 22px;
|
||||
min-width: 22px;
|
||||
`
|
||||
const ReadMoreLink = styled(ExternalLink)`
|
||||
color: black;
|
||||
text-decoration: underline;
|
||||
`
|
||||
|
||||
function Wrapper({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<Root>
|
||||
<WarningIcon />
|
||||
<div>{children}</div>
|
||||
</Root>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a downtime warning for the network if it's relevant
|
||||
*/
|
||||
export default function DowntimeWarning() {
|
||||
const { chainId } = useWeb3React()
|
||||
if (!isL2ChainId(chainId)) {
|
||||
return null
|
||||
}
|
||||
|
||||
switch (chainId) {
|
||||
case SupportedChainId.OPTIMISM:
|
||||
case SupportedChainId.OPTIMISM_GOERLI:
|
||||
return (
|
||||
<Wrapper>
|
||||
<Trans>
|
||||
Optimism is in Beta and may experience downtime. Optimism expects planned downtime to upgrade the network in
|
||||
the near future. During downtime, your position will not earn fees and you will be unable to remove
|
||||
liquidity.{' '}
|
||||
<ReadMoreLink href="https://help.uniswap.org/en/articles/5406082-what-happens-if-the-optimistic-ethereum-network-experiences-downtime">
|
||||
Read more.
|
||||
</ReadMoreLink>
|
||||
</Trans>
|
||||
</Wrapper>
|
||||
)
|
||||
case SupportedChainId.ARBITRUM_ONE:
|
||||
case SupportedChainId.ARBITRUM_RINKEBY:
|
||||
return (
|
||||
<Wrapper>
|
||||
<Trans>
|
||||
Arbitrum is in Beta and may experience downtime. During downtime, your position will not earn fees and you
|
||||
will be unable to remove liquidity.{' '}
|
||||
<ReadMoreLink href="https://help.uniswap.org/en/articles/5576122-arbitrum-network-downtime">
|
||||
Read more.
|
||||
</ReadMoreLink>
|
||||
</Trans>
|
||||
</Wrapper>
|
||||
)
|
||||
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -1,53 +1,156 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { sendEvent } from 'components/analytics'
|
||||
import React, { ErrorInfo, PropsWithChildren } from 'react'
|
||||
import * as Sentry from '@sentry/react'
|
||||
import { ButtonLight, SmallButtonPrimary } from 'components/Button'
|
||||
import { ChevronUpIcon } from 'nft/components/icons'
|
||||
import { useIsMobile } from 'nft/hooks'
|
||||
import React, { PropsWithChildren, useState } from 'react'
|
||||
import { Copy } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import store, { AppState } from '../../state'
|
||||
import { ExternalLink, ThemedText } from '../../theme'
|
||||
import { userAgent } from '../../utils/userAgent'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { AutoRow } from '../Row'
|
||||
import { CopyToClipboard, ExternalLink, ThemedText } from '../../theme'
|
||||
import { Column } from '../Column'
|
||||
|
||||
const FallbackWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
z-index: 1;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
`
|
||||
|
||||
const BodyWrapper = styled.div<{ margin?: string }>`
|
||||
padding: 1rem;
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
margin: auto;
|
||||
padding: 1rem;
|
||||
`
|
||||
|
||||
const SmallButtonLight = styled(ButtonLight)`
|
||||
font-size: 16px;
|
||||
padding: 10px 16px;
|
||||
border-radius: 12px;
|
||||
`
|
||||
|
||||
const StretchedRow = styled.div`
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
|
||||
> * {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
||||
`
|
||||
|
||||
const Code = styled.code`
|
||||
font-weight: 300;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
word-wrap: break-word;
|
||||
width: 100%;
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
font-family: ${({ theme }) => theme.fonts.code};
|
||||
overflow: scroll;
|
||||
max-height: calc(100vh - 450px);
|
||||
`
|
||||
|
||||
const Separator = styled.div`
|
||||
border-bottom: 1px solid ${({ theme }) => theme.backgroundOutline};
|
||||
`
|
||||
|
||||
const CodeBlockWrapper = styled.div`
|
||||
background: ${({ theme }) => theme.deprecated_bg0};
|
||||
overflow: auto;
|
||||
white-space: pre;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: ${({ theme }) => theme.backgroundModule};
|
||||
box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04),
|
||||
0px 24px 32px rgba(0, 0, 0, 0.01);
|
||||
border-radius: 24px;
|
||||
padding: 18px 24px;
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
padding: 24px;
|
||||
gap: 10px;
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
`
|
||||
|
||||
const LinkWrapper = styled.div`
|
||||
color: ${({ theme }) => theme.deprecated_blue1};
|
||||
padding: 6px 24px;
|
||||
const ShowMoreButton = styled.div`
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
justify-content: space-between;
|
||||
`
|
||||
|
||||
const SomethingWentWrongWrapper = styled.div`
|
||||
padding: 6px 24px;
|
||||
const CopyIcon = styled(Copy)`
|
||||
stroke: ${({ theme }) => theme.textSecondary};
|
||||
`
|
||||
|
||||
type ErrorBoundaryState = {
|
||||
error: Error | null
|
||||
const ShowMoreIcon = styled(ChevronUpIcon)<{ $isExpanded?: boolean }>`
|
||||
transform: ${({ $isExpanded }) => ($isExpanded ? 'none' : 'rotate(180deg)')};
|
||||
`
|
||||
|
||||
const CodeTitle = styled.div`
|
||||
display: flex;
|
||||
gap: 14px;
|
||||
align-items: center;
|
||||
word-break: break-word;
|
||||
`
|
||||
|
||||
const Fallback = ({ error, eventId }: { error: Error; eventId: string | null }) => {
|
||||
const [isExpanded, setExpanded] = useState(false)
|
||||
const isMobile = useIsMobile()
|
||||
|
||||
const errorId = eventId || 'unknown-error'
|
||||
|
||||
// @todo: ThemedText components should be responsive by default
|
||||
const [Title, Description] = isMobile
|
||||
? [ThemedText.HeadlineSmall, ThemedText.BodySmall]
|
||||
: [ThemedText.HeadlineLarge, ThemedText.BodySecondary]
|
||||
|
||||
return (
|
||||
<FallbackWrapper>
|
||||
<BodyWrapper>
|
||||
<Column gap="xl">
|
||||
<Column gap="sm">
|
||||
<Title textAlign="center">
|
||||
<Trans>Something went wrong</Trans>
|
||||
</Title>
|
||||
<Description textAlign="center" color="textSecondary">
|
||||
<Trans>
|
||||
Sorry, an error occured while processing your request. If you request support, be sure to provide your
|
||||
error ID.
|
||||
</Trans>
|
||||
</Description>
|
||||
</Column>
|
||||
<CodeBlockWrapper>
|
||||
<CodeTitle>
|
||||
<ThemedText.SubHeader fontWeight={500}>Error ID: {errorId}</ThemedText.SubHeader>
|
||||
<CopyToClipboard toCopy={errorId}>
|
||||
<CopyIcon />
|
||||
</CopyToClipboard>
|
||||
</CodeTitle>
|
||||
<Separator />
|
||||
{isExpanded && (
|
||||
<>
|
||||
<Code>{error.stack}</Code>
|
||||
<Separator />
|
||||
</>
|
||||
)}
|
||||
<ShowMoreButton onClick={() => setExpanded((s) => !s)}>
|
||||
<ThemedText.Link color="textSecondary">
|
||||
<Trans>{isExpanded ? 'Show less' : 'Show more'}</Trans>
|
||||
</ThemedText.Link>
|
||||
<ShowMoreIcon $isExpanded={isExpanded} secondaryWidth="20" secondaryHeight="20" />
|
||||
</ShowMoreButton>
|
||||
</CodeBlockWrapper>
|
||||
<StretchedRow>
|
||||
<SmallButtonPrimary onClick={() => window.location.reload()}>
|
||||
<Trans>Reload the app</Trans>
|
||||
</SmallButtonPrimary>
|
||||
<ExternalLink id="get-support-on-discord" href="https://discord.gg/FCfyBSbCU5" target="_blank">
|
||||
<SmallButtonLight>
|
||||
<Trans>Get support</Trans>
|
||||
</SmallButtonLight>
|
||||
</ExternalLink>
|
||||
</StretchedRow>
|
||||
</Column>
|
||||
</BodyWrapper>
|
||||
</FallbackWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
const IS_UNISWAP = window.location.hostname === 'app.uniswap.org'
|
||||
|
||||
async function updateServiceWorker(): Promise<ServiceWorkerRegistration> {
|
||||
const ready = await navigator.serviceWorker.ready
|
||||
// the return type of update is incorrectly typed as Promise<void>. See
|
||||
@@ -55,157 +158,34 @@ async function updateServiceWorker(): Promise<ServiceWorkerRegistration> {
|
||||
return ready.update() as unknown as Promise<ServiceWorkerRegistration>
|
||||
}
|
||||
|
||||
export default class ErrorBoundary extends React.Component<PropsWithChildren<unknown>, ErrorBoundaryState> {
|
||||
constructor(props: PropsWithChildren<unknown>) {
|
||||
super(props)
|
||||
this.state = { error: null }
|
||||
}
|
||||
const updateServiceWorkerInBackground = async () => {
|
||||
try {
|
||||
const registration = await updateServiceWorker()
|
||||
|
||||
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
|
||||
updateServiceWorker()
|
||||
.then(async (registration) => {
|
||||
// We want to refresh only if we detect a new service worker is waiting to be activated.
|
||||
// See details about it: https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle
|
||||
if (registration?.waiting) {
|
||||
await registration.unregister()
|
||||
// We want to refresh only if we detect a new service worker is waiting to be activated.
|
||||
// See details about it: https://web.dev/service-worker-lifecycle/
|
||||
if (registration?.waiting) {
|
||||
await registration.unregister()
|
||||
|
||||
// Makes Workbox call skipWaiting(). For more info on skipWaiting see: https://developer.chrome.com/docs/workbox/handling-service-worker-updates/
|
||||
registration.waiting.postMessage({ type: 'SKIP_WAITING' })
|
||||
|
||||
// Once the service worker is unregistered, we can reload the page to let
|
||||
// the browser download a fresh copy of our app (invalidating the cache)
|
||||
window.location.reload()
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to update service worker', error)
|
||||
})
|
||||
return { error }
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
||||
sendEvent('exception', {
|
||||
description: error.toString() + errorInfo.toString(),
|
||||
fatal: true,
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const { error } = this.state
|
||||
|
||||
if (error !== null) {
|
||||
const encodedBody = encodeURIComponent(issueBody(error))
|
||||
return (
|
||||
<FallbackWrapper>
|
||||
<BodyWrapper>
|
||||
<AutoColumn gap="md">
|
||||
<SomethingWentWrongWrapper>
|
||||
<ThemedText.DeprecatedLabel fontSize={24} fontWeight={600}>
|
||||
<Trans>Something went wrong</Trans>
|
||||
</ThemedText.DeprecatedLabel>
|
||||
</SomethingWentWrongWrapper>
|
||||
<CodeBlockWrapper>
|
||||
<code>
|
||||
<ThemedText.DeprecatedMain fontSize={10}>{error.stack}</ThemedText.DeprecatedMain>
|
||||
</code>
|
||||
</CodeBlockWrapper>
|
||||
{IS_UNISWAP ? (
|
||||
<AutoRow>
|
||||
<LinkWrapper>
|
||||
<ExternalLink
|
||||
id="create-github-issue-link"
|
||||
href={`https://github.com/Uniswap/uniswap-interface/issues/new?assignees=&labels=bug&body=${encodedBody}&title=${encodeURIComponent(
|
||||
`Crash report: \`${error.name}${error.message && `: ${error.message}`}\``
|
||||
)}`}
|
||||
target="_blank"
|
||||
>
|
||||
<ThemedText.DeprecatedLink fontSize={16}>
|
||||
<Trans>Create an issue on GitHub</Trans>
|
||||
<span>↗</span>
|
||||
</ThemedText.DeprecatedLink>
|
||||
</ExternalLink>
|
||||
</LinkWrapper>
|
||||
<LinkWrapper>
|
||||
<ExternalLink id="get-support-on-discord" href="https://discord.gg/FCfyBSbCU5" target="_blank">
|
||||
<ThemedText.DeprecatedLink fontSize={16}>
|
||||
<Trans>Get support on Discord</Trans>
|
||||
<span>↗</span>
|
||||
</ThemedText.DeprecatedLink>
|
||||
</ExternalLink>
|
||||
</LinkWrapper>
|
||||
</AutoRow>
|
||||
) : null}
|
||||
</AutoColumn>
|
||||
</BodyWrapper>
|
||||
</FallbackWrapper>
|
||||
)
|
||||
// Makes Workbox call skipWaiting().
|
||||
// For more info on skipWaiting see: https://web.dev/service-worker-lifecycle/#skip-the-waiting-phase
|
||||
registration.waiting.postMessage({ type: 'SKIP_WAITING' })
|
||||
}
|
||||
return this.props.children
|
||||
} catch (error) {
|
||||
console.error('Failed to update service worker', error)
|
||||
}
|
||||
}
|
||||
|
||||
function getRelevantState(): null | keyof AppState {
|
||||
const path = window.location.hash
|
||||
if (!path.startsWith('#/')) {
|
||||
return null
|
||||
}
|
||||
const pieces = path.substring(2).split(/[/\\?]/)
|
||||
switch (pieces[0]) {
|
||||
case 'swap':
|
||||
return 'swap'
|
||||
case 'add':
|
||||
if (pieces[1] === 'v2') return 'mint'
|
||||
else return 'mintV3'
|
||||
case 'remove':
|
||||
if (pieces[1] === 'v2') return 'burn'
|
||||
else return 'burnV3'
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function issueBody(error: Error): string {
|
||||
const relevantState = getRelevantState()
|
||||
const deviceData = userAgent
|
||||
return `## URL
|
||||
|
||||
${window.location.href}
|
||||
|
||||
${
|
||||
relevantState
|
||||
? `## \`${relevantState}\` state
|
||||
|
||||
\`\`\`json
|
||||
${JSON.stringify(store.getState()[relevantState], null, 2)}
|
||||
\`\`\`
|
||||
`
|
||||
: ''
|
||||
}
|
||||
${
|
||||
error.name &&
|
||||
`## Error
|
||||
|
||||
\`\`\`
|
||||
${error.name}${error.message && `: ${error.message}`}
|
||||
\`\`\`
|
||||
`
|
||||
}
|
||||
${
|
||||
error.stack &&
|
||||
`## Stacktrace
|
||||
|
||||
\`\`\`
|
||||
${error.stack}
|
||||
\`\`\`
|
||||
`
|
||||
}
|
||||
${
|
||||
deviceData &&
|
||||
`## Device data
|
||||
|
||||
\`\`\`json
|
||||
${JSON.stringify(deviceData, null, 2)}
|
||||
\`\`\`
|
||||
`
|
||||
}
|
||||
`
|
||||
export default function ErrorBoundary({ children }: PropsWithChildren): JSX.Element {
|
||||
return (
|
||||
<Sentry.ErrorBoundary
|
||||
fallback={({ error, eventId }) => <Fallback error={error} eventId={eventId} />}
|
||||
beforeCapture={(scope) => {
|
||||
scope.setLevel('fatal')
|
||||
}}
|
||||
onError={updateServiceWorkerInBackground}
|
||||
>
|
||||
{children}
|
||||
</Sentry.ErrorBoundary>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BaseVariant, FeatureFlag, featureFlagSettings, useUpdateFlag } from 'featureFlags'
|
||||
import { NftVariant, useNftFlag } from 'featureFlags/flags/nft'
|
||||
import { Permit2Variant, usePermit2Flag } from 'featureFlags/flags/permit2'
|
||||
import { TraceJsonRpcVariant, useTraceJsonRpcFlag } from 'featureFlags/flags/traceJsonRpc'
|
||||
import { useAtomValue, useUpdateAtom } from 'jotai/utils'
|
||||
import { Children, PropsWithChildren, ReactElement, ReactNode, useCallback, useState } from 'react'
|
||||
@@ -202,9 +202,12 @@ export default function FeatureFlagModal() {
|
||||
<X size={24} />
|
||||
</CloseButton>
|
||||
</Header>
|
||||
<FeatureFlagGroup name="Phase 1">
|
||||
<FeatureFlagOption variant={NftVariant} value={useNftFlag()} featureFlag={FeatureFlag.nft} label="NFTs" />
|
||||
</FeatureFlagGroup>
|
||||
<FeatureFlagOption
|
||||
variant={Permit2Variant}
|
||||
value={usePermit2Flag()}
|
||||
featureFlag={FeatureFlag.permit2}
|
||||
label="Permit 2 / Universal Router"
|
||||
/>
|
||||
<FeatureFlagGroup name="Debug">
|
||||
<FeatureFlagOption
|
||||
variant={TraceJsonRpcVariant}
|
||||
|
||||
@@ -34,8 +34,8 @@ const pulse = (color: string) => keyframes`
|
||||
}
|
||||
`
|
||||
const FocusedOutlineCard = styled(Card)<{ pulsing: boolean }>`
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_bg2};
|
||||
animation: ${({ pulsing, theme }) => pulsing && pulse(theme.deprecated_primary1)} 0.6s linear;
|
||||
border: 1px solid ${({ theme }) => theme.backgroundInteractive};
|
||||
animation: ${({ pulsing, theme }) => pulsing && pulse(theme.accentAction)} 0.6s linear;
|
||||
align-self: center;
|
||||
`
|
||||
|
||||
|
||||
@@ -36,9 +36,9 @@ const SmallButton = styled(ButtonGray)`
|
||||
`
|
||||
|
||||
const FocusedOutlineCard = styled(OutlineCard)<{ active?: boolean; pulsing?: boolean }>`
|
||||
border-color: ${({ active, theme }) => active && theme.deprecated_blue1};
|
||||
border-color: ${({ active, theme }) => active && theme.accentAction};
|
||||
padding: 12px;
|
||||
animation: ${({ pulsing, theme }) => pulsing && pulse(theme.deprecated_blue1)} 0.8s linear;
|
||||
animation: ${({ pulsing, theme }) => pulsing && pulse(theme.accentAction)} 0.8s linear;
|
||||
`
|
||||
|
||||
const StyledInput = styled(NumericalInput)<{ usePercent?: boolean }>`
|
||||
@@ -58,13 +58,13 @@ const StyledInput = styled(NumericalInput)<{ usePercent?: boolean }>`
|
||||
`
|
||||
|
||||
const InputTitle = styled(ThemedText.DeprecatedSmall)`
|
||||
color: ${({ theme }) => theme.deprecated_text2};
|
||||
color: ${({ theme }) => theme.textSecondary};
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
`
|
||||
|
||||
const ButtonLabel = styled(ThemedText.DeprecatedWhite)<{ disabled: boolean }>`
|
||||
color: ${({ theme, disabled }) => (disabled ? theme.deprecated_text2 : theme.deprecated_text1)} !important;
|
||||
color: ${({ theme, disabled }) => (disabled ? theme.textSecondary : theme.textPrimary)} !important;
|
||||
`
|
||||
|
||||
interface StepCounterProps {
|
||||
|
||||
@@ -6,8 +6,8 @@ import { ChartEntry } from './types'
|
||||
|
||||
const Path = styled.path<{ fill: string | undefined }>`
|
||||
opacity: 0.5;
|
||||
stroke: ${({ fill, theme }) => fill ?? theme.deprecated_blue2};
|
||||
fill: ${({ fill, theme }) => fill ?? theme.deprecated_blue2};
|
||||
stroke: ${({ fill, theme }) => fill ?? theme.accentAction};
|
||||
fill: ${({ fill, theme }) => fill ?? theme.accentAction};
|
||||
`
|
||||
|
||||
export const Area = ({
|
||||
|
||||
@@ -8,7 +8,7 @@ const StyledGroup = styled.g`
|
||||
}
|
||||
|
||||
text {
|
||||
color: ${({ theme }) => theme.deprecated_text2};
|
||||
color: ${({ theme }) => theme.textSecondary};
|
||||
transform: translateY(5px);
|
||||
}
|
||||
`
|
||||
|
||||
@@ -18,7 +18,7 @@ const HandleAccent = styled.path`
|
||||
pointer-events: none;
|
||||
|
||||
stroke-width: 1.5;
|
||||
stroke: ${({ theme }) => theme.deprecated_white};
|
||||
stroke: ${({ theme }) => theme.white};
|
||||
opacity: ${({ theme }) => theme.opacity.hover};
|
||||
`
|
||||
|
||||
@@ -28,13 +28,13 @@ const LabelGroup = styled.g<{ visible: boolean }>`
|
||||
`
|
||||
|
||||
const TooltipBackground = styled.rect`
|
||||
fill: ${({ theme }) => theme.deprecated_bg2};
|
||||
fill: ${({ theme }) => theme.backgroundInteractive};
|
||||
`
|
||||
|
||||
const Tooltip = styled.text`
|
||||
text-anchor: middle;
|
||||
font-size: 13px;
|
||||
fill: ${({ theme }) => theme.deprecated_text1};
|
||||
fill: ${({ theme }) => theme.textPrimary};
|
||||
`
|
||||
|
||||
// flips the handles draggers when close to the container edges
|
||||
|
||||
@@ -9,8 +9,8 @@ import { Line } from './Line'
|
||||
import { ChartEntry, LiquidityChartRangeInputProps } from './types'
|
||||
import Zoom, { ZoomOverlay } from './Zoom'
|
||||
|
||||
export const xAccessor = (d: ChartEntry) => d.price0
|
||||
export const yAccessor = (d: ChartEntry) => d.activeLiquidity
|
||||
const xAccessor = (d: ChartEntry) => d.price0
|
||||
const yAccessor = (d: ChartEntry) => d.activeLiquidity
|
||||
|
||||
export function Chart({
|
||||
id = 'liquidityChartRangeInput',
|
||||
|
||||
@@ -5,7 +5,7 @@ import styled from 'styled-components/macro'
|
||||
const StyledLine = styled.line`
|
||||
opacity: 0.5;
|
||||
stroke-width: 2;
|
||||
stroke: ${({ theme }) => theme.deprecated_text1};
|
||||
stroke: ${({ theme }) => theme.textPrimary};
|
||||
fill: none;
|
||||
`
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ const Wrapper = styled.div<{ count: number }>`
|
||||
|
||||
const Button = styled(ButtonGray)`
|
||||
&:hover {
|
||||
background-color: ${({ theme }) => theme.deprecated_bg2};
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
background-color: ${({ theme }) => theme.backgroundInteractive};
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
}
|
||||
|
||||
width: 32px;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import * as Sentry from '@sentry/react'
|
||||
import { Currency, Price, Token } from '@uniswap/sdk-core'
|
||||
import { FeeAmount } from '@uniswap/v3-sdk'
|
||||
import { sendEvent } from 'components/analytics'
|
||||
import { AutoColumn, ColumnCenter } from 'components/Column'
|
||||
import Loader from 'components/Loader'
|
||||
import { format } from 'd3'
|
||||
@@ -157,7 +157,7 @@ export default function LiquidityChartRangeInput({
|
||||
)
|
||||
|
||||
if (error) {
|
||||
sendEvent('exception', { description: error.toString(), fatal: false })
|
||||
Sentry.captureMessage(error.toString(), 'log')
|
||||
}
|
||||
|
||||
const isUninitialized = !currencyA || !currencyB || (formattedData === undefined && !isLoading)
|
||||
@@ -167,7 +167,7 @@ export default function LiquidityChartRangeInput({
|
||||
{isUninitialized ? (
|
||||
<InfoBox
|
||||
message={<Trans>Your position will appear here.</Trans>}
|
||||
icon={<Inbox size={56} stroke={theme.deprecated_text1} />}
|
||||
icon={<Inbox size={56} stroke={theme.textPrimary} />}
|
||||
/>
|
||||
) : isLoading ? (
|
||||
<InfoBox icon={<Loader size="40px" stroke={theme.deprecated_text4} />} />
|
||||
@@ -189,12 +189,12 @@ export default function LiquidityChartRangeInput({
|
||||
margins={{ top: 10, right: 2, bottom: 20, left: 0 }}
|
||||
styles={{
|
||||
area: {
|
||||
selection: theme.deprecated_blue1,
|
||||
selection: theme.accentAction,
|
||||
},
|
||||
brush: {
|
||||
handle: {
|
||||
west: saturate(0.1, tokenAColor) ?? theme.deprecated_red1,
|
||||
east: saturate(0.1, tokenBColor) ?? theme.deprecated_blue1,
|
||||
west: saturate(0.1, tokenAColor) ?? theme.accentFailure,
|
||||
east: saturate(0.1, tokenBColor) ?? theme.accentAction,
|
||||
},
|
||||
},
|
||||
}}
|
||||
|
||||
@@ -5,12 +5,12 @@ export interface ChartEntry {
|
||||
price0: number
|
||||
}
|
||||
|
||||
export interface Dimensions {
|
||||
interface Dimensions {
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
|
||||
export interface Margins {
|
||||
interface Margins {
|
||||
top: number
|
||||
right: number
|
||||
bottom: number
|
||||
|
||||
@@ -18,7 +18,7 @@ export const LoadingRows = styled.div`
|
||||
background: linear-gradient(
|
||||
to left,
|
||||
${({ theme }) => theme.deprecated_bg1} 25%,
|
||||
${({ theme }) => theme.deprecated_bg2} 50%,
|
||||
${({ theme }) => theme.backgroundInteractive} 50%,
|
||||
${({ theme }) => theme.deprecated_bg1} 75%
|
||||
);
|
||||
background-size: 400%;
|
||||
|
||||
@@ -3,7 +3,7 @@ import useTokenLogoSource from 'hooks/useAssetLogoSource'
|
||||
import React from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
export const MissingImageLogo = styled.div<{ size?: string }>`
|
||||
const MissingImageLogo = styled.div<{ size?: string }>`
|
||||
--size: ${({ size }) => size};
|
||||
border-radius: 100px;
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
|
||||
@@ -1,38 +1,12 @@
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { t, Trans } from '@lingui/macro'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import FeatureFlagModal from 'components/FeatureFlagModal/FeatureFlagModal'
|
||||
import { PrivacyPolicyModal } from 'components/PrivacyPolicy'
|
||||
import { L2_CHAIN_IDS } from 'constants/chains'
|
||||
import { LOCALE_LABEL, SUPPORTED_LOCALES, SupportedLocale } from 'constants/locales'
|
||||
import { useActiveLocale } from 'hooks/useActiveLocale'
|
||||
import { useLocationLinkProps } from 'hooks/useLocationLinkProps'
|
||||
import { FunctionComponent, PropsWithChildren, useEffect, useRef, useState } from 'react'
|
||||
import {
|
||||
BookOpen,
|
||||
Check,
|
||||
ChevronLeft,
|
||||
Coffee,
|
||||
FileText,
|
||||
Flag,
|
||||
Globe,
|
||||
HelpCircle,
|
||||
Info,
|
||||
MessageCircle,
|
||||
Moon,
|
||||
Sun,
|
||||
} from 'react-feather'
|
||||
import { FunctionComponent, PropsWithChildren, useRef } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { useDarkModeManager } from 'state/user/hooks'
|
||||
import styled, { css } from 'styled-components/macro'
|
||||
import { isDevelopmentEnv, isStagingEnv } from 'utils/env'
|
||||
|
||||
import { ReactComponent as MenuIcon } from '../../assets/images/menu.svg'
|
||||
import { useOnClickOutside } from '../../hooks/useOnClickOutside'
|
||||
import { useModalIsOpen, useToggleModal } from '../../state/application/hooks'
|
||||
import { ApplicationModal } from '../../state/application/reducer'
|
||||
import { ExternalLink } from '../../theme'
|
||||
import { ButtonPrimary } from '../Button'
|
||||
|
||||
export enum FlyoutAlignment {
|
||||
LEFT = 'LEFT',
|
||||
@@ -41,41 +15,10 @@ export enum FlyoutAlignment {
|
||||
|
||||
const StyledMenuIcon = styled(MenuIcon)`
|
||||
path {
|
||||
stroke: ${({ theme }) => theme.deprecated_text1};
|
||||
stroke: ${({ theme }) => theme.textPrimary};
|
||||
}
|
||||
`
|
||||
|
||||
const StyledMenuButton = styled.button`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 40px;
|
||||
background-color: ${({ theme }) => theme.deprecated_bg0};
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_bg0};
|
||||
padding: 0.15rem 0.5rem;
|
||||
border-radius: 16px;
|
||||
|
||||
:hover,
|
||||
:focus {
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_bg3};
|
||||
}
|
||||
|
||||
svg {
|
||||
margin-top: 2px;
|
||||
}
|
||||
`
|
||||
|
||||
const UNIbutton = styled(ButtonPrimary)`
|
||||
background-color: ${({ theme }) => theme.deprecated_bg3};
|
||||
background: radial-gradient(174.47% 188.91% at 1.84% 0%, #ff007a 0%, #2172e5 100%), #edeef2;
|
||||
border: none;
|
||||
`
|
||||
|
||||
const StyledMenu = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@@ -92,7 +35,7 @@ const MenuFlyout = styled.span<{ flyoutAlignment?: FlyoutAlignment }>`
|
||||
background-color: ${({ theme }) => theme.deprecated_bg1};
|
||||
box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04),
|
||||
0px 24px 32px rgba(0, 0, 0, 0.01);
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_bg0};
|
||||
border: 1px solid ${({ theme }) => theme.backgroundSurface};
|
||||
border-radius: 12px;
|
||||
padding: 0.5rem;
|
||||
display: flex;
|
||||
@@ -124,9 +67,9 @@ const MenuItem = styled(ExternalLink)`
|
||||
align-items: center;
|
||||
padding: 0.5rem 0.5rem;
|
||||
justify-content: space-between;
|
||||
color: ${({ theme }) => theme.deprecated_text2};
|
||||
color: ${({ theme }) => theme.textSecondary};
|
||||
:hover {
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
@@ -135,9 +78,11 @@ const MenuItem = styled(ExternalLink)`
|
||||
const InternalMenuItem = styled(Link)`
|
||||
flex: 1;
|
||||
padding: 0.5rem 0.5rem;
|
||||
color: ${({ theme }) => theme.deprecated_text2};
|
||||
color: ${({ theme }) => theme.textSecondary};
|
||||
width: max-content;
|
||||
text-decoration: none;
|
||||
:hover {
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
@@ -146,180 +91,7 @@ const InternalMenuItem = styled(Link)`
|
||||
}
|
||||
`
|
||||
|
||||
const InternalLinkMenuItem = styled(InternalMenuItem)`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 0.5rem 0.5rem;
|
||||
justify-content: space-between;
|
||||
text-decoration: none;
|
||||
:hover {
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
`
|
||||
|
||||
const ToggleMenuItem = styled.button`
|
||||
background-color: transparent;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 0.5rem 0.5rem;
|
||||
justify-content: space-between;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
color: ${({ theme }) => theme.deprecated_text2};
|
||||
:hover {
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
`
|
||||
|
||||
function LanguageMenuItem({ locale, active, key }: { locale: SupportedLocale; active: boolean; key: string }) {
|
||||
const { to, onClick } = useLocationLinkProps(locale)
|
||||
|
||||
if (!to) return null
|
||||
|
||||
return (
|
||||
<InternalLinkMenuItem onClick={onClick} key={key} to={to}>
|
||||
<div>{LOCALE_LABEL[locale]}</div>
|
||||
{active && <Check opacity={0.6} size={16} />}
|
||||
</InternalLinkMenuItem>
|
||||
)
|
||||
}
|
||||
|
||||
function LanguageMenu({ close }: { close: () => void }) {
|
||||
const activeLocale = useActiveLocale()
|
||||
|
||||
return (
|
||||
<MenuFlyout>
|
||||
<ToggleMenuItem onClick={close}>
|
||||
<ChevronLeft size={16} />
|
||||
</ToggleMenuItem>
|
||||
{SUPPORTED_LOCALES.map((locale) => (
|
||||
<LanguageMenuItem locale={locale} active={activeLocale === locale} key={locale} />
|
||||
))}
|
||||
</MenuFlyout>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Menu() {
|
||||
const { account, chainId } = useWeb3React()
|
||||
|
||||
const node = useRef<HTMLDivElement>()
|
||||
const open = useModalIsOpen(ApplicationModal.MENU)
|
||||
const toggleMenu = useToggleModal(ApplicationModal.MENU)
|
||||
useOnClickOutside(node, open ? toggleMenu : undefined)
|
||||
const togglePrivacyPolicy = useToggleModal(ApplicationModal.PRIVACY_POLICY)
|
||||
const openFeatureFlagsModal = useToggleModal(ApplicationModal.FEATURE_FLAGS)
|
||||
const openClaimModal = useToggleModal(ApplicationModal.ADDRESS_CLAIM)
|
||||
const showUNIClaimOption = Boolean(!!account && !!chainId && !L2_CHAIN_IDS.includes(chainId))
|
||||
|
||||
const [darkMode, toggleDarkMode] = useDarkModeManager()
|
||||
|
||||
const [menu, setMenu] = useState<'main' | 'lang'>('main')
|
||||
|
||||
useEffect(() => {
|
||||
setMenu('main')
|
||||
}, [open])
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30451 */}
|
||||
<StyledMenu ref={node as any}>
|
||||
<StyledMenuButton onClick={toggleMenu} aria-label={t`Menu`}>
|
||||
<StyledMenuIcon />
|
||||
</StyledMenuButton>
|
||||
|
||||
{open &&
|
||||
(() => {
|
||||
switch (menu) {
|
||||
case 'lang':
|
||||
return <LanguageMenu close={() => setMenu('main')} />
|
||||
case 'main':
|
||||
default:
|
||||
return (
|
||||
<MenuFlyout>
|
||||
<MenuItem href="https://uniswap.org/">
|
||||
<div>
|
||||
<Trans>About</Trans>
|
||||
</div>
|
||||
<Info opacity={0.6} size={16} />
|
||||
</MenuItem>
|
||||
<MenuItem href="https://help.uniswap.org/">
|
||||
<div>
|
||||
<Trans>Help Center</Trans>
|
||||
</div>
|
||||
<HelpCircle opacity={0.6} size={16} />
|
||||
</MenuItem>
|
||||
<MenuItem href="https://uniswap.canny.io/feature-requests">
|
||||
<div>
|
||||
<Trans>Request Features</Trans>
|
||||
</div>
|
||||
<Coffee opacity={0.6} size={16} />
|
||||
</MenuItem>
|
||||
<MenuItem href="https://discord.gg/FCfyBSbCU5">
|
||||
<div>
|
||||
<Trans>Discord</Trans>
|
||||
</div>
|
||||
<MessageCircle opacity={0.6} size={16} />
|
||||
</MenuItem>
|
||||
<ToggleMenuItem onClick={() => setMenu('lang')}>
|
||||
<div>
|
||||
<Trans>Language</Trans>
|
||||
</div>
|
||||
<Globe opacity={0.6} size={16} />
|
||||
</ToggleMenuItem>
|
||||
<ToggleMenuItem onClick={() => toggleDarkMode()}>
|
||||
<div>{darkMode ? <Trans>Light Theme</Trans> : <Trans>Dark Theme</Trans>}</div>
|
||||
{darkMode ? <Sun opacity={0.6} size={16} /> : <Moon opacity={0.6} size={16} />}
|
||||
</ToggleMenuItem>
|
||||
<MenuItem href="https://docs.uniswap.org/">
|
||||
<div>
|
||||
<Trans>Docs</Trans>
|
||||
</div>
|
||||
<BookOpen opacity={0.6} size={16} />
|
||||
</MenuItem>
|
||||
<ToggleMenuItem onClick={() => togglePrivacyPolicy()}>
|
||||
<div>
|
||||
<Trans>Legal & Privacy</Trans>
|
||||
</div>
|
||||
<FileText opacity={0.6} size={16} />
|
||||
</ToggleMenuItem>
|
||||
{(isDevelopmentEnv() || isStagingEnv()) && (
|
||||
<ToggleMenuItem onClick={openFeatureFlagsModal}>
|
||||
Feature Flags <Flag opacity={0.6} size={16} />
|
||||
</ToggleMenuItem>
|
||||
)}
|
||||
{showUNIClaimOption && (
|
||||
<UNIbutton
|
||||
onClick={openClaimModal}
|
||||
padding="8px 16px"
|
||||
width="100%"
|
||||
$borderRadius="12px"
|
||||
mt="0.5rem"
|
||||
>
|
||||
<Trans>Claim UNI</Trans>
|
||||
</UNIbutton>
|
||||
)}
|
||||
</MenuFlyout>
|
||||
)
|
||||
}
|
||||
})()}
|
||||
</StyledMenu>
|
||||
<PrivacyPolicyModal />
|
||||
<FeatureFlagModal />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
interface NewMenuProps {
|
||||
interface MenuProps {
|
||||
flyoutAlignment?: FlyoutAlignment
|
||||
ToggleUI?: FunctionComponent<PropsWithChildren<unknown>>
|
||||
menuItems: {
|
||||
@@ -329,20 +101,12 @@ interface NewMenuProps {
|
||||
}[]
|
||||
}
|
||||
|
||||
const NewMenuFlyout = styled(MenuFlyout)`
|
||||
top: 3rem !important;
|
||||
`
|
||||
const NewMenuItem = styled(InternalMenuItem)`
|
||||
width: max-content;
|
||||
text-decoration: none;
|
||||
`
|
||||
|
||||
const ExternalMenuItem = styled(MenuItem)`
|
||||
width: max-content;
|
||||
text-decoration: none;
|
||||
`
|
||||
|
||||
export const NewMenu = ({ flyoutAlignment = FlyoutAlignment.RIGHT, ToggleUI, menuItems, ...rest }: NewMenuProps) => {
|
||||
export const Menu = ({ flyoutAlignment = FlyoutAlignment.RIGHT, ToggleUI, menuItems, ...rest }: MenuProps) => {
|
||||
const node = useRef<HTMLDivElement>()
|
||||
const open = useModalIsOpen(ApplicationModal.POOL_OVERVIEW_OPTIONS)
|
||||
const toggle = useToggleModal(ApplicationModal.POOL_OVERVIEW_OPTIONS)
|
||||
@@ -352,19 +116,19 @@ export const NewMenu = ({ flyoutAlignment = FlyoutAlignment.RIGHT, ToggleUI, men
|
||||
<StyledMenu ref={node as any} {...rest}>
|
||||
<ToggleElement onClick={toggle} />
|
||||
{open && (
|
||||
<NewMenuFlyout flyoutAlignment={flyoutAlignment}>
|
||||
<MenuFlyout flyoutAlignment={flyoutAlignment}>
|
||||
{menuItems.map(({ content, link, external }, i) =>
|
||||
external ? (
|
||||
<ExternalMenuItem href={link} key={i}>
|
||||
{content}
|
||||
</ExternalMenuItem>
|
||||
) : (
|
||||
<NewMenuItem to={link} key={i}>
|
||||
<InternalMenuItem to={link} key={i}>
|
||||
{content}
|
||||
</NewMenuItem>
|
||||
</InternalMenuItem>
|
||||
)
|
||||
)}
|
||||
</NewMenuFlyout>
|
||||
</MenuFlyout>
|
||||
)}
|
||||
</StyledMenu>
|
||||
)
|
||||
|
||||
@@ -39,7 +39,7 @@ const StyledDialogContent = styled(AnimatedDialogContent)<StyledDialogProps>`
|
||||
|
||||
&[data-reach-dialog-content] {
|
||||
margin: auto;
|
||||
background-color: ${({ theme }) => theme.deprecated_bg0};
|
||||
background-color: ${({ theme }) => theme.backgroundSurface};
|
||||
border: ${({ theme, $hideBorder }) => !$hideBorder && `1px solid ${theme.deprecated_bg1}`};
|
||||
box-shadow: ${({ theme }) => theme.deepShadow};
|
||||
padding: 0px;
|
||||
|
||||
@@ -58,7 +58,7 @@ export function SubmittedView({
|
||||
<CloseIcon onClick={onDismiss} />
|
||||
</RowBetween>
|
||||
<ConfirmedIcon>
|
||||
<ArrowUpCircle strokeWidth={0.5} size={90} color={theme.deprecated_primary1} />
|
||||
<ArrowUpCircle strokeWidth={0.5} size={90} color={theme.accentAction} />
|
||||
</ConfirmedIcon>
|
||||
<AutoColumn gap="100px" justify="center">
|
||||
{children}
|
||||
|
||||
@@ -72,13 +72,6 @@ export const searchBarContainerNft = style([
|
||||
},
|
||||
])
|
||||
|
||||
export const searchBarContainerNftOpen = style([
|
||||
searchBarContainerNft,
|
||||
{
|
||||
boxShadow: vars.color.cardDropShadow,
|
||||
},
|
||||
])
|
||||
|
||||
export const searchBar = style([
|
||||
baseSearchStyle,
|
||||
sprinkles({
|
||||
@@ -111,16 +104,6 @@ export const searchBarInput = style([
|
||||
}),
|
||||
])
|
||||
|
||||
export const searchBarDropdown = style([
|
||||
baseSearchStyle,
|
||||
sprinkles({
|
||||
borderBottomLeftRadius: '12',
|
||||
borderBottomRightRadius: '12',
|
||||
height: { sm: 'viewHeight', md: 'auto' },
|
||||
borderTop: 'none',
|
||||
}),
|
||||
])
|
||||
|
||||
export const searchBarDropdownNft = style([
|
||||
baseSearchNftStyle,
|
||||
sprinkles({
|
||||
@@ -244,16 +227,6 @@ export const visible = style([
|
||||
}),
|
||||
])
|
||||
|
||||
export const searchContentCentered = style({
|
||||
'@media': {
|
||||
[`screen and (min-width: ${breakpoints.lg}px)`]: {
|
||||
transform: `translateX(${DESKTOP_NAVBAR_WIDTH / 4}px)`,
|
||||
transition: `transform ${vars.time[125]}`,
|
||||
transitionTimingFunction: 'ease-out',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export const searchContentLeftAlign = style({
|
||||
'@media': {
|
||||
[`screen and (min-width: ${breakpoints.lg}px)`]: {
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { t } from '@lingui/macro'
|
||||
import { t, Trans } from '@lingui/macro'
|
||||
import { sendAnalyticsEvent, Trace, TraceEvent, useTrace } from '@uniswap/analytics'
|
||||
import { BrowserEvent, ElementName, EventName, SectionName } from '@uniswap/analytics-events'
|
||||
import clsx from 'clsx'
|
||||
import { NftVariant, useNftFlag } from 'featureFlags/flags/nft'
|
||||
import useDebounce from 'hooks/useDebounce'
|
||||
import { useIsNftPage } from 'hooks/useIsNftPage'
|
||||
import { useOnClickOutside } from 'hooks/useOnClickOutside'
|
||||
@@ -47,10 +46,8 @@ export const SearchBar = () => {
|
||||
const searchRef = useRef<HTMLDivElement>(null)
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
const { pathname } = useLocation()
|
||||
const phase1Flag = useNftFlag()
|
||||
const isMobile = useIsMobile()
|
||||
const isTablet = useIsTablet()
|
||||
const isPhase1 = phase1Flag === NftVariant.Enabled
|
||||
|
||||
useOnClickOutside(searchRef, () => {
|
||||
isOpen && toggleOpen()
|
||||
@@ -63,7 +60,7 @@ export const SearchBar = () => {
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: false,
|
||||
refetchOnReconnect: false,
|
||||
enabled: !!debouncedSearchValue.length && isPhase1,
|
||||
enabled: !!debouncedSearchValue.length,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -111,8 +108,6 @@ export const SearchBar = () => {
|
||||
}, [isOpen])
|
||||
|
||||
const isMobileOrTablet = isMobile || isTablet
|
||||
const showCenteredSearchContent =
|
||||
!isOpen && phase1Flag !== NftVariant.Enabled && !isMobileOrTablet && searchValue.length === 0
|
||||
|
||||
const trace = useTrace({ section: SectionName.NAVBAR_SEARCH })
|
||||
|
||||
@@ -122,12 +117,8 @@ export const SearchBar = () => {
|
||||
...trace,
|
||||
}
|
||||
const placeholderText = useMemo(() => {
|
||||
return phase1Flag === NftVariant.Enabled
|
||||
? isMobileOrTablet
|
||||
? t`Search`
|
||||
: t`Search tokens and NFT collections`
|
||||
: t`Search tokens`
|
||||
}, [phase1Flag, isMobileOrTablet])
|
||||
return isMobileOrTablet ? t`Search` : t`Search tokens and NFT collections`
|
||||
}, [isMobileOrTablet])
|
||||
|
||||
const handleKeyPress = useCallback(
|
||||
(event: any) => {
|
||||
@@ -160,14 +151,14 @@ export const SearchBar = () => {
|
||||
position={{ sm: 'fixed', md: 'absolute', xl: 'relative' }}
|
||||
width={{ sm: isOpen ? 'viewWidth' : 'auto', md: 'auto' }}
|
||||
ref={searchRef}
|
||||
className={isPhase1 ? styles.searchBarContainerNft : styles.searchBarContainer}
|
||||
className={styles.searchBarContainerNft}
|
||||
display={{ sm: isOpen ? 'inline-block' : 'none', xl: 'inline-block' }}
|
||||
>
|
||||
<Row
|
||||
className={clsx(
|
||||
` ${isPhase1 ? styles.nftSearchBar : styles.searchBar} ${!isOpen && !isMobile && magicalGradientOnHover} ${
|
||||
isMobileOrTablet && (isOpen ? styles.visible : styles.hidden)
|
||||
} `
|
||||
styles.nftSearchBar,
|
||||
!isOpen && !isMobile && magicalGradientOnHover,
|
||||
isMobileOrTablet && (isOpen ? styles.visible : styles.hidden)
|
||||
)}
|
||||
borderRadius={isOpen || isMobileOrTablet ? undefined : '12'}
|
||||
borderTopRightRadius={isOpen && !isMobile ? '12' : undefined}
|
||||
@@ -177,7 +168,7 @@ export const SearchBar = () => {
|
||||
onClick={() => !isOpen && toggleOpen()}
|
||||
gap="12"
|
||||
>
|
||||
<Box className={showCenteredSearchContent ? styles.searchContentCentered : styles.searchContentLeftAlign}>
|
||||
<Box className={styles.searchContentLeftAlign}>
|
||||
<Box display={{ sm: 'none', md: 'flex' }}>
|
||||
<MagnifyingGlassIcon />
|
||||
</Box>
|
||||
@@ -191,23 +182,26 @@ export const SearchBar = () => {
|
||||
element={ElementName.NAVBAR_SEARCH_INPUT}
|
||||
properties={{ ...trace }}
|
||||
>
|
||||
<Box
|
||||
as="input"
|
||||
placeholder={placeholderText}
|
||||
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
||||
!isOpen && toggleOpen()
|
||||
setSearchValue(event.target.value)
|
||||
}}
|
||||
onBlur={() => sendAnalyticsEvent(EventName.NAVBAR_SEARCH_EXITED, navbarSearchEventProperties)}
|
||||
className={`${styles.searchBarInput} ${
|
||||
showCenteredSearchContent ? styles.searchContentCentered : styles.searchContentLeftAlign
|
||||
}`}
|
||||
value={searchValue}
|
||||
ref={inputRef}
|
||||
width={phase1Flag === NftVariant.Enabled || isOpen ? 'full' : '160'}
|
||||
<Trans
|
||||
id={placeholderText}
|
||||
render={({ translation }) => (
|
||||
<Box
|
||||
as="input"
|
||||
placeholder={translation as string}
|
||||
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
||||
!isOpen && toggleOpen()
|
||||
setSearchValue(event.target.value)
|
||||
}}
|
||||
onBlur={() => sendAnalyticsEvent(EventName.NAVBAR_SEARCH_EXITED, navbarSearchEventProperties)}
|
||||
className={`${styles.searchBarInput} ${styles.searchContentLeftAlign}`}
|
||||
value={searchValue}
|
||||
ref={inputRef}
|
||||
width="full"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</TraceEvent>
|
||||
{!isOpen && isPhase1 && <KeyShortCut>/</KeyShortCut>}
|
||||
{!isOpen && <KeyShortCut>/</KeyShortCut>}
|
||||
</Row>
|
||||
<Box className={clsx(isOpen ? styles.visible : styles.hidden)}>
|
||||
{isOpen && (
|
||||
@@ -217,7 +211,7 @@ export const SearchBar = () => {
|
||||
collections={reducedCollections}
|
||||
queryText={debouncedSearchValue}
|
||||
hasInput={debouncedSearchValue.length > 0}
|
||||
isLoading={tokensAreLoading || (collectionsAreLoading && phase1Flag === NftVariant.Enabled)}
|
||||
isLoading={tokensAreLoading || collectionsAreLoading}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { useTrace } from '@uniswap/analytics'
|
||||
import { NavBarSearchTypes, SectionName } from '@uniswap/analytics-events'
|
||||
import { NftVariant, useNftFlag } from 'featureFlags/flags/nft'
|
||||
import { useIsNftPage } from 'hooks/useIsNftPage'
|
||||
import { Box } from 'nft/components/Box'
|
||||
import { Column, Row } from 'nft/components/Flex'
|
||||
@@ -35,7 +34,7 @@ interface SearchBarDropdownSectionProps {
|
||||
eventProperties: Record<string, unknown>
|
||||
}
|
||||
|
||||
export const SearchBarDropdownSection = ({
|
||||
const SearchBarDropdownSection = ({
|
||||
toggleOpen,
|
||||
suggestions,
|
||||
header,
|
||||
@@ -116,9 +115,7 @@ export const SearchBarDropdown = ({
|
||||
const { pathname } = useLocation()
|
||||
const isNFTPage = useIsNftPage()
|
||||
const isTokenPage = pathname.includes('/tokens')
|
||||
const phase1Flag = useNftFlag()
|
||||
const [resultsState, setResultsState] = useState<ReactNode>()
|
||||
const isPhase1 = phase1Flag === NftVariant.Enabled
|
||||
|
||||
const { data: trendingCollectionResults, isLoading: trendingCollectionsAreLoading } = useQuery(
|
||||
['trendingCollections', 'eth', 'twenty_four_hours'],
|
||||
@@ -157,7 +154,7 @@ export const SearchBarDropdown = ({
|
||||
trendingTokenResults?.forEach(updateSearchHistory)
|
||||
}, [trendingTokenResults, updateSearchHistory])
|
||||
|
||||
const trendingTokensLength = phase1Flag === NftVariant.Enabled ? (isTokenPage ? 3 : 2) : 4
|
||||
const trendingTokensLength = isTokenPage ? 3 : 2
|
||||
const trendingTokens = useMemo(
|
||||
() =>
|
||||
trendingTokenResults
|
||||
@@ -231,24 +228,22 @@ export const SearchBarDropdown = ({
|
||||
)
|
||||
|
||||
const collectionSearchResults =
|
||||
phase1Flag === NftVariant.Enabled ? (
|
||||
collections.length > 0 ? (
|
||||
<SearchBarDropdownSection
|
||||
hoveredIndex={hoveredIndex}
|
||||
startingIndex={showCollectionsFirst ? 0 : tokens.length}
|
||||
setHoveredIndex={setHoveredIndex}
|
||||
toggleOpen={toggleOpen}
|
||||
suggestions={collections}
|
||||
eventProperties={{
|
||||
suggestion_type: NavBarSearchTypes.COLLECTION_SUGGESTION,
|
||||
...eventProperties,
|
||||
}}
|
||||
header={<Trans>NFT Collections</Trans>}
|
||||
/>
|
||||
) : (
|
||||
<Box className={styles.notFoundContainer}>No NFT collections found.</Box>
|
||||
)
|
||||
) : null
|
||||
collections.length > 0 ? (
|
||||
<SearchBarDropdownSection
|
||||
hoveredIndex={hoveredIndex}
|
||||
startingIndex={showCollectionsFirst ? 0 : tokens.length}
|
||||
setHoveredIndex={setHoveredIndex}
|
||||
toggleOpen={toggleOpen}
|
||||
suggestions={collections}
|
||||
eventProperties={{
|
||||
suggestion_type: NavBarSearchTypes.COLLECTION_SUGGESTION,
|
||||
...eventProperties,
|
||||
}}
|
||||
header={<Trans>NFT Collections</Trans>}
|
||||
/>
|
||||
) : (
|
||||
<Box className={styles.notFoundContainer}>No NFT collections found.</Box>
|
||||
)
|
||||
|
||||
const currentState = () =>
|
||||
hasInput ? (
|
||||
@@ -300,7 +295,7 @@ export const SearchBarDropdown = ({
|
||||
isLoading={trendingTokensAreLoading}
|
||||
/>
|
||||
)}
|
||||
{!isTokenPage && phase1Flag === NftVariant.Enabled && (
|
||||
{!isTokenPage && (
|
||||
<SearchBarDropdownSection
|
||||
hoveredIndex={hoveredIndex}
|
||||
startingIndex={shortenedHistory.length + (isNFTPage ? 0 : trendingTokens?.length ?? 0)}
|
||||
@@ -330,7 +325,6 @@ export const SearchBarDropdown = ({
|
||||
trendingTokens,
|
||||
trendingTokensAreLoading,
|
||||
hoveredIndex,
|
||||
phase1Flag,
|
||||
toggleOpen,
|
||||
shortenedHistory,
|
||||
hasInput,
|
||||
@@ -343,7 +337,7 @@ export const SearchBarDropdown = ({
|
||||
])
|
||||
|
||||
return (
|
||||
<Box className={isPhase1 ? styles.searchBarDropdownNft : styles.searchBarDropdown}>
|
||||
<Box className={styles.searchBarDropdownNft}>
|
||||
<Box opacity={isLoading ? '0.3' : '1'} transition="125">
|
||||
{resultsState}
|
||||
</Box>
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import Web3Status from 'components/Web3Status'
|
||||
import { NftVariant, useNftFlag } from 'featureFlags/flags/nft'
|
||||
import { chainIdToBackendName } from 'graphql/data/util'
|
||||
import { useIsNftPage } from 'hooks/useIsNftPage'
|
||||
import { Box } from 'nft/components/Box'
|
||||
import { Row } from 'nft/components/Flex'
|
||||
import { UniIcon } from 'nft/components/icons'
|
||||
import { ReactNode } from 'react'
|
||||
import { NavLink, NavLinkProps, useLocation } from 'react-router-dom'
|
||||
import { NavLink, NavLinkProps, useLocation, useNavigate } from 'react-router-dom'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import { Bag } from './Bag'
|
||||
@@ -17,21 +16,11 @@ import { MenuDropdown } from './MenuDropdown'
|
||||
import { SearchBar } from './SearchBar'
|
||||
import * as styles from './style.css'
|
||||
|
||||
const MobileBottomBar = styled.div`
|
||||
position: fixed;
|
||||
display: flex;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
justify-content: space-between;
|
||||
padding: 4px 8px;
|
||||
height: 56px;
|
||||
background: ${({ theme }) => theme.backgroundSurface};
|
||||
border-top: 1px solid ${({ theme }) => theme.backgroundOutline};
|
||||
|
||||
@media screen and (min-width: ${({ theme }) => theme.breakpoint.md}px) {
|
||||
display: none;
|
||||
}
|
||||
const Nav = styled.nav`
|
||||
padding: 20px 12px;
|
||||
width: 100%;
|
||||
height: ${({ theme }) => theme.navHeight}px;
|
||||
z-index: 2;
|
||||
`
|
||||
|
||||
interface MenuItemProps {
|
||||
@@ -39,24 +28,25 @@ interface MenuItemProps {
|
||||
id?: NavLinkProps['id']
|
||||
isActive?: boolean
|
||||
children: ReactNode
|
||||
dataTestId?: string
|
||||
}
|
||||
|
||||
const MenuItem = ({ href, id, isActive, children }: MenuItemProps) => {
|
||||
const MenuItem = ({ href, dataTestId, id, isActive, children }: MenuItemProps) => {
|
||||
return (
|
||||
<NavLink
|
||||
to={href}
|
||||
className={isActive ? styles.activeMenuItem : styles.menuItem}
|
||||
id={id}
|
||||
style={{ textDecoration: 'none' }}
|
||||
data-testid={dataTestId}
|
||||
>
|
||||
{children}
|
||||
</NavLink>
|
||||
)
|
||||
}
|
||||
|
||||
const PageTabs = () => {
|
||||
export const PageTabs = () => {
|
||||
const { pathname } = useLocation()
|
||||
const nftFlag = useNftFlag()
|
||||
const { chainId: connectedChainId } = useWeb3React()
|
||||
const chainName = chainIdToBackendName(connectedChainId)
|
||||
|
||||
@@ -77,11 +67,9 @@ const PageTabs = () => {
|
||||
<MenuItem href={`/tokens/${chainName.toLowerCase()}`} isActive={pathname.startsWith('/tokens')}>
|
||||
<Trans>Tokens</Trans>
|
||||
</MenuItem>
|
||||
{nftFlag === NftVariant.Enabled && (
|
||||
<MenuItem href="/nfts" isActive={isNftPage}>
|
||||
<Trans>NFTs</Trans>
|
||||
</MenuItem>
|
||||
)}
|
||||
<MenuItem dataTestId="nft-nav" href="/nfts" isActive={isNftPage}>
|
||||
<Trans>NFTs</Trans>
|
||||
</MenuItem>
|
||||
<MenuItem href="/pool" id="pool-nav-link" isActive={isPoolActive}>
|
||||
<Trans>Pool</Trans>
|
||||
</MenuItem>
|
||||
@@ -91,14 +79,22 @@ const PageTabs = () => {
|
||||
|
||||
const Navbar = () => {
|
||||
const isNftPage = useIsNftPage()
|
||||
const navigate = useNavigate()
|
||||
|
||||
return (
|
||||
<>
|
||||
<nav className={styles.nav}>
|
||||
<Box display="flex" height="full" flexWrap="nowrap" alignItems="stretch">
|
||||
<Nav>
|
||||
<Box display="flex" height="full" flexWrap="nowrap">
|
||||
<Box className={styles.leftSideContainer}>
|
||||
<Box as="a" href="#/swap" className={styles.logoContainer}>
|
||||
<UniIcon width="48" height="48" className={styles.logo} />
|
||||
<Box className={styles.logoContainer}>
|
||||
<UniIcon
|
||||
width="48"
|
||||
height="48"
|
||||
className={styles.logo}
|
||||
onClick={() => {
|
||||
navigate('/')
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
{!isNftPage && (
|
||||
<Box display={{ sm: 'flex', lg: 'none' }}>
|
||||
@@ -109,7 +105,7 @@ const Navbar = () => {
|
||||
<PageTabs />
|
||||
</Row>
|
||||
</Box>
|
||||
<Box className={styles.middleContainer} alignItems="flex-start">
|
||||
<Box className={styles.searchContainer}>
|
||||
<SearchBar />
|
||||
</Box>
|
||||
<Box className={styles.rightSideContainer}>
|
||||
@@ -131,13 +127,7 @@ const Navbar = () => {
|
||||
</Row>
|
||||
</Box>
|
||||
</Box>
|
||||
</nav>
|
||||
<MobileBottomBar>
|
||||
<PageTabs />
|
||||
<Box marginY="4">
|
||||
<MenuDropdown />
|
||||
</Box>
|
||||
</MobileBottomBar>
|
||||
</Nav>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,22 +3,11 @@ import { style } from '@vanilla-extract/css'
|
||||
import { subhead } from '../../nft/css/common.css'
|
||||
import { sprinkles, vars } from '../../nft/css/sprinkles.css'
|
||||
|
||||
export const nav = style([
|
||||
sprinkles({
|
||||
paddingX: '20',
|
||||
paddingY: '12',
|
||||
width: 'full',
|
||||
height: '72',
|
||||
zIndex: '2',
|
||||
background: 'backgroundFloating',
|
||||
}),
|
||||
])
|
||||
|
||||
export const logoContainer = style([
|
||||
sprinkles({
|
||||
display: 'flex',
|
||||
marginRight: { sm: '12', xxl: '20' },
|
||||
alignItems: 'center',
|
||||
cursor: 'pointer',
|
||||
}),
|
||||
])
|
||||
|
||||
@@ -41,16 +30,19 @@ export const baseSideContainer = style([
|
||||
export const leftSideContainer = style([
|
||||
baseSideContainer,
|
||||
sprinkles({
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
}),
|
||||
])
|
||||
|
||||
export const middleContainer = style([
|
||||
export const searchContainer = style([
|
||||
sprinkles({
|
||||
flex: '1',
|
||||
flexShrink: '1',
|
||||
justifyContent: { lg: 'flex-end', xl: 'center' },
|
||||
display: { sm: 'none', xl: 'flex' },
|
||||
alignSelf: 'center',
|
||||
height: '48',
|
||||
alignItems: 'flex-start',
|
||||
}),
|
||||
])
|
||||
@@ -58,6 +50,7 @@ export const middleContainer = style([
|
||||
export const rightSideContainer = style([
|
||||
baseSideContainer,
|
||||
sprinkles({
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
}),
|
||||
])
|
||||
|
||||
@@ -36,7 +36,7 @@ const ActiveText = styled.div`
|
||||
`
|
||||
|
||||
const StyledArrowLeft = styled(ArrowLeft)`
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
`
|
||||
|
||||
export function FindPoolTabs({ origin }: { origin: string }) {
|
||||
@@ -92,7 +92,7 @@ export function AddRemoveTabs({
|
||||
}}
|
||||
flex={children ? '1' : undefined}
|
||||
>
|
||||
<StyledArrowLeft stroke={theme.deprecated_text2} />
|
||||
<StyledArrowLeft stroke={theme.textSecondary} />
|
||||
</StyledHistoryLink>
|
||||
<ThemedText.DeprecatedMediumHeader
|
||||
fontWeight={500}
|
||||
|
||||
@@ -15,13 +15,6 @@ const L2Icon = styled.img`
|
||||
margin-right: 16px;
|
||||
`
|
||||
|
||||
export const Controls = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
padding: 0 20px 20px 20px;
|
||||
`
|
||||
|
||||
const BodyText = styled.div`
|
||||
color: ${({ color }) => color};
|
||||
display: flex;
|
||||
@@ -46,7 +39,7 @@ const SHOULD_SHOW_ALERT = {
|
||||
[SupportedChainId.CELO_ALFAJORES]: true,
|
||||
}
|
||||
|
||||
export type NetworkAlertChains = keyof typeof SHOULD_SHOW_ALERT
|
||||
type NetworkAlertChains = keyof typeof SHOULD_SHOW_ALERT
|
||||
|
||||
const BG_COLORS_BY_DARK_MODE_AND_CHAIN_ID: {
|
||||
[darkMode in 'dark' | 'light']: { [chainId in NetworkAlertChains]: string }
|
||||
|
||||
@@ -4,7 +4,7 @@ import styled from 'styled-components/macro'
|
||||
import { escapeRegExp } from '../../utils'
|
||||
|
||||
const StyledInput = styled.input<{ error?: boolean; fontSize?: string; align?: string }>`
|
||||
color: ${({ error, theme }) => (error ? theme.deprecated_red1 : theme.deprecated_text1)};
|
||||
color: ${({ error, theme }) => (error ? theme.accentFailure : theme.textPrimary)};
|
||||
width: 0;
|
||||
position: relative;
|
||||
font-weight: 400;
|
||||
|
||||
@@ -17,7 +17,7 @@ const CautionTriangle = styled(AlertTriangle)`
|
||||
color: ${({ theme }) => theme.accentWarning};
|
||||
`
|
||||
const Link = styled(ExternalLink)`
|
||||
color: ${({ theme }) => theme.deprecated_black};
|
||||
color: ${({ theme }) => theme.black};
|
||||
text-decoration: underline;
|
||||
`
|
||||
const TitleRow = styled.div`
|
||||
|
||||
@@ -44,7 +44,7 @@ const StyledPollingBlockNumber = styled(ThemedText.DeprecatedSmall)<{
|
||||
hovering: boolean
|
||||
warning: boolean
|
||||
}>`
|
||||
color: ${({ theme, warning }) => (warning ? theme.deprecated_yellow3 : theme.deprecated_green1)};
|
||||
color: ${({ theme, warning }) => (warning ? theme.deprecated_yellow3 : theme.accentSuccess)};
|
||||
transition: opacity 0.25s ease;
|
||||
opacity: ${({ breathe, hovering }) => (hovering ? 0.7 : breathe ? 1 : 0.5)};
|
||||
:hover {
|
||||
@@ -66,12 +66,12 @@ const StyledPollingDot = styled.div<{ warning: boolean }>`
|
||||
min-width: 8px;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
background-color: ${({ theme, warning }) => (warning ? theme.deprecated_yellow3 : theme.deprecated_green1)};
|
||||
background-color: ${({ theme, warning }) => (warning ? theme.deprecated_yellow3 : theme.accentSuccess)};
|
||||
transition: 250ms ease background-color;
|
||||
`
|
||||
|
||||
const StyledGasDot = styled.div`
|
||||
background-color: ${({ theme }) => theme.deprecated_text3};
|
||||
background-color: ${({ theme }) => theme.textTertiary};
|
||||
border-radius: 50%;
|
||||
height: 4px;
|
||||
min-height: 4px;
|
||||
@@ -97,7 +97,7 @@ const Spinner = styled.div<{ warning: boolean }>`
|
||||
border-top: 1px solid transparent;
|
||||
border-right: 1px solid transparent;
|
||||
border-bottom: 1px solid transparent;
|
||||
border-left: 2px solid ${({ theme, warning }) => (warning ? theme.deprecated_yellow3 : theme.deprecated_green1)};
|
||||
border-left: 2px solid ${({ theme, warning }) => (warning ? theme.deprecated_yellow3 : theme.accentSuccess)};
|
||||
background: transparent;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
|
||||
@@ -12,7 +12,7 @@ const PopoverContainer = styled.div<{ show: boolean }>`
|
||||
visibility: ${(props) => (props.show ? 'visible' : 'hidden')};
|
||||
opacity: ${(props) => (props.show ? 1 : 0)};
|
||||
transition: visibility 150ms linear, opacity 150ms linear;
|
||||
color: ${({ theme }) => theme.deprecated_text2};
|
||||
color: ${({ theme }) => theme.textSecondary};
|
||||
`
|
||||
|
||||
const ReferenceElement = styled.div`
|
||||
@@ -33,9 +33,9 @@ const Arrow = styled.div`
|
||||
z-index: 9998;
|
||||
|
||||
content: '';
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_bg2};
|
||||
border: 1px solid ${({ theme }) => theme.backgroundInteractive};
|
||||
transform: rotate(45deg);
|
||||
background: ${({ theme }) => theme.deprecated_bg0};
|
||||
background: ${({ theme }) => theme.backgroundSurface};
|
||||
}
|
||||
|
||||
&.arrow-top {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { getChainInfo } from 'constants/chainInfo'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import { AlertCircle } from 'react-feather'
|
||||
import { AlertTriangle } from 'react-feather'
|
||||
import styled, { useTheme } from 'styled-components/macro'
|
||||
|
||||
import { ThemedText } from '../../theme'
|
||||
@@ -18,16 +18,19 @@ export default function FailedNetworkSwitchPopup({ chainId }: { chainId: Support
|
||||
|
||||
return (
|
||||
<RowNoFlex>
|
||||
<div style={{ paddingRight: 16 }}>
|
||||
<AlertCircle color={theme.deprecated_red1} size={24} />
|
||||
</div>
|
||||
<AutoColumn gap="8px">
|
||||
<ThemedText.DeprecatedBody fontWeight={500}>
|
||||
<Trans>
|
||||
Failed to switch networks from the Uniswap Interface. In order to use Uniswap on {chainInfo.label}, you must
|
||||
change the network in your wallet.
|
||||
</Trans>
|
||||
</ThemedText.DeprecatedBody>
|
||||
<AutoColumn gap="sm">
|
||||
<RowNoFlex style={{ alignItems: 'center' }}>
|
||||
<div style={{ paddingRight: 13 }}>
|
||||
<AlertTriangle color={theme.accentWarning} size={24} display="flex" />
|
||||
</div>
|
||||
<ThemedText.SubHeader>
|
||||
<Trans>Failed to switch networks</Trans>
|
||||
</ThemedText.SubHeader>
|
||||
</RowNoFlex>
|
||||
|
||||
<ThemedText.BodySmall>
|
||||
<Trans>To use Uniswap on {chainInfo.label}, switch the network in your wallet’s settings.</Trans>
|
||||
</ThemedText.BodySmall>
|
||||
</AutoColumn>
|
||||
</RowNoFlex>
|
||||
)
|
||||
|
||||
@@ -10,8 +10,8 @@ import FailedNetworkSwitchPopup from './FailedNetworkSwitchPopup'
|
||||
|
||||
const StyledClose = styled(X)`
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
right: 20px;
|
||||
top: 20px;
|
||||
|
||||
:hover {
|
||||
cursor: pointer;
|
||||
@@ -21,7 +21,7 @@ const Popup = styled.div`
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
padding: 1em;
|
||||
background-color: ${({ theme }) => theme.deprecated_bg0};
|
||||
background-color: ${({ theme }) => theme.backgroundSurface};
|
||||
position: relative;
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
@@ -83,7 +83,7 @@ export default function PopupItem({
|
||||
|
||||
return popupContent ? (
|
||||
<Popup>
|
||||
<StyledClose color={theme.deprecated_text2} onClick={removeThisPopup} />
|
||||
<StyledClose color={theme.textSecondary} onClick={removeThisPopup} />
|
||||
{popupContent}
|
||||
{removeAfterMs !== null ? <AnimatedFader style={faderStyle} /> : null}
|
||||
</Popup>
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { sendEvent } from 'components/analytics'
|
||||
import { AutoColumn } from 'components/Column'
|
||||
import { RowFixed } from 'components/Row'
|
||||
import useCurrentBlockTimestamp from 'hooks/useCurrentBlockTimestamp'
|
||||
import { useEffect } from 'react'
|
||||
import { MessageCircle, X } from 'react-feather'
|
||||
import { useShowSurveyPopup } from 'state/user/hooks'
|
||||
import styled, { useTheme } from 'styled-components/macro'
|
||||
import { ExternalLink, ThemedText } from 'theme'
|
||||
import { Z_INDEX } from 'theme/zIndex'
|
||||
|
||||
import BGImage from '../../assets/images/survey-orb.svg'
|
||||
|
||||
const Wrapper = styled(AutoColumn)`
|
||||
background: #edeef2;
|
||||
position: relative;
|
||||
border-radius: 12px;
|
||||
padding: 18px;
|
||||
max-width: 360px;
|
||||
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
overflow: hidden;
|
||||
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
|
||||
max-width: 100%;
|
||||
`}
|
||||
`
|
||||
|
||||
const BGOrb = styled.img`
|
||||
position: absolute;
|
||||
right: -64px;
|
||||
top: -64px;
|
||||
width: 180px;
|
||||
z-index: ${Z_INDEX.sticky};
|
||||
`
|
||||
|
||||
const WrappedCloseIcon = styled(X)`
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
stroke: #7c7c80;
|
||||
z-index: ${Z_INDEX.fixed};
|
||||
:hover {
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
}
|
||||
`
|
||||
|
||||
const END_TIMESTAMP = 1642272346 // Jan 15th
|
||||
|
||||
export default function SurveyPopup() {
|
||||
const theme = useTheme()
|
||||
const [showPopup, setShowSurveyPopup] = useShowSurveyPopup()
|
||||
|
||||
// show popup to 1% of users
|
||||
useEffect(() => {
|
||||
// has not visited page during A/B testing if undefined
|
||||
if (showPopup === undefined) {
|
||||
if (Math.random() < 0.01) {
|
||||
setShowSurveyPopup(true)
|
||||
// log a case of succesful view
|
||||
sendEvent({
|
||||
category: 'Survey',
|
||||
action: 'Saw Survey',
|
||||
})
|
||||
}
|
||||
}
|
||||
}, [setShowSurveyPopup, showPopup])
|
||||
|
||||
// limit survey to 24 hours based on timestamps
|
||||
const timestamp = useCurrentBlockTimestamp()
|
||||
const durationOver = timestamp ? timestamp.toNumber() > END_TIMESTAMP : false
|
||||
|
||||
return (
|
||||
<>
|
||||
{!showPopup || durationOver ? null : (
|
||||
<Wrapper gap="10px">
|
||||
<WrappedCloseIcon
|
||||
onClick={() => {
|
||||
sendEvent({
|
||||
category: 'Survey',
|
||||
action: 'Clicked Survey Link',
|
||||
})
|
||||
setShowSurveyPopup(false)
|
||||
}}
|
||||
/>
|
||||
<BGOrb src={BGImage} />
|
||||
<ExternalLink href="https://www.surveymonkey.com/r/YGWV9VD">
|
||||
<RowFixed>
|
||||
<MessageCircle stroke={theme.deprecated_black} size="20px" strokeWidth="1px" />
|
||||
<ThemedText.DeprecatedWhite fontWeight={600} color={theme.deprecated_black} ml="6px">
|
||||
<Trans>Tell us what you think ↗</Trans>
|
||||
</ThemedText.DeprecatedWhite>
|
||||
</RowFixed>
|
||||
</ExternalLink>
|
||||
<ThemedText.DeprecatedBlack
|
||||
style={{ zIndex: Z_INDEX.fixed }}
|
||||
fontWeight={400}
|
||||
fontSize="12px"
|
||||
color={theme.deprecated_black}
|
||||
>
|
||||
<Trans>Take a 10 minute survey to help us improve your experience in the Uniswap app.</Trans>
|
||||
</ThemedText.DeprecatedBlack>
|
||||
</Wrapper>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { AlertCircle, CheckCircle } from 'react-feather'
|
||||
import styled, { useTheme } from 'styled-components/macro'
|
||||
|
||||
import { useTransaction } from '../../state/transactions/hooks'
|
||||
import { ThemedText } from '../../theme'
|
||||
import { ExternalLink } from '../../theme'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
import { TransactionSummary } from '../AccountDetails/TransactionSummary'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { AutoRow } from '../Row'
|
||||
|
||||
const RowNoFlex = styled(AutoRow)`
|
||||
flex-wrap: nowrap;
|
||||
`
|
||||
|
||||
export default function TransactionPopup({ hash }: { hash: string }) {
|
||||
const { chainId } = useWeb3React()
|
||||
|
||||
const tx = useTransaction(hash)
|
||||
const theme = useTheme()
|
||||
|
||||
if (!tx) return null
|
||||
const success = Boolean(tx.receipt && tx.receipt.status === 1)
|
||||
|
||||
return (
|
||||
<RowNoFlex>
|
||||
<div style={{ paddingRight: 16 }}>
|
||||
{success ? (
|
||||
<CheckCircle color={theme.deprecated_green1} size={24} />
|
||||
) : (
|
||||
<AlertCircle color={theme.deprecated_red1} size={24} />
|
||||
)}
|
||||
</div>
|
||||
<AutoColumn gap="8px">
|
||||
<ThemedText.DeprecatedBody fontWeight={500}>
|
||||
<TransactionSummary info={tx.info} />
|
||||
</ThemedText.DeprecatedBody>
|
||||
{chainId && (
|
||||
<ExternalLink href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}>
|
||||
View on Explorer
|
||||
</ExternalLink>
|
||||
)}
|
||||
</AutoColumn>
|
||||
</RowNoFlex>
|
||||
)
|
||||
}
|
||||
@@ -9,14 +9,12 @@ import { AutoColumn } from '../Column'
|
||||
import ClaimPopup from './ClaimPopup'
|
||||
import PopupItem from './PopupItem'
|
||||
|
||||
const MobilePopupWrapper = styled.div<{ height: string | number }>`
|
||||
const MobilePopupWrapper = styled.div`
|
||||
position: relative;
|
||||
max-width: 100%;
|
||||
height: ${({ height }) => height};
|
||||
margin: ${({ height }) => (height ? '0 auto;' : 0)};
|
||||
margin-bottom: ${({ height }) => (height ? '20px' : 0)};
|
||||
|
||||
margin: 0 auto;
|
||||
display: none;
|
||||
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
|
||||
display: block;
|
||||
padding-top: 20px;
|
||||
@@ -74,16 +72,18 @@ export default function Popups() {
|
||||
<PopupItem key={item.key} content={item.content} popKey={item.key} removeAfterMs={item.removeAfterMs} />
|
||||
))}
|
||||
</FixedPopupColumn>
|
||||
<MobilePopupWrapper height={activePopups?.length > 0 ? 'fit-content' : 0}>
|
||||
<MobilePopupInner>
|
||||
{activePopups // reverse so new items up front
|
||||
.slice(0)
|
||||
.reverse()
|
||||
.map((item) => (
|
||||
<PopupItem key={item.key} content={item.content} popKey={item.key} removeAfterMs={item.removeAfterMs} />
|
||||
))}
|
||||
</MobilePopupInner>
|
||||
</MobilePopupWrapper>
|
||||
{activePopups?.length > 0 && (
|
||||
<MobilePopupWrapper>
|
||||
<MobilePopupInner>
|
||||
{activePopups // reverse so new items up front
|
||||
.slice(0)
|
||||
.reverse()
|
||||
.map((item) => (
|
||||
<PopupItem key={item.key} content={item.content} popKey={item.key} removeAfterMs={item.removeAfterMs} />
|
||||
))}
|
||||
</MobilePopupInner>
|
||||
</MobilePopupWrapper>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ export default function SushiPositionCard({ tokenA, tokenB, liquidityToken, bord
|
||||
return (
|
||||
<StyledPositionCard border={border} bgColor={backgroundColor}>
|
||||
<CardNoise />
|
||||
<AutoColumn gap="12px">
|
||||
<AutoColumn gap="md">
|
||||
<FixedHeightRow>
|
||||
<AutoRow gap="8px">
|
||||
<DoubleCurrencyLogo currency0={currency0} currency1={currency1} size={20} />
|
||||
|
||||
@@ -79,7 +79,7 @@ export default function V2PositionCard({ pair, border, stakedBalance }: Position
|
||||
return (
|
||||
<StyledPositionCard border={border} bgColor={backgroundColor}>
|
||||
<CardNoise />
|
||||
<AutoColumn gap="12px">
|
||||
<AutoColumn gap="md">
|
||||
<FixedHeightRow>
|
||||
<AutoRow gap="8px">
|
||||
<DoubleCurrencyLogo currency0={currency0} currency1={currency1} size={20} />
|
||||
@@ -116,7 +116,7 @@ export default function V2PositionCard({ pair, border, stakedBalance }: Position
|
||||
</FixedHeightRow>
|
||||
|
||||
{showMore && (
|
||||
<AutoColumn gap="8px">
|
||||
<AutoColumn gap="sm">
|
||||
<FixedHeightRow>
|
||||
<Text fontSize={16} fontWeight={500}>
|
||||
<Trans>Your total pool tokens:</Trans>
|
||||
|
||||
@@ -79,7 +79,7 @@ export function MinimalPositionCard({ pair, showUnwrapped = false, border }: Pos
|
||||
<>
|
||||
{userPoolBalance && JSBI.greaterThan(userPoolBalance.quotient, JSBI.BigInt(0)) ? (
|
||||
<GrayCard border={border}>
|
||||
<AutoColumn gap="12px">
|
||||
<AutoColumn gap="md">
|
||||
<FixedHeightRow>
|
||||
<RowFixed>
|
||||
<Text fontWeight={500} fontSize={16}>
|
||||
@@ -195,7 +195,7 @@ export default function FullPositionCard({ pair, border, stakedBalance }: Positi
|
||||
return (
|
||||
<StyledPositionCard border={border} bgColor={backgroundColor}>
|
||||
<CardNoise />
|
||||
<AutoColumn gap="12px">
|
||||
<AutoColumn gap="md">
|
||||
<FixedHeightRow>
|
||||
<AutoRow gap="8px" style={{ marginLeft: '8px' }}>
|
||||
<DoubleCurrencyLogo currency0={currency0} currency1={currency1} size={20} />
|
||||
@@ -227,7 +227,7 @@ export default function FullPositionCard({ pair, border, stakedBalance }: Positi
|
||||
</FixedHeightRow>
|
||||
|
||||
{showMore && (
|
||||
<AutoColumn gap="8px">
|
||||
<AutoColumn gap="sm">
|
||||
<FixedHeightRow>
|
||||
<Text fontSize={16} fontWeight={500}>
|
||||
<Trans>Your total pool tokens:</Trans>
|
||||
|
||||
@@ -29,7 +29,7 @@ const LinkRow = styled(Link)`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
padding: 16px;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
@@ -73,7 +73,7 @@ const RangeLineItem = styled(DataLineItem)`
|
||||
|
||||
const DoubleArrow = styled.span`
|
||||
margin: 0 2px;
|
||||
color: ${({ theme }) => theme.deprecated_text3};
|
||||
color: ${({ theme }) => theme.textTertiary};
|
||||
`
|
||||
|
||||
const RangeText = styled.span`
|
||||
@@ -82,7 +82,7 @@ const RangeText = styled.span`
|
||||
`
|
||||
|
||||
const ExtentsText = styled.span`
|
||||
color: ${({ theme }) => theme.deprecated_text3};
|
||||
color: ${({ theme }) => theme.textTertiary};
|
||||
font-size: 14px;
|
||||
margin-right: 4px;
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
|
||||
|
||||
@@ -135,11 +135,7 @@ export const PositionPreview = ({
|
||||
{quoteCurrency.symbol} per {baseCurrency.symbol}
|
||||
</Trans>
|
||||
</ThemedText.DeprecatedMain>
|
||||
<ThemedText.DeprecatedSmall
|
||||
textAlign="center"
|
||||
color={theme.deprecated_text3}
|
||||
style={{ marginTop: '4px' }}
|
||||
>
|
||||
<ThemedText.DeprecatedSmall textAlign="center" color={theme.textTertiary} style={{ marginTop: '4px' }}>
|
||||
<Trans>Your position will be 100% composed of {baseCurrency?.symbol} at this price</Trans>
|
||||
</ThemedText.DeprecatedSmall>
|
||||
</AutoColumn>
|
||||
@@ -160,11 +156,7 @@ export const PositionPreview = ({
|
||||
{quoteCurrency.symbol} per {baseCurrency.symbol}
|
||||
</Trans>
|
||||
</ThemedText.DeprecatedMain>
|
||||
<ThemedText.DeprecatedSmall
|
||||
textAlign="center"
|
||||
color={theme.deprecated_text3}
|
||||
style={{ marginTop: '4px' }}
|
||||
>
|
||||
<ThemedText.DeprecatedSmall textAlign="center" color={theme.textTertiary} style={{ marginTop: '4px' }}>
|
||||
<Trans>Your position will be 100% composed of {quoteCurrency?.symbol} at this price</Trans>
|
||||
</ThemedText.DeprecatedSmall>
|
||||
</AutoColumn>
|
||||
|
||||
@@ -33,7 +33,7 @@ const StyledExternalCard = styled(Card)`
|
||||
|
||||
const HoverText = styled.div`
|
||||
text-decoration: none;
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -95,7 +95,7 @@ export function PrivacyPolicyModal() {
|
||||
|
||||
return (
|
||||
<Modal isOpen={open} onDismiss={() => toggle()}>
|
||||
<AutoColumn gap="12px" ref={node as any}>
|
||||
<AutoColumn gap="md" ref={node as any}>
|
||||
<RowBetween padding="1rem 1rem 0.5rem 1rem">
|
||||
<ThemedText.DeprecatedMediumHeader>
|
||||
<Trans>Legal & Privacy</Trans>
|
||||
@@ -110,7 +110,7 @@ export function PrivacyPolicyModal() {
|
||||
)
|
||||
}
|
||||
|
||||
export function PrivacyPolicy() {
|
||||
function PrivacyPolicy() {
|
||||
return (
|
||||
<Wrapper
|
||||
draggable="true"
|
||||
@@ -122,13 +122,13 @@ export function PrivacyPolicy() {
|
||||
}}
|
||||
>
|
||||
<AutoColumn gap="16px">
|
||||
<AutoColumn gap="8px" style={{ width: '100%' }}>
|
||||
<AutoColumn gap="sm" style={{ width: '100%' }}>
|
||||
<StyledExternalCard>
|
||||
<ExternalLink href="https://uniswap.org/terms-of-service">
|
||||
<RowBetween>
|
||||
<AutoRow gap="4px">
|
||||
<Info size={20} />
|
||||
<ThemedText.DeprecatedMain fontSize={14} color="deprecated_primaryText1">
|
||||
<ThemedText.DeprecatedMain fontSize={14} color="accentAction">
|
||||
<Trans>Uniswap Labs' Terms of Service</Trans>
|
||||
</ThemedText.DeprecatedMain>
|
||||
</AutoRow>
|
||||
@@ -141,7 +141,7 @@ export function PrivacyPolicy() {
|
||||
<RowBetween>
|
||||
<AutoRow gap="4px">
|
||||
<Info size={20} />
|
||||
<ThemedText.DeprecatedMain fontSize={14} color="deprecated_primaryText1">
|
||||
<ThemedText.DeprecatedMain fontSize={14} color="accentAction">
|
||||
<Trans>Privacy Policy</Trans>
|
||||
</ThemedText.DeprecatedMain>
|
||||
</AutoRow>
|
||||
@@ -153,13 +153,13 @@ export function PrivacyPolicy() {
|
||||
<ThemedText.DeprecatedMain fontSize={14}>
|
||||
<Trans>This app uses the following third-party APIs:</Trans>
|
||||
</ThemedText.DeprecatedMain>
|
||||
<AutoColumn gap="12px">
|
||||
<AutoColumn gap="md">
|
||||
{EXTERNAL_APIS.map(({ name, description }, i) => (
|
||||
<DarkGrayCard key={i}>
|
||||
<AutoColumn gap="8px">
|
||||
<AutoColumn gap="sm">
|
||||
<AutoRow gap="4px">
|
||||
<Info size={18} />
|
||||
<ThemedText.DeprecatedMain fontSize={14} color="deprecated_text1">
|
||||
<ThemedText.DeprecatedMain fontSize={14} color="textPrimary">
|
||||
{name}
|
||||
</ThemedText.DeprecatedMain>
|
||||
</AutoRow>
|
||||
|
||||
@@ -11,7 +11,7 @@ const Wrapper = styled(AutoColumn)`
|
||||
const Grouping = styled(AutoColumn)`
|
||||
width: fit-content;
|
||||
padding: 4px;
|
||||
/* background-color: ${({ theme }) => theme.deprecated_bg2}; */
|
||||
/* background-color: ${({ theme }) => theme.backgroundInteractive}; */
|
||||
border-radius: 16px;
|
||||
`
|
||||
|
||||
@@ -19,9 +19,9 @@ const Circle = styled.div<{ confirmed?: boolean; disabled?: boolean }>`
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background-color: ${({ theme, confirmed, disabled }) =>
|
||||
disabled ? theme.deprecated_bg3 : confirmed ? theme.deprecated_green1 : theme.deprecated_primary1};
|
||||
disabled ? theme.deprecated_bg3 : confirmed ? theme.accentSuccess : theme.accentAction};
|
||||
border-radius: 50%;
|
||||
color: ${({ theme, disabled }) => (disabled ? theme.deprecated_text3 : theme.deprecated_text1)};
|
||||
color: ${({ theme, disabled }) => (disabled ? theme.textTertiary : theme.textPrimary)};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
@@ -10,7 +10,7 @@ const Button = styled(ButtonOutlined).attrs(() => ({
|
||||
padding: '8px',
|
||||
$borderRadius: '8px',
|
||||
}))`
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
flex: 1;
|
||||
`
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ const DotColor = styled(DotLine)`
|
||||
`
|
||||
|
||||
const OpaqueBadge = styled(Badge)`
|
||||
background-color: ${({ theme }) => theme.deprecated_bg2};
|
||||
background-color: ${({ theme }) => theme.backgroundInteractive};
|
||||
border-radius: 8px;
|
||||
display: grid;
|
||||
font-size: 12px;
|
||||
@@ -69,7 +69,7 @@ const OpaqueBadge = styled(Badge)`
|
||||
const ProtocolBadge = styled(Badge)`
|
||||
background-color: ${({ theme }) => theme.deprecated_bg3};
|
||||
border-radius: 4px;
|
||||
color: ${({ theme }) => theme.deprecated_text2};
|
||||
color: ${({ theme }) => theme.textSecondary};
|
||||
font-size: 10px;
|
||||
padding: 2px 4px;
|
||||
z-index: ${Z_INDEX.sticky + 1};
|
||||
|
||||
@@ -18,7 +18,7 @@ const MobileWrapper = styled(AutoColumn)`
|
||||
`
|
||||
|
||||
const BaseWrapper = styled.div<{ disable?: boolean }>`
|
||||
border: 1px solid ${({ theme, disable }) => (disable ? theme.accentAction : theme.backgroundOutline)};
|
||||
border: 1px solid ${({ theme, disable }) => (disable ? theme.accentActive : theme.backgroundOutline)};
|
||||
border-radius: 16px;
|
||||
display: flex;
|
||||
padding: 6px;
|
||||
@@ -30,8 +30,8 @@ const BaseWrapper = styled.div<{ disable?: boolean }>`
|
||||
background-color: ${({ theme }) => theme.hoverDefault};
|
||||
}
|
||||
|
||||
color: ${({ theme, disable }) => disable && theme.accentAction};
|
||||
background-color: ${({ theme, disable }) => disable && theme.accentActionSoft};
|
||||
color: ${({ theme, disable }) => disable && theme.accentActive};
|
||||
background-color: ${({ theme, disable }) => disable && theme.accentActiveSoft};
|
||||
`
|
||||
|
||||
const formatAnalyticsEventProperties = (currency: Currency, searchQuery: string, isAddressSearch: string | false) => ({
|
||||
|
||||
@@ -48,7 +48,7 @@ const CurrencyName = styled(Text)`
|
||||
|
||||
const Tag = styled.div`
|
||||
background-color: ${({ theme }) => theme.deprecated_bg3};
|
||||
color: ${({ theme }) => theme.deprecated_text2};
|
||||
color: ${({ theme }) => theme.textSecondary};
|
||||
font-size: 14px;
|
||||
border-radius: 4px;
|
||||
padding: 0.25rem 0.3rem 0.25rem 0.3rem;
|
||||
@@ -60,7 +60,7 @@ const Tag = styled.div`
|
||||
margin-right: 4px;
|
||||
`
|
||||
|
||||
export const WarningContainer = styled.div`
|
||||
const WarningContainer = styled.div`
|
||||
margin-left: 0.3em;
|
||||
`
|
||||
|
||||
|
||||
@@ -251,7 +251,7 @@ export function CurrencySearch({
|
||||
</div>
|
||||
) : (
|
||||
<Column style={{ padding: '20px', height: '100%' }}>
|
||||
<ThemedText.DeprecatedMain color={theme.deprecated_text3} textAlign="center" mb="20px">
|
||||
<ThemedText.DeprecatedMain color={theme.textTertiary} textAlign="center" mb="20px">
|
||||
<Trans>No results found.</Trans>
|
||||
</ThemedText.DeprecatedMain>
|
||||
</Column>
|
||||
|
||||
@@ -19,7 +19,7 @@ interface CurrencySearchModalProps {
|
||||
disableNonToken?: boolean
|
||||
}
|
||||
|
||||
export enum CurrencyModalView {
|
||||
enum CurrencyModalView {
|
||||
search,
|
||||
importToken,
|
||||
tokenSafety,
|
||||
|
||||
@@ -5,19 +5,6 @@ import styled from 'styled-components/macro'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { RowBetween } from '../Row'
|
||||
|
||||
export const TextDot = styled.div`
|
||||
height: 3px;
|
||||
width: 3px;
|
||||
background-color: ${({ theme }) => theme.deprecated_text2};
|
||||
border-radius: 50%;
|
||||
`
|
||||
|
||||
export const Checkbox = styled.input`
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_red3};
|
||||
height: 20px;
|
||||
margin: 0;
|
||||
`
|
||||
|
||||
export const PaddedColumn = styled(AutoColumn)`
|
||||
padding: 20px;
|
||||
`
|
||||
@@ -53,7 +40,7 @@ export const SearchInput = styled.input`
|
||||
border: none;
|
||||
outline: none;
|
||||
border-radius: 12px;
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
border-style: solid;
|
||||
border: 1px solid ${({ theme }) => theme.backgroundOutline};
|
||||
-webkit-appearance: none;
|
||||
@@ -77,12 +64,6 @@ export const Separator = styled.div`
|
||||
background-color: ${({ theme }) => theme.backgroundOutline};
|
||||
`
|
||||
|
||||
export const SeparatorDark = styled.div`
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background-color: ${({ theme }) => theme.deprecated_bg3};
|
||||
`
|
||||
|
||||
export const LoadingRows = styled(BaseLoadingRows)`
|
||||
grid-column-gap: 0.5em;
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
|
||||
@@ -116,7 +116,7 @@ const ModalContentWrapper = styled.div`
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 2rem 0;
|
||||
background-color: ${({ theme }) => theme.deprecated_bg2};
|
||||
background-color: ${({ theme }) => theme.backgroundInteractive};
|
||||
border-radius: 20px;
|
||||
`
|
||||
|
||||
@@ -208,7 +208,7 @@ export default function SettingsTab({ placeholderSlippage }: { placeholderSlippa
|
||||
{isSupportedChainId(chainId) && (
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<ThemedText.DeprecatedBlack fontWeight={400} fontSize={14} color={theme.deprecated_text2}>
|
||||
<ThemedText.DeprecatedBlack fontWeight={400} fontSize={14} color={theme.textSecondary}>
|
||||
<Trans>Auto Router API</Trans>
|
||||
</ThemedText.DeprecatedBlack>
|
||||
<QuestionHelper text={<Trans>Use the Uniswap Labs API to get faster quotes.</Trans>} />
|
||||
@@ -228,7 +228,7 @@ export default function SettingsTab({ placeholderSlippage }: { placeholderSlippa
|
||||
)}
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<ThemedText.DeprecatedBlack fontWeight={400} fontSize={14} color={theme.deprecated_text2}>
|
||||
<ThemedText.DeprecatedBlack fontWeight={400} fontSize={14} color={theme.textSecondary}>
|
||||
<Trans>Expert Mode</Trans>
|
||||
</ThemedText.DeprecatedBlack>
|
||||
<QuestionHelper
|
||||
|
||||
@@ -19,7 +19,7 @@ const StyledRangeInput = styled.input<{ size: number }>`
|
||||
-webkit-appearance: none;
|
||||
height: ${({ size }) => size}px;
|
||||
width: ${({ size }) => size}px;
|
||||
background-color: ${({ theme }) => theme.deprecated_blue1};
|
||||
background-color: ${({ theme }) => theme.accentAction};
|
||||
border-radius: 100%;
|
||||
border: none;
|
||||
transform: translateY(-50%);
|
||||
@@ -62,11 +62,7 @@ const StyledRangeInput = styled.input<{ size: number }>`
|
||||
}
|
||||
|
||||
&::-webkit-slider-runnable-track {
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
${({ theme }) => theme.deprecated_blue1},
|
||||
${({ theme }) => theme.deprecated_blue2}
|
||||
);
|
||||
background: linear-gradient(90deg, ${({ theme }) => theme.accentAction}, ${({ theme }) => theme.accentAction});
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ const Input = styled.input<{ error?: boolean; fontSize?: string }>`
|
||||
width: 0;
|
||||
background-color: ${({ theme }) => theme.deprecated_bg1};
|
||||
transition: color 300ms ${({ error }) => (error ? 'step-end' : 'step-start')};
|
||||
color: ${({ error, theme }) => (error ? theme.deprecated_red1 : theme.deprecated_text1)};
|
||||
color: ${({ error, theme }) => (error ? theme.accentFailure : theme.textPrimary)};
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-weight: 500;
|
||||
@@ -40,7 +40,7 @@ const TextAreaInput = styled.textarea<{ error?: boolean; fontSize?: string }>`
|
||||
resize: none;
|
||||
background-color: ${({ theme }) => theme.deprecated_bg1};
|
||||
transition: color 300ms ${({ error }) => (error ? 'step-end' : 'step-start')};
|
||||
color: ${({ error, theme }) => (error ? theme.deprecated_red1 : theme.deprecated_text1)};
|
||||
color: ${({ error, theme }) => (error ? theme.accentFailure : theme.textPrimary)};
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-weight: 500;
|
||||
|
||||
@@ -7,7 +7,7 @@ export const ToggleWrapper = styled.button<{ width?: string }>`
|
||||
padding: 1px;
|
||||
background: ${({ theme }) => theme.deprecated_bg1};
|
||||
border-radius: 8px;
|
||||
border: ${({ theme }) => '1px solid ' + theme.deprecated_bg2};
|
||||
border: ${({ theme }) => '1px solid ' + theme.backgroundInteractive};
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
`
|
||||
@@ -20,13 +20,13 @@ export const ToggleElement = styled.span<{ isActive?: boolean; fontSize?: string
|
||||
border-radius: 6px;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
background: ${({ theme, isActive }) => (isActive ? theme.deprecated_bg0 : 'none')};
|
||||
color: ${({ theme, isActive }) => (isActive ? theme.deprecated_text1 : theme.deprecated_text3)};
|
||||
background: ${({ theme, isActive }) => (isActive ? theme.backgroundSurface : 'none')};
|
||||
color: ${({ theme, isActive }) => (isActive ? theme.textPrimary : theme.textTertiary)};
|
||||
font-size: ${({ fontSize }) => fontSize ?? '1rem'};
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
:hover {
|
||||
user-select: initial;
|
||||
color: ${({ theme, isActive }) => (isActive ? theme.deprecated_text2 : theme.deprecated_text3)};
|
||||
color: ${({ theme, isActive }) => (isActive ? theme.textSecondary : theme.textTertiary)};
|
||||
}
|
||||
`
|
||||
|
||||
@@ -42,8 +42,8 @@ const ToggleElementHoverStyle = (hasBgColor: boolean, theme: any, isActive?: boo
|
||||
opacity: '0.8',
|
||||
}
|
||||
: {
|
||||
background: isActive ? darken(0.05, theme.deprecated_primary1) : darken(0.05, theme.deprecated_bg4),
|
||||
color: isActive ? theme.deprecated_white : theme.deprecated_text3,
|
||||
background: isActive ? darken(0.05, theme.accentAction) : darken(0.05, theme.deprecated_bg4),
|
||||
color: isActive ? theme.white : theme.textTertiary,
|
||||
}
|
||||
|
||||
const ToggleElement = styled.span<{ isActive?: boolean; bgColor?: string; isInitialToggleLoad?: boolean }>`
|
||||
@@ -51,7 +51,7 @@ const ToggleElement = styled.span<{ isActive?: boolean; bgColor?: string; isInit
|
||||
${({ isActive, isInitialToggleLoad }) => (isInitialToggleLoad ? 'none' : isActive ? turnOnToggle : turnOffToggle)}
|
||||
ease-in;
|
||||
background: ${({ theme, bgColor, isActive }) =>
|
||||
isActive ? bgColor ?? theme.deprecated_primary1 : !!bgColor ? theme.deprecated_bg4 : theme.deprecated_text3};
|
||||
isActive ? bgColor ?? theme.accentAction : !!bgColor ? theme.deprecated_bg4 : theme.textTertiary};
|
||||
border-radius: 50%;
|
||||
height: 24px;
|
||||
:hover {
|
||||
|
||||
@@ -157,7 +157,7 @@ const LinkIconWrapper = styled.div`
|
||||
display: flex;
|
||||
`
|
||||
|
||||
export function ExternalLinkIcon() {
|
||||
function ExternalLinkIcon() {
|
||||
return (
|
||||
<LinkIconWrapper>
|
||||
<ExplorerLinkIcon />
|
||||
@@ -183,6 +183,8 @@ function ExplorerView({ token }: { token: Token }) {
|
||||
}
|
||||
|
||||
const StyledExternalLink = styled(ExternalLink)`
|
||||
color: ${({ theme }) => theme.textSecondary};
|
||||
stroke: currentColor;
|
||||
font-weight: 600;
|
||||
`
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ export const AboutHeader = styled(ThemedText.MediumHeader)`
|
||||
font-size: 28px !important;
|
||||
`
|
||||
|
||||
export const ResourcesContainer = styled.div`
|
||||
const ResourcesContainer = styled.div`
|
||||
display: flex;
|
||||
padding-top: 12px;
|
||||
gap: 14px;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Trans } from '@lingui/macro'
|
||||
import styled from 'styled-components/macro'
|
||||
import { CopyContractAddress, ThemedText } from 'theme'
|
||||
|
||||
export const ContractAddressSection = styled.div`
|
||||
const ContractAddressSection = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: ${({ theme }) => theme.textSecondary};
|
||||
|
||||
@@ -11,6 +11,7 @@ export const BreadcrumbNavLink = styled(Link)`
|
||||
text-decoration: none;
|
||||
margin-bottom: 16px;
|
||||
transition-duration: ${({ theme }) => theme.transition.duration.fast};
|
||||
width: fit-content;
|
||||
|
||||
&:hover {
|
||||
color: ${({ theme }) => theme.textTertiary};
|
||||
|
||||
@@ -4,11 +4,11 @@ import { TokenPriceQuery, tokenPriceQuery } from 'graphql/data/TokenPrice'
|
||||
import { isPricePoint, PricePoint } from 'graphql/data/util'
|
||||
import { TimePeriod } from 'graphql/data/util'
|
||||
import { useAtomValue } from 'jotai/utils'
|
||||
import { startTransition, Suspense, useMemo, useState } from 'react'
|
||||
import { pageTimePeriodAtom } from 'pages/TokenDetails'
|
||||
import { startTransition, Suspense, useMemo } from 'react'
|
||||
import { PreloadedQuery, usePreloadedQuery } from 'react-relay'
|
||||
|
||||
import { filterTimeAtom } from '../state'
|
||||
import PriceChart from './PriceChart'
|
||||
import { PriceChart } from './PriceChart'
|
||||
import TimePeriodSelector from './TimeSelector'
|
||||
|
||||
function usePreloadedTokenPriceQuery(priceQueryReference: PreloadedQuery<TokenPriceQuery>): PricePoint[] | undefined {
|
||||
@@ -58,7 +58,7 @@ function Chart({
|
||||
}) {
|
||||
const prices = usePreloadedTokenPriceQuery(priceQueryReference)
|
||||
// Initializes time period to global & maintain separate time period for subsequent changes
|
||||
const [timePeriod, setTimePeriod] = useState(useAtomValue(filterTimeAtom))
|
||||
const timePeriod = useAtomValue(pageTimePeriodAtom)
|
||||
|
||||
return (
|
||||
<ChartContainer>
|
||||
@@ -69,7 +69,6 @@ function Chart({
|
||||
currentTimePeriod={timePeriod}
|
||||
onTimeChange={(t: TimePeriod) => {
|
||||
startTransition(() => refetchTokenPrices(t))
|
||||
setTimePeriod(t)
|
||||
}}
|
||||
/>
|
||||
</ChartContainer>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { formatUSDPrice } from '@uniswap/conedison/format'
|
||||
import { AxisBottom, TickFormatter } from '@visx/axis'
|
||||
import { localPoint } from '@visx/event'
|
||||
import { EventType } from '@visx/event/lib/types'
|
||||
import { GlyphCircle } from '@visx/glyph'
|
||||
import { Line } from '@visx/shape'
|
||||
import AnimatedInLineChart from 'components/Charts/AnimatedInLineChart'
|
||||
import FadedInLineChart from 'components/Charts/FadeInLineChart'
|
||||
import { bisect, curveCardinal, NumberValue, scaleLinear, timeDay, timeHour, timeMinute, timeMonth } from 'd3'
|
||||
import { PricePoint } from 'graphql/data/util'
|
||||
import { TimePeriod } from 'graphql/data/util'
|
||||
@@ -13,6 +13,8 @@ import { useActiveLocale } from 'hooks/useActiveLocale'
|
||||
import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { ArrowDownRight, ArrowUpRight, TrendingUp } from 'react-feather'
|
||||
import styled, { useTheme } from 'styled-components/macro'
|
||||
import { ThemedText } from 'theme'
|
||||
import { textFadeIn } from 'theme/styles'
|
||||
import {
|
||||
dayHourFormatter,
|
||||
hourFormatter,
|
||||
@@ -21,8 +23,9 @@ import {
|
||||
monthYearDayFormatter,
|
||||
weekFormatter,
|
||||
} from 'utils/formatChartTimes'
|
||||
import { formatDollar } from 'utils/formatNumbers'
|
||||
|
||||
export const DATA_EMPTY = { value: 0, timestamp: 0 }
|
||||
const DATA_EMPTY = { value: 0, timestamp: 0 }
|
||||
|
||||
export function getPriceBounds(pricePoints: PricePoint[]): [number, number] {
|
||||
const prices = pricePoints.map((x) => x.value)
|
||||
@@ -38,7 +41,7 @@ const StyledDownArrow = styled(ArrowDownRight)`
|
||||
color: ${({ theme }) => theme.accentFailure};
|
||||
`
|
||||
|
||||
export function calculateDelta(start: number, current: number) {
|
||||
function calculateDelta(start: number, current: number) {
|
||||
return (current / start - 1) * 100
|
||||
}
|
||||
|
||||
@@ -66,14 +69,22 @@ export const DeltaText = styled.span<{ delta: number | undefined }>`
|
||||
delta !== undefined ? (Math.sign(delta) < 0 ? theme.accentFailure : theme.accentSuccess) : theme.textPrimary};
|
||||
`
|
||||
|
||||
export const ChartHeader = styled.div`
|
||||
const ChartHeader = styled.div`
|
||||
position: absolute;
|
||||
${textFadeIn};
|
||||
animation-duration: ${({ theme }) => theme.transition.duration.medium};
|
||||
`
|
||||
export const TokenPrice = styled.span`
|
||||
font-size: 36px;
|
||||
line-height: 44px;
|
||||
`
|
||||
export const DeltaContainer = styled.div`
|
||||
const MissingPrice = styled(TokenPrice)`
|
||||
font-size: 24px;
|
||||
line-height: 44px;
|
||||
color: ${({ theme }) => theme.textTertiary};
|
||||
`
|
||||
|
||||
const DeltaContainer = styled.div`
|
||||
height: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -84,6 +95,29 @@ export const ArrowCell = styled.div`
|
||||
display: flex;
|
||||
`
|
||||
|
||||
function fixChart(prices: PricePoint[] | undefined | null) {
|
||||
if (!prices) return { prices: null, blanks: [] }
|
||||
|
||||
const fixedChart: PricePoint[] = []
|
||||
const blanks: PricePoint[][] = []
|
||||
let lastValue: PricePoint | undefined = undefined
|
||||
for (let i = 0; i < prices.length; i++) {
|
||||
if (prices[i].value !== 0) {
|
||||
if (fixedChart.length === 0 && i !== 0) {
|
||||
blanks.push([{ ...prices[0], value: prices[i].value }, prices[i]])
|
||||
}
|
||||
lastValue = prices[i]
|
||||
fixedChart.push(prices[i])
|
||||
}
|
||||
}
|
||||
|
||||
if (lastValue && lastValue !== prices[prices.length - 1]) {
|
||||
blanks.push([lastValue, { ...prices[prices.length - 1], value: lastValue.value }])
|
||||
}
|
||||
|
||||
return { prices: fixedChart, blanks }
|
||||
}
|
||||
|
||||
const margin = { top: 100, bottom: 48, crosshair: 72 }
|
||||
const timeOptionsHeight = 44
|
||||
|
||||
@@ -94,24 +128,30 @@ interface PriceChartProps {
|
||||
timePeriod: TimePeriod
|
||||
}
|
||||
|
||||
function formatDisplayPrice(value: number) {
|
||||
const str = value.toFixed(9)
|
||||
const [digits, decimals] = str.split('.')
|
||||
// Displays longer string for numbers < $2 to show changes in both stablecoins & small values
|
||||
if (digits === '0' || digits === '1')
|
||||
return `$${digits + '.' + decimals.substring(0, 2) + decimals.substring(2).replace(/0+$/, '')}`
|
||||
|
||||
return formatUSDPrice(value)
|
||||
}
|
||||
|
||||
export function PriceChart({ width, height, prices, timePeriod }: PriceChartProps) {
|
||||
export function PriceChart({ width, height, prices: originalPrices, timePeriod }: PriceChartProps) {
|
||||
const locale = useActiveLocale()
|
||||
const theme = useTheme()
|
||||
|
||||
const { prices, blanks } = useMemo(
|
||||
() => (originalPrices && originalPrices.length > 0 ? fixChart(originalPrices) : { prices: null, blanks: [] }),
|
||||
[originalPrices]
|
||||
)
|
||||
|
||||
const chartAvailable = !!prices && prices.length > 0
|
||||
const missingPricesMessage = !chartAvailable ? (
|
||||
prices?.length === 0 ? (
|
||||
<>
|
||||
<Trans>Missing price data due to recently low trading volume on Uniswap v3</Trans>
|
||||
</>
|
||||
) : (
|
||||
<Trans>Missing chart data</Trans>
|
||||
)
|
||||
) : null
|
||||
|
||||
// first price point on the x-axis of the current time period's chart
|
||||
const startingPrice = prices?.[0] ?? DATA_EMPTY
|
||||
const startingPrice = originalPrices?.[0] ?? DATA_EMPTY
|
||||
// last price point on the x-axis of the current time period's chart
|
||||
const endingPrice = prices?.[prices.length - 1] ?? DATA_EMPTY
|
||||
const endingPrice = originalPrices?.[originalPrices.length - 1] ?? DATA_EMPTY
|
||||
const [displayPrice, setDisplayPrice] = useState(startingPrice)
|
||||
|
||||
// set display price to ending price when prices have changed.
|
||||
@@ -133,9 +173,9 @@ export function PriceChart({ width, height, prices, timePeriod }: PriceChartProp
|
||||
const rdScale = useMemo(
|
||||
() =>
|
||||
scaleLinear()
|
||||
.domain(getPriceBounds(prices ?? []))
|
||||
.domain(getPriceBounds(originalPrices ?? []))
|
||||
.range([graphInnerHeight, 0]),
|
||||
[prices, graphInnerHeight]
|
||||
[originalPrices, graphInnerHeight]
|
||||
)
|
||||
|
||||
function tickFormat(
|
||||
@@ -221,7 +261,6 @@ export function PriceChart({ width, height, prices, timePeriod }: PriceChartProp
|
||||
const arrow = getDeltaArrow(delta)
|
||||
const crosshairEdgeMax = width * 0.85
|
||||
const crosshairAtEdge = !!crosshair && crosshair > crosshairEdgeMax
|
||||
const hasData = prices && prices.length > 0
|
||||
|
||||
/*
|
||||
* Default curve doesn't look good for the HOUR chart.
|
||||
@@ -233,27 +272,27 @@ export function PriceChart({ width, height, prices, timePeriod }: PriceChartProp
|
||||
const getX = useMemo(() => (p: PricePoint) => timeScale(p.timestamp), [timeScale])
|
||||
const getY = useMemo(() => (p: PricePoint) => rdScale(p.value), [rdScale])
|
||||
const curve = useMemo(() => curveCardinal.tension(curveTension), [curveTension])
|
||||
|
||||
return (
|
||||
<>
|
||||
<ChartHeader>
|
||||
<TokenPrice>{formatDisplayPrice(displayPrice.value)}</TokenPrice>
|
||||
<DeltaContainer>
|
||||
<ArrowCell>{arrow}</ArrowCell>
|
||||
<DeltaText delta={delta}>{formattedDelta}</DeltaText>
|
||||
</DeltaContainer>
|
||||
{displayPrice.value ? (
|
||||
<>
|
||||
<TokenPrice>{formatDollar({ num: displayPrice.value, isPrice: true })}</TokenPrice>
|
||||
<DeltaContainer>
|
||||
{formattedDelta}
|
||||
<ArrowCell>{arrow}</ArrowCell>
|
||||
</DeltaContainer>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<MissingPrice>Price Unavailable</MissingPrice>
|
||||
<ThemedText.Caption style={{ color: theme.textTertiary }}>{missingPricesMessage}</ThemedText.Caption>
|
||||
</>
|
||||
)}
|
||||
</ChartHeader>
|
||||
{!hasData ? (
|
||||
<MissingPriceChart
|
||||
width={width}
|
||||
height={graphHeight}
|
||||
message={
|
||||
prices?.length === 0 ? (
|
||||
<Trans>This token doesn't have chart data because it hasn't been traded on Uniswap v3</Trans>
|
||||
) : (
|
||||
<Trans>Missing chart data</Trans>
|
||||
)
|
||||
}
|
||||
/>
|
||||
{!chartAvailable ? (
|
||||
<MissingPriceChart width={width} height={graphHeight} message={!!displayPrice.value && missingPricesMessage} />
|
||||
) : (
|
||||
<svg width={width} height={graphHeight} style={{ minWidth: '100%' }}>
|
||||
<AnimatedInLineChart
|
||||
@@ -264,6 +303,19 @@ export function PriceChart({ width, height, prices, timePeriod }: PriceChartProp
|
||||
curve={curve}
|
||||
strokeWidth={2}
|
||||
/>
|
||||
{blanks.map((blank, index) => (
|
||||
<FadedInLineChart
|
||||
key={index}
|
||||
data={blank}
|
||||
getX={getX}
|
||||
getY={getY}
|
||||
marginTop={margin.top}
|
||||
curve={curve}
|
||||
strokeWidth={2}
|
||||
color={theme.textTertiary}
|
||||
dashed
|
||||
/>
|
||||
))}
|
||||
{crosshair !== null ? (
|
||||
<g>
|
||||
<AxisBottom
|
||||
@@ -310,7 +362,13 @@ export function PriceChart({ width, height, prices, timePeriod }: PriceChartProp
|
||||
/>
|
||||
</g>
|
||||
) : (
|
||||
<AxisBottom scale={timeScale} stroke={theme.backgroundOutline} top={graphHeight - 1} hideTicks />
|
||||
<AxisBottom
|
||||
hideAxisLine={true}
|
||||
scale={timeScale}
|
||||
stroke={theme.backgroundOutline}
|
||||
top={graphHeight - 1}
|
||||
hideTicks
|
||||
/>
|
||||
)}
|
||||
{!width && (
|
||||
// Ensures an axis is drawn even if the width is not yet initialized.
|
||||
@@ -348,9 +406,7 @@ const StyledMissingChart = styled.svg`
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
|
||||
const chartBottomPadding = 15
|
||||
|
||||
function MissingPriceChart({ width, height, message }: { width: number; height: number; message: ReactNode }) {
|
||||
const theme = useTheme()
|
||||
const midPoint = height / 2 + 45
|
||||
@@ -363,18 +419,10 @@ function MissingPriceChart({ width, height, message }: { width: number; height:
|
||||
fill="transparent"
|
||||
strokeWidth="2"
|
||||
/>
|
||||
<TrendingUp stroke={theme.textTertiary} x={0} size={12} y={height - chartBottomPadding - 10} />
|
||||
{message && <TrendingUp stroke={theme.textTertiary} x={0} size={12} y={height - chartBottomPadding - 10} />}
|
||||
<text y={height - chartBottomPadding} x="20" fill={theme.textTertiary}>
|
||||
{message || <Trans>Missing chart data</Trans>}
|
||||
{message}
|
||||
</text>
|
||||
<path
|
||||
d={`M 0 ${height - 1}, ${width} ${height - 1}`}
|
||||
stroke={theme.backgroundOutline}
|
||||
fill="transparent"
|
||||
strokeWidth="1"
|
||||
/>
|
||||
</StyledMissingChart>
|
||||
)
|
||||
}
|
||||
|
||||
export default PriceChart
|
||||
|
||||
@@ -9,7 +9,7 @@ import { LoadingBubble } from '../loading'
|
||||
import { LogoContainer } from '../TokenTable/TokenRow'
|
||||
import { AboutContainer, AboutHeader } from './About'
|
||||
import { BreadcrumbNavLink } from './BreadcrumbNavLink'
|
||||
import { DeltaContainer, TokenPrice } from './PriceChart'
|
||||
import { TokenPrice } from './PriceChart'
|
||||
import { StatPair, StatsWrapper, StatWrapper } from './StatsSection'
|
||||
|
||||
export const Hr = styled.hr`
|
||||
@@ -62,7 +62,6 @@ const LoadingChartContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-end;
|
||||
border-bottom: 1px solid ${({ theme }) => theme.backgroundOutline};
|
||||
height: 100%;
|
||||
margin-bottom: 44px;
|
||||
padding-bottom: 66px;
|
||||
@@ -73,6 +72,8 @@ export const TokenInfoContainer = styled.div`
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 4px;
|
||||
${textFadeIn};
|
||||
animation-duration: ${({ theme }) => theme.transition.duration.medium};
|
||||
`
|
||||
export const TokenNameCell = styled.div`
|
||||
display: flex;
|
||||
@@ -80,7 +81,6 @@ export const TokenNameCell = styled.div`
|
||||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
align-items: center;
|
||||
${textFadeIn}
|
||||
`
|
||||
/* Loading state bubbles */
|
||||
const DetailBubble = styled(LoadingBubble)`
|
||||
@@ -97,31 +97,32 @@ const TokenLogoBubble = styled(DetailBubble)`
|
||||
border-radius: 50%;
|
||||
`
|
||||
const TitleBubble = styled(DetailBubble)`
|
||||
width: 140px;
|
||||
width: 136px;
|
||||
`
|
||||
const PriceBubble = styled(SquaredBubble)`
|
||||
margin-top: 2px;
|
||||
height: 38px;
|
||||
`
|
||||
const DeltaBubble = styled(DetailBubble)`
|
||||
margin-top: 6px;
|
||||
width: 96px;
|
||||
height: 20px;
|
||||
margin-top: 4px;
|
||||
height: 40px;
|
||||
`
|
||||
|
||||
const SectionBubble = styled(SquaredBubble)`
|
||||
width: 96px;
|
||||
width: 120px;
|
||||
`
|
||||
const StatTitleBubble = styled(DetailBubble)`
|
||||
width: 25%;
|
||||
width: 80px;
|
||||
margin-bottom: 4px;
|
||||
`
|
||||
const StatBubble = styled(SquaredBubble)`
|
||||
width: 50%;
|
||||
width: 116px;
|
||||
`
|
||||
const WideBubble = styled(DetailBubble)`
|
||||
margin-bottom: 6px;
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
const ThinTitleBubble = styled(WideBubble)`
|
||||
width: 120px;
|
||||
`
|
||||
|
||||
const HalfWideBubble = styled(WideBubble)`
|
||||
width: 50%;
|
||||
`
|
||||
@@ -131,6 +132,11 @@ const StatsLoadingContainer = styled.div`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
`
|
||||
|
||||
const ExtraDetailsContainer = styled.div`
|
||||
padding-top: 24px;
|
||||
`
|
||||
|
||||
const ChartAnimation = styled.div`
|
||||
animation: wave 8s cubic-bezier(0.36, 0.45, 0.63, 0.53) infinite;
|
||||
display: flex;
|
||||
@@ -165,9 +171,6 @@ export function LoadingChart() {
|
||||
<TokenPrice>
|
||||
<PriceBubble />
|
||||
</TokenPrice>
|
||||
<DeltaContainer>
|
||||
<DeltaBubble />
|
||||
</DeltaContainer>
|
||||
<Space heightSize={6} />
|
||||
<LoadingChartContainer>
|
||||
<div>
|
||||
@@ -242,7 +245,15 @@ export default function TokenDetailsSkeleton() {
|
||||
</AboutContainer>
|
||||
<WideBubble />
|
||||
<WideBubble />
|
||||
<HalfWideBubble />
|
||||
<HalfWideBubble style={{ marginBottom: '24px' }} />
|
||||
<ExtraDetailsContainer>
|
||||
<ThinTitleBubble />
|
||||
<HalfWideBubble />
|
||||
</ExtraDetailsContainer>
|
||||
<ExtraDetailsContainer>
|
||||
<ThinTitleBubble />
|
||||
<HalfWideBubble />
|
||||
</ExtraDetailsContainer>
|
||||
</LeftPanel>
|
||||
)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user