Compare commits
381 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec686bcaa5 | ||
|
|
06291a15a6 | ||
|
|
3e40a6f5c6 | ||
|
|
f91b48e214 | ||
|
|
e340f405b4 | ||
|
|
24fc39b016 | ||
|
|
2fc3f3c00e | ||
|
|
21e0faeb1e | ||
|
|
4b71a8d5f4 | ||
|
|
4075965252 | ||
|
|
3538312769 | ||
|
|
2924f36970 | ||
|
|
bf16dfa09c | ||
|
|
910e86d6a2 | ||
|
|
537fea103e | ||
|
|
87a6e2709b | ||
|
|
d704e78223 | ||
|
|
8ceabd513c | ||
|
|
4806c69053 | ||
|
|
c9f333003b | ||
|
|
33fa32cb07 | ||
|
|
5d1377af80 | ||
|
|
a75e239fd2 | ||
|
|
a663482dc6 | ||
|
|
e8d6235529 | ||
|
|
d8677d8a6d | ||
|
|
351f66a83e | ||
|
|
79c7c01964 | ||
|
|
f51474b66d | ||
|
|
14f01905d7 | ||
|
|
23eb31e6a2 | ||
|
|
e8d4f00f49 | ||
|
|
978e3f945d | ||
|
|
072f394476 | ||
|
|
5926d7037d | ||
|
|
52b51ee7d0 | ||
|
|
54f831ede4 | ||
|
|
c9ead63ff6 | ||
|
|
9ca74cf8d2 | ||
|
|
2a0d455419 | ||
|
|
9e959ca455 | ||
|
|
d3f6796bb9 | ||
|
|
64d6eeabcb | ||
|
|
859258c25c | ||
|
|
2338255a54 | ||
|
|
843afa93c3 | ||
|
|
5441e63825 | ||
|
|
7bf741027e | ||
|
|
0017e2fcc8 | ||
|
|
5c9c8b4cb7 | ||
|
|
873d0ea2a3 | ||
|
|
db4987f557 | ||
|
|
b0b61f886d | ||
|
|
5d4b25f417 | ||
|
|
c88d7c880b | ||
|
|
a96d13978b | ||
|
|
22b26de78d | ||
|
|
53b57879a3 | ||
|
|
d794cef770 | ||
|
|
19f175ba89 | ||
|
|
aaf105ef51 | ||
|
|
974308f939 | ||
|
|
0ec738a48a | ||
|
|
904f6e22f4 | ||
|
|
66fad96e61 | ||
|
|
9037930e56 | ||
|
|
d62013177d | ||
|
|
fc08ede58a | ||
|
|
995a62985e | ||
|
|
67d5a00a0c | ||
|
|
84364c9df2 | ||
|
|
446eb9f9a4 | ||
|
|
a73e814167 | ||
|
|
7125562c9d | ||
|
|
1361f99639 | ||
|
|
d70a87a89a | ||
|
|
2cb0d9527e | ||
|
|
1839e145ec | ||
|
|
8c1e41a3a8 | ||
|
|
9859c0b4dd | ||
|
|
1138101dd0 | ||
|
|
106ac7ea35 | ||
|
|
19b4ee463b | ||
|
|
2aea96c3ba | ||
|
|
b1fb499e29 | ||
|
|
64207f29b0 | ||
|
|
7b6ac6cfaa | ||
|
|
8a9ade5f12 | ||
|
|
a3e567bc8a | ||
|
|
a887666bf5 | ||
|
|
ed8aa08255 | ||
|
|
53f4fb9ede | ||
|
|
bb1ccb7f1a | ||
|
|
03fe90ad53 | ||
|
|
1601962f03 | ||
|
|
ac8e59acba | ||
|
|
5f6d17bfe2 | ||
|
|
3c5fe00c30 | ||
|
|
91754848af | ||
|
|
d8eb4d188a | ||
|
|
25d64911d4 | ||
|
|
888f02dbaa | ||
|
|
728a5653be | ||
|
|
a5dc0fddb8 | ||
|
|
134f30e81f | ||
|
|
9b07ac2be4 | ||
|
|
571a49ba6f | ||
|
|
077437e1f1 | ||
|
|
ba9e509d67 | ||
|
|
181ab149e3 | ||
|
|
5ef64c7dd1 | ||
|
|
0f6a675d0c | ||
|
|
ec3552bbde | ||
|
|
5783602694 | ||
|
|
9ba76992e4 | ||
|
|
d075ab6a74 | ||
|
|
4cdfeaae34 | ||
|
|
e54b46910a | ||
|
|
9558406c90 | ||
|
|
f735c34841 | ||
|
|
1aa4afad5f | ||
|
|
58005d81d6 | ||
|
|
b0381c58e6 | ||
|
|
99a3cfafc9 | ||
|
|
6c908eb710 | ||
|
|
dc15144a29 | ||
|
|
34431bcb75 | ||
|
|
0041b787ec | ||
|
|
868edc6028 | ||
|
|
d8c84a91f4 | ||
|
|
68282af457 | ||
|
|
0ecb732331 | ||
|
|
86785c726a | ||
|
|
10fe7f5213 | ||
|
|
4deab7554c | ||
|
|
b92c8007e4 | ||
|
|
029f3acbd5 | ||
|
|
0b9fda5b25 | ||
|
|
47816f2530 | ||
|
|
73023499aa | ||
|
|
241043b616 | ||
|
|
e97e117298 | ||
|
|
b63e95388c | ||
|
|
ef8fba1d49 | ||
|
|
be64c03d06 | ||
|
|
45682ca59e | ||
|
|
397b9d423e | ||
|
|
d9113fb6d4 | ||
|
|
5dc0df2132 | ||
|
|
7f2cc9a3e6 | ||
|
|
d3c4ca6e09 | ||
|
|
28538214d2 | ||
|
|
4f48f3372c | ||
|
|
9e070107a2 | ||
|
|
2a92b2992f | ||
|
|
e180153c3a | ||
|
|
e35f9e16a1 | ||
|
|
8e955e9257 | ||
|
|
9ca44652b3 | ||
|
|
3d89d72426 | ||
|
|
c2ffab3273 | ||
|
|
dbb62b613c | ||
|
|
6f2d6e31c9 | ||
|
|
944939a2e9 | ||
|
|
04164a550d | ||
|
|
16a5e15070 | ||
|
|
ee97d8d902 | ||
|
|
3d4b077b89 | ||
|
|
0f4a89d938 | ||
|
|
0d0ec12dbf | ||
|
|
afe30a2c02 | ||
|
|
2f9289a2c5 | ||
|
|
7a9d2e80d0 | ||
|
|
d4b8735c04 | ||
|
|
d31687d0bf | ||
|
|
470535dd33 | ||
|
|
27f53f1e99 | ||
|
|
e7d498c95e | ||
|
|
02b617d297 | ||
|
|
96d04e1a7d | ||
|
|
fe9d805d7c | ||
|
|
b6c136839e | ||
|
|
8c947a0e0d | ||
|
|
49c5cbbf3b | ||
|
|
efaefe2e44 | ||
|
|
ed95f1b966 | ||
|
|
7ecbc552aa | ||
|
|
dadc997398 | ||
|
|
b90d6b5ab0 | ||
|
|
db5c6f82fd | ||
|
|
f161f9617b | ||
|
|
9d3249e6bd | ||
|
|
f1c65afa98 | ||
|
|
80c1f0cdf9 | ||
|
|
ea0fe83d00 | ||
|
|
994836fba7 | ||
|
|
41aa1dcb0b | ||
|
|
3965d3fdd9 | ||
|
|
ff6fd8a6e9 | ||
|
|
0a6906b23e | ||
|
|
c38b5c0ce3 | ||
|
|
86f3b5a036 | ||
|
|
382a44f040 | ||
|
|
2d9604cd14 | ||
|
|
7930709bc3 | ||
|
|
6fe2c92cee | ||
|
|
884dee2db3 | ||
|
|
1f00c2a9c4 | ||
|
|
84070835df | ||
|
|
fb389137e7 | ||
|
|
c14b6a78ae | ||
|
|
a6c1c49f98 | ||
|
|
7848ad86bd | ||
|
|
882c15dada | ||
|
|
704ad222d9 | ||
|
|
cfee80ce3c | ||
|
|
eb95cedd72 | ||
|
|
99a7fb3383 | ||
|
|
7f4dbf9346 | ||
|
|
09b00c9974 | ||
|
|
b74fb8174d | ||
|
|
a7ec5a64b7 | ||
|
|
c619dcf65d | ||
|
|
1221d88e13 | ||
|
|
48d2ead71d | ||
|
|
ed7099bfd6 | ||
|
|
2604cdfdae | ||
|
|
94dc389812 | ||
|
|
4a8c621f46 | ||
|
|
477af8af4e | ||
|
|
a9a7d524aa | ||
|
|
1cdaff8ddf | ||
|
|
eeea3d2dcc | ||
|
|
f46b6a0697 | ||
|
|
622581ee0a | ||
|
|
eb725f51ce | ||
|
|
4d4462368b | ||
|
|
6fe5d4363d | ||
|
|
b46fa27084 | ||
|
|
ade2440613 | ||
|
|
4dc4620b60 | ||
|
|
202c2662f1 | ||
|
|
d2afd71c81 | ||
|
|
bad1ce2618 | ||
|
|
f194845b2b | ||
|
|
98d4e108e6 | ||
|
|
ab43ed1900 | ||
|
|
b147e047a5 | ||
|
|
bbb616f56c | ||
|
|
35a429ea65 | ||
|
|
bd16543c10 | ||
|
|
cbdeae276e | ||
|
|
e733113963 | ||
|
|
272b030b89 | ||
|
|
472a553d13 | ||
|
|
3a1bff146c | ||
|
|
b82b9acc54 | ||
|
|
fac3845756 | ||
|
|
9381a74f1d | ||
|
|
f6662a3208 | ||
|
|
134af82d90 | ||
|
|
75175b8e54 | ||
|
|
e3d8599dc7 | ||
|
|
77ee69ad52 | ||
|
|
4b82838f80 | ||
|
|
a177829976 | ||
|
|
b8b4f960dd | ||
|
|
2466414307 | ||
|
|
a3a32f0d68 | ||
|
|
ee001f86f0 | ||
|
|
87d6975bd8 | ||
|
|
4cab4e27ff | ||
|
|
f105f0995b | ||
|
|
18e89a7353 | ||
|
|
445f9a67a4 | ||
|
|
4c039c900c | ||
|
|
2c2e0a3419 | ||
|
|
1b43e0b28a | ||
|
|
a23f7782b2 | ||
|
|
723db9d0ea | ||
|
|
cbf165dc40 | ||
|
|
6f3579acf1 | ||
|
|
c9e2f86e57 | ||
|
|
8c0199119e | ||
|
|
5e6e6be888 | ||
|
|
79fb6485b1 | ||
|
|
29baaaf2ed | ||
|
|
f824fb25c2 | ||
|
|
28a6ea7e1a | ||
|
|
65566faf17 | ||
|
|
eb4f90e669 | ||
|
|
40308158ca | ||
|
|
d0d5240474 | ||
|
|
751ce8e6d6 | ||
|
|
748a5eadc0 | ||
|
|
5659fe21ea | ||
|
|
bc899b74a3 | ||
|
|
516c8b05a4 | ||
|
|
8502f9e303 | ||
|
|
bc90d416e6 | ||
|
|
2fd1cd72fd | ||
|
|
3a2276dcd1 | ||
|
|
c432c583f6 | ||
|
|
9b8f5ed8f4 | ||
|
|
0713a15028 | ||
|
|
62c502615f | ||
|
|
fdbe4b8f5e | ||
|
|
33c73f4dc8 | ||
|
|
c207a576e7 | ||
|
|
aa426514f3 | ||
|
|
ba9c28892e | ||
|
|
49c31ddfc8 | ||
|
|
4424205814 | ||
|
|
874f3fb737 | ||
|
|
ac27c89a44 | ||
|
|
0e530cf92e | ||
|
|
ed66b00b20 | ||
|
|
84fb05239b | ||
|
|
7500bbc0be | ||
|
|
c43c8de6cd | ||
|
|
9d40db5b21 | ||
|
|
a61eca36ae | ||
|
|
60479a442f | ||
|
|
5257188f70 | ||
|
|
7599239983 | ||
|
|
1561c0d000 | ||
|
|
1f740cf8c0 | ||
|
|
55c5f03004 | ||
|
|
a345cff614 | ||
|
|
85d8566cfa | ||
|
|
44d68e3ef0 | ||
|
|
04bd4900b0 | ||
|
|
81f277b36f | ||
|
|
575660d3e8 | ||
|
|
1e692491f1 | ||
|
|
b3639b3453 | ||
|
|
53ebf37b40 | ||
|
|
624ec33652 | ||
|
|
2890040118 | ||
|
|
68db8b3e23 | ||
|
|
9873491db1 | ||
|
|
5d64ab0146 | ||
|
|
568267ce07 | ||
|
|
69cdefe996 | ||
|
|
61758db589 | ||
|
|
9e9d98bb31 | ||
|
|
6b2b771dc4 | ||
|
|
031bea0f50 | ||
|
|
6f2e447ec3 | ||
|
|
cf831fbcea | ||
|
|
bcd4c1c182 | ||
|
|
d954026cea | ||
|
|
2c2dad1415 | ||
|
|
d42ed88845 | ||
|
|
e12c00e980 | ||
|
|
c25971e5d2 | ||
|
|
293e56758c | ||
|
|
a6b17f0437 | ||
|
|
140d59b898 | ||
|
|
85742c5785 | ||
|
|
9b07d8ce64 | ||
|
|
b1b9da1b17 | ||
|
|
ffe670923e | ||
|
|
21649967aa | ||
|
|
3f40f60c1c | ||
|
|
176c275a06 | ||
|
|
ae2b4b1668 | ||
|
|
a27f8e2937 | ||
|
|
818b1c84b0 | ||
|
|
75eceaa5e1 | ||
|
|
c6b4cc8e01 | ||
|
|
819302b51f | ||
|
|
c53d7fcc32 | ||
|
|
3de2e65530 | ||
|
|
c5319b6bea | ||
|
|
801ddc0886 | ||
|
|
dfd9196aa7 | ||
|
|
c4362297f5 | ||
|
|
96c23af99c | ||
|
|
6a29dacdeb | ||
|
|
9ddad80f2a |
7
.env
@@ -1,3 +1,8 @@
|
||||
REACT_APP_INFURA_KEY="4bf032f2d38a4ed6bb975b80d6340847"
|
||||
REACT_APP_FORTMATIC_KEY="pk_live_357F77728B8EB880"
|
||||
REACT_APP_AMPLITUDE_TEST_KEY="add-the-real-test-key-if-you-need-to-test-amplitude-events"
|
||||
REACT_APP_AWS_API_REGION="us-east-2"
|
||||
REACT_APP_AWS_API_ACCESS_KEY="AKIAYJJWW6AQ47ODATHN"
|
||||
REACT_APP_AWS_API_ACCESS_SECRET="V9PoU0FhBP3cX760rPs9jMG/MIuDNLX6hYvVcaYO"
|
||||
REACT_APP_AWS_X_API_KEY="z9dReS5UtHu7iTrUsTuWRozLthi3AxOZlvobrIdr14"
|
||||
REACT_APP_AWS_API_ENDPOINT="https://beta.api.uniswap.org/v1/graphql"
|
||||
REACT_APP_TEMP_API_URL="https://temp.api.uniswap.org/v1"
|
||||
|
||||
@@ -4,3 +4,4 @@ REACT_APP_INFURA_KEY="099fc58e0de9451d80b18d7c74caa7c1"
|
||||
REACT_APP_FORTMATIC_KEY="pk_live_F937DF033A1666BF"
|
||||
REACT_APP_GOOGLE_ANALYTICS_ID="G-KDP9B6W4H8"
|
||||
REACT_APP_FIREBASE_KEY="AIzaSyBcZWwTcTJHj_R6ipZcrJkXdq05PuX0Rs0"
|
||||
REACT_APP_AWS_API_ENDPOINT="https://api.uniswap.org/v1/graphql"
|
||||
|
||||
@@ -81,6 +81,18 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
"paths": [
|
||||
{
|
||||
"name": "@ethersproject/providers",
|
||||
"message": "Please only use Providers instantiated in constants/providers to improve traceability.",
|
||||
"allowTypeImports": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
2
.gitignore
vendored
@@ -9,8 +9,8 @@
|
||||
/src/locales/**/pseudo.po
|
||||
|
||||
# generated graphql types
|
||||
/src/graphql/schema/
|
||||
__generated__/
|
||||
schema.graphql
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
@@ -30,7 +30,7 @@ or visit [app.uniswap.org](https://app.uniswap.org).
|
||||
|
||||
Check out `useUnsupportedTokenList()` in [src/state/lists/hooks.ts](./src/state/lists/hooks.ts) for blocking tokens in your instance of the interface.
|
||||
|
||||
You can block an entire list of tokens by passing in a tokenlist like [here](./src/constants/lists.ts) or you can block specific tokens by adding them to [unsupported.tokenlist.json](./src/constants/tokenLists/unsupported.tokenlist.json).
|
||||
You can block an entire list of tokens by passing in a tokenlist like [here](./src/constants/lists.ts)
|
||||
|
||||
## Contributions
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
overrideExisting: true
|
||||
schema: 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3'
|
||||
documents: 'src/**/!(*.d).{ts,tsx}'
|
||||
generates:
|
||||
./src/graphql/schema/schema.graphql:
|
||||
./src/graphql/thegraph/schema/schema.graphql:
|
||||
plugins:
|
||||
- schema-ast
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const { VanillaExtractPlugin } = require('@vanilla-extract/webpack-plugin')
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
|
||||
const { DefinePlugin } = require('webpack')
|
||||
|
||||
const commitHash = require('child_process').execSync('git rev-parse HEAD')
|
||||
|
||||
module.exports = {
|
||||
babel: {
|
||||
plugins: ['@vanilla-extract/babel-plugin'],
|
||||
},
|
||||
webpack: {
|
||||
plugins: [new VanillaExtractPlugin()],
|
||||
plugins: [
|
||||
new VanillaExtractPlugin(),
|
||||
new DefinePlugin({
|
||||
'process.env.REACT_APP_GIT_COMMIT_HASH': JSON.stringify(commitHash.toString()),
|
||||
}),
|
||||
],
|
||||
configure: (webpackConfig) => {
|
||||
const instanceOfMiniCssExtractPlugin = webpackConfig.plugins.find(
|
||||
(plugin) => plugin instanceof MiniCssExtractPlugin
|
||||
|
||||
@@ -21,20 +21,20 @@ describe('Add Liquidity', () => {
|
||||
cy.get('#add-liquidity-input-tokenb .token-symbol-container').should('not.contain.text', 'ETH')
|
||||
})
|
||||
|
||||
it('token not in storage is loaded', () => {
|
||||
it.skip('token not in storage is loaded', () => {
|
||||
cy.visit('/add/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
||||
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'SKL')
|
||||
cy.get('#add-liquidity-input-tokenb .token-symbol-container').should('contain.text', 'MKR')
|
||||
})
|
||||
|
||||
it('single token can be selected', () => {
|
||||
it.skip('single token can be selected', () => {
|
||||
cy.visit('/add/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d')
|
||||
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'SKL')
|
||||
cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
||||
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'MKR')
|
||||
})
|
||||
|
||||
it('loads fee tier distribution', () => {
|
||||
it.skip('loads fee tier distribution', () => {
|
||||
cy.fixture('feeTierDistribution.json').then((feeTierDistribution) => {
|
||||
cy.intercept('POST', '/subgraphs/name/uniswap/uniswap-v3', (req: CyHttpMessages.IncomingHttpRequest) => {
|
||||
if (hasQuery(req, 'FeeTierDistributionQuery')) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
describe('Link', () => {
|
||||
it('should update route', () => {
|
||||
cy.visit('/')
|
||||
cy.get('[data-cy="pool-nav-link"]').click()
|
||||
cy.contains('Pool').click()
|
||||
cy.get('[data-cy="join-pool-button"]').should('exist')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
describe('Lists', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/swap')
|
||||
})
|
||||
|
||||
// @TODO check if default lists are active when we have them
|
||||
it('change list', () => {
|
||||
cy.get('#swap-currency-output .open-currency-select-button').click()
|
||||
cy.get('.list-token-manage-button').click()
|
||||
})
|
||||
})
|
||||
@@ -23,7 +23,7 @@ describe('Remove Liquidity', () => {
|
||||
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'WETH')
|
||||
})
|
||||
|
||||
it('token not in storage is loaded', () => {
|
||||
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')
|
||||
|
||||
50
cypress/e2e/wallet-dropdown.test.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { FeatureFlag } from '../../src/featureFlags/flags/featureFlags'
|
||||
import { getTestSelector } from '../utils'
|
||||
|
||||
describe('Wallet Dropdown', () => {
|
||||
before(() => {
|
||||
cy.visit('/', { featureFlags: [FeatureFlag.navBar, FeatureFlag.tokenSafety] })
|
||||
})
|
||||
|
||||
it('should change the theme', () => {
|
||||
cy.get(getTestSelector('web3-status-connected')).click()
|
||||
cy.get(getTestSelector('wallet-select-theme')).click()
|
||||
cy.get(getTestSelector('wallet-select-theme')).contains('Light theme').should('exist')
|
||||
})
|
||||
|
||||
it('should select a language', () => {
|
||||
cy.get(getTestSelector('wallet-select-language')).click()
|
||||
cy.get(getTestSelector('wallet-language-item')).contains('Afrikaans').click({ force: true })
|
||||
cy.get(getTestSelector('wallet-header')).should('contain', 'Taal')
|
||||
cy.get(getTestSelector('wallet-language-item')).contains('English').click({ force: true })
|
||||
cy.get(getTestSelector('wallet-header')).should('contain', 'Language')
|
||||
cy.get(getTestSelector('wallet-back')).click()
|
||||
})
|
||||
|
||||
it('should be able to view transactions', () => {
|
||||
cy.get(getTestSelector('wallet-transactions')).click()
|
||||
cy.get(getTestSelector('wallet-empty-transaction-text')).should('exist')
|
||||
cy.get(getTestSelector('wallet-back')).click()
|
||||
})
|
||||
|
||||
it('should change the theme when not connected', () => {
|
||||
cy.get(getTestSelector('wallet-disconnect')).click()
|
||||
cy.get(getTestSelector('wallet-select-theme')).click()
|
||||
cy.get(getTestSelector('wallet-select-theme')).contains('Dark theme').should('exist')
|
||||
})
|
||||
|
||||
it('should select a language when not connected', () => {
|
||||
cy.get(getTestSelector('wallet-select-language')).click()
|
||||
cy.get(getTestSelector('wallet-language-item')).contains('Afrikaans').click({ force: true })
|
||||
cy.get(getTestSelector('wallet-header')).should('contain', 'Taal')
|
||||
cy.get(getTestSelector('wallet-language-item')).contains('English').click({ force: true })
|
||||
cy.get(getTestSelector('wallet-header')).should('contain', 'Language')
|
||||
cy.get(getTestSelector('wallet-back')).click()
|
||||
})
|
||||
|
||||
it('should open the wallet connect modal from the drop down when not connected', () => {
|
||||
cy.get(getTestSelector('wallet-connect-wallet')).click()
|
||||
cy.get(getTestSelector('wallet-modal')).should('exist')
|
||||
cy.get(getTestSelector('wallet-modal-close')).click()
|
||||
})
|
||||
})
|
||||
@@ -1,30 +0,0 @@
|
||||
import { TEST_ADDRESS_NEVER_USE_SHORTENED } from '../support/ethereum'
|
||||
|
||||
describe('Wallet', () => {
|
||||
before(() => {
|
||||
cy.visit('/swap')
|
||||
})
|
||||
|
||||
it('displays account details', () => {
|
||||
cy.get('[data-testid=web3-status-connected]').contains(TEST_ADDRESS_NEVER_USE_SHORTENED).click()
|
||||
})
|
||||
|
||||
it('displays account view in wallet modal', () => {
|
||||
cy.get('[data-testid=web3-account-identifier-row]').contains(TEST_ADDRESS_NEVER_USE_SHORTENED)
|
||||
})
|
||||
|
||||
it('changes back to the options grid', () => {
|
||||
cy.contains('Change').click()
|
||||
cy.get('[data-testid=option-grid]').should('exist')
|
||||
})
|
||||
|
||||
it('selects injected wallet option', () => {
|
||||
cy.contains('Injected').click()
|
||||
cy.get('[data-testid=web3-account-identifier-row]').contains(TEST_ADDRESS_NEVER_USE_SHORTENED)
|
||||
})
|
||||
|
||||
it('shows connect buttons after disconnect', () => {
|
||||
cy.contains('Disconnect').click()
|
||||
cy.get('[data-testid=option-grid]').should('exist')
|
||||
})
|
||||
})
|
||||
@@ -9,6 +9,8 @@
|
||||
import { injected } from './ethereum'
|
||||
import assert = require('assert')
|
||||
|
||||
import { FeatureFlag } from '../../src/featureFlags/flags/featureFlags'
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
@@ -17,6 +19,7 @@ declare global {
|
||||
}
|
||||
interface VisitOptions {
|
||||
serviceWorker?: true
|
||||
featureFlags?: Array<FeatureFlag>
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,11 +34,23 @@ Cypress.Commands.overwrite(
|
||||
cy.intercept('/service-worker.js', options?.serviceWorker ? undefined : { statusCode: 404 }).then(() => {
|
||||
original({
|
||||
...options,
|
||||
url: (url.startsWith('/') && url.length > 2 && !url.startsWith('/#') ? `/#${url}` : url) + '?chain=rinkeby',
|
||||
url: (url.startsWith('/') && url.length > 2 && !url.startsWith('/#') ? `/#${url}` : url) + '?chain=goerli',
|
||||
onBeforeLoad(win) {
|
||||
options?.onBeforeLoad?.(win)
|
||||
win.localStorage.clear()
|
||||
win.localStorage.setItem('redux_localstorage_simple_user', '{"selectedWallet":"INJECTED"}')
|
||||
|
||||
if (options?.featureFlags) {
|
||||
const featureFlags = options.featureFlags.reduce(
|
||||
(flags, flag) => ({
|
||||
...flags,
|
||||
[flag]: 'enabled',
|
||||
}),
|
||||
{}
|
||||
)
|
||||
win.localStorage.setItem('featureFlags', JSON.stringify(featureFlags))
|
||||
}
|
||||
|
||||
win.ethereum = injected
|
||||
},
|
||||
})
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
import { Eip1193Bridge } from '@ethersproject/experimental/lib/eip1193-bridge'
|
||||
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
|
||||
import { JsonRpcProvider } from '@ethersproject/providers'
|
||||
import { Wallet } from '@ethersproject/wallet'
|
||||
|
||||
@@ -18,10 +19,10 @@ export const TEST_ADDRESS_NEVER_USE_SHORTENED = `${TEST_ADDRESS_NEVER_USE.substr
|
||||
6
|
||||
)}...${TEST_ADDRESS_NEVER_USE.substr(-4, 4)}`
|
||||
|
||||
const provider = new JsonRpcProvider('https://rinkeby.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847', 4)
|
||||
const provider = new JsonRpcProvider('https://goerli.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847', 4)
|
||||
const signer = new Wallet(TEST_PRIVATE_KEY, provider)
|
||||
export const injected = new (class extends Eip1193Bridge {
|
||||
chainId = 4
|
||||
chainId = /* GOERLI= */ 5
|
||||
|
||||
async sendAsync(...args: any[]) {
|
||||
console.debug('sendAsync called', ...args)
|
||||
|
||||
1
cypress/utils/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const getTestSelector = (selectorId: string) => `[data-testid=${selectorId}]`
|
||||
14
fetch-schema.js
Normal file
@@ -0,0 +1,14 @@
|
||||
/* eslint-disable */
|
||||
require('dotenv').config({ path: '.env.production' })
|
||||
const { exec } = require('child_process')
|
||||
const dataConfig = require('./relay.config')
|
||||
const thegraphConfig = require('./relay_thegraph.config')
|
||||
/* eslint-enable */
|
||||
|
||||
const THEGRAPH_API_URL = 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3'
|
||||
exec(`get-graphql-schema ${THEGRAPH_API_URL} > ${thegraphConfig.schema}`)
|
||||
|
||||
console.log(process.env.REACT_APP_AWS_API_ENDPOINT)
|
||||
exec(
|
||||
`get-graphql-schema --h Origin=https://app.uniswap.org ${process.env.REACT_APP_AWS_API_ENDPOINT} > ${dataConfig.schema}`
|
||||
)
|
||||
43
package.json
@@ -8,25 +8,23 @@
|
||||
"contracts:compile:abi": "typechain --target ethers-v5 --out-dir src/abis/types \"./src/abis/**/*.json\"",
|
||||
"contracts:compile:v3": "typechain --target ethers-v5 --out-dir src/types/v3 \"./node_modules/@uniswap/**/artifacts/contracts/**/*[!dbg].json\"",
|
||||
"contracts:compile": "yarn contracts:compile:abi && yarn contracts:compile:v3",
|
||||
"relay": "relay-compiler",
|
||||
"graphql:generate": "graphql-codegen --config codegen.yml && yarn relay",
|
||||
"relay": "relay-compiler relay.config.js",
|
||||
"relay-thegraph": "relay-compiler relay_thegraph.config.js",
|
||||
"graphql:fetch": "node fetch-schema.js",
|
||||
"graphql:generate": "yarn relay && yarn relay-thegraph",
|
||||
"prei18n:extract": "node prei18n-extract.js",
|
||||
"i18n:extract": "lingui extract --locale en-US",
|
||||
"i18n:compile": "yarn i18n:extract && lingui compile",
|
||||
"i18n:pseudo": "lingui extract --locale pseudo && lingui compile",
|
||||
"prepare": "yarn contracts:compile && yarn graphql:generate && yarn i18n:compile",
|
||||
"prepare": "yarn contracts:compile && yarn graphql:fetch && yarn graphql:generate && yarn i18n:compile",
|
||||
"start": "craco start",
|
||||
"build": "craco build",
|
||||
"serve": "serve build -l 3000",
|
||||
"lint": "yarn eslint .",
|
||||
"test": "craco test --coverage",
|
||||
"cypress:open": "cypress open --browser chrome --e2e",
|
||||
"cypress:run": "cypress run --browser chrome --e2e"
|
||||
},
|
||||
"relay": {
|
||||
"src": "./src",
|
||||
"language": "typescript",
|
||||
"schema": "./src/graphql/schema/schema.graphql"
|
||||
"cypress:run": "cypress run --browser chrome --e2e",
|
||||
"postinstall": "patch-package"
|
||||
},
|
||||
"jest": {
|
||||
"collectCoverageFrom": [
|
||||
@@ -63,9 +61,6 @@
|
||||
"devDependencies": {
|
||||
"@craco/craco": "6.4.3",
|
||||
"@ethersproject/experimental": "^5.4.0",
|
||||
"@graphql-codegen/cli": "1.21.5",
|
||||
"@graphql-codegen/schema-ast": "^2.5.1",
|
||||
"@graphql-codegen/typescript-rtk-query": "^1.1.1",
|
||||
"@lingui/cli": "^3.9.0",
|
||||
"@testing-library/jest-dom": "^5.16.4",
|
||||
"@testing-library/react": "^13.1",
|
||||
@@ -85,6 +80,7 @@
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@types/react-redux": "^7.1.24",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"@types/react-table": "^7.7.12",
|
||||
"@types/react-virtualized-auto-sizer": "^1.0.0",
|
||||
"@types/react-window": "^1.8.2",
|
||||
"@types/rebass": "^4.0.7",
|
||||
@@ -95,9 +91,9 @@
|
||||
"@types/wcag-contrast": "^3.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4",
|
||||
"@typescript-eslint/parser": "^4",
|
||||
"babel-plugin-relay": "^14.1.0",
|
||||
"@vanilla-extract/babel-plugin": "^1.1.7",
|
||||
"@vanilla-extract/webpack-plugin": "^2.1.11",
|
||||
"babel-plugin-relay": "^14.1.0",
|
||||
"cypress": "^10.3.1",
|
||||
"env-cmd": "^10.1.0",
|
||||
"eslint": "^7.11.0",
|
||||
@@ -110,6 +106,8 @@
|
||||
"eslint-plugin-unused-imports": "^2.0.0",
|
||||
"jest-styled-components": "^7.0.8",
|
||||
"ms.macro": "^2.0.0",
|
||||
"patch-package": "^6.4.7",
|
||||
"postinstall-postinstall": "^2.1.0",
|
||||
"prettier": "^2.7.1",
|
||||
"react-scripts": "^4.0.3",
|
||||
"relay-compiler": "^14.1.0",
|
||||
@@ -137,10 +135,10 @@
|
||||
"@uniswap/governance": "^1.0.2",
|
||||
"@uniswap/liquidity-staker": "^1.0.2",
|
||||
"@uniswap/merkle-distributor": "1.0.1",
|
||||
"@uniswap/redux-multicall": "^1.1.5",
|
||||
"@uniswap/redux-multicall": "^1.1.6",
|
||||
"@uniswap/router-sdk": "^1.3.0",
|
||||
"@uniswap/sdk-core": "^3.0.1",
|
||||
"@uniswap/smart-order-router": "^2.9.2",
|
||||
"@uniswap/smart-order-router": "^2.10.0",
|
||||
"@uniswap/token-lists": "^1.0.0-beta.30",
|
||||
"@uniswap/v2-core": "1.0.0",
|
||||
"@uniswap/v2-periphery": "^1.1.0-beta.0",
|
||||
@@ -148,7 +146,7 @@
|
||||
"@uniswap/v3-core": "1.0.0",
|
||||
"@uniswap/v3-periphery": "^1.1.1",
|
||||
"@uniswap/v3-sdk": "^3.9.0",
|
||||
"@uniswap/widgets": "^2.1.1",
|
||||
"@uniswap/widgets": "^2.14.1",
|
||||
"@vanilla-extract/css": "^1.7.2",
|
||||
"@vanilla-extract/css-utils": "^0.1.2",
|
||||
"@vanilla-extract/dynamic": "^2.0.2",
|
||||
@@ -157,6 +155,7 @@
|
||||
"@visx/event": "^2.6.0",
|
||||
"@visx/glyph": "^2.10.0",
|
||||
"@visx/group": "^2.10.0",
|
||||
"@visx/react-spring": "^2.12.2",
|
||||
"@visx/responsive": "^2.10.0",
|
||||
"@visx/shape": "^2.11.1",
|
||||
"@walletconnect/ethereum-provider": "1.7.1",
|
||||
@@ -177,11 +176,10 @@
|
||||
"clsx": "^1.1.1",
|
||||
"copy-to-clipboard": "^3.2.0",
|
||||
"d3": "^7.6.1",
|
||||
"d3-curve-circlecorners": "^0.1.6",
|
||||
"ethers": "^5.1.4",
|
||||
"firebase": "^9.1.3",
|
||||
"focus-visible": "^5.2.0",
|
||||
"fortmatic": "^2.4.0",
|
||||
"get-graphql-schema": "^2.1.2",
|
||||
"graphql": "^16.5.0",
|
||||
"graphql-request": "^3.4.0",
|
||||
"immer": "^9.0.6",
|
||||
@@ -202,6 +200,7 @@
|
||||
"react-dom": "^18.2.0",
|
||||
"react-feather": "^2.0.8",
|
||||
"react-ga4": "^1.4.1",
|
||||
"react-infinite-scroll-component": "^6.1.0",
|
||||
"react-is": "^17.0.2",
|
||||
"react-markdown": "^4.3.1",
|
||||
"react-popper": "^2.2.3",
|
||||
@@ -209,7 +208,7 @@
|
||||
"react-redux": "^8.0.2",
|
||||
"react-relay": "^14.1.0",
|
||||
"react-router-dom": "^6.3.0",
|
||||
"react-spring": "^8.0.27",
|
||||
"react-spring": "^9.5.5",
|
||||
"react-table": "^7.8.0",
|
||||
"react-use-gesture": "^6.0.14",
|
||||
"react-virtualized-auto-sizer": "^1.0.2",
|
||||
@@ -217,7 +216,6 @@
|
||||
"rebass": "^4.0.7",
|
||||
"redux": "^4.1.2",
|
||||
"redux-localstorage-simple": "^2.3.1",
|
||||
"relay-hooks": "^7.1.0",
|
||||
"setimmediate": "^1.0.5",
|
||||
"styled-components": "^5.3.5",
|
||||
"tiny-invariant": "^1.2.0",
|
||||
@@ -233,5 +231,10 @@
|
||||
"workbox-precaching": "^6.1.0",
|
||||
"workbox-routing": "^6.1.0",
|
||||
"zustand": "^4.0.0-rc.1"
|
||||
},
|
||||
"engines": {
|
||||
"npm": "please-use-yarn",
|
||||
"node": "14",
|
||||
"yarn": ">=1.22"
|
||||
}
|
||||
}
|
||||
|
||||
22
patches/@vanilla-extract+css+1.7.2.patch
Normal file
@@ -0,0 +1,22 @@
|
||||
diff --git a/node_modules/@vanilla-extract/css/dist/vanilla-extract-css.cjs.dev.js b/node_modules/@vanilla-extract/css/dist/vanilla-extract-css.cjs.dev.js
|
||||
index 6e40061..10283a2 100644
|
||||
--- a/node_modules/@vanilla-extract/css/dist/vanilla-extract-css.cjs.dev.js
|
||||
+++ b/node_modules/@vanilla-extract/css/dist/vanilla-extract-css.cjs.dev.js
|
||||
@@ -177,11 +177,13 @@ function generateIdentifier(debugId) {
|
||||
var fileScopeHash = hash__default["default"](packageName ? "".concat(packageName).concat(filePath) : filePath);
|
||||
var identifier = "".concat(fileScopeHash).concat(refCount);
|
||||
|
||||
- if (adapter_dist_vanillaExtractCssAdapter.getIdentOption() === 'debug') {
|
||||
- var devPrefix = getDevPrefix(debugId);
|
||||
+ if (process.env.VANILLA_EXTRACT_DEV_PREFIX) {
|
||||
+ if (adapter_dist_vanillaExtractCssAdapter.getIdentOption() === 'debug') {
|
||||
+ var devPrefix = getDevPrefix(debugId);
|
||||
|
||||
- if (devPrefix) {
|
||||
- identifier = "".concat(devPrefix, "__").concat(identifier);
|
||||
+ if (devPrefix) {
|
||||
+ identifier = "".concat(devPrefix, "__").concat(identifier);
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const exec = require('child_process').exec
|
||||
const { exec } = require('child_process')
|
||||
const isWindows = process.platform === 'win32' || /^(msys|cygwin)$/.test(process.env.OSTYPE)
|
||||
|
||||
if (isWindows) {
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<meta name="theme-color" content="#ff007a" />
|
||||
<meta name="fortmatic-site-verification" content="j93LgcVZk79qcgyo" />
|
||||
|
||||
<!--
|
||||
manifest.json provides metadata used when the app is installed as a PWA.
|
||||
@@ -71,7 +70,6 @@
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
font-feature-settings: 'ss01' on, 'ss02' on, 'cv01' on, 'cv03' on;
|
||||
}
|
||||
|
||||
#background-radial-gradient {
|
||||
|
||||
6
relay.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
src: './src',
|
||||
language: 'typescript',
|
||||
schema: './src/graphql/data/schema.graphql',
|
||||
exclude: ['**/node_modules/**', '**/__mocks__/**', '**/__generated__/**', '**/thegraph/**'],
|
||||
}
|
||||
9
relay_thegraph.config.js
Normal file
@@ -0,0 +1,9 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const defaultConfig = require('./relay.config')
|
||||
|
||||
module.exports = {
|
||||
src: defaultConfig.src,
|
||||
language: defaultConfig.language,
|
||||
schema: './src/graphql/thegraph/schema.graphql',
|
||||
exclude: ['**/node_modules/**', '**/__mocks__/**', '**/__generated__/**', '**/data/**'],
|
||||
}
|
||||
@@ -44,7 +44,14 @@ export const Trace = memo(
|
||||
|
||||
useEffect(() => {
|
||||
if (shouldLogImpression) {
|
||||
sendAnalyticsEvent(name ?? EventName.PAGE_VIEWED, { ...combinedProps, ...properties })
|
||||
const origin = window.location.origin
|
||||
const commitHash = process.env.REACT_APP_GIT_COMMIT_HASH
|
||||
sendAnalyticsEvent(name ?? EventName.PAGE_VIEWED, {
|
||||
...combinedProps,
|
||||
...properties,
|
||||
origin,
|
||||
git_commit_hash: commitHash,
|
||||
})
|
||||
}
|
||||
// Impressions should only be logged on mount.
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@@ -8,14 +8,18 @@ export enum EventName {
|
||||
APP_LOADED = 'Application Loaded',
|
||||
APPROVE_TOKEN_TXN_SUBMITTED = 'Approve Token Transaction Submitted',
|
||||
CONNECT_WALLET_BUTTON_CLICKED = 'Connect Wallet Button Clicked',
|
||||
EXPLORE_SEARCH_SELECTED = 'Explore Search Selected',
|
||||
EXPLORE_TOKEN_ROW_CLICKED = 'Explore Token Row Clicked',
|
||||
PAGE_VIEWED = 'Page Viewed',
|
||||
NAVBAR_SEARCH_SELECTED = 'Navbar Search Selected',
|
||||
NAVBAR_SEARCH_EXITED = 'Navbar Search Exited',
|
||||
SWAP_AUTOROUTER_VISUALIZATION_EXPANDED = 'Swap Autorouter Visualization Expanded',
|
||||
SWAP_DETAILS_EXPANDED = 'Swap Details Expanded',
|
||||
SWAP_MAX_TOKEN_AMOUNT_SELECTED = 'Swap Max Token Amount Selected',
|
||||
SWAP_PRICE_UPDATE_ACKNOWLEDGED = 'Swap Price Update Acknowledged',
|
||||
SWAP_QUOTE_RECEIVED = 'Swap Quote Received',
|
||||
SWAP_SUBMITTED = 'Swap Submitted',
|
||||
SWAP_SIGNED = 'Swap Signed',
|
||||
SWAP_SUBMITTED_BUTTON_CLICKED = 'Swap Submit Button Clicked',
|
||||
SWAP_TOKENS_REVERSED = 'Swap Tokens Reversed',
|
||||
SWAP_TRANSACTION_COMPLETED = 'Swap Transaction Completed',
|
||||
TOKEN_IMPORTED = 'Token Imported',
|
||||
@@ -24,6 +28,7 @@ export enum EventName {
|
||||
WALLET_CONNECT_TXN_COMPLETED = 'Wallet Connect Transaction Completed',
|
||||
WALLET_SELECTED = 'Wallet Selected',
|
||||
WEB_VITALS = 'Web Vitals',
|
||||
WRAP_TOKEN_TXN_INVALIDATED = 'Wrap Token Transaction Invalidated',
|
||||
WRAP_TOKEN_TXN_SUBMITTED = 'Wrap Token Transaction Submitted',
|
||||
// alphabetize additional event names.
|
||||
}
|
||||
@@ -31,22 +36,16 @@ export enum EventName {
|
||||
export enum CUSTOM_USER_PROPERTIES {
|
||||
ALL_WALLET_ADDRESSES_CONNECTED = 'all_wallet_addresses_connected',
|
||||
ALL_WALLET_CHAIN_IDS = 'all_wallet_chain_ids',
|
||||
USER_AGENT = 'user_agent',
|
||||
BROWSER = 'browser',
|
||||
DARK_MODE = 'is_dark_mode',
|
||||
EXPERT_MODE = 'is_expert_mode',
|
||||
SCREEN_RESOLUTION_HEIGHT = 'screen_resolution_height',
|
||||
SCREEN_RESOLUTION_WIDTH = 'screen_resolution_width',
|
||||
WALLET_ADDRESS = 'wallet_address',
|
||||
WALLET_NATIVE_CURRENCY_BALANCE_USD = 'wallet_native_currency_balance_usd',
|
||||
WALLET_TOKENS_ADDRESSES = 'wallet_tokens_addresses',
|
||||
WALLET_TOKENS_SYMBOLS = 'wallet_tokens_symbols',
|
||||
WALLET_TYPE = 'wallet_type',
|
||||
}
|
||||
|
||||
export enum CUSTOM_USER_PROPERTY_SUFFIXES {
|
||||
WALLET_TOKEN_AMOUNT_SUFFIX = '_token_amount',
|
||||
}
|
||||
|
||||
export enum BROWSER {
|
||||
FIREFOX = 'Mozilla Firefox',
|
||||
SAMSUNG = 'Samsung Internet',
|
||||
@@ -56,6 +55,7 @@ export enum BROWSER {
|
||||
EDGE_CHROMIUM = 'Microsoft Edge (Chromium)',
|
||||
CHROME = 'Google Chrome or Chromium',
|
||||
SAFARI = 'Apple Safari',
|
||||
BRAVE = 'Brave',
|
||||
UNKNOWN = 'unknown',
|
||||
}
|
||||
|
||||
@@ -64,8 +64,6 @@ export enum WALLET_CONNECTION_RESULT {
|
||||
FAILED = 'Failed',
|
||||
}
|
||||
|
||||
export const NATIVE_CHAIN_ID = 'NATIVE'
|
||||
|
||||
export enum SWAP_PRICE_UPDATE_USER_RESPONSE {
|
||||
ACCEPTED = 'Accepted',
|
||||
REJECTED = 'Rejected',
|
||||
@@ -109,8 +107,10 @@ export enum ElementName {
|
||||
COMMON_BASES_CURRENCY_BUTTON = 'common-bases-currency-button',
|
||||
CONFIRM_SWAP_BUTTON = 'confirm-swap-or-send',
|
||||
CONNECT_WALLET_BUTTON = 'connect-wallet-button',
|
||||
EXPLORE_SEARCH_INPUT = 'explore_search_input',
|
||||
IMPORT_TOKEN_BUTTON = 'import-token-button',
|
||||
MAX_TOKEN_AMOUNT_BUTTON = 'max-token-amount-button',
|
||||
NAVBAR_SEARCH_INPUT = 'navbar-search-input',
|
||||
PRICE_UPDATE_ACCEPT_BUTTON = 'price-update-accept-button',
|
||||
SWAP_BUTTON = 'swap-button',
|
||||
SWAP_DETAILS_DROPDOWN = 'swap-details-dropdown',
|
||||
@@ -127,6 +127,7 @@ export enum ElementName {
|
||||
*/
|
||||
export enum Event {
|
||||
onClick = 'onClick',
|
||||
onFocus = 'onFocus',
|
||||
onKeyPress = 'onKeyPress',
|
||||
onSelect = 'onSelect',
|
||||
// alphabetize additional events.
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Identify, identify, init, track } from '@amplitude/analytics-browser'
|
||||
import { isProductionEnv } from 'utils/env'
|
||||
|
||||
const API_KEY = isProductionEnv() ? process.env.REACT_APP_AMPLITUDE_KEY : process.env.REACT_APP_AMPLITUDE_TEST_KEY
|
||||
|
||||
/**
|
||||
* Initializes Amplitude with API key for project.
|
||||
*
|
||||
@@ -8,14 +10,11 @@ import { isProductionEnv } from 'utils/env'
|
||||
* member of the organization on Amplitude to view details.
|
||||
*/
|
||||
export function initializeAnalytics() {
|
||||
const API_KEY = isProductionEnv() ? process.env.REACT_APP_AMPLITUDE_KEY : process.env.REACT_APP_AMPLITUDE_TEST_KEY
|
||||
|
||||
if (typeof API_KEY === 'undefined') {
|
||||
const keyName = isProductionEnv() ? 'REACT_APP_AMPLITUDE_KEY' : 'REACT_APP_AMPLITUDE_TEST_KEY'
|
||||
console.error(`${keyName} is undefined, Amplitude analytics will not run.`)
|
||||
return
|
||||
}
|
||||
|
||||
init(
|
||||
API_KEY,
|
||||
/* userId= */ undefined, // User ID should be undefined to let Amplitude default to Device ID
|
||||
@@ -23,7 +22,8 @@ export function initializeAnalytics() {
|
||||
{
|
||||
// Disable tracking of private user information by Amplitude
|
||||
trackingOptions: {
|
||||
ipAddress: false,
|
||||
// IP is being dropped before ingestion on Amplitude side, only being used to determine country.
|
||||
ipAddress: isProductionEnv() ? false : true,
|
||||
carrier: false,
|
||||
city: false,
|
||||
region: false,
|
||||
@@ -33,23 +33,16 @@ export function initializeAnalytics() {
|
||||
)
|
||||
}
|
||||
|
||||
/** Sends an approved (finalized) event to Amplitude production project. */
|
||||
/** Sends an event to Amplitude. */
|
||||
export function sendAnalyticsEvent(eventName: string, eventProperties?: Record<string, unknown>) {
|
||||
if (!isProductionEnv()) {
|
||||
console.log(`[amplitude(${eventName})]: ${JSON.stringify(eventProperties)}`)
|
||||
if (!API_KEY) {
|
||||
console.log(`[analytics(${eventName})]: ${JSON.stringify(eventProperties)}`)
|
||||
return
|
||||
}
|
||||
|
||||
track(eventName, eventProperties)
|
||||
}
|
||||
|
||||
/** Sends a draft event to Amplitude test project. */
|
||||
export function sendTestAnalyticsEvent(eventName: string, eventProperties?: Record<string, unknown>) {
|
||||
if (isProductionEnv()) return
|
||||
|
||||
track(eventName, eventProperties)
|
||||
}
|
||||
|
||||
type Value = string | number | boolean | string[] | number[]
|
||||
|
||||
/**
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Currency, CurrencyAmount, Percent, Token } from '@uniswap/sdk-core'
|
||||
|
||||
import { NATIVE_CHAIN_ID } from './constants'
|
||||
import { NATIVE_CHAIN_ID } from 'constants/tokens'
|
||||
|
||||
export const getDurationUntilTimestampSeconds = (futureTimestampInSecondsSinceEpoch?: number): number | undefined => {
|
||||
if (!futureTimestampInSecondsSinceEpoch) return undefined
|
||||
BIN
src/assets/images/nft-marketplaces.png
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
src/assets/images/sizingImage.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
src/assets/images/tokensPromoDark.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
src/assets/images/tokensPromoLight.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
186
src/assets/svg/socks.svg
Normal file
@@ -0,0 +1,186 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="none" viewBox="0 0 20 20">
|
||||
<g filter="url(#a)">
|
||||
<path fill="url(#b)" d="M17.654 4.868c0-.69-.56-1.25-1.25-1.25h-2.932c-.69 0-1.25.56-1.25 1.25v5.704a2.5 2.5 0 0 1-.67 1.703l-1.741 1.873c-.898.999-1.115 2.687-.058 3.748 1.013 1.017 2.918.988 3.87 0l3.167-3.32a3.125 3.125 0 0 0 .864-2.158v-7.55Z"/>
|
||||
<path fill="url(#c)" d="M17.654 4.868c0-.69-.56-1.25-1.25-1.25h-2.932c-.69 0-1.25.56-1.25 1.25v5.704a2.5 2.5 0 0 1-.67 1.703l-1.741 1.873c-.898.999-1.115 2.687-.058 3.748 1.013 1.017 2.918.988 3.87 0l3.167-3.32a3.125 3.125 0 0 0 .864-2.158v-7.55Z"/>
|
||||
<path fill="url(#d)" d="M17.654 4.868c0-.69-.56-1.25-1.25-1.25h-2.932c-.69 0-1.25.56-1.25 1.25v5.704a2.5 2.5 0 0 1-.67 1.703l-1.741 1.873c-.898.999-1.115 2.687-.058 3.748 1.013 1.017 2.918.988 3.87 0l3.167-3.32a3.125 3.125 0 0 0 .864-2.158v-7.55Z"/>
|
||||
<path fill="url(#e)" d="M17.654 4.868c0-.69-.56-1.25-1.25-1.25h-2.932c-.69 0-1.25.56-1.25 1.25v5.704a2.5 2.5 0 0 1-.67 1.703l-1.741 1.873c-.898.999-1.115 2.687-.058 3.748 1.013 1.017 2.918.988 3.87 0l3.167-3.32a3.125 3.125 0 0 0 .864-2.158v-7.55Z"/>
|
||||
<path fill="url(#f)" d="M17.654 4.868c0-.69-.56-1.25-1.25-1.25h-2.932c-.69 0-1.25.56-1.25 1.25v5.704a2.5 2.5 0 0 1-.67 1.703l-1.741 1.873c-.898.999-1.115 2.687-.058 3.748 1.013 1.017 2.918.988 3.87 0l3.167-3.32a3.125 3.125 0 0 0 .864-2.158v-7.55Z"/>
|
||||
</g>
|
||||
<g filter="url(#g)">
|
||||
<path fill="url(#h)" d="M10.954 2.602c0-.69-.56-1.25-1.25-1.25H6.773c-.69 0-1.25.56-1.25 1.25v5.704a2.5 2.5 0 0 1-.67 1.703l-1.742 1.873c-.898 1-1.115 2.687-.058 3.748 1.014 1.018 2.919.988 3.871 0l3.167-3.32a3.125 3.125 0 0 0 .864-2.157V2.602Z"/>
|
||||
<path fill="url(#i)" d="M10.954 2.602c0-.69-.56-1.25-1.25-1.25H6.773c-.69 0-1.25.56-1.25 1.25v5.704a2.5 2.5 0 0 1-.67 1.703l-1.742 1.873c-.898 1-1.115 2.687-.058 3.748 1.014 1.018 2.919.988 3.871 0l3.167-3.32a3.125 3.125 0 0 0 .864-2.157V2.602Z"/>
|
||||
<path fill="url(#j)" d="M10.954 2.602c0-.69-.56-1.25-1.25-1.25H6.773c-.69 0-1.25.56-1.25 1.25v5.704a2.5 2.5 0 0 1-.67 1.703l-1.742 1.873c-.898 1-1.115 2.687-.058 3.748 1.014 1.018 2.919.988 3.871 0l3.167-3.32a3.125 3.125 0 0 0 .864-2.157V2.602Z"/>
|
||||
<path fill="url(#k)" d="M10.954 2.602c0-.69-.56-1.25-1.25-1.25H6.773c-.69 0-1.25.56-1.25 1.25v5.704a2.5 2.5 0 0 1-.67 1.703l-1.742 1.873c-.898 1-1.115 2.687-.058 3.748 1.014 1.018 2.919.988 3.871 0l3.167-3.32a3.125 3.125 0 0 0 .864-2.157V2.602Z"/>
|
||||
<path fill="url(#l)" d="M10.954 2.602c0-.69-.56-1.25-1.25-1.25H6.773c-.69 0-1.25.56-1.25 1.25v5.704a2.5 2.5 0 0 1-.67 1.703l-1.742 1.873c-.898 1-1.115 2.687-.058 3.748 1.014 1.018 2.919.988 3.871 0l3.167-3.32a3.125 3.125 0 0 0 .864-2.157V2.602Z"/>
|
||||
</g>
|
||||
<path fill="url(#m)" d="M17.654 5.776h-5.431v.673h5.431v-.673Z"/>
|
||||
<path fill="url(#n)" d="M17.654 5.776h-5.431v.673h5.431v-.673Z"/>
|
||||
<path fill="url(#o)" d="M10.955 3.51H5.523v.674h5.432V3.51Z"/>
|
||||
<path fill="url(#p)" d="M10.955 3.51H5.523v.674h5.432V3.51Z"/>
|
||||
<path fill="url(#q)" d="M17.606 4.523c.031.11.048.225.048.345v.328h-5.431v-.328c0-.12.016-.236.048-.345h5.335Z"/>
|
||||
<path fill="url(#r)" d="M17.606 4.523c.031.11.048.225.048.345v.328h-5.431v-.328c0-.12.016-.236.048-.345h5.335Z"/>
|
||||
<path fill="url(#s)" d="M10.907 2.257c.031.11.048.225.048.345v.329H5.523v-.329c0-.12.017-.235.049-.345h5.335Z"/>
|
||||
<path fill="url(#t)" d="M10.907 2.257c.031.11.048.225.048.345v.329H5.523v-.329c0-.12.017-.235.049-.345h5.335Z"/>
|
||||
<g filter="url(#u)">
|
||||
<path fill="url(#v)" d="M17.654 12.236h-3.116a.173.173 0 0 0-.176.17c0 1.255.904 2.3 2.096 2.518l.332-.349a3.125 3.125 0 0 0 .864-2.157v-.182Z"/>
|
||||
</g>
|
||||
<g filter="url(#w)">
|
||||
<path fill="url(#x)" d="M10.955 9.97H7.839a.173.173 0 0 0-.176.17c0 1.255.903 2.3 2.096 2.518l.332-.349a3.125 3.125 0 0 0 .864-2.156V9.97Z"/>
|
||||
</g>
|
||||
<g filter="url(#y)">
|
||||
<path fill="url(#z)" d="M13.243 18.21v-.05a3.424 3.424 0 0 0-3.856-3.397c-.511.986-.496 2.268.366 3.133.892.895 2.474.98 3.49.314Z"/>
|
||||
</g>
|
||||
<g filter="url(#A)">
|
||||
<path fill="url(#B)" d="M6.544 15.944v-.05a3.424 3.424 0 0 0-3.857-3.396c-.51.985-.495 2.267.366 3.132.892.896 2.475.98 3.49.314Z"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="b" x1="17.248" x2="13.193" y1="4.4" y2="16.827" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FDF2FF"/>
|
||||
<stop offset="1" stop-color="#FFECFB"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="c" x1="17.654" x2="15.615" y1="11.854" y2="11.854" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFCCF1"/>
|
||||
<stop offset="1" stop-color="#FFC5EF" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="f" x1="14.99" x2="13.818" y1="16.932" y2="15.721" gradientUnits="userSpaceOnUse">
|
||||
<stop offset=".209" stop-color="#EDB0DF"/>
|
||||
<stop offset="1" stop-color="#ECAAED" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="h" x1="10.549" x2="6.493" y1="2.134" y2="14.562" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FDF2FF"/>
|
||||
<stop offset="1" stop-color="#FFECFB"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="i" x1="10.954" x2="8.915" y1="9.588" y2="9.588" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFCCF1"/>
|
||||
<stop offset="1" stop-color="#FFC5EF" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="l" x1="8.29" x2="7.119" y1="14.666" y2="13.456" gradientUnits="userSpaceOnUse">
|
||||
<stop offset=".209" stop-color="#EDB0DF"/>
|
||||
<stop offset="1" stop-color="#ECAAED" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="m" x1="12.773" x2="16.915" y1="6.449" y2="6.449" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#E95FDB"/>
|
||||
<stop offset="1" stop-color="#FF46CB"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="n" x1="12.223" x2="12.793" y1="6.449" y2="6.449" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#9F4977"/>
|
||||
<stop offset="1" stop-color="#CA5284" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="o" x1="6.074" x2="10.216" y1="4.184" y2="4.184" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#E95FDB"/>
|
||||
<stop offset="1" stop-color="#FF46CB"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="p" x1="5.523" x2="6.094" y1="4.184" y2="4.184" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#9F4977"/>
|
||||
<stop offset="1" stop-color="#CA5284" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="q" x1="12.773" x2="16.915" y1="5.196" y2="5.196" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#E95FDB"/>
|
||||
<stop offset="1" stop-color="#FF46CB"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="r" x1="12.223" x2="12.793" y1="5.196" y2="5.196" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#9F4977"/>
|
||||
<stop offset="1" stop-color="#CA5284" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="s" x1="6.074" x2="10.216" y1="2.931" y2="2.931" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#E95FDB"/>
|
||||
<stop offset="1" stop-color="#FF46CB"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="t" x1="5.523" x2="6.094" y1="2.931" y2="2.931" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#9F4977"/>
|
||||
<stop offset="1" stop-color="#CA5284" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="d" cx="0" cy="0" r="1" gradientTransform="matrix(2.8125 0 0 6.01563 12.02 8.026)" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFD1F5"/>
|
||||
<stop offset="1" stop-color="#FECAFF" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="e" cx="0" cy="0" r="1" gradientTransform="matrix(1.54348 -1.53472 2.30642 2.31958 11.806 16.12)" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFC0FC"/>
|
||||
<stop offset="1" stop-color="#FFBCFC" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="j" cx="0" cy="0" r="1" gradientTransform="matrix(2.8125 0 0 6.01563 5.322 5.76)" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFD1F5"/>
|
||||
<stop offset="1" stop-color="#FECAFF" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="k" cx="0" cy="0" r="1" gradientTransform="matrix(1.54347 -1.53473 2.30643 2.31957 5.107 13.854)" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFC0FC"/>
|
||||
<stop offset="1" stop-color="#FFBCFC" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="v" cx="0" cy="0" r="1" gradientTransform="matrix(-.42911 2.50572 -2.20218 -.37713 16.437 12.236)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset=".147" stop-color="#FF52CF"/>
|
||||
<stop offset="1" stop-color="#FB3FFF"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="x" cx="0" cy="0" r="1" gradientTransform="matrix(-.42911 2.50572 -2.20218 -.37713 9.738 9.97)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset=".147" stop-color="#FF52CF"/>
|
||||
<stop offset="1" stop-color="#FB3FFF"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="z" cx="0" cy="0" r="1" gradientTransform="rotate(167.471 5.464 9.437) scale(4.54024 4.83245)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset=".268" stop-color="#FF4EE3"/>
|
||||
<stop offset=".92" stop-color="#D12396"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="B" cx="0" cy="0" r="1" gradientTransform="rotate(167.471 2.239 7.937) scale(4.54024 4.83245)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset=".268" stop-color="#FF4EE3"/>
|
||||
<stop offset=".92" stop-color="#D12396"/>
|
||||
</radialGradient>
|
||||
<filter id="a" width="8.808" height="15.23" x="9.045" y="3.418" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
<feOffset dx=".2" dy="-.2"/>
|
||||
<feGaussianBlur stdDeviation=".3"/>
|
||||
<feComposite in2="hardAlpha" k2="-1" k3="1" operator="arithmetic"/>
|
||||
<feColorMatrix values="0 0 0 0 0.522043 0 0 0 0 0.119948 0 0 0 0 0.5875 0 0 0 1 0"/>
|
||||
<feBlend in2="shape" result="effect1_innerShadow_6126_86420"/>
|
||||
</filter>
|
||||
<filter id="g" width="8.808" height="15.23" x="2.346" y="1.152" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
<feOffset dx=".2" dy="-.2"/>
|
||||
<feGaussianBlur stdDeviation=".3"/>
|
||||
<feComposite in2="hardAlpha" k2="-1" k3="1" operator="arithmetic"/>
|
||||
<feColorMatrix values="0 0 0 0 0.545098 0 0 0 0 0.219608 0 0 0 0 0.512549 0 0 0 1 0"/>
|
||||
<feBlend in2="shape" result="effect1_innerShadow_6126_86420"/>
|
||||
</filter>
|
||||
<filter id="u" width="3.541" height="2.688" x="14.112" y="12.236" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
<feOffset dx="-.25"/>
|
||||
<feGaussianBlur stdDeviation=".25"/>
|
||||
<feComposite in2="hardAlpha" k2="-1" k3="1" operator="arithmetic"/>
|
||||
<feColorMatrix values="0 0 0 0 0.976471 0 0 0 0 0.145098 0 0 0 0 0.743686 0 0 0 1 0"/>
|
||||
<feBlend in2="shape" result="effect1_innerShadow_6126_86420"/>
|
||||
</filter>
|
||||
<filter id="w" width="3.541" height="2.688" x="7.413" y="9.97" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
<feOffset dx="-.25"/>
|
||||
<feGaussianBlur stdDeviation=".25"/>
|
||||
<feComposite in2="hardAlpha" k2="-1" k3="1" operator="arithmetic"/>
|
||||
<feColorMatrix values="0 0 0 0 0.976471 0 0 0 0 0.145098 0 0 0 0 0.743686 0 0 0 1 0"/>
|
||||
<feBlend in2="shape" result="effect1_innerShadow_6126_86420"/>
|
||||
</filter>
|
||||
<filter id="y" width="4.448" height="4.112" x="9.045" y="14.536" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
<feOffset dx=".25" dy="-.2"/>
|
||||
<feGaussianBlur stdDeviation=".25"/>
|
||||
<feComposite in2="hardAlpha" k2="-1" k3="1" operator="arithmetic"/>
|
||||
<feColorMatrix values="0 0 0 0 0.906118 0 0 0 0 0.329412 0 0 0 0 1 0 0 0 1 0"/>
|
||||
<feBlend in2="shape" result="effect1_innerShadow_6126_86420"/>
|
||||
</filter>
|
||||
<filter id="A" width="4.448" height="4.112" x="2.346" y="12.27" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
<feOffset dx=".25" dy="-.2"/>
|
||||
<feGaussianBlur stdDeviation=".25"/>
|
||||
<feComposite in2="hardAlpha" k2="-1" k3="1" operator="arithmetic"/>
|
||||
<feColorMatrix values="0 0 0 0 0.906118 0 0 0 0 0.329412 0 0 0 0 1 0 0 0 1 0"/>
|
||||
<feBlend in2="shape" result="effect1_innerShadow_6126_86420"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 13 KiB |
3
src/assets/svg/swap-arrows.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.80333 4.8863C7.51044 5.17919 7.51044 5.65406 7.80333 5.94696C8.09622 6.23985 8.5711 6.23985 8.86399 5.94696L7.80333 4.8863ZM12.0837 1.66663L12.614 1.1363C12.3211 0.843403 11.8462 0.843403 11.5533 1.1363L12.0837 1.66663ZM15.3033 5.94696C15.5962 6.23985 16.0711 6.23985 16.364 5.94696C16.6569 5.65406 16.6569 5.17919 16.364 4.8863L15.3033 5.94696ZM11.3337 9.99996C11.3337 10.4142 11.6694 10.75 12.0837 10.75C12.4979 10.75 12.8337 10.4142 12.8337 9.99996H11.3337ZM12.1973 15.1136C12.4902 14.8207 12.4902 14.3459 12.1973 14.053C11.9044 13.7601 11.4296 13.7601 11.1367 14.053L12.1973 15.1136ZM7.91699 18.3333L7.38666 18.8636C7.52731 19.0043 7.71808 19.0833 7.91699 19.0833C8.1159 19.0833 8.30667 19.0043 8.44732 18.8636L7.91699 18.3333ZM4.69732 14.053C4.40443 13.7601 3.92956 13.7601 3.63666 14.053C3.34377 14.3459 3.34377 14.8207 3.63666 15.1136L4.69732 14.053ZM8.66699 10.8333C8.66699 10.4191 8.33121 10.0833 7.91699 10.0833C7.50278 10.0833 7.16699 10.4191 7.16699 10.8333H8.66699ZM8.86399 5.94696L12.614 2.19696L11.5533 1.1363L7.80333 4.8863L8.86399 5.94696ZM11.5533 2.19696L15.3033 5.94696L16.364 4.8863L12.614 1.1363L11.5533 2.19696ZM11.3337 1.66663V9.99996H12.8337V1.66663H11.3337ZM11.1367 14.053L7.38666 17.803L8.44732 18.8636L12.1973 15.1136L11.1367 14.053ZM8.44732 17.803L4.69732 14.053L3.63666 15.1136L7.38666 18.8636L8.44732 17.803ZM8.66699 18.3333L8.66699 10.8333H7.16699L7.16699 18.3333H8.66699Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.24453 18.0887C3.24331 19.0467 3.47372 19.7558 3.93576 20.2158C4.39658 20.6771 5.09574 20.904 6.03326 20.8967H8.11975C8.20693 20.8934 8.29386 20.9079 8.37521 20.9395C8.45656 20.9711 8.53062 21.019 8.5928 21.0802L10.0779 22.5484C10.7527 23.2226 11.4139 23.5578 12.0617 23.5541C12.7096 23.5504 13.3709 23.2152 14.0456 22.5484L15.5124 21.0802C15.5767 21.0182 15.6529 20.97 15.7365 20.9385C15.82 20.9069 15.9091 20.8927 15.9982 20.8967H18.0719C19.0192 20.8979 19.7251 20.6673 20.1896 20.2048C20.6541 19.7423 20.8864 19.0333 20.8864 18.0777V16.0021C20.8816 15.8222 20.9474 15.6476 21.0697 15.5157L22.5365 14.0475C23.2198 13.3758 23.559 12.7145 23.5541 12.0636C23.5492 11.4127 23.21 10.7508 22.5365 10.0779L21.0697 8.6097C20.9471 8.47802 20.8812 8.30329 20.8864 8.12336V6.04769C20.8851 5.09092 20.6547 4.3819 20.1951 3.92064C19.7355 3.45939 19.0278 3.22875 18.0719 3.22875H15.9982C15.9091 3.23242 15.8201 3.21807 15.7366 3.18653C15.6532 3.155 15.5769 3.10694 15.5124 3.04523L14.0456 1.57703C13.3709 0.902883 12.7096 0.567648 12.0617 0.571319C11.4139 0.574989 10.7527 0.910224 10.0779 1.57703L8.5928 3.04523C8.53043 3.10622 8.45638 3.15393 8.37508 3.18547C8.29377 3.21701 8.20689 3.23173 8.11975 3.22875H6.03326C5.08718 3.22998 4.38373 3.45877 3.92291 3.91513C3.4621 4.3715 3.23168 5.08235 3.23168 6.04769V8.12887C3.23683 8.3088 3.17096 8.48352 3.04833 8.6152L1.58154 10.0834C0.908042 10.7551 0.571289 11.417 0.571289 12.0691C0.571289 12.7213 0.912332 13.3844 1.59439 14.0585L3.06118 15.5267C3.18346 15.6586 3.24928 15.8332 3.24453 16.0131V18.0887Z" fill="#4C82FB"/>
|
||||
<path d="M3.24453 18.0887C3.24331 19.0467 3.47372 19.7558 3.93576 20.2158C4.39658 20.6771 5.09574 20.904 6.03326 20.8967H8.11975C8.20693 20.8934 8.29386 20.9079 8.37521 20.9395C8.45656 20.9711 8.53062 21.019 8.5928 21.0802L10.0779 22.5484C10.7527 23.2226 11.4139 23.5578 12.0617 23.5541C12.7096 23.5504 13.3709 23.2152 14.0456 22.5484L15.5124 21.0802C15.5767 21.0182 15.6529 20.97 15.7365 20.9385C15.82 20.9069 15.9091 20.8927 15.9982 20.8967H18.0719C19.0192 20.8979 19.7251 20.6673 20.1896 20.2048C20.6541 19.7423 20.8864 19.0333 20.8864 18.0777V16.0021C20.8816 15.8222 20.9474 15.6476 21.0697 15.5157L22.5365 14.0475C23.2198 13.3758 23.559 12.7145 23.5541 12.0636C23.5492 11.4127 23.21 10.7508 22.5365 10.0779L21.0697 8.6097C20.9471 8.47802 20.8812 8.30329 20.8864 8.12336V6.04769C20.8851 5.09092 20.6547 4.3819 20.1951 3.92064C19.7355 3.45939 19.0278 3.22875 18.0719 3.22875H15.9982C15.9091 3.23242 15.8201 3.21807 15.7366 3.18653C15.6532 3.155 15.5769 3.10694 15.5124 3.04523L14.0456 1.57703C13.3709 0.902883 12.7096 0.567648 12.0617 0.571319C11.4139 0.574989 10.7527 0.910224 10.0779 1.57703L8.5928 3.04523C8.53043 3.10622 8.45638 3.15393 8.37508 3.18547C8.29377 3.21701 8.20689 3.23173 8.11975 3.22875H6.03326C5.08718 3.22998 4.38373 3.45877 3.92291 3.91513C3.4621 4.3715 3.23168 5.08235 3.23168 6.04769V8.12887C3.23683 8.3088 3.17096 8.48352 3.04833 8.6152L1.58154 10.0834C0.908042 10.7551 0.571289 11.417 0.571289 12.0691C0.571289 12.7213 0.912332 13.3844 1.59439 14.0585L3.06118 15.5267C3.18346 15.6586 3.24928 15.8332 3.24453 16.0131V18.0887Z" fill="currentColor"/>
|
||||
<path d="M11.996 15.9909C11.7795 16.3208 11.4599 16.5064 11.0887 16.5064C10.7072 16.5064 10.4083 16.3517 10.1299 15.9909L7.69677 13.0216C7.5215 12.8051 7.42871 12.5783 7.42871 12.3309C7.42871 11.8154 7.82049 11.4133 8.32567 11.4133C8.63497 11.4133 8.8824 11.5267 9.12984 11.8463L11.0475 14.2897L15.1199 7.75329C15.3364 7.40275 15.6147 7.23779 15.924 7.23779C16.4086 7.23779 16.8622 7.57802 16.8622 8.0832C16.8622 8.32033 16.7385 8.56777 16.6045 8.78427L11.996 15.9909Z" fill="white"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#99A1BD" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-x"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-x"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 294 B After Width: | Height: | Size: 300 B |
@@ -24,7 +24,7 @@ const HeaderRow = styled.div`
|
||||
padding: 1rem 1rem;
|
||||
font-weight: 500;
|
||||
color: ${(props) => (props.color === 'blue' ? ({ theme }) => theme.deprecated_primary1 : 'inherit')};
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
|
||||
padding: 1rem;
|
||||
`};
|
||||
`
|
||||
@@ -74,7 +74,7 @@ const AccountGroupingRow = styled.div`
|
||||
|
||||
const AccountSection = styled.div`
|
||||
padding: 0rem 1rem;
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`padding: 0rem 1rem 1.5rem 1rem;`};
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`padding: 0rem 1rem 1.5rem 1rem;`};
|
||||
`
|
||||
|
||||
const YourAccount = styled.div`
|
||||
@@ -145,7 +145,7 @@ const CloseIcon = styled.div`
|
||||
top: 14px;
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
opacity: 0.6;
|
||||
opacity: ${({ theme }) => theme.opacity.hover};
|
||||
}
|
||||
`
|
||||
|
||||
|
||||
88
src/components/AccountDetailsV2/LogoView.tsx
Normal file
@@ -0,0 +1,88 @@
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { UNI_ADDRESS } from 'constants/addresses'
|
||||
import { TransactionInfo, TransactionType } from 'state/transactions/types'
|
||||
import styled, { css } from 'styled-components/macro'
|
||||
|
||||
import { nativeOnChain } from '../../constants/tokens'
|
||||
import { useCurrency } from '../../hooks/Tokens'
|
||||
import CurrencyLogo from '../CurrencyLogo'
|
||||
|
||||
const CurrencyWrap = styled.div`
|
||||
position: relative;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
`
|
||||
|
||||
const CurrencyWrapStyles = css`
|
||||
position: absolute;
|
||||
height: 24px;
|
||||
`
|
||||
|
||||
const CurrencyLogoWrap = styled.span<{ isCentered: boolean }>`
|
||||
${CurrencyWrapStyles};
|
||||
left: ${({ isCentered }) => (isCentered ? '50%' : '0')};
|
||||
top: ${({ isCentered }) => (isCentered ? '50%' : '0')};
|
||||
transform: ${({ isCentered }) => isCentered && 'translate(-50%, -50%)'};
|
||||
`
|
||||
const CurrencyLogoWrapTwo = styled.span`
|
||||
${CurrencyWrapStyles};
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
`
|
||||
|
||||
interface CurrencyPair {
|
||||
currencyId0: string | undefined
|
||||
currencyId1: string | undefined
|
||||
}
|
||||
|
||||
const getCurrency = ({ info, chainId }: { info: TransactionInfo; chainId: number | undefined }): CurrencyPair => {
|
||||
switch (info.type) {
|
||||
case TransactionType.ADD_LIQUIDITY_V3_POOL:
|
||||
case TransactionType.REMOVE_LIQUIDITY_V3:
|
||||
case TransactionType.CREATE_V3_POOL:
|
||||
const { baseCurrencyId, quoteCurrencyId } = info
|
||||
return { currencyId0: baseCurrencyId, currencyId1: quoteCurrencyId }
|
||||
case TransactionType.SWAP:
|
||||
const { inputCurrencyId, outputCurrencyId } = info
|
||||
return { currencyId0: inputCurrencyId, currencyId1: outputCurrencyId }
|
||||
case TransactionType.WRAP:
|
||||
const { unwrapped } = info
|
||||
const native = info.chainId ? nativeOnChain(info.chainId) : undefined
|
||||
const base = 'ETH'
|
||||
const wrappedCurrency = native?.wrapped.address ?? 'WETH'
|
||||
return { currencyId0: unwrapped ? wrappedCurrency : base, currencyId1: unwrapped ? base : wrappedCurrency }
|
||||
case TransactionType.COLLECT_FEES:
|
||||
const { currencyId0, currencyId1 } = info
|
||||
return { currencyId0, currencyId1 }
|
||||
case TransactionType.APPROVAL:
|
||||
return { currencyId0: info.tokenAddress, currencyId1: undefined }
|
||||
case TransactionType.CLAIM:
|
||||
const uniAddress = chainId ? UNI_ADDRESS[chainId] : undefined
|
||||
return { currencyId0: uniAddress, currencyId1: undefined }
|
||||
default:
|
||||
return { currencyId0: undefined, currencyId1: undefined }
|
||||
}
|
||||
}
|
||||
|
||||
const LogoView = ({ info }: { info: TransactionInfo }) => {
|
||||
const { chainId } = useWeb3React()
|
||||
const { currencyId0, currencyId1 } = getCurrency({ info, chainId })
|
||||
const currency0 = useCurrency(currencyId0)
|
||||
const currency1 = useCurrency(currencyId1)
|
||||
const isCentered = !(currency0 && currency1)
|
||||
|
||||
return (
|
||||
<CurrencyWrap>
|
||||
<CurrencyLogoWrap isCentered={isCentered}>
|
||||
<CurrencyLogo size="24px" currency={currency0} />
|
||||
</CurrencyLogoWrap>
|
||||
{!isCentered && (
|
||||
<CurrencyLogoWrapTwo>
|
||||
<CurrencyLogo size="24px" currency={currency1} />
|
||||
</CurrencyLogoWrapTwo>
|
||||
)}
|
||||
</CurrencyWrap>
|
||||
)
|
||||
}
|
||||
|
||||
export default LogoView
|
||||
337
src/components/AccountDetailsV2/TransactionBody.tsx
Normal file
@@ -0,0 +1,337 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { Fraction, TradeType } from '@uniswap/sdk-core'
|
||||
import JSBI from 'jsbi'
|
||||
import {
|
||||
AddLiquidityV3PoolTransactionInfo,
|
||||
ApproveTransactionInfo,
|
||||
ClaimTransactionInfo,
|
||||
CollectFeesTransactionInfo,
|
||||
ExactInputSwapTransactionInfo,
|
||||
ExactOutputSwapTransactionInfo,
|
||||
RemoveLiquidityV3TransactionInfo,
|
||||
TransactionInfo,
|
||||
TransactionType,
|
||||
WrapTransactionInfo,
|
||||
} from 'state/transactions/types'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import { nativeOnChain } from '../../constants/tokens'
|
||||
import { useCurrency, useToken } from '../../hooks/Tokens'
|
||||
import useENSName from '../../hooks/useENSName'
|
||||
import { shortenAddress } from '../../utils'
|
||||
import { TransactionState } from './index'
|
||||
|
||||
const HighlightText = styled.span`
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
font-weight: 600;
|
||||
`
|
||||
|
||||
const BodyWrap = styled.div`
|
||||
line-height: 20px;
|
||||
`
|
||||
|
||||
interface ActionProps {
|
||||
pending: JSX.Element
|
||||
success: JSX.Element
|
||||
failed: JSX.Element
|
||||
transactionState: TransactionState
|
||||
}
|
||||
|
||||
const Action = ({ pending, success, failed, transactionState }: ActionProps) => {
|
||||
switch (transactionState) {
|
||||
case TransactionState.Failed:
|
||||
return failed
|
||||
case TransactionState.Success:
|
||||
return success
|
||||
default:
|
||||
return pending
|
||||
}
|
||||
}
|
||||
|
||||
const formatAmount = (amountRaw: string, decimals: number, sigFigs: number): string =>
|
||||
new Fraction(amountRaw, JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(decimals))).toSignificant(sigFigs)
|
||||
|
||||
const FailedText = ({ transactionState }: { transactionState: TransactionState }) =>
|
||||
transactionState === TransactionState.Failed ? <Trans>failed</Trans> : <span />
|
||||
|
||||
const FormattedCurrencyAmount = ({
|
||||
rawAmount,
|
||||
currencyId,
|
||||
sigFigs = 2,
|
||||
}: {
|
||||
rawAmount: string
|
||||
currencyId: string
|
||||
sigFigs: number
|
||||
}) => {
|
||||
const currency = useCurrency(currencyId)
|
||||
|
||||
return currency ? (
|
||||
<HighlightText>
|
||||
{formatAmount(rawAmount, currency.decimals, sigFigs)} {currency.symbol}
|
||||
</HighlightText>
|
||||
) : null
|
||||
}
|
||||
|
||||
const getRawAmounts = (
|
||||
info: ExactInputSwapTransactionInfo | ExactOutputSwapTransactionInfo
|
||||
): { rawAmountFrom: string; rawAmountTo: string } => {
|
||||
return info.tradeType === TradeType.EXACT_INPUT
|
||||
? { rawAmountFrom: info.inputCurrencyAmountRaw, rawAmountTo: info.expectedOutputCurrencyAmountRaw }
|
||||
: { rawAmountFrom: info.expectedInputCurrencyAmountRaw, rawAmountTo: info.outputCurrencyAmountRaw }
|
||||
}
|
||||
|
||||
const SwapSummary = ({
|
||||
info,
|
||||
transactionState,
|
||||
}: {
|
||||
info: ExactInputSwapTransactionInfo | ExactOutputSwapTransactionInfo
|
||||
transactionState: TransactionState
|
||||
}) => {
|
||||
const actionProps = {
|
||||
transactionState,
|
||||
pending: <Trans>Swapping</Trans>,
|
||||
success: <Trans>Swapped</Trans>,
|
||||
failed: <Trans>Swap</Trans>,
|
||||
}
|
||||
const { rawAmountFrom, rawAmountTo } = getRawAmounts(info)
|
||||
|
||||
return (
|
||||
<BodyWrap>
|
||||
<Action {...actionProps} />{' '}
|
||||
<FormattedCurrencyAmount rawAmount={rawAmountFrom} currencyId={info.inputCurrencyId} sigFigs={2} />{' '}
|
||||
<Trans>for </Trans>{' '}
|
||||
<FormattedCurrencyAmount rawAmount={rawAmountTo} currencyId={info.outputCurrencyId} sigFigs={2} />{' '}
|
||||
<FailedText transactionState={transactionState} />
|
||||
</BodyWrap>
|
||||
)
|
||||
}
|
||||
|
||||
const AddLiquidityV3PoolSummary = ({
|
||||
info,
|
||||
transactionState,
|
||||
}: {
|
||||
info: AddLiquidityV3PoolTransactionInfo
|
||||
transactionState: TransactionState
|
||||
}) => {
|
||||
const { createPool, quoteCurrencyId, baseCurrencyId } = info
|
||||
|
||||
const actionProps = {
|
||||
transactionState,
|
||||
pending: <Trans>Adding</Trans>,
|
||||
success: <Trans>Added</Trans>,
|
||||
failed: <Trans>Add</Trans>,
|
||||
}
|
||||
|
||||
return (
|
||||
<BodyWrap>
|
||||
{createPool ? (
|
||||
<CreateV3PoolSummary info={info} transactionState={transactionState} />
|
||||
) : (
|
||||
<>
|
||||
<Action {...actionProps} />{' '}
|
||||
<FormattedCurrencyAmount rawAmount={info.expectedAmountBaseRaw} currencyId={baseCurrencyId} sigFigs={2} />{' '}
|
||||
<Trans>and</Trans>{' '}
|
||||
<FormattedCurrencyAmount rawAmount={info.expectedAmountQuoteRaw} currencyId={quoteCurrencyId} sigFigs={2} />
|
||||
</>
|
||||
)}{' '}
|
||||
<FailedText transactionState={transactionState} />
|
||||
</BodyWrap>
|
||||
)
|
||||
}
|
||||
|
||||
const RemoveLiquidityV3Summary = ({
|
||||
info: { baseCurrencyId, quoteCurrencyId, expectedAmountBaseRaw, expectedAmountQuoteRaw },
|
||||
transactionState,
|
||||
}: {
|
||||
info: RemoveLiquidityV3TransactionInfo
|
||||
transactionState: TransactionState
|
||||
}) => {
|
||||
const actionProps = {
|
||||
transactionState,
|
||||
pending: <Trans>Removing</Trans>,
|
||||
success: <Trans>Removed</Trans>,
|
||||
failed: <Trans>Remove</Trans>,
|
||||
}
|
||||
|
||||
return (
|
||||
<BodyWrap>
|
||||
<Action {...actionProps} />{' '}
|
||||
<FormattedCurrencyAmount rawAmount={expectedAmountBaseRaw} currencyId={baseCurrencyId} sigFigs={2} />{' '}
|
||||
<Trans>and</Trans>{' '}
|
||||
<FormattedCurrencyAmount rawAmount={expectedAmountQuoteRaw} currencyId={quoteCurrencyId} sigFigs={2} />{' '}
|
||||
<FailedText transactionState={transactionState} />
|
||||
</BodyWrap>
|
||||
)
|
||||
}
|
||||
|
||||
const CreateV3PoolSummary = ({
|
||||
info: { baseCurrencyId, quoteCurrencyId },
|
||||
transactionState,
|
||||
}: {
|
||||
info: AddLiquidityV3PoolTransactionInfo
|
||||
transactionState: TransactionState
|
||||
}) => {
|
||||
const baseCurrency = useCurrency(baseCurrencyId)
|
||||
const quoteCurrency = useCurrency(quoteCurrencyId)
|
||||
const actionProps = {
|
||||
transactionState,
|
||||
pending: <Trans>Creating</Trans>,
|
||||
success: <Trans>Created</Trans>,
|
||||
failed: <Trans>Create</Trans>,
|
||||
}
|
||||
|
||||
return (
|
||||
<BodyWrap>
|
||||
<Action {...actionProps} />{' '}
|
||||
<HighlightText>
|
||||
{baseCurrency?.symbol}/{quoteCurrency?.symbol}{' '}
|
||||
</HighlightText>
|
||||
<Trans>Pool</Trans> <FailedText transactionState={transactionState} />
|
||||
</BodyWrap>
|
||||
)
|
||||
}
|
||||
|
||||
const CollectFeesSummary = ({
|
||||
info,
|
||||
transactionState,
|
||||
}: {
|
||||
info: CollectFeesTransactionInfo
|
||||
transactionState: TransactionState
|
||||
}) => {
|
||||
const { currencyId0, expectedCurrencyOwed0 = '0', expectedCurrencyOwed1 = '0', currencyId1 } = info
|
||||
const actionProps = {
|
||||
transactionState,
|
||||
pending: <Trans>Collecting</Trans>,
|
||||
success: <Trans>Collected</Trans>,
|
||||
failed: <Trans>Collect</Trans>,
|
||||
}
|
||||
|
||||
return (
|
||||
<BodyWrap>
|
||||
<Action {...actionProps} />{' '}
|
||||
<FormattedCurrencyAmount rawAmount={expectedCurrencyOwed0} currencyId={currencyId0} sigFigs={2} />{' '}
|
||||
<Trans>and</Trans>{' '}
|
||||
<FormattedCurrencyAmount rawAmount={expectedCurrencyOwed1} currencyId={currencyId1} sigFigs={2} />{' '}
|
||||
<Trans>fees</Trans> <FailedText transactionState={transactionState} />
|
||||
</BodyWrap>
|
||||
)
|
||||
}
|
||||
|
||||
const ApprovalSummary = ({
|
||||
info,
|
||||
transactionState,
|
||||
}: {
|
||||
info: ApproveTransactionInfo
|
||||
transactionState: TransactionState
|
||||
}) => {
|
||||
const token = useToken(info.tokenAddress)
|
||||
const actionProps = {
|
||||
transactionState,
|
||||
pending: <Trans>Approving</Trans>,
|
||||
success: <Trans>Approved</Trans>,
|
||||
failed: <Trans>Approve</Trans>,
|
||||
}
|
||||
|
||||
return (
|
||||
<BodyWrap>
|
||||
<Action {...actionProps} /> <HighlightText>{token?.symbol}</HighlightText>{' '}
|
||||
<FailedText transactionState={transactionState} />
|
||||
</BodyWrap>
|
||||
)
|
||||
}
|
||||
|
||||
const ClaimSummary = ({
|
||||
info: { recipient, uniAmountRaw },
|
||||
transactionState,
|
||||
}: {
|
||||
info: ClaimTransactionInfo
|
||||
transactionState: TransactionState
|
||||
}) => {
|
||||
const { ENSName } = useENSName()
|
||||
const actionProps = {
|
||||
transactionState,
|
||||
pending: <Trans>Claiming</Trans>,
|
||||
success: <Trans>Claimed</Trans>,
|
||||
failed: <Trans>Claim</Trans>,
|
||||
}
|
||||
|
||||
return (
|
||||
<BodyWrap>
|
||||
{uniAmountRaw && (
|
||||
<>
|
||||
<Action {...actionProps} />{' '}
|
||||
<HighlightText>
|
||||
{formatAmount(uniAmountRaw, 18, 4)}
|
||||
UNI{' '}
|
||||
</HighlightText>{' '}
|
||||
<Trans>for</Trans> <HighlightText>{ENSName ?? shortenAddress(recipient)}</HighlightText>
|
||||
</>
|
||||
)}{' '}
|
||||
<FailedText transactionState={transactionState} />
|
||||
</BodyWrap>
|
||||
)
|
||||
}
|
||||
|
||||
const WrapSummary = ({
|
||||
info: { chainId, currencyAmountRaw, unwrapped },
|
||||
transactionState,
|
||||
}: {
|
||||
info: WrapTransactionInfo
|
||||
transactionState: TransactionState
|
||||
}) => {
|
||||
const native = chainId ? nativeOnChain(chainId) : undefined
|
||||
const from = unwrapped ? native?.wrapped.symbol ?? 'WETH' : native?.symbol ?? 'ETH'
|
||||
const to = unwrapped ? native?.symbol ?? 'ETH' : native?.wrapped.symbol ?? 'WETH'
|
||||
|
||||
const amount = formatAmount(currencyAmountRaw, 18, 6)
|
||||
const actionProps = unwrapped
|
||||
? {
|
||||
transactionState,
|
||||
pending: <Trans>Unwrapping</Trans>,
|
||||
success: <Trans>Unwrapped</Trans>,
|
||||
failed: <Trans>Unwrap</Trans>,
|
||||
}
|
||||
: {
|
||||
transactionState,
|
||||
pending: <Trans>Wrapping</Trans>,
|
||||
success: <Trans>Wrapped</Trans>,
|
||||
failed: <Trans>Wrap</Trans>,
|
||||
}
|
||||
|
||||
return (
|
||||
<BodyWrap>
|
||||
<Action {...actionProps} />{' '}
|
||||
<HighlightText>
|
||||
{amount} {from}
|
||||
</HighlightText>{' '}
|
||||
<Trans>to</Trans>{' '}
|
||||
<HighlightText>
|
||||
{amount} {to}
|
||||
</HighlightText>{' '}
|
||||
<FailedText transactionState={transactionState} />
|
||||
</BodyWrap>
|
||||
)
|
||||
}
|
||||
|
||||
const TransactionBody = ({ info, transactionState }: { info: TransactionInfo; transactionState: TransactionState }) => {
|
||||
switch (info.type) {
|
||||
case TransactionType.SWAP:
|
||||
return <SwapSummary info={info} transactionState={transactionState} />
|
||||
case TransactionType.ADD_LIQUIDITY_V3_POOL:
|
||||
return <AddLiquidityV3PoolSummary info={info} transactionState={transactionState} />
|
||||
case TransactionType.REMOVE_LIQUIDITY_V3:
|
||||
return <RemoveLiquidityV3Summary info={info} transactionState={transactionState} />
|
||||
case TransactionType.WRAP:
|
||||
return <WrapSummary info={info} transactionState={transactionState} />
|
||||
case TransactionType.COLLECT_FEES:
|
||||
return <CollectFeesSummary info={info} transactionState={transactionState} />
|
||||
case TransactionType.APPROVAL:
|
||||
return <ApprovalSummary info={info} transactionState={transactionState} />
|
||||
case TransactionType.CLAIM:
|
||||
return <ClaimSummary info={info} transactionState={transactionState} />
|
||||
default:
|
||||
return <span />
|
||||
}
|
||||
}
|
||||
|
||||
export default TransactionBody
|
||||
90
src/components/AccountDetailsV2/index.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { getChainInfoOrDefault } from 'constants/chainInfo'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import { useMemo } from 'react'
|
||||
import { AlertTriangle, CheckCircle } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
import { colors } from 'theme/colors'
|
||||
|
||||
import { TransactionDetails } from '../../state/transactions/types'
|
||||
import Loader from '../Loader'
|
||||
import LogoView from './LogoView'
|
||||
import TransactionBody from './TransactionBody'
|
||||
|
||||
export enum TransactionState {
|
||||
Pending,
|
||||
Success,
|
||||
Failed,
|
||||
}
|
||||
|
||||
const Grid = styled.a`
|
||||
cursor: pointer;
|
||||
display: grid;
|
||||
grid-template-columns: 44px auto 24px;
|
||||
width: 100%;
|
||||
text-decoration: none;
|
||||
border-bottom: ${({ theme }) => `1px solid ${theme.backgroundOutline}`};
|
||||
padding: 12px;
|
||||
|
||||
&:hover {
|
||||
background-color: ${({ theme }) => theme.backgroundModule};
|
||||
transition: 250ms background-color ease;
|
||||
}
|
||||
`
|
||||
|
||||
const TextContainer = styled.span`
|
||||
font-size: 14px;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
color: ${({ theme }) => theme.textTertiary};
|
||||
`
|
||||
|
||||
const IconStyleWrap = styled.span`
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
margin-left: auto;
|
||||
height: 16px;
|
||||
`
|
||||
|
||||
export const TransactionSummary = ({ transactionDetails }: { transactionDetails: TransactionDetails }) => {
|
||||
const { chainId = 1 } = useWeb3React()
|
||||
const tx = transactionDetails
|
||||
const { explorer } = getChainInfoOrDefault(chainId ? chainId : SupportedChainId.MAINNET)
|
||||
const { info, receipt, hash } = tx
|
||||
|
||||
const transactionState = useMemo(() => {
|
||||
const pending = !receipt
|
||||
const success = !pending && tx && (receipt?.status === 1 || typeof receipt?.status === 'undefined')
|
||||
const transactionState = pending
|
||||
? TransactionState.Pending
|
||||
: success
|
||||
? TransactionState.Success
|
||||
: TransactionState.Failed
|
||||
|
||||
return transactionState
|
||||
}, [receipt, tx])
|
||||
|
||||
const link = `${explorer}tx/${hash}`
|
||||
|
||||
return chainId ? (
|
||||
<Grid href={link} target="_blank">
|
||||
<LogoView info={info} />
|
||||
<TextContainer as="span">
|
||||
<TransactionBody info={info} transactionState={transactionState} />
|
||||
</TextContainer>
|
||||
{transactionState === TransactionState.Pending ? (
|
||||
<IconStyleWrap>
|
||||
<Loader />
|
||||
</IconStyleWrap>
|
||||
) : transactionState === TransactionState.Success ? (
|
||||
<IconStyleWrap>
|
||||
<CheckCircle color={colors.green200} size="16px" />
|
||||
</IconStyleWrap>
|
||||
) : (
|
||||
<IconStyleWrap>
|
||||
<AlertTriangle color={colors.gold200} size="16px" />
|
||||
</IconStyleWrap>
|
||||
)}
|
||||
</Grid>
|
||||
) : null
|
||||
}
|
||||
@@ -176,23 +176,28 @@ export const ButtonOutlined = styled(BaseButton)`
|
||||
}
|
||||
`
|
||||
|
||||
export const ButtonYellow = styled(BaseButton)`
|
||||
background-color: ${({ theme }) => theme.deprecated_yellow3};
|
||||
color: white;
|
||||
export const ButtonYellow = styled(BaseButton)<{ redesignFlag?: boolean }>`
|
||||
background-color: ${({ theme, redesignFlag }) => (redesignFlag ? theme.accentWarningSoft : theme.deprecated_yellow3)};
|
||||
color: ${({ theme, redesignFlag }) => (redesignFlag ? theme.accentWarning : 'white')};
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, theme.deprecated_yellow3)};
|
||||
background-color: ${({ theme }) => darken(0.05, theme.deprecated_yellow3)};
|
||||
box-shadow: ${({ theme, redesignFlag }) => !redesignFlag && `0 0 0 1pt ${theme.deprecated_yellow3}`};
|
||||
background-color: ${({ theme, redesignFlag }) =>
|
||||
redesignFlag ? theme.accentWarningSoft : darken(0.05, theme.deprecated_yellow3)};
|
||||
}
|
||||
&:hover {
|
||||
background-color: ${({ theme }) => darken(0.05, theme.deprecated_yellow3)};
|
||||
background: ${({ theme, redesignFlag }) => redesignFlag && theme.stateOverlayHover};
|
||||
mix-blend-mode: ${({ redesignFlag }) => redesignFlag && 'normal'};
|
||||
background-color: ${({ theme, redesignFlag }) => !redesignFlag && darken(0.05, theme.deprecated_yellow3)};
|
||||
}
|
||||
&:active {
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, theme.deprecated_yellow3)};
|
||||
background-color: ${({ theme }) => darken(0.1, theme.deprecated_yellow3)};
|
||||
box-shadow: ${({ theme, redesignFlag }) => !redesignFlag && `0 0 0 1pt ${darken(0.1, theme.deprecated_yellow3)}`};
|
||||
background-color: ${({ theme, redesignFlag }) =>
|
||||
redesignFlag ? theme.accentWarningSoft : darken(0.1, theme.deprecated_yellow3)};
|
||||
}
|
||||
&:disabled {
|
||||
background-color: ${({ theme }) => theme.deprecated_yellow3};
|
||||
opacity: 50%;
|
||||
background-color: ${({ theme, redesignFlag }) =>
|
||||
redesignFlag ? theme.accentWarningSoft : theme.deprecated_yellow3};
|
||||
opacity: ${({ redesignFlag }) => (redesignFlag ? '60%' : '50%')};
|
||||
cursor: auto;
|
||||
}
|
||||
`
|
||||
|
||||
94
src/components/Charts/AnimatedInLineChart.tsx
Normal file
@@ -0,0 +1,94 @@
|
||||
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 AnimatedInLineChartProps<T> = Omit<LineChartProps<T>, 'height' | 'width' | 'children'>
|
||||
|
||||
const config = {
|
||||
duration: 800,
|
||||
easing: easeCubicInOut,
|
||||
}
|
||||
|
||||
// code reference: https://airbnb.io/visx/lineradial
|
||||
|
||||
function AnimatedInLineChart<T>({
|
||||
data,
|
||||
getX,
|
||||
getY,
|
||||
marginTop,
|
||||
curve,
|
||||
color,
|
||||
strokeWidth,
|
||||
}: AnimatedInLineChartProps<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 : 0}
|
||||
fill="none"
|
||||
stroke={lineColor}
|
||||
/>
|
||||
{shouldAnimate && lineLength !== 0 && (
|
||||
<animated.path
|
||||
d={d}
|
||||
strokeWidth={strokeWidth}
|
||||
fill="none"
|
||||
stroke={lineColor}
|
||||
strokeDashoffset={spring.frame.to((v) => v * lineLength)}
|
||||
strokeDasharray={lineLength}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}}
|
||||
</LinePath>
|
||||
</Group>
|
||||
)
|
||||
}
|
||||
|
||||
export default AnimatedInLineChart
|
||||
@@ -1,18 +1,17 @@
|
||||
import { Group } from '@visx/group'
|
||||
import { LinePath } from '@visx/shape'
|
||||
import { CurveFactory } from 'd3'
|
||||
import { radius } from 'd3-curve-circlecorners'
|
||||
import React from 'react'
|
||||
import { ReactNode } from 'react'
|
||||
import { useTheme } from 'styled-components/macro'
|
||||
import { Color } from 'theme/styled'
|
||||
|
||||
interface LineChartProps<T> {
|
||||
export interface LineChartProps<T> {
|
||||
data: T[]
|
||||
getX: (t: T) => number
|
||||
getY: (t: T) => number
|
||||
marginTop?: number
|
||||
curve?: CurveFactory
|
||||
curve: CurveFactory
|
||||
color?: Color
|
||||
strokeWidth: number
|
||||
children?: ReactNode
|
||||
@@ -37,7 +36,7 @@ function LineChart<T>({
|
||||
<svg width={width} height={height}>
|
||||
<Group top={marginTop}>
|
||||
<LinePath
|
||||
curve={curve ?? radius(0.25)}
|
||||
curve={curve}
|
||||
stroke={color ?? theme.accentAction}
|
||||
strokeWidth={strokeWidth}
|
||||
data={data}
|
||||
|
||||
@@ -1,49 +1,78 @@
|
||||
import { scaleLinear } from 'd3'
|
||||
import React from 'react'
|
||||
import { useTheme } from 'styled-components/macro'
|
||||
import { SparkLineLoadingBubble } from 'components/Tokens/TokenTable/TokenRow'
|
||||
import { curveCardinal, scaleLinear } from 'd3'
|
||||
import { PricePoint } from 'graphql/data/Token'
|
||||
import { SparklineMap, TopToken } from 'graphql/data/TopTokens'
|
||||
import { TimePeriod } from 'graphql/data/util'
|
||||
import { memo } from 'react'
|
||||
import styled, { useTheme } from 'styled-components/macro'
|
||||
|
||||
import data from './data.json'
|
||||
import { getPriceBounds } from '../Tokens/TokenDetails/PriceChart'
|
||||
import LineChart from './LineChart'
|
||||
|
||||
type PricePoint = { value: number; timestamp: number }
|
||||
|
||||
function getPriceBounds(pricePoints: PricePoint[]): [number, number] {
|
||||
const prices = pricePoints.map((x) => x.value)
|
||||
const min = Math.min(...prices)
|
||||
const max = Math.max(...prices)
|
||||
return [min, max]
|
||||
}
|
||||
const LoadingContainer = styled.div`
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
`
|
||||
|
||||
interface SparklineChartProps {
|
||||
width: number
|
||||
height: number
|
||||
tokenData: TopToken
|
||||
pricePercentChange: number | undefined | null
|
||||
timePeriod: TimePeriod
|
||||
sparklineMap: SparklineMap
|
||||
}
|
||||
|
||||
function SparklineChart({ width, height }: SparklineChartProps) {
|
||||
function _SparklineChart({
|
||||
width,
|
||||
height,
|
||||
tokenData,
|
||||
pricePercentChange,
|
||||
timePeriod,
|
||||
sparklineMap,
|
||||
}: SparklineChartProps) {
|
||||
const theme = useTheme()
|
||||
// for sparkline
|
||||
const pricePoints = tokenData?.address ? sparklineMap[tokenData.address] : null
|
||||
|
||||
// Don't display if there's one or less pricepoints
|
||||
if (!pricePoints || pricePoints.length <= 1) {
|
||||
return (
|
||||
<LoadingContainer>
|
||||
<SparkLineLoadingBubble />
|
||||
</LoadingContainer>
|
||||
)
|
||||
}
|
||||
|
||||
/* TODO: Implement API calls & cache to use here */
|
||||
const pricePoints = data.day
|
||||
const startingPrice = pricePoints[0]
|
||||
const endingPrice = pricePoints[pricePoints.length - 1]
|
||||
|
||||
const timeScale = scaleLinear().domain([startingPrice.timestamp, endingPrice.timestamp]).range([0, width])
|
||||
const rdScale = scaleLinear().domain(getPriceBounds(pricePoints)).range([height, 0])
|
||||
|
||||
const isPositive = endingPrice.value >= startingPrice.value
|
||||
const widthScale = scaleLinear()
|
||||
.domain(
|
||||
// the range of possible input values
|
||||
[startingPrice.timestamp, endingPrice.timestamp]
|
||||
)
|
||||
.range(
|
||||
// the range of possible output values that the inputs should be transformed to (see https://www.d3indepth.com/scales/ for details)
|
||||
[0, 110]
|
||||
)
|
||||
const rdScale = scaleLinear().domain(getPriceBounds(pricePoints)).range([30, 0])
|
||||
const curveTension = 0.9
|
||||
|
||||
return (
|
||||
<LineChart
|
||||
data={pricePoints}
|
||||
getX={(p: PricePoint) => timeScale(p.timestamp)}
|
||||
getX={(p: PricePoint) => widthScale(p.timestamp)}
|
||||
getY={(p: PricePoint) => rdScale(p.value)}
|
||||
marginTop={0}
|
||||
color={isPositive ? theme.accentSuccess : theme.accentFailure}
|
||||
curve={curveCardinal.tension(curveTension)}
|
||||
marginTop={5}
|
||||
color={pricePercentChange && pricePercentChange < 0 ? theme.accentFailure : theme.accentSuccess}
|
||||
strokeWidth={1.5}
|
||||
width={width}
|
||||
height={height}
|
||||
></LineChart>
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(SparklineChart)
|
||||
export default memo(_SparklineChart)
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Trans } from '@lingui/macro'
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { t } from '@lingui/macro'
|
||||
import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core'
|
||||
import HoverInlineText from 'components/HoverInlineText'
|
||||
import { RedesignVariant, useRedesignFlag } from 'featureFlags/flags/redesign'
|
||||
import { useMemo } from 'react'
|
||||
import { useTheme } from 'styled-components/macro'
|
||||
|
||||
@@ -18,6 +18,7 @@ export function FiatValue({
|
||||
priceImpact?: Percent
|
||||
}) {
|
||||
const theme = useTheme()
|
||||
const redesignFlagEnabled = useRedesignFlag() === RedesignVariant.Enabled
|
||||
const priceImpactColor = useMemo(() => {
|
||||
if (!priceImpact) return undefined
|
||||
if (priceImpact.lessThan('0')) return theme.deprecated_green1
|
||||
@@ -30,19 +31,15 @@ export function FiatValue({
|
||||
const p = Number(fiatValue?.toFixed())
|
||||
const visibleDecimalPlaces = p < 1.05 ? 4 : 2
|
||||
|
||||
const textColor = redesignFlagEnabled
|
||||
? theme.textSecondary
|
||||
: fiatValue
|
||||
? theme.deprecated_text3
|
||||
: theme.deprecated_text4
|
||||
|
||||
return (
|
||||
<ThemedText.DeprecatedBody fontSize={14} color={fiatValue ? theme.deprecated_text3 : theme.deprecated_text4}>
|
||||
{fiatValue ? (
|
||||
<Trans>
|
||||
$
|
||||
<HoverInlineText
|
||||
text={fiatValue?.toFixed(visibleDecimalPlaces, { groupSeparator: ',' })}
|
||||
textColor={fiatValue ? theme.deprecated_text3 : theme.deprecated_text4}
|
||||
/>
|
||||
</Trans>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
<ThemedText.DeprecatedBody fontSize={14} color={textColor}>
|
||||
{fiatValue && <>${fiatValue?.toFixed(visibleDecimalPlaces, { groupSeparator: ',' })}</>}
|
||||
{priceImpact ? (
|
||||
<span style={{ color: priceImpactColor }}>
|
||||
{' '}
|
||||
|
||||
419
src/components/CurrencyInputPanel/SwapCurrencyInputPanel.tsx
Normal file
@@ -0,0 +1,419 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { Currency, CurrencyAmount, Percent, Token } from '@uniswap/sdk-core'
|
||||
import { Pair } from '@uniswap/v2-sdk'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { ElementName, Event, EventName } from 'analytics/constants'
|
||||
import { TraceEvent } from 'analytics/TraceEvent'
|
||||
import { AutoColumn } from 'components/Column'
|
||||
import { LoadingOpacityContainer, loadingOpacityMixin } from 'components/Loader/styled'
|
||||
import { isSupportedChain } from 'constants/chains'
|
||||
import { RedesignVariant, useRedesignFlag } from 'featureFlags/flags/redesign'
|
||||
import { darken } from 'polished'
|
||||
import { ReactNode, useCallback, useState } from 'react'
|
||||
import { Lock } from 'react-feather'
|
||||
import styled, { css, useTheme } from 'styled-components/macro'
|
||||
import { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
|
||||
|
||||
import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg'
|
||||
import { useCurrencyBalance } from '../../state/connection/hooks'
|
||||
import { ThemedText } from '../../theme'
|
||||
import { ButtonGray } from '../Button'
|
||||
import CurrencyLogo from '../CurrencyLogo'
|
||||
import DoubleCurrencyLogo from '../DoubleLogo'
|
||||
import { Input as NumericalInput } from '../NumericalInput'
|
||||
import { RowBetween, RowFixed } from '../Row'
|
||||
import CurrencySearchModal from '../SearchModal/CurrencySearchModal'
|
||||
import { FiatValue } from './FiatValue'
|
||||
|
||||
const InputPanel = styled.div<{ hideInput?: boolean; redesignFlag: boolean }>`
|
||||
${({ theme }) => theme.flexColumnNoWrap}
|
||||
position: relative;
|
||||
border-radius: ${({ hideInput }) => (hideInput ? '16px' : '20px')};
|
||||
background-color: ${({ theme, redesignFlag, hideInput }) =>
|
||||
redesignFlag ? 'transparent' : hideInput ? 'transparent' : theme.deprecated_bg2};
|
||||
z-index: 1;
|
||||
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
|
||||
transition: height 1s ease;
|
||||
will-change: height;
|
||||
`
|
||||
|
||||
const FixedContainer = styled.div<{ redesignFlag: boolean }>`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
border-radius: 20px;
|
||||
background-color: ${({ theme, redesignFlag }) => (redesignFlag ? 'transparent' : theme.deprecated_bg2)};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 2;
|
||||
`
|
||||
|
||||
const Container = styled.div<{ hideInput: boolean; disabled: boolean; redesignFlag: boolean }>`
|
||||
min-height: ${({ redesignFlag }) => redesignFlag && '44px'};
|
||||
border-radius: ${({ hideInput }) => (hideInput ? '16px' : '20px')};
|
||||
border: 1px solid ${({ theme, redesignFlag }) => (redesignFlag ? 'transparent' : theme.deprecated_bg0)};
|
||||
background-color: ${({ theme, redesignFlag }) => (redesignFlag ? 'transparent' : theme.deprecated_bg1)};
|
||||
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
|
||||
${({ theme, hideInput, disabled, redesignFlag }) =>
|
||||
!redesignFlag &&
|
||||
!disabled &&
|
||||
`
|
||||
:focus,
|
||||
:hover {
|
||||
border: 1px solid ${hideInput ? ' transparent' : theme.deprecated_bg3};
|
||||
}
|
||||
`}
|
||||
`
|
||||
|
||||
const CurrencySelect = styled(ButtonGray)<{
|
||||
visible: boolean
|
||||
selected: boolean
|
||||
hideInput?: boolean
|
||||
disabled?: boolean
|
||||
redesignFlag: boolean
|
||||
}>`
|
||||
align-items: center;
|
||||
background-color: ${({ selected, theme, redesignFlag }) =>
|
||||
redesignFlag
|
||||
? selected
|
||||
? theme.backgroundInteractive
|
||||
: theme.accentAction
|
||||
: selected
|
||||
? theme.deprecated_bg2
|
||||
: theme.deprecated_primary1};
|
||||
opacity: ${({ disabled }) => (!disabled ? 1 : 0.4)};
|
||||
box-shadow: ${({ selected }) => (selected ? 'none' : '0px 6px 10px rgba(0, 0, 0, 0.075)')};
|
||||
color: ${({ selected, theme }) => (selected ? theme.deprecated_text1 : theme.deprecated_white)};
|
||||
cursor: pointer;
|
||||
height: ${({ hideInput, redesignFlag }) => (redesignFlag ? 'unset' : hideInput ? '2.8rem' : '2.4rem')};
|
||||
border-radius: 16px;
|
||||
outline: none;
|
||||
user-select: none;
|
||||
border: none;
|
||||
font-size: 24px;
|
||||
font-weight: 400;
|
||||
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
|
||||
padding: ${({ selected, redesignFlag }) =>
|
||||
redesignFlag ? (selected ? '4px 8px 4px 4px' : '6px 6px 6px 8px') : '0 8px'};
|
||||
gap: ${({ redesignFlag }) => (redesignFlag ? '8px' : '0px')};
|
||||
justify-content: space-between;
|
||||
margin-left: ${({ hideInput }) => (hideInput ? '0' : '12px')};
|
||||
|
||||
${({ redesignFlag, selected }) =>
|
||||
!redesignFlag &&
|
||||
css`
|
||||
&:hover {
|
||||
background-color: ${({ theme }) => (selected ? theme.deprecated_bg3 : darken(0.05, theme.deprecated_primary1))};
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: ${({ theme }) => (selected ? theme.deprecated_bg3 : darken(0.05, theme.deprecated_primary1))};
|
||||
}
|
||||
`}
|
||||
|
||||
${({ redesignFlag, selected }) =>
|
||||
redesignFlag &&
|
||||
css`
|
||||
&:hover,
|
||||
&:active {
|
||||
background-color: ${({ theme }) => (selected ? theme.backgroundInteractive : theme.accentAction)};
|
||||
}
|
||||
|
||||
&:before {
|
||||
background-size: 100%;
|
||||
border-radius: inherit;
|
||||
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
content: '';
|
||||
}
|
||||
|
||||
&:hover:before {
|
||||
background-color: ${({ theme }) => theme.stateOverlayHover};
|
||||
}
|
||||
|
||||
&:active:before {
|
||||
background-color: ${({ theme }) => theme.stateOverlayPressed};
|
||||
}
|
||||
`}
|
||||
|
||||
visibility: ${({ visible }) => (visible ? 'visible' : 'hidden')};
|
||||
`
|
||||
|
||||
const InputRow = styled.div<{ selected: boolean; redesignFlag: boolean }>`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: ${({ selected, redesignFlag }) =>
|
||||
redesignFlag ? '0px' : selected ? ' 1rem 1rem 0.75rem 1rem' : '1rem 1rem 1rem 1rem'};
|
||||
`
|
||||
|
||||
const LabelRow = styled.div<{ redesignFlag: boolean }>`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
align-items: center;
|
||||
color: ${({ theme, redesignFlag }) => (redesignFlag ? theme.textSecondary : theme.deprecated_text1)};
|
||||
font-size: 0.75rem;
|
||||
line-height: 1rem;
|
||||
padding: ${({ redesignFlag }) => (redesignFlag ? '0px' : '0 1rem 1rem')};
|
||||
|
||||
span:hover {
|
||||
cursor: pointer;
|
||||
color: ${({ theme }) => darken(0.2, theme.deprecated_text2)};
|
||||
}
|
||||
`
|
||||
|
||||
const FiatRow = styled(LabelRow)<{ redesignFlag: boolean }>`
|
||||
justify-content: flex-end;
|
||||
min-height: ${({ redesignFlag }) => redesignFlag && '20px'};
|
||||
padding: ${({ redesignFlag }) => redesignFlag && '8px 0px 0px 0px'};
|
||||
height: ${({ redesignFlag }) => !redesignFlag && '24px'};
|
||||
`
|
||||
|
||||
const Aligner = styled.span`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
const StyledDropDown = styled(DropDown)<{ selected: boolean; redesignFlag: boolean }>`
|
||||
margin: 0 0.25rem 0 0.35rem;
|
||||
height: 35%;
|
||||
margin-left: ${({ redesignFlag }) => redesignFlag && '8px'};
|
||||
|
||||
path {
|
||||
stroke: ${({ selected, theme }) => (selected ? theme.deprecated_text1 : theme.deprecated_white)};
|
||||
stroke-width: ${({ redesignFlag }) => (redesignFlag ? '2px' : '1.5px')};
|
||||
}
|
||||
`
|
||||
|
||||
const StyledTokenName = styled.span<{ active?: boolean; redesignFlag: boolean }>`
|
||||
${({ active }) => (active ? ' margin: 0 0.25rem 0 0.25rem;' : ' margin: 0 0.25rem 0 0.25rem;')}
|
||||
font-size: ${({ redesignFlag }) => (redesignFlag ? '20px' : '18px')};
|
||||
font-weight: ${({ redesignFlag }) => (redesignFlag ? '600' : '500')};
|
||||
`
|
||||
|
||||
const StyledBalanceMax = styled.button<{ disabled?: boolean; redesignFlag: boolean }>`
|
||||
background-color: transparent;
|
||||
background-color: ${({ theme, redesignFlag }) => !redesignFlag && theme.deprecated_primary5};
|
||||
border: none;
|
||||
text-transform: ${({ redesignFlag }) => !redesignFlag && 'uppercase'};
|
||||
border-radius: ${({ redesignFlag }) => !redesignFlag && '12px'};
|
||||
color: ${({ theme, redesignFlag }) => (redesignFlag ? theme.accentAction : theme.deprecated_primary1)};
|
||||
cursor: pointer;
|
||||
font-size: ${({ redesignFlag }) => (redesignFlag ? '14px' : '11px')};
|
||||
font-weight: ${({ redesignFlag }) => (redesignFlag ? '600' : '500')};
|
||||
margin-left: ${({ redesignFlag }) => (redesignFlag ? '0px' : '0.25rem')};
|
||||
opacity: ${({ disabled }) => (!disabled ? 1 : 0.4)};
|
||||
padding: 4px 6px;
|
||||
pointer-events: ${({ disabled }) => (!disabled ? 'initial' : 'none')};
|
||||
|
||||
:hover {
|
||||
opacity: ${({ disabled }) => (!disabled ? 0.8 : 0.4)};
|
||||
}
|
||||
|
||||
:focus {
|
||||
outline: none;
|
||||
}
|
||||
`
|
||||
|
||||
const StyledNumericalInput = styled(NumericalInput)<{ $loading: boolean; redesignFlag: boolean }>`
|
||||
${loadingOpacityMixin};
|
||||
text-align: left;
|
||||
font-size: ${({ redesignFlag }) => redesignFlag && '36px'};
|
||||
line-height: ${({ redesignFlag }) => redesignFlag && '44px'};
|
||||
font-variant: ${({ redesignFlag }) => redesignFlag && 'small-caps'};
|
||||
`
|
||||
|
||||
interface SwapCurrencyInputPanelProps {
|
||||
value: string
|
||||
onUserInput: (value: string) => void
|
||||
onMax?: () => void
|
||||
showMaxButton: boolean
|
||||
label?: ReactNode
|
||||
onCurrencySelect?: (currency: Currency) => void
|
||||
currency?: Currency | null
|
||||
hideBalance?: boolean
|
||||
pair?: Pair | null
|
||||
hideInput?: boolean
|
||||
otherCurrency?: Currency | null
|
||||
fiatValue?: CurrencyAmount<Token> | null
|
||||
priceImpact?: Percent
|
||||
id: string
|
||||
showCommonBases?: boolean
|
||||
showCurrencyAmount?: boolean
|
||||
disableNonToken?: boolean
|
||||
renderBalance?: (amount: CurrencyAmount<Currency>) => ReactNode
|
||||
locked?: boolean
|
||||
loading?: boolean
|
||||
}
|
||||
|
||||
export default function SwapCurrencyInputPanel({
|
||||
value,
|
||||
onUserInput,
|
||||
onMax,
|
||||
showMaxButton,
|
||||
onCurrencySelect,
|
||||
currency,
|
||||
otherCurrency,
|
||||
id,
|
||||
showCommonBases,
|
||||
showCurrencyAmount,
|
||||
disableNonToken,
|
||||
renderBalance,
|
||||
fiatValue,
|
||||
priceImpact,
|
||||
hideBalance = false,
|
||||
pair = null, // used for double token logo
|
||||
hideInput = false,
|
||||
locked = false,
|
||||
loading = false,
|
||||
...rest
|
||||
}: SwapCurrencyInputPanelProps) {
|
||||
const [modalOpen, setModalOpen] = useState(false)
|
||||
const { account, chainId } = useWeb3React()
|
||||
const redesignFlag = useRedesignFlag()
|
||||
const redesignFlagEnabled = redesignFlag === RedesignVariant.Enabled
|
||||
const selectedCurrencyBalance = useCurrencyBalance(account ?? undefined, currency ?? undefined)
|
||||
const theme = useTheme()
|
||||
|
||||
const handleDismissSearch = useCallback(() => {
|
||||
setModalOpen(false)
|
||||
}, [setModalOpen])
|
||||
|
||||
const chainAllowed = isSupportedChain(chainId)
|
||||
|
||||
return (
|
||||
<InputPanel id={id} hideInput={hideInput} {...rest} redesignFlag={redesignFlagEnabled}>
|
||||
{locked && (
|
||||
<FixedContainer redesignFlag={redesignFlagEnabled}>
|
||||
<AutoColumn gap="sm" justify="center">
|
||||
<Lock />
|
||||
<ThemedText.DeprecatedLabel fontSize="12px" textAlign="center" padding="0 12px">
|
||||
<Trans>The market price is outside your specified price range. Single-asset deposit only.</Trans>
|
||||
</ThemedText.DeprecatedLabel>
|
||||
</AutoColumn>
|
||||
</FixedContainer>
|
||||
)}
|
||||
<Container hideInput={hideInput} disabled={!chainAllowed} redesignFlag={redesignFlagEnabled}>
|
||||
<InputRow
|
||||
style={hideInput ? { padding: '0', borderRadius: '8px' } : {}}
|
||||
selected={!onCurrencySelect}
|
||||
redesignFlag={redesignFlagEnabled}
|
||||
>
|
||||
{!hideInput && (
|
||||
<StyledNumericalInput
|
||||
className="token-amount-input"
|
||||
value={value}
|
||||
onUserInput={onUserInput}
|
||||
disabled={!chainAllowed}
|
||||
$loading={loading}
|
||||
redesignFlag={redesignFlagEnabled}
|
||||
/>
|
||||
)}
|
||||
|
||||
<CurrencySelect
|
||||
disabled={!chainAllowed}
|
||||
visible={currency !== undefined}
|
||||
selected={!!currency}
|
||||
hideInput={hideInput}
|
||||
redesignFlag={redesignFlagEnabled}
|
||||
className="open-currency-select-button"
|
||||
onClick={() => {
|
||||
if (onCurrencySelect) {
|
||||
setModalOpen(true)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Aligner>
|
||||
<RowFixed>
|
||||
{pair ? (
|
||||
<span style={{ marginRight: '0.5rem' }}>
|
||||
<DoubleCurrencyLogo currency0={pair.token0} currency1={pair.token1} size={24} margin={true} />
|
||||
</span>
|
||||
) : currency ? (
|
||||
<CurrencyLogo style={{ marginRight: '2px' }} currency={currency} size={'24px'} />
|
||||
) : null}
|
||||
{pair ? (
|
||||
<StyledTokenName className="pair-name-container" redesignFlag={redesignFlagEnabled}>
|
||||
{pair?.token0.symbol}:{pair?.token1.symbol}
|
||||
</StyledTokenName>
|
||||
) : (
|
||||
<StyledTokenName
|
||||
className="token-symbol-container"
|
||||
active={Boolean(currency && currency.symbol)}
|
||||
redesignFlag={redesignFlagEnabled}
|
||||
>
|
||||
{(currency && currency.symbol && currency.symbol.length > 20
|
||||
? currency.symbol.slice(0, 4) +
|
||||
'...' +
|
||||
currency.symbol.slice(currency.symbol.length - 5, currency.symbol.length)
|
||||
: currency?.symbol) || <Trans>Select token</Trans>}
|
||||
</StyledTokenName>
|
||||
)}
|
||||
</RowFixed>
|
||||
{onCurrencySelect && <StyledDropDown selected={!!currency} redesignFlag={redesignFlagEnabled} />}
|
||||
</Aligner>
|
||||
</CurrencySelect>
|
||||
</InputRow>
|
||||
{!hideInput && !hideBalance && currency && (
|
||||
<FiatRow redesignFlag={redesignFlagEnabled}>
|
||||
<RowBetween>
|
||||
<LoadingOpacityContainer $loading={loading}>
|
||||
<FiatValue fiatValue={fiatValue} priceImpact={priceImpact} />
|
||||
</LoadingOpacityContainer>
|
||||
{account ? (
|
||||
<RowFixed style={{ height: '17px' }}>
|
||||
<ThemedText.DeprecatedBody
|
||||
color={redesignFlag ? theme.textSecondary : theme.deprecated_text3}
|
||||
fontWeight={redesignFlag ? 400 : 500}
|
||||
fontSize={14}
|
||||
style={{ display: 'inline' }}
|
||||
>
|
||||
{!hideBalance && currency && selectedCurrencyBalance ? (
|
||||
renderBalance ? (
|
||||
renderBalance(selectedCurrencyBalance)
|
||||
) : (
|
||||
<Trans>Balance: {formatCurrencyAmount(selectedCurrencyBalance, 4)}</Trans>
|
||||
)
|
||||
) : null}
|
||||
</ThemedText.DeprecatedBody>
|
||||
{showMaxButton && selectedCurrencyBalance ? (
|
||||
<TraceEvent
|
||||
events={[Event.onClick]}
|
||||
name={EventName.SWAP_MAX_TOKEN_AMOUNT_SELECTED}
|
||||
element={ElementName.MAX_TOKEN_AMOUNT_BUTTON}
|
||||
>
|
||||
<StyledBalanceMax onClick={onMax} redesignFlag={redesignFlagEnabled}>
|
||||
<Trans>Max</Trans>
|
||||
</StyledBalanceMax>
|
||||
</TraceEvent>
|
||||
) : null}
|
||||
</RowFixed>
|
||||
) : (
|
||||
<span />
|
||||
)}
|
||||
</RowBetween>
|
||||
</FiatRow>
|
||||
)}
|
||||
</Container>
|
||||
{onCurrencySelect && (
|
||||
<CurrencySearchModal
|
||||
isOpen={modalOpen}
|
||||
onDismiss={handleDismissSearch}
|
||||
onCurrencySelect={onCurrencySelect}
|
||||
selectedCurrency={currency}
|
||||
otherSelectedCurrency={otherCurrency}
|
||||
showCommonBases={showCommonBases}
|
||||
showCurrencyAmount={showCurrencyAmount}
|
||||
disableNonToken={disableNonToken}
|
||||
/>
|
||||
)}
|
||||
</InputPanel>
|
||||
)
|
||||
}
|
||||
@@ -2,8 +2,8 @@ import { Trans } from '@lingui/macro'
|
||||
import { Currency, CurrencyAmount, Percent, Token } from '@uniswap/sdk-core'
|
||||
import { Pair } from '@uniswap/v2-sdk'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { ElementName, Event, EventName } from 'components/AmplitudeAnalytics/constants'
|
||||
import { TraceEvent } from 'components/AmplitudeAnalytics/TraceEvent'
|
||||
import { ElementName, Event, EventName } from 'analytics/constants'
|
||||
import { TraceEvent } from 'analytics/TraceEvent'
|
||||
import { AutoColumn } from 'components/Column'
|
||||
import { LoadingOpacityContainer, loadingOpacityMixin } from 'components/Loader/styled'
|
||||
import { isSupportedChain } from 'constants/chains'
|
||||
@@ -25,37 +25,35 @@ import { RowBetween, RowFixed } from '../Row'
|
||||
import CurrencySearchModal from '../SearchModal/CurrencySearchModal'
|
||||
import { FiatValue } from './FiatValue'
|
||||
|
||||
const InputPanel = styled.div<{ hideInput?: boolean; redesignFlag: boolean }>`
|
||||
const InputPanel = styled.div<{ hideInput?: boolean }>`
|
||||
${({ theme }) => theme.flexColumnNoWrap}
|
||||
position: relative;
|
||||
border-radius: ${({ hideInput }) => (hideInput ? '16px' : '20px')};
|
||||
background-color: ${({ theme, redesignFlag, hideInput }) =>
|
||||
redesignFlag ? 'transparent' : hideInput ? 'transparent' : theme.deprecated_bg2};
|
||||
background-color: ${({ theme, hideInput }) => (hideInput ? 'transparent' : theme.deprecated_bg2)};
|
||||
z-index: 1;
|
||||
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
|
||||
transition: height 1s ease;
|
||||
will-change: height;
|
||||
`
|
||||
|
||||
const FixedContainer = styled.div<{ redesignFlag: boolean }>`
|
||||
const FixedContainer = styled.div`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
border-radius: 20px;
|
||||
background-color: ${({ theme, redesignFlag }) => (redesignFlag ? 'transparent' : theme.deprecated_bg2)};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg2};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 2;
|
||||
`
|
||||
|
||||
const Container = styled.div<{ hideInput: boolean; disabled: boolean; redesignFlag: boolean }>`
|
||||
const Container = styled.div<{ hideInput: boolean; disabled: boolean }>`
|
||||
border-radius: ${({ hideInput }) => (hideInput ? '16px' : '20px')};
|
||||
border: 1px solid ${({ theme, redesignFlag }) => (redesignFlag ? 'transparent' : theme.deprecated_bg0)};
|
||||
background-color: ${({ theme, redesignFlag }) => (redesignFlag ? 'transparent' : theme.deprecated_bg1)};
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_bg0};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg1};
|
||||
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
|
||||
${({ theme, hideInput, disabled, redesignFlag }) =>
|
||||
!redesignFlag &&
|
||||
${({ theme, hideInput, disabled }) =>
|
||||
!disabled &&
|
||||
`
|
||||
:focus,
|
||||
@@ -70,65 +68,38 @@ const CurrencySelect = styled(ButtonGray)<{
|
||||
selected: boolean
|
||||
hideInput?: boolean
|
||||
disabled?: boolean
|
||||
redesignFlag: boolean
|
||||
}>`
|
||||
align-items: center;
|
||||
background-color: ${({ selected, theme, redesignFlag }) =>
|
||||
redesignFlag
|
||||
? selected
|
||||
? theme.backgroundSurface
|
||||
: theme.accentAction
|
||||
: selected
|
||||
? theme.deprecated_bg2
|
||||
: theme.deprecated_primary1};
|
||||
background-color: ${({ selected, theme }) => (selected ? theme.deprecated_bg2 : theme.deprecated_primary1)};
|
||||
opacity: ${({ disabled }) => (!disabled ? 1 : 0.4)};
|
||||
box-shadow: ${({ selected }) => (selected ? 'none' : '0px 6px 10px rgba(0, 0, 0, 0.075)')};
|
||||
box-shadow: 0px 6px 10px rgba(0, 0, 0, 0.075);
|
||||
color: ${({ selected, theme }) => (selected ? theme.deprecated_text1 : theme.deprecated_white)};
|
||||
cursor: pointer;
|
||||
height: ${({ hideInput, redesignFlag }) => (redesignFlag ? 'unset' : hideInput ? '2.8rem' : '2.4rem')};
|
||||
border-radius: 16px;
|
||||
outline: none;
|
||||
user-select: none;
|
||||
border: none;
|
||||
font-size: 24px;
|
||||
font-weight: 400;
|
||||
font-weight: 500;
|
||||
height: ${({ hideInput }) => (hideInput ? '2.8rem' : '2.4rem')};
|
||||
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
|
||||
padding: ${({ selected, redesignFlag }) =>
|
||||
redesignFlag ? (selected ? '4px 8px 4px 4px' : '6px 6px 6px 8px') : '0 8px'};
|
||||
gap: ${({ redesignFlag }) => (redesignFlag ? '8px' : '0px')};
|
||||
padding: 0 8px;
|
||||
justify-content: space-between;
|
||||
margin-left: ${({ hideInput }) => (hideInput ? '0' : '12px')};
|
||||
:focus,
|
||||
:hover {
|
||||
background-color: ${({ selected, theme, redesignFlag }) =>
|
||||
selected
|
||||
? redesignFlag
|
||||
? theme.backgroundSurface
|
||||
: theme.deprecated_bg3
|
||||
: darken(0.05, theme.deprecated_primary1)};
|
||||
background-color: ${({ selected, theme }) =>
|
||||
selected ? theme.deprecated_bg3 : darken(0.05, theme.deprecated_primary1)};
|
||||
}
|
||||
visibility: ${({ visible }) => (visible ? 'visible' : 'hidden')};
|
||||
`
|
||||
const InputCurrencySelect = styled(CurrencySelect)<{ redesignFlag: boolean }>`
|
||||
background-color: ${({ theme, selected, redesignFlag }) =>
|
||||
redesignFlag && (selected ? theme.backgroundModule : theme.accentAction)};
|
||||
:focus,
|
||||
:hover {
|
||||
background-color: ${({ selected, theme, redesignFlag }) =>
|
||||
selected
|
||||
? redesignFlag
|
||||
? theme.backgroundInteractive
|
||||
: theme.deprecated_bg3
|
||||
: darken(0.05, theme.deprecated_primary1)};
|
||||
}
|
||||
`
|
||||
|
||||
const InputRow = styled.div<{ selected: boolean; redesignFlag: boolean }>`
|
||||
const InputRow = styled.div<{ selected: boolean }>`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: ${({ selected, redesignFlag }) =>
|
||||
redesignFlag ? '0px' : selected ? ' 1rem 1rem 0.75rem 1rem' : '1rem 1rem 1rem 1rem'};
|
||||
padding: ${({ selected }) => (selected ? ' 1rem 1rem 0.75rem 1rem' : '1rem 1rem 1rem 1rem')};
|
||||
`
|
||||
|
||||
const LabelRow = styled.div`
|
||||
@@ -138,7 +109,6 @@ const LabelRow = styled.div`
|
||||
font-size: 0.75rem;
|
||||
line-height: 1rem;
|
||||
padding: 0 1rem 1rem;
|
||||
|
||||
span:hover {
|
||||
cursor: pointer;
|
||||
color: ${({ theme }) => darken(0.2, theme.deprecated_text2)};
|
||||
@@ -147,20 +117,8 @@ const LabelRow = styled.div`
|
||||
|
||||
const FiatRow = styled(LabelRow)<{ redesignFlag: boolean }>`
|
||||
justify-content: flex-end;
|
||||
padding: ${({ redesignFlag }) => redesignFlag && '8px 0px'};
|
||||
height: ${({ redesignFlag }) => !redesignFlag && '24px'};
|
||||
`
|
||||
|
||||
const NoBalanceState = styled.div`
|
||||
color: ${({ theme }) => theme.textTertiary};
|
||||
font-weight: 400;
|
||||
justify-content: space-between;
|
||||
padding: 0px 4px;
|
||||
`
|
||||
const NoBalanceDash = styled.span`
|
||||
color: ${({ theme }) => theme.textTertiary};
|
||||
font-variant: small-caps;
|
||||
font-feature-settings: 'pnum' on, 'lnum' on;
|
||||
padding: ${({ redesignFlag }) => redesignFlag && '0px 1rem 0.75rem'};
|
||||
height: ${({ redesignFlag }) => (redesignFlag ? '32px' : '16px')};
|
||||
`
|
||||
|
||||
const Aligner = styled.span`
|
||||
@@ -170,34 +128,31 @@ const Aligner = styled.span`
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
const StyledDropDown = styled(DropDown)<{ selected: boolean; redesignFlag: boolean }>`
|
||||
const StyledDropDown = styled(DropDown)<{ selected: boolean }>`
|
||||
margin: 0 0.25rem 0 0.35rem;
|
||||
height: 35%;
|
||||
margin-left: ${({ redesignFlag }) => redesignFlag && '8px'};
|
||||
|
||||
path {
|
||||
stroke: ${({ selected, theme }) => (selected ? theme.deprecated_text1 : theme.deprecated_white)};
|
||||
stroke-width: ${({ redesignFlag }) => (redesignFlag ? '2px' : '1.5px')};
|
||||
stroke-width: 1.5px;
|
||||
}
|
||||
`
|
||||
|
||||
const StyledTokenName = styled.span<{ active?: boolean; redesignFlag: boolean }>`
|
||||
const StyledTokenName = styled.span<{ active?: boolean }>`
|
||||
${({ active }) => (active ? ' margin: 0 0.25rem 0 0.25rem;' : ' margin: 0 0.25rem 0 0.25rem;')}
|
||||
font-size: ${({ active }) => (active ? '18px' : '18px')};
|
||||
font-weight: ${({ redesignFlag }) => (redesignFlag ? '600' : '500')};
|
||||
font-size: 20px;
|
||||
`
|
||||
|
||||
const StyledBalanceMax = styled.button<{ disabled?: boolean; redesignFlag: boolean }>`
|
||||
const StyledBalanceMax = styled.button<{ disabled?: boolean }>`
|
||||
background-color: transparent;
|
||||
background-color: ${({ theme, redesignFlag }) => !redesignFlag && theme.deprecated_primary5};
|
||||
background-color: ${({ theme }) => theme.deprecated_primary5};
|
||||
border: none;
|
||||
text-transform: ${({ redesignFlag }) => !redesignFlag && 'uppercase'};
|
||||
border-radius: ${({ redesignFlag }) => !redesignFlag && '12px'};
|
||||
color: ${({ theme, redesignFlag }) => (redesignFlag ? theme.accentAction : theme.deprecated_primary1)};
|
||||
border-radius: 12px;
|
||||
color: ${({ theme }) => theme.deprecated_primary1};
|
||||
cursor: pointer;
|
||||
font-size: ${({ redesignFlag }) => (redesignFlag ? '14px' : '11px')};
|
||||
font-weight: ${({ redesignFlag }) => (redesignFlag ? '600' : '500')};
|
||||
margin-left: ${({ redesignFlag }) => (redesignFlag ? '0px' : '0.25rem')};
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
margin-left: 0.25rem;
|
||||
opacity: ${({ disabled }) => (!disabled ? 1 : 0.4)};
|
||||
padding: 4px 6px;
|
||||
pointer-events: ${({ disabled }) => (!disabled ? 'initial' : 'none')};
|
||||
@@ -211,11 +166,9 @@ const StyledBalanceMax = styled.button<{ disabled?: boolean; redesignFlag: boole
|
||||
}
|
||||
`
|
||||
|
||||
const StyledNumericalInput = styled(NumericalInput)<{ $loading: boolean; redesignFlag: boolean }>`
|
||||
const StyledNumericalInput = styled(NumericalInput)<{ $loading: boolean }>`
|
||||
${loadingOpacityMixin};
|
||||
text-align: left;
|
||||
font-variant: ${({ redesignFlag }) => redesignFlag && 'small-caps'};
|
||||
font-feature-settings: ${({ redesignFlag }) => redesignFlag && 'pnum on, lnum on'};
|
||||
`
|
||||
|
||||
interface CurrencyInputPanelProps {
|
||||
@@ -265,10 +218,10 @@ export default function CurrencyInputPanel({
|
||||
}: CurrencyInputPanelProps) {
|
||||
const [modalOpen, setModalOpen] = useState(false)
|
||||
const { account, chainId } = useWeb3React()
|
||||
const redesignFlag = useRedesignFlag()
|
||||
const redesignFlagEnabled = redesignFlag === RedesignVariant.Enabled
|
||||
const selectedCurrencyBalance = useCurrencyBalance(account ?? undefined, currency ?? undefined)
|
||||
const theme = useTheme()
|
||||
const redesignFlag = useRedesignFlag()
|
||||
const redesignFlagEnabled = redesignFlag === RedesignVariant.Enabled
|
||||
|
||||
const handleDismissSearch = useCallback(() => {
|
||||
setModalOpen(false)
|
||||
@@ -277,9 +230,9 @@ export default function CurrencyInputPanel({
|
||||
const chainAllowed = isSupportedChain(chainId)
|
||||
|
||||
return (
|
||||
<InputPanel id={id} hideInput={hideInput} {...rest} redesignFlag={redesignFlagEnabled}>
|
||||
<InputPanel id={id} hideInput={hideInput} {...rest}>
|
||||
{locked && (
|
||||
<FixedContainer redesignFlag={redesignFlagEnabled}>
|
||||
<FixedContainer>
|
||||
<AutoColumn gap="sm" justify="center">
|
||||
<Lock />
|
||||
<ThemedText.DeprecatedLabel fontSize="12px" textAlign="center" padding="0 12px">
|
||||
@@ -288,12 +241,8 @@ export default function CurrencyInputPanel({
|
||||
</AutoColumn>
|
||||
</FixedContainer>
|
||||
)}
|
||||
<Container hideInput={hideInput} disabled={!chainAllowed} redesignFlag={redesignFlagEnabled}>
|
||||
<InputRow
|
||||
style={hideInput ? { padding: '0', borderRadius: '8px' } : {}}
|
||||
selected={!onCurrencySelect}
|
||||
redesignFlag={redesignFlagEnabled}
|
||||
>
|
||||
<Container hideInput={hideInput} disabled={!chainAllowed}>
|
||||
<InputRow style={hideInput ? { padding: '0', borderRadius: '8px' } : {}} selected={!onCurrencySelect}>
|
||||
{!hideInput && (
|
||||
<StyledNumericalInput
|
||||
className="token-amount-input"
|
||||
@@ -301,16 +250,14 @@ export default function CurrencyInputPanel({
|
||||
onUserInput={onUserInput}
|
||||
disabled={!chainAllowed}
|
||||
$loading={loading}
|
||||
redesignFlag={redesignFlagEnabled}
|
||||
/>
|
||||
)}
|
||||
|
||||
<InputCurrencySelect
|
||||
<CurrencySelect
|
||||
disabled={!chainAllowed}
|
||||
visible={currency !== undefined}
|
||||
selected={!!currency}
|
||||
hideInput={hideInput}
|
||||
redesignFlag={redesignFlagEnabled}
|
||||
className="open-currency-select-button"
|
||||
onClick={() => {
|
||||
if (onCurrencySelect) {
|
||||
@@ -325,40 +272,26 @@ export default function CurrencyInputPanel({
|
||||
<DoubleCurrencyLogo currency0={pair.token0} currency1={pair.token1} size={24} margin={true} />
|
||||
</span>
|
||||
) : currency ? (
|
||||
<CurrencyLogo style={{ marginRight: '2px' }} currency={currency} size={'24px'} />
|
||||
<CurrencyLogo style={{ marginRight: '0.5rem' }} currency={currency} size={'24px'} />
|
||||
) : null}
|
||||
{pair ? (
|
||||
<StyledTokenName className="pair-name-container" redesignFlag={redesignFlagEnabled}>
|
||||
<StyledTokenName className="pair-name-container">
|
||||
{pair?.token0.symbol}:{pair?.token1.symbol}
|
||||
</StyledTokenName>
|
||||
) : (
|
||||
<StyledTokenName
|
||||
className="token-symbol-container"
|
||||
active={Boolean(currency && currency.symbol)}
|
||||
redesignFlag={redesignFlagEnabled}
|
||||
>
|
||||
<StyledTokenName className="token-symbol-container" active={Boolean(currency && currency.symbol)}>
|
||||
{(currency && currency.symbol && currency.symbol.length > 20
|
||||
? currency.symbol.slice(0, 4) +
|
||||
'...' +
|
||||
currency.symbol.slice(currency.symbol.length - 5, currency.symbol.length)
|
||||
: currency?.symbol) || <Trans>Select token</Trans>}
|
||||
: currency?.symbol) || <Trans>Select a token</Trans>}
|
||||
</StyledTokenName>
|
||||
)}
|
||||
</RowFixed>
|
||||
{onCurrencySelect && <StyledDropDown selected={!!currency} redesignFlag={redesignFlagEnabled} />}
|
||||
{onCurrencySelect && <StyledDropDown selected={!!currency} />}
|
||||
</Aligner>
|
||||
</InputCurrencySelect>
|
||||
</CurrencySelect>
|
||||
</InputRow>
|
||||
{redesignFlagEnabled && !currency && (
|
||||
<NoBalanceState>
|
||||
<FiatRow redesignFlag={redesignFlagEnabled}>
|
||||
<RowBetween>
|
||||
<NoBalanceDash>-</NoBalanceDash>
|
||||
<NoBalanceDash>-</NoBalanceDash>
|
||||
</RowBetween>
|
||||
</FiatRow>
|
||||
</NoBalanceState>
|
||||
)}
|
||||
{!hideInput && !hideBalance && currency && (
|
||||
<FiatRow redesignFlag={redesignFlagEnabled}>
|
||||
<RowBetween>
|
||||
@@ -388,8 +321,8 @@ export default function CurrencyInputPanel({
|
||||
name={EventName.SWAP_MAX_TOKEN_AMOUNT_SELECTED}
|
||||
element={ElementName.MAX_TOKEN_AMOUNT_BUTTON}
|
||||
>
|
||||
<StyledBalanceMax onClick={onMax} redesignFlag={redesignFlagEnabled}>
|
||||
<Trans>Max</Trans>
|
||||
<StyledBalanceMax onClick={onMax}>
|
||||
<Trans>MAX</Trans>
|
||||
</StyledBalanceMax>
|
||||
</TraceEvent>
|
||||
) : null}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import useCurrencyLogoURIs from 'lib/hooks/useCurrencyLogoURIs'
|
||||
import React from 'react'
|
||||
import React, { useMemo } from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import Logo from '../Logo'
|
||||
@@ -24,18 +24,25 @@ const StyledNativeLogo = styled(StyledLogo)`
|
||||
|
||||
export default function CurrencyLogo({
|
||||
currency,
|
||||
symbol,
|
||||
size = '24px',
|
||||
style,
|
||||
src,
|
||||
...rest
|
||||
}: {
|
||||
currency?: Currency | null
|
||||
symbol?: string | null
|
||||
size?: string
|
||||
style?: React.CSSProperties
|
||||
src?: string | null
|
||||
}) {
|
||||
const logoURIs = useCurrencyLogoURIs(currency)
|
||||
const srcs = useMemo(() => (src ? [src] : logoURIs), [src, logoURIs])
|
||||
const props = {
|
||||
alt: `${currency?.symbol ?? 'token'} logo`,
|
||||
size,
|
||||
srcs: useCurrencyLogoURIs(currency),
|
||||
srcs,
|
||||
symbol: symbol ?? currency?.symbol,
|
||||
style,
|
||||
...rest,
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ interface DoubleCurrencyLogoProps {
|
||||
}
|
||||
|
||||
const HigherLogo = styled(CurrencyLogo)`
|
||||
z-index: 2;
|
||||
z-index: 1;
|
||||
`
|
||||
const CoveredLogo = styled(CurrencyLogo)<{ sizeraw: number }>`
|
||||
position: absolute;
|
||||
|
||||
@@ -49,7 +49,7 @@ export default function DowntimeWarning() {
|
||||
|
||||
switch (chainId) {
|
||||
case SupportedChainId.OPTIMISM:
|
||||
case SupportedChainId.OPTIMISTIC_KOVAN:
|
||||
case SupportedChainId.OPTIMISM_GOERLI:
|
||||
return (
|
||||
<Wrapper>
|
||||
<Trans>
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { FeatureFlag, featureFlagSettings, useUpdateFlag } from 'featureFlags'
|
||||
import { NavBarVariant, useNavBarFlag } from 'featureFlags/flags/navBar'
|
||||
import { Phase1Variant, usePhase1Flag } from 'featureFlags/flags/phase1'
|
||||
import { RedesignVariant, useRedesignFlag } from 'featureFlags/flags/redesign'
|
||||
import { TokensVariant, useTokensFlag } from 'featureFlags/flags/tokens'
|
||||
import { TokenSafetyVariant, useTokenSafetyFlag } from 'featureFlags/flags/tokenSafety'
|
||||
import { useAtomValue } from 'jotai/utils'
|
||||
import { ReactNode, useState } from 'react'
|
||||
import { BaseVariant, FeatureFlag, featureFlagSettings, useUpdateFlag } from 'featureFlags'
|
||||
import { FavoriteTokensVariant, useFavoriteTokensFlag } from 'featureFlags/flags/favoriteTokens'
|
||||
import { NftVariant, useNftFlag } from 'featureFlags/flags/nft'
|
||||
import { TraceJsonRpcVariant, useTraceJsonRpcFlag } from 'featureFlags/flags/traceJsonRpc'
|
||||
import { useAtomValue, useUpdateAtom } from 'jotai/utils'
|
||||
import { Children, PropsWithChildren, ReactElement, ReactNode, useCallback, useState } from 'react'
|
||||
import { X } from 'react-feather'
|
||||
import { useModalIsOpen, useToggleFeatureFlags } from 'state/application/hooks'
|
||||
import { ApplicationModal } from 'state/application/reducer'
|
||||
@@ -44,7 +42,14 @@ const Row = styled.div`
|
||||
|
||||
const CloseButton = styled.button`
|
||||
cursor: pointer;
|
||||
background: 'transparent';
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
`
|
||||
|
||||
const ToggleButton = styled.button`
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
`
|
||||
@@ -58,7 +63,6 @@ const Header = styled(Row)`
|
||||
const FlagName = styled.span`
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
padding-left: 8px;
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
`
|
||||
const FlagGroupName = styled.span`
|
||||
@@ -114,17 +118,53 @@ function Variant({ option }: { option: string }) {
|
||||
return <option value={option}>{option}</option>
|
||||
}
|
||||
|
||||
function FeatureFlagOption({
|
||||
variants,
|
||||
featureFlag,
|
||||
value,
|
||||
label,
|
||||
}: {
|
||||
variants: string[]
|
||||
interface FeatureFlagProps {
|
||||
variant: Record<string, string>
|
||||
featureFlag: FeatureFlag
|
||||
value: string
|
||||
label: string
|
||||
}) {
|
||||
}
|
||||
|
||||
function FeatureFlagGroup({ name, children }: PropsWithChildren<{ name: string }>) {
|
||||
// type FeatureFlagOption = { props: FeatureFlagProps }
|
||||
const togglableOptions = Children.toArray(children)
|
||||
.filter<ReactElement<FeatureFlagProps>>(
|
||||
(child): child is ReactElement<FeatureFlagProps> =>
|
||||
child instanceof Object && 'type' in child && child.type === FeatureFlagOption
|
||||
)
|
||||
.map(({ props }) => props)
|
||||
.filter(({ variant }) => {
|
||||
const values = Object.values(variant)
|
||||
return values.includes(BaseVariant.Control) && values.includes(BaseVariant.Enabled)
|
||||
})
|
||||
|
||||
const setFeatureFlags = useUpdateAtom(featureFlagSettings)
|
||||
const allEnabled = togglableOptions.every(({ value }) => value === BaseVariant.Enabled)
|
||||
const onToggle = useCallback(() => {
|
||||
setFeatureFlags((flags) => ({
|
||||
...flags,
|
||||
...togglableOptions.reduce(
|
||||
(flags, { featureFlag }) => ({
|
||||
...flags,
|
||||
[featureFlag]: allEnabled ? BaseVariant.Control : BaseVariant.Enabled,
|
||||
}),
|
||||
{}
|
||||
),
|
||||
}))
|
||||
}, [allEnabled, setFeatureFlags, togglableOptions])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Row key={name}>
|
||||
<FlagGroupName>{name}</FlagGroupName>
|
||||
<ToggleButton onClick={onToggle}>{allEnabled ? 'Disable' : 'Enable'} group</ToggleButton>
|
||||
</Row>
|
||||
{children}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function FeatureFlagOption({ variant, featureFlag, value, label }: FeatureFlagProps) {
|
||||
const updateFlag = useUpdateFlag()
|
||||
const [count, setCount] = useState(0)
|
||||
const featureFlags = useAtomValue(featureFlagSettings)
|
||||
@@ -143,7 +183,7 @@ function FeatureFlagOption({
|
||||
}}
|
||||
value={featureFlags[featureFlag]}
|
||||
>
|
||||
{variants.map((variant) => (
|
||||
{Object.values(variant).map((variant) => (
|
||||
<Variant key={variant} option={variant} />
|
||||
))}
|
||||
</FlagVariantSelection>
|
||||
@@ -163,39 +203,26 @@ export default function FeatureFlagModal() {
|
||||
<X size={24} />
|
||||
</CloseButton>
|
||||
</Header>
|
||||
<FlagGroupName>Phase 1</FlagGroupName>
|
||||
<FeatureFlagOption
|
||||
variants={Object.values(Phase1Variant)}
|
||||
value={usePhase1Flag()}
|
||||
featureFlag={FeatureFlag.phase1}
|
||||
label="All Phase 1 changes (nft features)."
|
||||
/>
|
||||
<FlagGroupName>Phase 0</FlagGroupName>
|
||||
<FeatureFlagOption
|
||||
variants={Object.values(RedesignVariant)}
|
||||
value={useRedesignFlag()}
|
||||
featureFlag={FeatureFlag.redesign}
|
||||
label="Redesign"
|
||||
/>
|
||||
<FeatureFlagOption
|
||||
variants={Object.values(NavBarVariant)}
|
||||
value={useNavBarFlag()}
|
||||
featureFlag={FeatureFlag.navBar}
|
||||
label="NavBar"
|
||||
/>
|
||||
<FeatureFlagOption
|
||||
variants={Object.values(TokensVariant)}
|
||||
value={useTokensFlag()}
|
||||
featureFlag={FeatureFlag.tokens}
|
||||
label="Tokens"
|
||||
/>
|
||||
<FeatureFlagOption
|
||||
variants={Object.values(TokenSafetyVariant)}
|
||||
value={useTokenSafetyFlag()}
|
||||
featureFlag={FeatureFlag.tokenSafety}
|
||||
label="Token Safety"
|
||||
/>
|
||||
<SaveButton onClick={() => window.location.reload()}>Save Settings</SaveButton>
|
||||
<FeatureFlagGroup name="Phase 0 Follow-ups">
|
||||
<FeatureFlagOption
|
||||
variant={FavoriteTokensVariant}
|
||||
value={useFavoriteTokensFlag()}
|
||||
featureFlag={FeatureFlag.favoriteTokens}
|
||||
label="Favorite Tokens"
|
||||
/>
|
||||
</FeatureFlagGroup>
|
||||
<FeatureFlagGroup name="Phase 1">
|
||||
<FeatureFlagOption variant={NftVariant} value={useNftFlag()} featureFlag={FeatureFlag.nft} label="NFTs" />
|
||||
</FeatureFlagGroup>
|
||||
<FeatureFlagGroup name="Debug">
|
||||
<FeatureFlagOption
|
||||
variant={TraceJsonRpcVariant}
|
||||
value={useTraceJsonRpcFlag()}
|
||||
featureFlag={FeatureFlag.traceJsonRpc}
|
||||
label="Enables JSON-RPC tracing"
|
||||
/>
|
||||
</FeatureFlagGroup>
|
||||
<SaveButton onClick={() => window.location.reload()}>Reload</SaveButton>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ const ResponsiveText = styled(ThemedText.DeprecatedLabel)`
|
||||
line-height: 16px;
|
||||
font-size: 14px;
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
`};
|
||||
|
||||
@@ -2,17 +2,25 @@ import { Trans } from '@lingui/macro'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { getChainInfoOrDefault, L2ChainInfo } from 'constants/chainInfo'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import { AlertOctagon } from 'react-feather'
|
||||
import { RedesignVariant, useRedesignFlag } from 'featureFlags/flags/redesign'
|
||||
import { AlertOctagon, AlertTriangle } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ExternalLink, MEDIA_WIDTHS } from 'theme'
|
||||
|
||||
const BodyRow = styled.div`
|
||||
color: ${({ theme }) => theme.deprecated_black};
|
||||
const BodyRow = styled.div<{ $redesignFlag?: boolean }>`
|
||||
color: ${({ theme, $redesignFlag }) => ($redesignFlag ? theme.textPrimary : theme.black)};
|
||||
font-size: 12px;
|
||||
font-weight: ${({ $redesignFlag }) => $redesignFlag && '400'};
|
||||
font-size: ${({ $redesignFlag }) => ($redesignFlag ? '14px' : '12px')};
|
||||
line-height: ${({ $redesignFlag }) => $redesignFlag && '20px'};
|
||||
`
|
||||
const CautionIcon = styled(AlertOctagon)`
|
||||
const CautionOctagon = styled(AlertOctagon)`
|
||||
color: ${({ theme }) => theme.deprecated_black};
|
||||
`
|
||||
|
||||
const CautionTriangle = styled(AlertTriangle)`
|
||||
color: ${({ theme }) => theme.accentWarning};
|
||||
`
|
||||
const Link = styled(ExternalLink)`
|
||||
color: ${({ theme }) => theme.deprecated_black};
|
||||
text-decoration: underline;
|
||||
@@ -23,23 +31,24 @@ const TitleRow = styled.div`
|
||||
justify-content: flex-start;
|
||||
margin-bottom: 8px;
|
||||
`
|
||||
const TitleText = styled.div`
|
||||
color: black;
|
||||
font-weight: 600;
|
||||
const TitleText = styled.div<{ redesignFlag?: boolean }>`
|
||||
color: ${({ theme, redesignFlag }) => (redesignFlag ? theme.textPrimary : theme.black)};
|
||||
font-weight: ${({ redesignFlag }) => (redesignFlag ? '500' : '600')};
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
line-height: ${({ redesignFlag }) => (redesignFlag ? '24px' : '20px')};
|
||||
margin: 0px 12px;
|
||||
`
|
||||
const Wrapper = styled.div`
|
||||
background-color: ${({ theme }) => theme.deprecated_yellow3};
|
||||
const Wrapper = styled.div<{ redesignFlag?: boolean }>`
|
||||
background-color: ${({ theme, redesignFlag }) => (redesignFlag ? theme.backgroundSurface : theme.deprecated_yellow3)};
|
||||
border-radius: 12px;
|
||||
border: 1px solid ${({ theme }) => theme.backgroundOutline};
|
||||
bottom: 60px;
|
||||
display: none;
|
||||
max-width: 348px;
|
||||
padding: 16px 20px;
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
right: 16px;
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.upToMedium}px) {
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.deprecated_upToMedium}px) {
|
||||
display: block;
|
||||
}
|
||||
`
|
||||
@@ -48,20 +57,21 @@ export function ChainConnectivityWarning() {
|
||||
const { chainId } = useWeb3React()
|
||||
const info = getChainInfoOrDefault(chainId)
|
||||
const label = info?.label
|
||||
const redesignFlag = useRedesignFlag() === RedesignVariant.Enabled
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<Wrapper redesignFlag={redesignFlag}>
|
||||
<TitleRow>
|
||||
<CautionIcon />
|
||||
<TitleText>
|
||||
{redesignFlag ? <CautionTriangle /> : <CautionOctagon />}
|
||||
<TitleText redesignFlag={redesignFlag}>
|
||||
<Trans>Network Warning</Trans>
|
||||
</TitleText>
|
||||
</TitleRow>
|
||||
<BodyRow>
|
||||
<BodyRow $redesignFlag={redesignFlag}>
|
||||
{chainId === SupportedChainId.MAINNET ? (
|
||||
<Trans>You may have lost your network connection.</Trans>
|
||||
) : (
|
||||
<Trans>You may have lost your network connection, or {label} might be down right now.</Trans>
|
||||
<Trans>{label} might be down right now, or you may have lost your network connection.</Trans>
|
||||
)}{' '}
|
||||
{(info as L2ChainInfo).statusPage !== undefined && (
|
||||
<span>
|
||||
|
||||
@@ -11,7 +11,6 @@ import { useCloseModal, useModalIsOpen, useOpenModal, useToggleModal } from 'sta
|
||||
import { ApplicationModal } from 'state/application/reducer'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ExternalLink, MEDIA_WIDTHS } from 'theme'
|
||||
import { isChainAllowed } from 'utils/switchChain'
|
||||
import { isMobile } from 'utils/userAgent'
|
||||
|
||||
const ActiveRowLinkList = styled.div`
|
||||
@@ -53,7 +52,7 @@ const FlyoutMenu = styled.div`
|
||||
width: 272px;
|
||||
z-index: 99;
|
||||
padding-top: 10px;
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.deprecated_upToSmall}px) {
|
||||
top: 40px;
|
||||
}
|
||||
`
|
||||
@@ -112,7 +111,7 @@ const NetworkLabel = styled.div`
|
||||
`
|
||||
const SelectorLabel = styled(NetworkLabel)`
|
||||
display: none;
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.deprecated_upToSmall}px) {
|
||||
display: block;
|
||||
margin-right: 8px;
|
||||
}
|
||||
@@ -126,7 +125,7 @@ const NetworkAlertLabel = styled(NetworkLabel)`
|
||||
font-size: 1rem;
|
||||
width: fit-content;
|
||||
font-weight: 500;
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.deprecated_upToSmall}px) {
|
||||
display: block;
|
||||
}
|
||||
`
|
||||
@@ -153,12 +152,12 @@ const SelectorControls = styled.div<{ supportedChain: boolean }>`
|
||||
}
|
||||
`
|
||||
const SelectorLogo = styled(Logo)`
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.deprecated_upToSmall}px) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
`
|
||||
const SelectorWrapper = styled.div`
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.deprecated_upToSmall}px) {
|
||||
position: relative;
|
||||
}
|
||||
`
|
||||
@@ -179,7 +178,7 @@ const BridgeLabel = ({ chainId }: { chainId: SupportedChainId }) => {
|
||||
case SupportedChainId.ARBITRUM_RINKEBY:
|
||||
return <Trans>Arbitrum Bridge</Trans>
|
||||
case SupportedChainId.OPTIMISM:
|
||||
case SupportedChainId.OPTIMISTIC_KOVAN:
|
||||
case SupportedChainId.OPTIMISM_GOERLI:
|
||||
return <Trans>Optimism Bridge</Trans>
|
||||
case SupportedChainId.POLYGON:
|
||||
case SupportedChainId.POLYGON_MUMBAI:
|
||||
@@ -197,7 +196,7 @@ const ExplorerLabel = ({ chainId }: { chainId: SupportedChainId }) => {
|
||||
case SupportedChainId.ARBITRUM_RINKEBY:
|
||||
return <Trans>Arbiscan</Trans>
|
||||
case SupportedChainId.OPTIMISM:
|
||||
case SupportedChainId.OPTIMISTIC_KOVAN:
|
||||
case SupportedChainId.OPTIMISM_GOERLI:
|
||||
return <Trans>Optimistic Etherscan</Trans>
|
||||
case SupportedChainId.POLYGON:
|
||||
case SupportedChainId.POLYGON_MUMBAI:
|
||||
@@ -281,7 +280,7 @@ const NETWORK_SELECTOR_CHAINS = [
|
||||
]
|
||||
|
||||
export default function NetworkSelector() {
|
||||
const { chainId, provider, connector } = useWeb3React()
|
||||
const { chainId, provider } = useWeb3React()
|
||||
|
||||
const node = useRef<HTMLDivElement>(null)
|
||||
const isOpen = useModalIsOpen(ApplicationModal.NETWORK_SELECTOR)
|
||||
@@ -328,18 +327,16 @@ export default function NetworkSelector() {
|
||||
<FlyoutHeader>
|
||||
<Trans>Select a {!onSupportedChain ? ' supported ' : ''}network</Trans>
|
||||
</FlyoutHeader>
|
||||
{NETWORK_SELECTOR_CHAINS.map((chainId: SupportedChainId) =>
|
||||
isChainAllowed(connector, chainId) ? (
|
||||
<Row
|
||||
onSelectChain={async (targetChainId: SupportedChainId) => {
|
||||
await selectChain(targetChainId)
|
||||
closeModal()
|
||||
}}
|
||||
targetChain={chainId}
|
||||
key={chainId}
|
||||
/>
|
||||
) : null
|
||||
)}
|
||||
{NETWORK_SELECTOR_CHAINS.map((chainId: SupportedChainId) => (
|
||||
<Row
|
||||
onSelectChain={async (targetChainId: SupportedChainId) => {
|
||||
await selectChain(targetChainId)
|
||||
closeModal()
|
||||
}}
|
||||
targetChain={chainId}
|
||||
key={chainId}
|
||||
/>
|
||||
))}
|
||||
</FlyoutMenuContents>
|
||||
</FlyoutMenu>
|
||||
)}
|
||||
|
||||
@@ -26,7 +26,7 @@ const StyledPolling = styled.div<{ warning: boolean }>`
|
||||
color: ${({ theme, warning }) => (warning ? theme.deprecated_yellow3 : theme.deprecated_green1)};
|
||||
transition: 250ms ease color;
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
|
||||
display: none;
|
||||
`}
|
||||
`
|
||||
|
||||
@@ -46,16 +46,16 @@ const HeaderFrame = styled.div<{ showBackground: boolean }>`
|
||||
transition: background-position 0.1s, box-shadow 0.1s;
|
||||
background-blend-mode: hard-light;
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToLarge`
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToLarge`
|
||||
grid-template-columns: 48px 1fr 1fr;
|
||||
`};
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
|
||||
padding: 1rem;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
`};
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
|
||||
padding: 1rem;
|
||||
grid-template-columns: 36px 1fr;
|
||||
`};
|
||||
@@ -81,7 +81,7 @@ const HeaderElement = styled.div`
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
|
||||
align-items: center;
|
||||
`};
|
||||
`
|
||||
@@ -97,13 +97,13 @@ const HeaderLinks = styled(Row)`
|
||||
grid-gap: 10px;
|
||||
overflow: auto;
|
||||
align-items: center;
|
||||
${({ theme }) => theme.mediaWidth.upToLarge`
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToLarge`
|
||||
justify-self: start;
|
||||
`};
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
|
||||
justify-self: center;
|
||||
`};
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
justify-self: center;
|
||||
@@ -157,7 +157,7 @@ const UNIWrapper = styled.span`
|
||||
`
|
||||
|
||||
const BalanceText = styled(Text)`
|
||||
${({ theme }) => theme.mediaWidth.upToExtraSmall`
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToExtraSmall`
|
||||
display: none;
|
||||
`};
|
||||
`
|
||||
@@ -168,7 +168,7 @@ const Title = styled.a`
|
||||
pointer-events: auto;
|
||||
justify-self: flex-start;
|
||||
margin-right: 12px;
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
|
||||
justify-self: center;
|
||||
`};
|
||||
:hover {
|
||||
|
||||
@@ -10,7 +10,6 @@ const TextWrapper = styled.span<{
|
||||
textColor?: string
|
||||
}>`
|
||||
margin-left: ${({ margin }) => margin && '4px'};
|
||||
color: ${({ theme, link, textColor }) => (link ? theme.deprecated_blue1 : textColor ?? theme.deprecated_text1)};
|
||||
font-size: ${({ fontSize }) => fontSize ?? 'inherit'};
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { ConnectionType } from 'connection'
|
||||
import { NavBarVariant, useNavBarFlag } from 'featureFlags/flags/navBar'
|
||||
import useENSAvatar from 'hooks/useENSAvatar'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import CoinbaseWalletIcon from '../../assets/images/coinbaseWalletIcon.svg'
|
||||
import FortmaticIcon from '../../assets/images/fortmaticIcon.png'
|
||||
import WalletConnectIcon from '../../assets/images/walletConnectIcon.svg'
|
||||
import sockImg from '../../assets/svg/socks.svg'
|
||||
import { useHasSocks } from '../../hooks/useSocksBalance'
|
||||
import Identicon from '../Identicon'
|
||||
|
||||
const IconWrapper = styled.div<{ size?: number }>`
|
||||
position: relative;
|
||||
${({ theme }) => theme.flexColumnNoWrap};
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -16,27 +21,60 @@ const IconWrapper = styled.div<{ size?: number }>`
|
||||
height: ${({ size }) => (size ? size + 'px' : '32px')};
|
||||
width: ${({ size }) => (size ? size + 'px' : '32px')};
|
||||
}
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
|
||||
align-items: flex-end;
|
||||
`};
|
||||
`
|
||||
|
||||
export default function StatusIcon({ connectionType }: { connectionType: ConnectionType }) {
|
||||
let image
|
||||
switch (connectionType) {
|
||||
case ConnectionType.INJECTED:
|
||||
image = <Identicon />
|
||||
break
|
||||
case ConnectionType.WALLET_CONNECT:
|
||||
image = <img src={WalletConnectIcon} alt="WalletConnect" />
|
||||
break
|
||||
case ConnectionType.COINBASE_WALLET:
|
||||
image = <img src={CoinbaseWalletIcon} alt="Coinbase Wallet" />
|
||||
break
|
||||
case ConnectionType.FORTMATIC:
|
||||
image = <img src={FortmaticIcon} alt="Fortmatic" />
|
||||
break
|
||||
const SockContainer = styled.div`
|
||||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
bottom: -4px;
|
||||
right: -4px;
|
||||
`
|
||||
|
||||
const SockImg = styled.img`
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
`
|
||||
|
||||
const Socks = () => {
|
||||
return (
|
||||
<SockContainer>
|
||||
<SockImg src={sockImg} />
|
||||
</SockContainer>
|
||||
)
|
||||
}
|
||||
|
||||
const useIcon = (connectionType: ConnectionType) => {
|
||||
const { account } = useWeb3React()
|
||||
const { avatar } = useENSAvatar(account ?? undefined)
|
||||
const isNavbarEnabled = useNavBarFlag() === NavBarVariant.Enabled
|
||||
|
||||
if ((isNavbarEnabled && avatar) || connectionType === ConnectionType.INJECTED) {
|
||||
return <Identicon />
|
||||
} else if (connectionType === ConnectionType.WALLET_CONNECT) {
|
||||
return <img src={WalletConnectIcon} alt="WalletConnect" />
|
||||
} else if (connectionType === ConnectionType.COINBASE_WALLET) {
|
||||
return <img src={CoinbaseWalletIcon} alt="Coinbase Wallet" />
|
||||
}
|
||||
|
||||
return <IconWrapper size={16}>{image}</IconWrapper>
|
||||
return undefined
|
||||
}
|
||||
|
||||
export default function StatusIcon({ connectionType, size }: { connectionType: ConnectionType; size?: number }) {
|
||||
const hasSocks = useHasSocks()
|
||||
const isNavbarEnabled = useNavBarFlag() === NavBarVariant.Enabled
|
||||
const icon = useIcon(connectionType)
|
||||
|
||||
return (
|
||||
<IconWrapper size={size ?? 16}>
|
||||
{isNavbarEnabled && hasSocks && <Socks />}
|
||||
{icon}
|
||||
</IconWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import jazzicon from '@metamask/jazzicon'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { NavBarVariant, useNavBarFlag } from 'featureFlags/flags/navBar'
|
||||
import useENSAvatar from 'hooks/useENSAvatar'
|
||||
import { useLayoutEffect, useMemo, useRef, useState } from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
const StyledIdenticon = styled.div`
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
const StyledIdenticon = styled.div<{ iconSize: number }>`
|
||||
height: ${({ iconSize }) => `${iconSize}px`};
|
||||
width: ${({ iconSize }) => `${iconSize}px`};
|
||||
border-radius: 1.125rem;
|
||||
background-color: ${({ theme }) => theme.deprecated_bg4};
|
||||
font-size: initial;
|
||||
@@ -18,12 +19,14 @@ const StyledAvatar = styled.img`
|
||||
border-radius: inherit;
|
||||
`
|
||||
|
||||
export default function Identicon() {
|
||||
export default function Identicon({ size }: { size?: number }) {
|
||||
const { account } = useWeb3React()
|
||||
const { avatar } = useENSAvatar(account ?? undefined)
|
||||
const [fetchable, setFetchable] = useState(true)
|
||||
const isNavbarEnabled = useNavBarFlag() === NavBarVariant.Enabled
|
||||
const iconSize = size ? size : isNavbarEnabled ? 24 : 16
|
||||
|
||||
const icon = useMemo(() => account && jazzicon(16, parseInt(account.slice(2, 10), 16)), [account])
|
||||
const icon = useMemo(() => account && jazzicon(iconSize, parseInt(account.slice(2, 10), 16)), [account, iconSize])
|
||||
const iconRef = useRef<HTMLDivElement>(null)
|
||||
useLayoutEffect(() => {
|
||||
const current = iconRef.current
|
||||
@@ -41,7 +44,7 @@ export default function Identicon() {
|
||||
}, [icon, iconRef])
|
||||
|
||||
return (
|
||||
<StyledIdenticon>
|
||||
<StyledIdenticon iconSize={iconSize}>
|
||||
{avatar && fetchable ? (
|
||||
<StyledAvatar alt="avatar" src={avatar} onError={() => setFetchable(false)}></StyledAvatar>
|
||||
) : (
|
||||
|
||||
@@ -48,11 +48,11 @@ const StyledInput = styled(NumericalInput)<{ usePercent?: boolean }>`
|
||||
font-weight: 500;
|
||||
padding: 0 10px;
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
|
||||
font-size: 16px;
|
||||
`};
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToExtraSmall`
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToExtraSmall`
|
||||
font-size: 12px;
|
||||
`};
|
||||
`
|
||||
|
||||
@@ -19,7 +19,7 @@ const HandleAccent = styled.path`
|
||||
|
||||
stroke-width: 1.5;
|
||||
stroke: ${({ theme }) => theme.deprecated_white};
|
||||
opacity: 0.6;
|
||||
opacity: ${({ theme }) => theme.opacity.hover};
|
||||
`
|
||||
|
||||
const LabelGroup = styled.g<{ visible: boolean }>`
|
||||
|
||||
@@ -176,7 +176,7 @@ export default function LiquidityChartRangeInput({
|
||||
message={<Trans>Liquidity data not available.</Trans>}
|
||||
icon={<CloudOff size={56} stroke={theme.deprecated_text4} />}
|
||||
/>
|
||||
) : !formattedData || formattedData === [] || !price ? (
|
||||
) : !formattedData || formattedData.length === 0 || !price ? (
|
||||
<InfoBox
|
||||
message={<Trans>There is no liquidity data.</Trans>}
|
||||
icon={<BarChart2 size={56} stroke={theme.deprecated_text4} />}
|
||||
|
||||
@@ -14,13 +14,15 @@ export default function ListLogo({
|
||||
style,
|
||||
size = '24px',
|
||||
alt,
|
||||
symbol,
|
||||
}: {
|
||||
logoURI: string
|
||||
size?: string
|
||||
style?: React.CSSProperties
|
||||
alt?: string
|
||||
symbol?: string
|
||||
}) {
|
||||
const srcs: string[] = useHttpLocations(logoURI)
|
||||
|
||||
return <StyledListLogo alt={alt} size={size} srcs={srcs} style={style} />
|
||||
return <StyledListLogo alt={alt} size={size} symbol={symbol} srcs={srcs} style={style} />
|
||||
}
|
||||
|
||||
@@ -1,22 +1,34 @@
|
||||
import { useState } from 'react'
|
||||
import { Slash } from 'react-feather'
|
||||
import { ImageProps } from 'rebass'
|
||||
import { useTheme } from 'styled-components/macro'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
const BAD_SRCS: { [tokenAddress: string]: true } = {}
|
||||
|
||||
interface LogoProps extends Pick<ImageProps, 'style' | 'alt' | 'className'> {
|
||||
srcs: string[]
|
||||
symbol?: string
|
||||
size?: string
|
||||
}
|
||||
|
||||
const MissingImageLogo = styled.div<{ size?: string }>`
|
||||
--size: ${({ size }) => size};
|
||||
border-radius: 100px;
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
background-color: ${({ theme }) => theme.backgroundInteractive};
|
||||
font-size: calc(var(--size) / 3);
|
||||
font-weight: 500;
|
||||
height: ${({ size }) => size ?? '24px'};
|
||||
line-height: ${({ size }) => size ?? '24px'};
|
||||
text-align: center;
|
||||
width: ${({ size }) => size ?? '24px'};
|
||||
`
|
||||
|
||||
/**
|
||||
* Renders an image by sequentially trying a list of URIs, and then eventually a fallback triangle alert
|
||||
*/
|
||||
export default function Logo({ srcs, alt, style, ...rest }: LogoProps) {
|
||||
export default function Logo({ srcs, alt, style, size, symbol, ...rest }: LogoProps) {
|
||||
const [, refresh] = useState<number>(0)
|
||||
|
||||
const theme = useTheme()
|
||||
|
||||
const src: string | undefined = srcs.find((src) => !BAD_SRCS[src])
|
||||
|
||||
if (src) {
|
||||
@@ -34,5 +46,10 @@ export default function Logo({ srcs, alt, style, ...rest }: LogoProps) {
|
||||
)
|
||||
}
|
||||
|
||||
return <Slash {...rest} style={{ ...style, color: theme.deprecated_bg4 }} />
|
||||
return (
|
||||
<MissingImageLogo size={size}>
|
||||
{/* use only first 3 characters of Symbol for design reasons */}
|
||||
{symbol?.toUpperCase().replace('$', '').replace(/\s+/g, '').slice(0, 3)}
|
||||
</MissingImageLogo>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ const MenuFlyout = styled.span<{ flyoutAlignment?: FlyoutAlignment }>`
|
||||
: css`
|
||||
left: 0rem;
|
||||
`};
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
|
||||
bottom: unset;
|
||||
right: 0;
|
||||
left: unset;
|
||||
@@ -278,7 +278,7 @@ export default function Menu() {
|
||||
</ToggleMenuItem>
|
||||
<ToggleMenuItem onClick={() => toggleDarkMode()}>
|
||||
<div>{darkMode ? <Trans>Light Theme</Trans> : <Trans>Dark Theme</Trans>}</div>
|
||||
{darkMode ? <Moon opacity={0.6} size={16} /> : <Sun opacity={0.6} size={16} />}
|
||||
{darkMode ? <Sun opacity={0.6} size={16} /> : <Moon opacity={0.6} size={16} />}
|
||||
</ToggleMenuItem>
|
||||
<MenuItem href="https://docs.uniswap.org/">
|
||||
<div>
|
||||
|
||||
@@ -4,19 +4,21 @@ import React from 'react'
|
||||
import { animated, useSpring, useTransition } from 'react-spring'
|
||||
import { useGesture } from 'react-use-gesture'
|
||||
import styled, { css } from 'styled-components/macro'
|
||||
import { Z_INDEX } from 'theme/zIndex'
|
||||
|
||||
import { isMobile } from '../../utils/userAgent'
|
||||
|
||||
const AnimatedDialogOverlay = animated(DialogOverlay)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const StyledDialogOverlay = styled(AnimatedDialogOverlay)<{ redesignFlag?: boolean }>`
|
||||
const StyledDialogOverlay = styled(AnimatedDialogOverlay)<{ redesignFlag?: boolean; scrollOverlay?: boolean }>`
|
||||
&[data-reach-dialog-overlay] {
|
||||
z-index: 2;
|
||||
z-index: ${Z_INDEX.modalBackdrop};
|
||||
background-color: transparent;
|
||||
overflow: hidden;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow-y: ${({ scrollOverlay }) => scrollOverlay && 'scroll'};
|
||||
justify-content: center;
|
||||
|
||||
background-color: ${({ theme, redesignFlag }) => (redesignFlag ? theme.backgroundScrim : theme.deprecated_modalBG)};
|
||||
@@ -26,7 +28,7 @@ const StyledDialogOverlay = styled(AnimatedDialogOverlay)<{ redesignFlag?: boole
|
||||
const AnimatedDialogContent = animated(DialogContent)
|
||||
// destructure to not pass custom props to Dialog DOM element
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const StyledDialogContent = styled(({ minHeight, maxHeight, mobile, isOpen, ...rest }) => (
|
||||
const StyledDialogContent = styled(({ minHeight, maxHeight, mobile, isOpen, redesignFlag, scrollOverlay, ...rest }) => (
|
||||
<AnimatedDialogContent {...rest} />
|
||||
)).attrs({
|
||||
'aria-label': 'dialog',
|
||||
@@ -34,16 +36,17 @@ const StyledDialogContent = styled(({ minHeight, maxHeight, mobile, isOpen, ...r
|
||||
overflow-y: auto;
|
||||
|
||||
&[data-reach-dialog-content] {
|
||||
margin: 0 0 2rem 0;
|
||||
margin: ${({ redesignFlag }) => (redesignFlag ? 'auto' : '0 0 2rem 0')};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg0};
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_bg1};
|
||||
box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.95, theme.shadow1)};
|
||||
box-shadow: ${({ theme, redesignFlag }) =>
|
||||
redesignFlag ? theme.deepShadow : `0 4px 8px 0 ${transparentize(0.95, theme.shadow1)}`};
|
||||
padding: 0px;
|
||||
width: 50vw;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
align-self: ${({ mobile }) => (mobile ? 'flex-end' : 'center')};
|
||||
align-self: ${({ mobile }) => mobile && 'flex-end'};
|
||||
|
||||
max-width: 420px;
|
||||
${({ maxHeight }) =>
|
||||
@@ -56,13 +59,13 @@ const StyledDialogContent = styled(({ minHeight, maxHeight, mobile, isOpen, ...r
|
||||
css`
|
||||
min-height: ${minHeight}vh;
|
||||
`}
|
||||
display: flex;
|
||||
display: ${({ scrollOverlay }) => (scrollOverlay ? 'inline-table' : 'flex')};
|
||||
border-radius: 20px;
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
${({ theme, redesignFlag }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
|
||||
width: 65vw;
|
||||
margin: 0;
|
||||
margin: ${redesignFlag ? 'auto' : '0'};
|
||||
`}
|
||||
${({ theme, mobile }) => theme.mediaWidth.upToSmall`
|
||||
${({ theme, mobile }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
|
||||
width: 85vw;
|
||||
${
|
||||
mobile &&
|
||||
@@ -85,6 +88,7 @@ interface ModalProps {
|
||||
initialFocusRef?: React.RefObject<any>
|
||||
children?: React.ReactNode
|
||||
redesignFlag?: boolean
|
||||
scrollOverlay?: boolean
|
||||
}
|
||||
|
||||
export default function Modal({
|
||||
@@ -95,8 +99,9 @@ export default function Modal({
|
||||
initialFocusRef,
|
||||
children,
|
||||
redesignFlag,
|
||||
scrollOverlay,
|
||||
}: ModalProps) {
|
||||
const fadeTransition = useTransition(isOpen, null, {
|
||||
const fadeTransition = useTransition(isOpen, {
|
||||
config: { duration: 200 },
|
||||
from: { opacity: 0 },
|
||||
enter: { opacity: 1 },
|
||||
@@ -117,16 +122,17 @@ export default function Modal({
|
||||
|
||||
return (
|
||||
<>
|
||||
{fadeTransition.map(
|
||||
({ item, key, props }) =>
|
||||
{fadeTransition(
|
||||
({ opacity }, item) =>
|
||||
item && (
|
||||
<StyledDialogOverlay
|
||||
key={key}
|
||||
style={props}
|
||||
as={AnimatedDialogOverlay}
|
||||
style={{ opacity: opacity.to({ range: [0.0, 1.0], output: [0, 1] }) }}
|
||||
onDismiss={onDismiss}
|
||||
initialFocusRef={initialFocusRef}
|
||||
unstable_lockFocusAcrossFrames={false}
|
||||
redesignFlag={redesignFlag}
|
||||
scrollOverlay={scrollOverlay}
|
||||
>
|
||||
<StyledDialogContent
|
||||
{...(isMobile
|
||||
@@ -139,6 +145,8 @@ export default function Modal({
|
||||
minHeight={minHeight}
|
||||
maxHeight={maxHeight}
|
||||
mobile={isMobile}
|
||||
redesignFlag={redesignFlag}
|
||||
scrollOverlay={scrollOverlay}
|
||||
>
|
||||
{/* prevents the automatic focusing of inputs on mobile by the reach dialog */}
|
||||
{!initialFocusRef && isMobile ? <div tabIndex={1} /> : null}
|
||||
|
||||
23
src/components/NavBar/ChainSelector.css.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { style } from '@vanilla-extract/css'
|
||||
import { lightGrayOverlayOnHover } from 'nft/css/common.css'
|
||||
|
||||
import { sprinkles } from '../../nft/css/sprinkles.css'
|
||||
|
||||
export const ChainSelector = style([
|
||||
lightGrayOverlayOnHover,
|
||||
sprinkles({
|
||||
borderRadius: '8',
|
||||
height: '40',
|
||||
cursor: 'pointer',
|
||||
border: 'none',
|
||||
color: 'textPrimary',
|
||||
background: 'none',
|
||||
}),
|
||||
])
|
||||
|
||||
export const Image = style([
|
||||
sprinkles({
|
||||
width: '20',
|
||||
height: '20',
|
||||
}),
|
||||
])
|
||||
118
src/components/NavBar/ChainSelector.tsx
Normal file
@@ -0,0 +1,118 @@
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { getChainInfo } from 'constants/chainInfo'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import { useOnClickOutside } from 'hooks/useOnClickOutside'
|
||||
import useSelectChain from 'hooks/useSelectChain'
|
||||
import useSyncChainQuery from 'hooks/useSyncChainQuery'
|
||||
import { Box } from 'nft/components/Box'
|
||||
import { Portal } from 'nft/components/common/Portal'
|
||||
import { Column, Row } from 'nft/components/Flex'
|
||||
import { TokenWarningRedIcon } from 'nft/components/icons'
|
||||
import { subhead } from 'nft/css/common.css'
|
||||
import { themeVars } from 'nft/css/sprinkles.css'
|
||||
import { useIsMobile } from 'nft/hooks'
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
import { ChevronDown, ChevronUp } from 'react-feather'
|
||||
import { useTheme } from 'styled-components/macro'
|
||||
|
||||
import * as styles from './ChainSelector.css'
|
||||
import ChainSelectorRow from './ChainSelectorRow'
|
||||
import { NavDropdown } from './NavDropdown'
|
||||
|
||||
const NETWORK_SELECTOR_CHAINS = [
|
||||
SupportedChainId.MAINNET,
|
||||
SupportedChainId.POLYGON,
|
||||
SupportedChainId.OPTIMISM,
|
||||
SupportedChainId.ARBITRUM_ONE,
|
||||
SupportedChainId.CELO,
|
||||
]
|
||||
|
||||
interface ChainSelectorProps {
|
||||
leftAlign?: boolean
|
||||
}
|
||||
|
||||
export const ChainSelector = ({ leftAlign }: ChainSelectorProps) => {
|
||||
const { chainId } = useWeb3React()
|
||||
const [isOpen, setIsOpen] = useState<boolean>(false)
|
||||
const isMobile = useIsMobile()
|
||||
|
||||
const theme = useTheme()
|
||||
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
const modalRef = useRef<HTMLDivElement>(null)
|
||||
useOnClickOutside(ref, () => setIsOpen(false), [modalRef])
|
||||
|
||||
const info = chainId ? getChainInfo(chainId) : undefined
|
||||
|
||||
const selectChain = useSelectChain()
|
||||
useSyncChainQuery()
|
||||
|
||||
const [pendingChainId, setPendingChainId] = useState<SupportedChainId | undefined>(undefined)
|
||||
|
||||
const onSelectChain = useCallback(
|
||||
async (targetChainId: SupportedChainId) => {
|
||||
setPendingChainId(targetChainId)
|
||||
await selectChain(targetChainId)
|
||||
setPendingChainId(undefined)
|
||||
setIsOpen(false)
|
||||
},
|
||||
[selectChain, setIsOpen]
|
||||
)
|
||||
|
||||
if (!chainId) {
|
||||
return null
|
||||
}
|
||||
|
||||
const isSupported = !!info
|
||||
|
||||
const dropdown = (
|
||||
<NavDropdown top="56" left={leftAlign ? '0' : 'auto'} right={leftAlign ? 'auto' : '0'} ref={modalRef}>
|
||||
<Column paddingX="8">
|
||||
{NETWORK_SELECTOR_CHAINS.map((chainId: SupportedChainId) => (
|
||||
<ChainSelectorRow
|
||||
onSelectChain={onSelectChain}
|
||||
targetChain={chainId}
|
||||
key={chainId}
|
||||
isPending={chainId === pendingChainId}
|
||||
/>
|
||||
))}
|
||||
</Column>
|
||||
</NavDropdown>
|
||||
)
|
||||
|
||||
const chevronProps = {
|
||||
height: 20,
|
||||
width: 20,
|
||||
color: theme.textSecondary,
|
||||
}
|
||||
|
||||
return (
|
||||
<Box position="relative" ref={ref}>
|
||||
<Row
|
||||
as="button"
|
||||
gap="8"
|
||||
className={styles.ChainSelector}
|
||||
background={isOpen ? 'accentActiveSoft' : 'none'}
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
>
|
||||
{!isSupported ? (
|
||||
<>
|
||||
<TokenWarningRedIcon fill={themeVars.colors.textSecondary} width={24} height={24} />
|
||||
<Box as="span" className={subhead} display={{ sm: 'none', xxl: 'flex' }} style={{ lineHeight: '20px' }}>
|
||||
Unsupported
|
||||
</Box>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<img src={info.logoUrl} alt={info.label} className={styles.Image} />
|
||||
<Box as="span" className={subhead} display={{ sm: 'none', xxl: 'flex' }} style={{ lineHeight: '20px' }}>
|
||||
{info.label}
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
{isOpen ? <ChevronUp {...chevronProps} /> : <ChevronDown {...chevronProps} />}
|
||||
</Row>
|
||||
{isOpen && (isMobile ? <Portal>{dropdown}</Portal> : <>{dropdown}</>)}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
89
src/components/NavBar/ChainSelectorRow.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import Loader from 'components/Loader'
|
||||
import { getChainInfo } from 'constants/chainInfo'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import { CheckMarkIcon } from 'nft/components/icons'
|
||||
import styled, { useTheme } from 'styled-components/macro'
|
||||
|
||||
const LOGO_SIZE = 20
|
||||
|
||||
const Container = styled.button`
|
||||
display: grid;
|
||||
background: none;
|
||||
grid-template-columns: min-content 1fr min-content;
|
||||
align-items: center;
|
||||
text-align: left;
|
||||
line-height: 24px;
|
||||
border: none;
|
||||
justify-content: space-between;
|
||||
padding: 10px 8px;
|
||||
cursor: pointer;
|
||||
border-radius: 12px;
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
width: 240px;
|
||||
transition: ${({ theme }) => theme.transition.duration.medium} ${({ theme }) => theme.transition.timing.ease}
|
||||
background-color;
|
||||
|
||||
@media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: ${({ theme }) => theme.backgroundOutline};
|
||||
}
|
||||
`
|
||||
|
||||
const Label = styled.div`
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
font-size: 16px;
|
||||
`
|
||||
|
||||
const Status = styled.div`
|
||||
grid-column: 3;
|
||||
grid-row: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: ${LOGO_SIZE}px;
|
||||
`
|
||||
|
||||
const ApproveText = styled.div`
|
||||
color: ${({ theme }) => theme.textSecondary};
|
||||
font-size: 12px;
|
||||
grid-column: 2;
|
||||
grid-row: 2;
|
||||
`
|
||||
|
||||
const Logo = styled.img`
|
||||
height: ${LOGO_SIZE}px;
|
||||
width: ${LOGO_SIZE}px;
|
||||
margin-right: 12px;
|
||||
`
|
||||
|
||||
export default function ChainSelectorRow({
|
||||
targetChain,
|
||||
onSelectChain,
|
||||
isPending,
|
||||
}: {
|
||||
targetChain: SupportedChainId
|
||||
onSelectChain: (targetChain: number) => void
|
||||
isPending: boolean
|
||||
}) {
|
||||
const { chainId } = useWeb3React()
|
||||
const active = chainId === targetChain
|
||||
const { label, logoUrl } = getChainInfo(targetChain)
|
||||
|
||||
const theme = useTheme()
|
||||
|
||||
return (
|
||||
<Container onClick={() => onSelectChain(targetChain)}>
|
||||
<Logo src={logoUrl} alt={label} />
|
||||
<Label>{label}</Label>
|
||||
{isPending && <ApproveText>Approve in wallet</ApproveText>}
|
||||
<Status>
|
||||
{active && <CheckMarkIcon width={LOGO_SIZE} height={LOGO_SIZE} color={theme.accentActive} />}
|
||||
{isPending && <Loader width={LOGO_SIZE} height={LOGO_SIZE} />}
|
||||
</Status>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import { style } from '@vanilla-extract/css'
|
||||
|
||||
import { sprinkles } from '../../nft/css/sprinkles.css'
|
||||
|
||||
export const ChainSwitcher = style([
|
||||
sprinkles({
|
||||
background: 'lightGrayContainer',
|
||||
borderRadius: '8',
|
||||
paddingY: '8',
|
||||
paddingX: '12',
|
||||
cursor: 'pointer',
|
||||
border: 'none',
|
||||
}),
|
||||
])
|
||||
|
||||
export const ChainSwitcherRow = style([
|
||||
sprinkles({
|
||||
border: 'none',
|
||||
color: 'blackBlue',
|
||||
justifyContent: 'space-between',
|
||||
paddingX: '16',
|
||||
paddingY: '12',
|
||||
cursor: 'pointer',
|
||||
}),
|
||||
{
|
||||
lineHeight: '24px',
|
||||
width: '308px',
|
||||
},
|
||||
])
|
||||
|
||||
export const Image = style([
|
||||
sprinkles({
|
||||
width: '28',
|
||||
height: '28',
|
||||
}),
|
||||
])
|
||||
|
||||
export const Icon = style([
|
||||
Image,
|
||||
sprinkles({
|
||||
marginRight: '12',
|
||||
}),
|
||||
])
|
||||
|
||||
export const Indicator = style([
|
||||
sprinkles({
|
||||
marginLeft: '8',
|
||||
}),
|
||||
])
|
||||
@@ -1,108 +0,0 @@
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { getChainInfo } from 'constants/chainInfo'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import { useOnClickOutside } from 'hooks/useOnClickOutside'
|
||||
import useSelectChain from 'hooks/useSelectChain'
|
||||
import useSyncChainQuery from 'hooks/useSyncChainQuery'
|
||||
import { Box } from 'nft/components/Box'
|
||||
import { Column, Row } from 'nft/components/Flex'
|
||||
import { NewChevronDownIcon, NewChevronUpIcon } from 'nft/components/icons'
|
||||
import { CheckMarkIcon } from 'nft/components/icons'
|
||||
import { subhead } from 'nft/css/common.css'
|
||||
import { ReactNode, useReducer, useRef } from 'react'
|
||||
import { isChainAllowed } from 'utils/switchChain'
|
||||
|
||||
import * as styles from './ChainSwitcher.css'
|
||||
import { NavDropdown } from './NavDropdown'
|
||||
|
||||
const ChainRow = ({
|
||||
targetChain,
|
||||
onSelectChain,
|
||||
}: {
|
||||
targetChain: SupportedChainId
|
||||
onSelectChain: (targetChain: number) => void
|
||||
}) => {
|
||||
const { chainId } = useWeb3React()
|
||||
const active = chainId === targetChain
|
||||
const { label, logoUrl } = getChainInfo(targetChain)
|
||||
|
||||
return (
|
||||
<Row
|
||||
as="button"
|
||||
background={active ? 'lightGrayContainer' : 'none'}
|
||||
className={`${styles.ChainSwitcherRow} ${subhead}`}
|
||||
onClick={() => onSelectChain(targetChain)}
|
||||
>
|
||||
<ChainDetails>
|
||||
<img src={logoUrl} alt={label} className={styles.Icon} />
|
||||
{label}
|
||||
</ChainDetails>
|
||||
{active && <CheckMarkIcon width={20} height={20} />}
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
|
||||
const ChainDetails = ({ children }: { children: ReactNode }) => <Row>{children}</Row>
|
||||
|
||||
const NETWORK_SELECTOR_CHAINS = [
|
||||
SupportedChainId.MAINNET,
|
||||
SupportedChainId.POLYGON,
|
||||
SupportedChainId.OPTIMISM,
|
||||
SupportedChainId.ARBITRUM_ONE,
|
||||
SupportedChainId.CELO,
|
||||
]
|
||||
|
||||
interface ChainSwitcherProps {
|
||||
isMobile?: boolean
|
||||
}
|
||||
|
||||
export const ChainSwitcher = ({ isMobile }: ChainSwitcherProps) => {
|
||||
const { chainId, connector } = useWeb3React()
|
||||
const [isOpen, toggleOpen] = useReducer((s) => !s, false)
|
||||
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
useOnClickOutside(ref, isOpen ? toggleOpen : undefined)
|
||||
|
||||
const info = chainId ? getChainInfo(chainId) : undefined
|
||||
|
||||
const selectChain = useSelectChain()
|
||||
useSyncChainQuery()
|
||||
|
||||
if (!chainId || !info) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<Box position="relative" ref={ref}>
|
||||
<Row as="button" gap="8" className={styles.ChainSwitcher} onClick={toggleOpen}>
|
||||
<img src={info.logoUrl} alt={info.label} className={styles.Image} />
|
||||
<Box as="span" className={subhead} color="explicitWhite" style={{ lineHeight: '20px' }}>
|
||||
{info.label}
|
||||
</Box>
|
||||
{isOpen ? (
|
||||
<NewChevronUpIcon width={16} height={16} color="darkGray" />
|
||||
) : (
|
||||
<NewChevronDownIcon width={16} height={16} color="darkGray" />
|
||||
)}
|
||||
</Row>
|
||||
{isOpen && (
|
||||
<NavDropdown top={60} leftAligned={isMobile}>
|
||||
<Column gap="4">
|
||||
{NETWORK_SELECTOR_CHAINS.map((chainId: SupportedChainId) =>
|
||||
isChainAllowed(connector, chainId) ? (
|
||||
<ChainRow
|
||||
onSelectChain={async (targetChainId: SupportedChainId) => {
|
||||
await selectChain(targetChainId)
|
||||
toggleOpen()
|
||||
}}
|
||||
targetChain={chainId}
|
||||
key={chainId}
|
||||
/>
|
||||
) : null
|
||||
)}
|
||||
</Column>
|
||||
</NavDropdown>
|
||||
)}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@@ -1,13 +1,27 @@
|
||||
import { style } from '@vanilla-extract/css'
|
||||
|
||||
import { sprinkles, themeVars } from '../../nft/css/sprinkles.css'
|
||||
import { sprinkles, themeVars, vars } from '../../nft/css/sprinkles.css'
|
||||
|
||||
export const hover = style([
|
||||
sprinkles({
|
||||
transition: '250',
|
||||
borderRadius: '12',
|
||||
}),
|
||||
{
|
||||
':hover': {
|
||||
background: vars.color.lightGrayOverlay,
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
export const MenuRow = style([
|
||||
hover,
|
||||
sprinkles({
|
||||
color: 'blackBlue',
|
||||
paddingY: '12',
|
||||
width: 'max',
|
||||
marginRight: '52',
|
||||
color: 'textPrimary',
|
||||
paddingY: '8',
|
||||
paddingX: '8',
|
||||
width: 'full',
|
||||
whiteSpace: 'nowrap',
|
||||
}),
|
||||
{
|
||||
lineHeight: '24px',
|
||||
@@ -22,9 +36,12 @@ export const PrimaryText = style([
|
||||
])
|
||||
|
||||
export const SecondaryText = style([
|
||||
hover,
|
||||
sprinkles({
|
||||
paddingY: '8',
|
||||
color: 'darkGray',
|
||||
paddingX: '8',
|
||||
color: 'textSecondary',
|
||||
width: 'full',
|
||||
}),
|
||||
{
|
||||
lineHeight: '20px',
|
||||
@@ -34,10 +51,11 @@ export const SecondaryText = style([
|
||||
export const Separator = style([
|
||||
sprinkles({
|
||||
height: '0',
|
||||
marginX: '16',
|
||||
}),
|
||||
{
|
||||
borderTop: 'solid',
|
||||
borderColor: themeVars.colors.medGray,
|
||||
borderColor: themeVars.colors.backgroundOutline,
|
||||
borderWidth: '1px',
|
||||
},
|
||||
])
|
||||
@@ -45,6 +63,6 @@ export const Separator = style([
|
||||
export const IconRow = style([
|
||||
sprinkles({
|
||||
paddingX: '16',
|
||||
paddingY: '8',
|
||||
justifyContent: { sm: 'center', md: 'flex-start' },
|
||||
}),
|
||||
])
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import FeatureFlagModal from 'components/FeatureFlagModal/FeatureFlagModal'
|
||||
import { PrivacyPolicyModal } from 'components/PrivacyPolicy'
|
||||
import { useOnClickOutside } from 'hooks/useOnClickOutside'
|
||||
@@ -97,7 +98,7 @@ const Icon = ({ href, children }: { href?: string; children: ReactNode }) => {
|
||||
rel={href ? 'noopener noreferrer' : undefined}
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
color="blackBlue"
|
||||
color="textPrimary"
|
||||
background="none"
|
||||
border="none"
|
||||
justifyContent="center"
|
||||
@@ -121,50 +122,83 @@ export const MenuDropdown = () => {
|
||||
return (
|
||||
<>
|
||||
<Box position="relative" ref={ref}>
|
||||
<NavIcon onClick={toggleOpen}>
|
||||
<EllipsisIcon width={28} height={28} />
|
||||
<NavIcon isActive={isOpen} onClick={toggleOpen}>
|
||||
<EllipsisIcon width={20} height={20} />
|
||||
</NavIcon>
|
||||
|
||||
{isOpen && (
|
||||
<NavDropdown top={60}>
|
||||
<Column gap="12">
|
||||
<Column paddingX="16" gap="4">
|
||||
<NavDropdown top={{ sm: 'unset', lg: '56' }} bottom={{ sm: '56', lg: 'unset' }} right="0">
|
||||
<Column gap="16">
|
||||
<Column paddingX="8" gap="4">
|
||||
<PrimaryMenuRow to="/vote" close={toggleOpen}>
|
||||
<Icon>
|
||||
<GovernanceIcon width={24} height={24} />
|
||||
</Icon>
|
||||
<PrimaryMenuRow.Text>Vote in governance</PrimaryMenuRow.Text>
|
||||
<PrimaryMenuRow.Text>
|
||||
<Trans>Vote in governance</Trans>
|
||||
</PrimaryMenuRow.Text>
|
||||
</PrimaryMenuRow>
|
||||
<PrimaryMenuRow href="https://info.uniswap.org/#/">
|
||||
<Icon>
|
||||
<BarChartIcon width={24} height={24} />
|
||||
</Icon>
|
||||
<PrimaryMenuRow.Text>View token analytics ↗</PrimaryMenuRow.Text>
|
||||
<PrimaryMenuRow.Text>
|
||||
<Trans>View more analytics</Trans>
|
||||
</PrimaryMenuRow.Text>
|
||||
</PrimaryMenuRow>
|
||||
</Column>
|
||||
<Separator />
|
||||
<Column paddingX="16" gap="4">
|
||||
<SecondaryLinkedText href="https://help.uniswap.org/en/">Help center ↗</SecondaryLinkedText>
|
||||
<SecondaryLinkedText href="https://docs.uniswap.org/">Documentation ↗</SecondaryLinkedText>
|
||||
<Box
|
||||
display="flex"
|
||||
flexDirection={{ sm: 'row', md: 'column' }}
|
||||
flexWrap="wrap"
|
||||
alignItems={{ sm: 'center', md: 'flex-start' }}
|
||||
paddingX="8"
|
||||
>
|
||||
<SecondaryLinkedText href="https://help.uniswap.org/en/">
|
||||
<Trans>Help center</Trans> ↗
|
||||
</SecondaryLinkedText>
|
||||
<SecondaryLinkedText href="https://docs.uniswap.org/">
|
||||
<Trans>Documentation</Trans> ↗
|
||||
</SecondaryLinkedText>
|
||||
<SecondaryLinkedText
|
||||
onClick={() => {
|
||||
toggleOpen()
|
||||
togglePrivacyPolicy()
|
||||
}}
|
||||
>{`Legal & Privacy`}</SecondaryLinkedText>
|
||||
>
|
||||
<Trans>Legal & Privacy</Trans> ↗
|
||||
</SecondaryLinkedText>
|
||||
{(isDevelopmentEnv() || isStagingEnv()) && (
|
||||
<SecondaryLinkedText onClick={openFeatureFlagsModal}>{`Feature Flags`}</SecondaryLinkedText>
|
||||
<SecondaryLinkedText onClick={openFeatureFlagsModal}>
|
||||
<Trans>Feature Flags</Trans>
|
||||
</SecondaryLinkedText>
|
||||
)}
|
||||
</Column>
|
||||
</Box>
|
||||
<IconRow>
|
||||
<Icon href="https://discord.com/invite/FCfyBSbCU5">
|
||||
<DiscordIconMenu width={24} height={24} color={themeVars.colors.darkGray} />
|
||||
<DiscordIconMenu
|
||||
className={styles.hover}
|
||||
width={24}
|
||||
height={24}
|
||||
color={themeVars.colors.textSecondary}
|
||||
/>
|
||||
</Icon>
|
||||
<Icon href="https://twitter.com/Uniswap">
|
||||
<TwitterIconMenu width={24} height={24} color={themeVars.colors.darkGray} />
|
||||
<TwitterIconMenu
|
||||
className={styles.hover}
|
||||
width={24}
|
||||
height={24}
|
||||
color={themeVars.colors.textSecondary}
|
||||
/>
|
||||
</Icon>
|
||||
<Icon href="https://github.com/Uniswap">
|
||||
<GithubIconMenu width={24} height={24} color={themeVars.colors.darkGray} />
|
||||
<GithubIconMenu
|
||||
className={styles.hover}
|
||||
width={24}
|
||||
height={24}
|
||||
color={themeVars.colors.textSecondary}
|
||||
/>
|
||||
</Icon>
|
||||
</IconRow>
|
||||
</Column>
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
import { style } from '@vanilla-extract/css'
|
||||
import { subhead } from 'nft/css/common.css'
|
||||
|
||||
import { sprinkles } from '../../nft/css/sprinkles.css'
|
||||
|
||||
export const sidebar = style([
|
||||
sprinkles({
|
||||
display: 'flex',
|
||||
position: 'fixed',
|
||||
background: 'white',
|
||||
height: 'full',
|
||||
top: '0',
|
||||
left: '0',
|
||||
right: '0',
|
||||
bottom: '0',
|
||||
paddingBottom: '16',
|
||||
justifyContent: 'space-between',
|
||||
}),
|
||||
{
|
||||
zIndex: 20,
|
||||
},
|
||||
])
|
||||
|
||||
export const icon = style([
|
||||
sprinkles({
|
||||
width: '32',
|
||||
height: '32',
|
||||
}),
|
||||
])
|
||||
|
||||
export const iconContainer = style([
|
||||
sprinkles({
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
color: 'darkGray',
|
||||
background: 'none',
|
||||
border: 'none',
|
||||
justifyContent: 'flex-end',
|
||||
textAlign: 'center',
|
||||
cursor: 'pointer',
|
||||
padding: '6',
|
||||
}),
|
||||
])
|
||||
|
||||
export const linkRow = style([
|
||||
subhead,
|
||||
sprinkles({
|
||||
color: 'blackBlue',
|
||||
width: 'full',
|
||||
paddingLeft: '16',
|
||||
paddingY: '12',
|
||||
cursor: 'pointer',
|
||||
}),
|
||||
{
|
||||
lineHeight: '24px',
|
||||
textDecoration: 'none',
|
||||
},
|
||||
])
|
||||
|
||||
export const activeLinkRow = style([
|
||||
linkRow,
|
||||
sprinkles({
|
||||
background: 'lightGrayButton',
|
||||
}),
|
||||
])
|
||||
|
||||
export const separator = style([
|
||||
sprinkles({
|
||||
height: '0',
|
||||
borderStyle: 'solid',
|
||||
borderColor: 'medGray',
|
||||
borderWidth: '1px',
|
||||
marginY: '8',
|
||||
marginX: '16',
|
||||
}),
|
||||
])
|
||||
|
||||
export const extraLinkRow = style([
|
||||
subhead,
|
||||
sprinkles({
|
||||
width: 'full',
|
||||
color: 'blackBlue',
|
||||
paddingY: '12',
|
||||
paddingLeft: '16',
|
||||
cursor: 'pointer',
|
||||
}),
|
||||
{
|
||||
lineHeight: '24px',
|
||||
textDecoration: 'none',
|
||||
},
|
||||
])
|
||||
|
||||
export const bottomExternalLinks = style([
|
||||
sprinkles({
|
||||
gap: '4',
|
||||
paddingX: '4',
|
||||
width: 'max',
|
||||
flexWrap: 'wrap',
|
||||
}),
|
||||
])
|
||||
|
||||
export const bottomJointExternalLinksContainer = style([
|
||||
sprinkles({
|
||||
paddingX: '8',
|
||||
paddingY: '4',
|
||||
color: 'darkGray',
|
||||
fontWeight: 'medium',
|
||||
fontSize: '12',
|
||||
}),
|
||||
{
|
||||
lineHeight: '20px',
|
||||
},
|
||||
])
|
||||
|
||||
export const IconRow = style([
|
||||
sprinkles({
|
||||
gap: '12',
|
||||
width: 'max',
|
||||
}),
|
||||
])
|
||||
@@ -1,233 +0,0 @@
|
||||
import FeatureFlagModal from 'components/FeatureFlagModal/FeatureFlagModal'
|
||||
import { PrivacyPolicyModal } from 'components/PrivacyPolicy'
|
||||
import { Box } from 'nft/components/Box'
|
||||
import { Portal } from 'nft/components/common/Portal'
|
||||
import { Column, Row } from 'nft/components/Flex'
|
||||
import {
|
||||
BarChartIconMobile,
|
||||
BulletIcon,
|
||||
CloseIcon,
|
||||
DiscordIconMenuMobile,
|
||||
GithubIconMenuMobile,
|
||||
GovernanceIconMobile,
|
||||
HamburgerIcon,
|
||||
TwitterIconMenuMobile,
|
||||
} from 'nft/components/icons'
|
||||
import { themeVars } from 'nft/css/sprinkles.css'
|
||||
import { ReactNode, useReducer } from 'react'
|
||||
import { NavLink, NavLinkProps, useLocation } from 'react-router-dom'
|
||||
import { useToggleModal, useTogglePrivacyPolicy } from 'state/application/hooks'
|
||||
import { ApplicationModal } from 'state/application/reducer'
|
||||
import { isDevelopmentEnv, isStagingEnv } from 'utils/env'
|
||||
|
||||
import * as styles from './MobileSidebar.css'
|
||||
import { NavIcon } from './NavIcon'
|
||||
|
||||
interface NavLinkRowProps {
|
||||
href: string
|
||||
id?: NavLinkProps['id']
|
||||
isActive?: boolean
|
||||
close: () => void
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
const NavLinkRow = ({ href, id, isActive, close, children }: NavLinkRowProps) => {
|
||||
return (
|
||||
<NavLink to={href} className={isActive ? styles.activeLinkRow : styles.linkRow} id={id} onClick={close}>
|
||||
{children}
|
||||
</NavLink>
|
||||
)
|
||||
}
|
||||
|
||||
const ExtraLinkRow = ({
|
||||
to,
|
||||
href,
|
||||
close,
|
||||
children,
|
||||
}: {
|
||||
to?: NavLinkProps['to']
|
||||
href?: string
|
||||
close: () => void
|
||||
children: ReactNode
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
{to ? (
|
||||
<NavLink to={to} className={styles.extraLinkRow}>
|
||||
<Row gap="12" onClick={close}>
|
||||
{children}
|
||||
</Row>
|
||||
</NavLink>
|
||||
) : (
|
||||
<Row
|
||||
as="a"
|
||||
href={href}
|
||||
target={'_blank'}
|
||||
rel={'noopener noreferrer'}
|
||||
gap="12"
|
||||
onClick={close}
|
||||
className={styles.extraLinkRow}
|
||||
>
|
||||
{children}
|
||||
</Row>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const BottomExternalLink = ({
|
||||
href,
|
||||
onClick,
|
||||
children,
|
||||
}: {
|
||||
href?: string
|
||||
onClick?: () => void
|
||||
children: ReactNode
|
||||
}) => {
|
||||
return (
|
||||
<Box
|
||||
as={href ? 'a' : 'div'}
|
||||
href={href ?? undefined}
|
||||
target={href ? '_blank' : undefined}
|
||||
rel={href ? 'noopener noreferrer' : undefined}
|
||||
className={`${styles.bottomJointExternalLinksContainer}`}
|
||||
onClick={onClick}
|
||||
cursor="pointer"
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
const Icon = ({ href, children }: { href?: string; children: ReactNode }) => {
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
as={href ? 'a' : 'div'}
|
||||
href={href ?? undefined}
|
||||
target={href ? '_blank' : undefined}
|
||||
rel={href ? 'noopener noreferrer' : undefined}
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
color="blackBlue"
|
||||
background="none"
|
||||
border="none"
|
||||
justifyContent="center"
|
||||
textAlign="center"
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const IconRow = ({ children }: { children: ReactNode }) => {
|
||||
return <Row className={styles.IconRow}>{children}</Row>
|
||||
}
|
||||
|
||||
const Seperator = () => {
|
||||
return <Box className={styles.separator} />
|
||||
}
|
||||
|
||||
export const MobileSideBar = () => {
|
||||
const [isOpen, toggleOpen] = useReducer((s) => !s, false)
|
||||
const togglePrivacyPolicy = useTogglePrivacyPolicy()
|
||||
const openFeatureFlagsModal = useToggleModal(ApplicationModal.FEATURE_FLAGS)
|
||||
const { pathname } = useLocation()
|
||||
const isPoolActive =
|
||||
pathname.startsWith('/pool') ||
|
||||
pathname.startsWith('/add') ||
|
||||
pathname.startsWith('/remove') ||
|
||||
pathname.startsWith('/increase') ||
|
||||
pathname.startsWith('/find')
|
||||
|
||||
return (
|
||||
<>
|
||||
<NavIcon onClick={toggleOpen}>
|
||||
<HamburgerIcon width={28} height={28} />
|
||||
</NavIcon>
|
||||
{isOpen && (
|
||||
<Portal>
|
||||
<Column className={styles.sidebar}>
|
||||
<Column>
|
||||
<Row justifyContent="flex-end" marginTop="14" marginBottom="20" marginRight="8">
|
||||
<Box as="button" onClick={toggleOpen} className={styles.iconContainer}>
|
||||
<CloseIcon className={styles.icon} />
|
||||
</Box>
|
||||
</Row>
|
||||
<Column gap="4">
|
||||
<NavLinkRow href="/swap" close={toggleOpen} isActive={pathname.startsWith('/swap')}>
|
||||
Swap
|
||||
</NavLinkRow>
|
||||
<NavLinkRow href="/tokens" close={toggleOpen} isActive={pathname.startsWith('/tokens')}>
|
||||
Tokens
|
||||
</NavLinkRow>
|
||||
<NavLinkRow href="/pool" id={'pool-nav-link'} isActive={isPoolActive} close={toggleOpen}>
|
||||
Pool
|
||||
</NavLinkRow>
|
||||
</Column>
|
||||
<Seperator />
|
||||
<Column gap="4">
|
||||
<ExtraLinkRow to="/vote" close={toggleOpen}>
|
||||
<Icon>
|
||||
<GovernanceIconMobile width={24} height={24} />
|
||||
</Icon>
|
||||
Vote in governance
|
||||
</ExtraLinkRow>
|
||||
<ExtraLinkRow href="https://info.uniswap.org/#/" close={toggleOpen}>
|
||||
<Icon>
|
||||
<BarChartIconMobile width={24} height={24} />
|
||||
</Icon>
|
||||
View token analytics ↗
|
||||
</ExtraLinkRow>
|
||||
</Column>
|
||||
</Column>
|
||||
<Column>
|
||||
<Row justifyContent="center" marginBottom="12" flexWrap="wrap">
|
||||
<Row className={styles.bottomExternalLinks}>
|
||||
<BottomExternalLink href="https://help.uniswap.org/en/" onClick={toggleOpen}>
|
||||
Help center ↗
|
||||
</BottomExternalLink>
|
||||
<BulletIcon />
|
||||
<BottomExternalLink href="https://docs.uniswap.org/" onClick={toggleOpen}>
|
||||
Documentation ↗
|
||||
</BottomExternalLink>
|
||||
<BulletIcon />
|
||||
<BottomExternalLink
|
||||
onClick={() => {
|
||||
toggleOpen()
|
||||
togglePrivacyPolicy()
|
||||
}}
|
||||
>
|
||||
{`Legal & Privacy`}
|
||||
</BottomExternalLink>
|
||||
</Row>
|
||||
{(isDevelopmentEnv() || isStagingEnv()) && (
|
||||
<>
|
||||
<BulletIcon />
|
||||
<BottomExternalLink onClick={openFeatureFlagsModal}>{`Feature Flags`}</BottomExternalLink>
|
||||
</>
|
||||
)}
|
||||
</Row>
|
||||
<Row justifyContent="center">
|
||||
<IconRow>
|
||||
<Icon href="https://discord.com/invite/FCfyBSbCU5">
|
||||
<DiscordIconMenuMobile width={32} height={32} color={themeVars.colors.darkGray} />
|
||||
</Icon>
|
||||
<Icon href="https://twitter.com/Uniswap">
|
||||
<TwitterIconMenuMobile width={32} height={32} color={themeVars.colors.darkGray} />
|
||||
</Icon>
|
||||
<Icon href="https://github.com/Uniswap">
|
||||
<GithubIconMenuMobile width={32} height={32} color={themeVars.colors.darkGray} />
|
||||
</Icon>
|
||||
</IconRow>
|
||||
</Row>
|
||||
</Column>
|
||||
</Column>
|
||||
</Portal>
|
||||
)}
|
||||
<PrivacyPolicyModal />
|
||||
<FeatureFlagModal />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -2,18 +2,44 @@ import { style } from '@vanilla-extract/css'
|
||||
|
||||
import { sprinkles } from '../../nft/css/sprinkles.css'
|
||||
|
||||
export const NavDropdown = style([
|
||||
const baseNavDropdown = style([
|
||||
sprinkles({
|
||||
position: 'absolute',
|
||||
background: 'white95',
|
||||
borderRadius: '12',
|
||||
background: 'backgroundSurface',
|
||||
borderStyle: 'solid',
|
||||
borderColor: 'medGray',
|
||||
paddingY: '20',
|
||||
borderColor: 'backgroundOutline',
|
||||
borderWidth: '1px',
|
||||
paddingBottom: '8',
|
||||
paddingTop: '8',
|
||||
zIndex: '2',
|
||||
}),
|
||||
{
|
||||
boxShadow: '0px 4px 12px 0px #00000026',
|
||||
zIndex: 10,
|
||||
},
|
||||
])
|
||||
|
||||
export const NavDropdown = style([
|
||||
baseNavDropdown,
|
||||
sprinkles({
|
||||
position: 'absolute',
|
||||
borderRadius: '12',
|
||||
}),
|
||||
{},
|
||||
])
|
||||
|
||||
export const mobileNavDropdown = style([
|
||||
baseNavDropdown,
|
||||
sprinkles({
|
||||
position: 'fixed',
|
||||
borderTopRightRadius: '12',
|
||||
borderTopLeftRadius: '12',
|
||||
top: 'unset',
|
||||
bottom: '56',
|
||||
left: '0',
|
||||
right: '0',
|
||||
width: 'full',
|
||||
}),
|
||||
{
|
||||
borderRightWidth: '0px',
|
||||
borderLeftWidth: '0px',
|
||||
},
|
||||
])
|
||||
|
||||
@@ -1,37 +1,12 @@
|
||||
import { Box } from 'nft/components/Box'
|
||||
import { ReactNode } from 'react'
|
||||
import { Box, BoxProps } from 'nft/components/Box'
|
||||
import { useIsMobile } from 'nft/hooks'
|
||||
import { ForwardedRef, forwardRef } from 'react'
|
||||
|
||||
import * as styles from './NavDropdown.css'
|
||||
|
||||
interface NavDropdownProps {
|
||||
top: number
|
||||
right?: number
|
||||
leftAligned?: boolean
|
||||
horizontalPadding?: boolean
|
||||
centerHorizontally?: boolean
|
||||
children: ReactNode
|
||||
}
|
||||
export const NavDropdown = forwardRef((props: BoxProps, ref: ForwardedRef<HTMLElement>) => {
|
||||
const isMobile = useIsMobile()
|
||||
return <Box ref={ref} className={isMobile ? styles.mobileNavDropdown : styles.NavDropdown} {...props} />
|
||||
})
|
||||
|
||||
export const NavDropdown = ({
|
||||
top,
|
||||
centerHorizontally,
|
||||
leftAligned,
|
||||
horizontalPadding,
|
||||
children,
|
||||
}: NavDropdownProps) => {
|
||||
return (
|
||||
<Box
|
||||
paddingX={horizontalPadding ? '16' : undefined}
|
||||
style={{
|
||||
top: `${top}px`,
|
||||
left: centerHorizontally ? '50%' : leftAligned ? '0px' : 'auto',
|
||||
right: centerHorizontally || leftAligned ? 'auto' : '10px',
|
||||
transform: centerHorizontally ? 'translateX(-50%)' : 'unset',
|
||||
zIndex: 3,
|
||||
}}
|
||||
className={styles.NavDropdown}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
NavDropdown.displayName = 'NavDropdown'
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
import { style } from '@vanilla-extract/css'
|
||||
|
||||
import { sprinkles, themeVars } from '../../nft/css/sprinkles.css'
|
||||
import { sprinkles, vars } from '../../nft/css/sprinkles.css'
|
||||
|
||||
export const navIcon = style([
|
||||
sprinkles({
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
color: 'blackBlue',
|
||||
background: 'none',
|
||||
border: 'none',
|
||||
justifyContent: 'center',
|
||||
textAlign: 'center',
|
||||
cursor: 'pointer',
|
||||
padding: '8',
|
||||
padding: '10',
|
||||
borderRadius: '8',
|
||||
transition: '250',
|
||||
}),
|
||||
{
|
||||
':hover': {
|
||||
background: themeVars.colors.lightGrayContainer,
|
||||
background: vars.color.lightGrayOverlay,
|
||||
},
|
||||
zIndex: 2,
|
||||
zIndex: 1,
|
||||
},
|
||||
])
|
||||
|
||||
@@ -5,12 +5,20 @@ import * as styles from './NavIcon.css'
|
||||
|
||||
interface NavIconProps {
|
||||
children: ReactNode
|
||||
isActive?: boolean
|
||||
onClick: () => void
|
||||
}
|
||||
|
||||
export const NavIcon = ({ children, onClick }: NavIconProps) => {
|
||||
export const NavIcon = ({ children, isActive, onClick }: NavIconProps) => {
|
||||
return (
|
||||
<Box as="button" className={styles.navIcon} onClick={onClick}>
|
||||
<Box
|
||||
as="button"
|
||||
className={styles.navIcon}
|
||||
background={isActive ? 'accentActiveSoft' : 'none'}
|
||||
color={isActive ? 'textPrimary' : 'textSecondary'}
|
||||
onClick={onClick}
|
||||
height="40"
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
)
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
import Web3Status from 'components/Web3Status'
|
||||
import { useWindowSize } from 'hooks/useWindowSize'
|
||||
import { ReactNode } from 'react'
|
||||
import { NavLink, NavLinkProps, useLocation } from 'react-router-dom'
|
||||
|
||||
import { Box } from '../../nft/components/Box'
|
||||
import { Row } from '../../nft/components/Flex'
|
||||
import { UniIcon, UniIconMobile } from '../../nft/components/icons'
|
||||
import { breakpoints } from '../../nft/css/sprinkles.css'
|
||||
import { ChainSwitcher } from './ChainSwitcher'
|
||||
import { MenuDropdown } from './MenuDropdown'
|
||||
import { MobileSideBar } from './MobileSidebar'
|
||||
import * as styles from './Navbar.css'
|
||||
|
||||
interface MenuItemProps {
|
||||
href: string
|
||||
id?: NavLinkProps['id']
|
||||
isActive?: boolean
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
const MenuItem = ({ href, id, isActive, children }: MenuItemProps) => {
|
||||
return (
|
||||
<NavLink
|
||||
to={href}
|
||||
className={isActive ? styles.activeMenuItem : styles.menuItem}
|
||||
id={id}
|
||||
style={{ textDecoration: 'none' }}
|
||||
>
|
||||
{children}
|
||||
</NavLink>
|
||||
)
|
||||
}
|
||||
|
||||
const MobileNavbar = () => {
|
||||
return (
|
||||
<>
|
||||
<nav className={styles.nav}>
|
||||
<Box display="flex" height="full" flexWrap="nowrap" alignItems="stretch">
|
||||
<Box className={styles.leftSideMobileContainer}>
|
||||
<Box as="a" href="#/swap" className={styles.logoContainer}>
|
||||
<UniIconMobile width="44" height="44" className={styles.logo} />
|
||||
</Box>
|
||||
<ChainSwitcher isMobile={true} />
|
||||
</Box>
|
||||
<Box className={styles.rightSideMobileContainer}>
|
||||
<Row gap="16">
|
||||
{/* TODO add Searchbar */}
|
||||
<MobileSideBar />
|
||||
</Row>
|
||||
</Box>
|
||||
</Box>
|
||||
</nav>
|
||||
<Box className={styles.mobileWalletContainer}>
|
||||
<Web3Status />
|
||||
</Box>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const Navbar = () => {
|
||||
const { width: windowWidth } = useWindowSize()
|
||||
const { pathname } = useLocation()
|
||||
|
||||
if (windowWidth && windowWidth < breakpoints.desktopXl) {
|
||||
return <MobileNavbar />
|
||||
}
|
||||
|
||||
const isPoolActive =
|
||||
pathname.startsWith('/pool') ||
|
||||
pathname.startsWith('/add') ||
|
||||
pathname.startsWith('/remove') ||
|
||||
pathname.startsWith('/increase') ||
|
||||
pathname.startsWith('/find')
|
||||
|
||||
return (
|
||||
<nav className={styles.nav}>
|
||||
<Box display="flex" height="full" flexWrap="nowrap" alignItems="stretch">
|
||||
<Box className={styles.leftSideContainer}>
|
||||
<Box as="a" href="#/swap" className={styles.logoContainer}>
|
||||
<UniIcon width="48" height="48" className={styles.logo} />
|
||||
</Box>
|
||||
<Row gap="8">
|
||||
<MenuItem href="/swap" isActive={pathname.startsWith('/swap')}>
|
||||
Swap
|
||||
</MenuItem>
|
||||
<MenuItem href="/tokens" isActive={pathname.startsWith('/explore')}>
|
||||
Tokens
|
||||
</MenuItem>
|
||||
<MenuItem href="/pool" id={'pool-nav-link'} isActive={isPoolActive}>
|
||||
Pool
|
||||
</MenuItem>
|
||||
</Row>
|
||||
</Box>
|
||||
<Box className={styles.middleContainer}>{/* TODO add Searchbar */}</Box>
|
||||
<Box className={styles.rightSideContainer}>
|
||||
<Row gap="12">
|
||||
<MenuDropdown />
|
||||
<ChainSwitcher />
|
||||
<Web3Status />
|
||||
</Row>
|
||||
</Box>
|
||||
</Box>
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
export default Navbar
|
||||
214
src/components/NavBar/SearchBar.css.ts
Normal file
@@ -0,0 +1,214 @@
|
||||
import { style } from '@vanilla-extract/css'
|
||||
import { buttonTextSmall, subhead, subheadSmall } from 'nft/css/common.css'
|
||||
|
||||
import { breakpoints, sprinkles, vars } from '../../nft/css/sprinkles.css'
|
||||
|
||||
const DESKTOP_NAVBAR_WIDTH = 360
|
||||
const MAGNIFYING_GLASS_ICON_WIDTH = 28
|
||||
|
||||
const baseSearchStyle = style([
|
||||
sprinkles({
|
||||
paddingY: '12',
|
||||
width: { sm: 'viewWidth' },
|
||||
borderStyle: 'solid',
|
||||
borderWidth: '1px',
|
||||
borderColor: 'backgroundOutline',
|
||||
}),
|
||||
{
|
||||
'@media': {
|
||||
[`screen and (min-width: ${breakpoints.sm}px)`]: {
|
||||
width: `${DESKTOP_NAVBAR_WIDTH}px`,
|
||||
},
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
export const searchBarContainer = style([
|
||||
sprinkles({
|
||||
right: '0',
|
||||
top: '0',
|
||||
zIndex: '3',
|
||||
display: 'inline-block',
|
||||
}),
|
||||
{
|
||||
'@media': {
|
||||
[`screen and (min-width: ${breakpoints.lg}px)`]: {
|
||||
right: `-${DESKTOP_NAVBAR_WIDTH / 2 - MAGNIFYING_GLASS_ICON_WIDTH}px`,
|
||||
top: '-3px',
|
||||
},
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
export const searchBar = style([
|
||||
baseSearchStyle,
|
||||
sprinkles({
|
||||
color: 'textTertiary',
|
||||
paddingX: '16',
|
||||
background: 'backgroundSurface',
|
||||
}),
|
||||
])
|
||||
|
||||
export const searchBarInput = style([
|
||||
sprinkles({
|
||||
padding: '0',
|
||||
fontWeight: 'normal',
|
||||
fontSize: '16',
|
||||
color: { default: 'textPrimary', placeholder: 'textTertiary' },
|
||||
border: 'none',
|
||||
background: 'none',
|
||||
lineHeight: '24',
|
||||
height: 'full',
|
||||
}),
|
||||
])
|
||||
|
||||
export const searchBarDropdown = style([
|
||||
baseSearchStyle,
|
||||
sprinkles({
|
||||
borderBottomLeftRadius: '12',
|
||||
borderBottomRightRadius: '12',
|
||||
background: 'backgroundSurface',
|
||||
height: { sm: 'viewHeight', md: 'auto' },
|
||||
}),
|
||||
{
|
||||
borderTop: 'none',
|
||||
},
|
||||
])
|
||||
|
||||
export const suggestionRow = style([
|
||||
sprinkles({
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
paddingY: '8',
|
||||
paddingX: '16',
|
||||
cursor: 'pointer',
|
||||
}),
|
||||
{
|
||||
':hover': {
|
||||
background: vars.color.lightGrayOverlay,
|
||||
},
|
||||
textDecoration: 'none',
|
||||
},
|
||||
])
|
||||
|
||||
export const suggestionImage = sprinkles({
|
||||
width: '36',
|
||||
height: '36',
|
||||
borderRadius: 'round',
|
||||
marginRight: '8',
|
||||
})
|
||||
|
||||
export const suggestionPrimaryContainer = style([
|
||||
sprinkles({
|
||||
alignItems: 'flex-start',
|
||||
width: 'full',
|
||||
}),
|
||||
])
|
||||
|
||||
export const suggestionSecondaryContainer = sprinkles({
|
||||
textAlign: 'right',
|
||||
alignItems: 'flex-end',
|
||||
})
|
||||
|
||||
export const primaryText = style([
|
||||
subhead,
|
||||
sprinkles({
|
||||
overflow: 'hidden',
|
||||
whiteSpace: 'nowrap',
|
||||
textOverflow: 'ellipsis',
|
||||
color: 'textPrimary',
|
||||
}),
|
||||
{
|
||||
lineHeight: '24px',
|
||||
},
|
||||
])
|
||||
|
||||
export const secondaryText = style([
|
||||
buttonTextSmall,
|
||||
sprinkles({
|
||||
color: 'textSecondary',
|
||||
}),
|
||||
{
|
||||
lineHeight: '20px',
|
||||
},
|
||||
])
|
||||
|
||||
export const imageHolder = style([
|
||||
suggestionImage,
|
||||
sprinkles({
|
||||
background: 'backgroundModule',
|
||||
flexShrink: '0',
|
||||
}),
|
||||
])
|
||||
|
||||
export const suggestionIcon = sprinkles({
|
||||
display: 'flex',
|
||||
flexShrink: '0',
|
||||
})
|
||||
|
||||
export const sectionHeader = style([
|
||||
subheadSmall,
|
||||
sprinkles({
|
||||
color: 'textSecondary',
|
||||
}),
|
||||
{
|
||||
lineHeight: '20px',
|
||||
},
|
||||
])
|
||||
|
||||
export const notFoundContainer = style([
|
||||
sectionHeader,
|
||||
sprinkles({
|
||||
paddingY: '4',
|
||||
paddingLeft: '16',
|
||||
}),
|
||||
])
|
||||
|
||||
const visibilityTransition = `visibility ${vars.time[125]}, opacity ${vars.time[125]}`
|
||||
const delayedTransitionProperties = `padding 0s ${vars.time[125]}, height 0s ${vars.time[125]}`
|
||||
|
||||
export const hidden = style([
|
||||
sprinkles({
|
||||
visibility: 'hidden',
|
||||
opacity: '0',
|
||||
padding: '0',
|
||||
height: '0',
|
||||
}),
|
||||
{
|
||||
transition: `${visibilityTransition}, ${delayedTransitionProperties}`,
|
||||
transitionTimingFunction: 'ease-in',
|
||||
},
|
||||
])
|
||||
export const visible = style([
|
||||
sprinkles({
|
||||
visibility: 'visible',
|
||||
opacity: '1',
|
||||
height: 'full',
|
||||
}),
|
||||
{
|
||||
transition: `${visibilityTransition}`,
|
||||
transitionTimingFunction: 'ease-out',
|
||||
},
|
||||
])
|
||||
|
||||
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)`]: {
|
||||
transform: 'translateX(0)',
|
||||
transition: `transform ${vars.time[125]}`,
|
||||
transitionTimingFunction: 'ease-in',
|
||||
},
|
||||
},
|
||||
})
|
||||
171
src/components/NavBar/SearchBar.tsx
Normal file
@@ -0,0 +1,171 @@
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { t } from '@lingui/macro'
|
||||
import { sendAnalyticsEvent } from 'analytics'
|
||||
import { ElementName, Event, EventName } from 'analytics/constants'
|
||||
import { TraceEvent } from 'analytics/TraceEvent'
|
||||
import clsx from 'clsx'
|
||||
import { NftVariant, useNftFlag } from 'featureFlags/flags/nft'
|
||||
import useDebounce from 'hooks/useDebounce'
|
||||
import { useOnClickOutside } from 'hooks/useOnClickOutside'
|
||||
import { organizeSearchResults } from 'lib/utils/searchBar'
|
||||
import { Box } from 'nft/components/Box'
|
||||
import { Row } from 'nft/components/Flex'
|
||||
import { magicalGradientOnHover } from 'nft/css/common.css'
|
||||
import { useIsMobile, useIsTablet } from 'nft/hooks'
|
||||
import { fetchSearchCollections } from 'nft/queries'
|
||||
import { fetchSearchTokens } from 'nft/queries/genie/SearchTokensFetcher'
|
||||
import { ChangeEvent, useEffect, useReducer, useRef, useState } from 'react'
|
||||
import { useQuery } from 'react-query'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
import { ChevronLeftIcon, MagnifyingGlassIcon, NavMagnifyingGlassIcon } from '../../nft/components/icons'
|
||||
import { NavIcon } from './NavIcon'
|
||||
import * as styles from './SearchBar.css'
|
||||
import { SearchBarDropdown } from './SearchBarDropdown'
|
||||
|
||||
export const SearchBar = () => {
|
||||
const [isOpen, toggleOpen] = useReducer((state: boolean) => !state, false)
|
||||
const [searchValue, setSearchValue] = useState('')
|
||||
const debouncedSearchValue = useDebounce(searchValue, 300)
|
||||
const searchRef = useRef<HTMLDivElement>(null)
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
const { pathname } = useLocation()
|
||||
const phase1Flag = useNftFlag()
|
||||
const isMobile = useIsMobile()
|
||||
const isTablet = useIsTablet()
|
||||
|
||||
useOnClickOutside(searchRef, () => {
|
||||
isOpen && toggleOpen()
|
||||
})
|
||||
|
||||
const { data: collections, isLoading: collectionsAreLoading } = useQuery(
|
||||
['searchCollections', debouncedSearchValue],
|
||||
() => fetchSearchCollections(debouncedSearchValue),
|
||||
{
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: false,
|
||||
refetchOnReconnect: false,
|
||||
}
|
||||
)
|
||||
|
||||
const { data: tokens, isLoading: tokensAreLoading } = useQuery(
|
||||
['searchTokens', debouncedSearchValue],
|
||||
() => fetchSearchTokens(debouncedSearchValue),
|
||||
{
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: false,
|
||||
refetchOnReconnect: false,
|
||||
}
|
||||
)
|
||||
|
||||
const isNFTPage = pathname.includes('/nfts')
|
||||
|
||||
const [reducedTokens, reducedCollections] = organizeSearchResults(isNFTPage, tokens ?? [], collections ?? [])
|
||||
|
||||
// close dropdown on escape
|
||||
useEffect(() => {
|
||||
const escapeKeyDownHandler = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape' && isOpen) {
|
||||
event.preventDefault()
|
||||
toggleOpen()
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', escapeKeyDownHandler)
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('keydown', escapeKeyDownHandler)
|
||||
}
|
||||
}, [isOpen, toggleOpen, collections])
|
||||
|
||||
// clear searchbar when changing pages
|
||||
useEffect(() => {
|
||||
setSearchValue('')
|
||||
}, [pathname])
|
||||
|
||||
// auto set cursor when searchbar is opened
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
inputRef.current?.focus()
|
||||
}
|
||||
}, [isOpen])
|
||||
|
||||
const placeholderText = phase1Flag === NftVariant.Enabled ? t`Search tokens and NFT collections` : t`Search tokens`
|
||||
const isMobileOrTablet = isMobile || isTablet
|
||||
const showCenteredSearchContent =
|
||||
!isOpen && phase1Flag !== NftVariant.Enabled && !isMobileOrTablet && searchValue.length === 0
|
||||
|
||||
const navbarSearchEventProperties = {
|
||||
navbar_search_input_text: debouncedSearchValue,
|
||||
}
|
||||
|
||||
return (
|
||||
<Box position="relative">
|
||||
<Box
|
||||
position={{ sm: 'fixed', md: 'absolute' }}
|
||||
width={{ sm: isOpen ? 'viewWidth' : 'auto', md: 'auto' }}
|
||||
ref={searchRef}
|
||||
className={styles.searchBarContainer}
|
||||
display={{ sm: isOpen ? 'inline-block' : 'none', xl: 'inline-block' }}
|
||||
>
|
||||
<Row
|
||||
className={clsx(
|
||||
` ${styles.searchBar} ${!isOpen && !isMobile && magicalGradientOnHover} ${
|
||||
isMobileOrTablet && (isOpen ? styles.visible : styles.hidden)
|
||||
}`
|
||||
)}
|
||||
borderRadius={isOpen || isMobileOrTablet ? undefined : '12'}
|
||||
borderTopRightRadius={isOpen && !isMobile ? '12' : undefined}
|
||||
borderTopLeftRadius={isOpen && !isMobile ? '12' : undefined}
|
||||
borderBottomWidth={isOpen || isMobileOrTablet ? '0px' : '1px'}
|
||||
onClick={() => !isOpen && toggleOpen()}
|
||||
gap="12"
|
||||
>
|
||||
<Box className={showCenteredSearchContent ? styles.searchContentCentered : styles.searchContentLeftAlign}>
|
||||
<Box display={{ sm: 'none', md: 'flex' }}>
|
||||
<MagnifyingGlassIcon />
|
||||
</Box>
|
||||
<Box display={{ sm: 'flex', md: 'none' }} color="textTertiary" onClick={toggleOpen}>
|
||||
<ChevronLeftIcon />
|
||||
</Box>
|
||||
</Box>
|
||||
<TraceEvent
|
||||
events={[Event.onFocus]}
|
||||
name={EventName.NAVBAR_SEARCH_SELECTED}
|
||||
element={ElementName.NAVBAR_SEARCH_INPUT}
|
||||
>
|
||||
<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'}
|
||||
/>
|
||||
</TraceEvent>
|
||||
</Row>
|
||||
<Box className={clsx(isOpen ? styles.visible : styles.hidden)}>
|
||||
{isOpen && (
|
||||
<SearchBarDropdown
|
||||
toggleOpen={toggleOpen}
|
||||
tokens={reducedTokens}
|
||||
collections={reducedCollections}
|
||||
hasInput={debouncedSearchValue.length > 0}
|
||||
isLoading={tokensAreLoading || (collectionsAreLoading && phase1Flag === NftVariant.Enabled)}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
<NavIcon onClick={toggleOpen}>
|
||||
<NavMagnifyingGlassIcon />
|
||||
</NavIcon>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
312
src/components/NavBar/SearchBarDropdown.tsx
Normal file
@@ -0,0 +1,312 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { sendAnalyticsEvent } from 'analytics'
|
||||
import { EventName } from 'analytics/constants'
|
||||
import { NftVariant, useNftFlag } from 'featureFlags/flags/nft'
|
||||
import { Box } from 'nft/components/Box'
|
||||
import { Column, Row } from 'nft/components/Flex'
|
||||
import { subheadSmall } from 'nft/css/common.css'
|
||||
import { useSearchHistory } from 'nft/hooks'
|
||||
import { fetchTrendingCollections } from 'nft/queries'
|
||||
import { fetchTrendingTokens } from 'nft/queries/genie/TrendingTokensFetcher'
|
||||
import { FungibleToken, GenieCollection, TimePeriod, TrendingCollection } from 'nft/types'
|
||||
import { formatEthPrice } from 'nft/utils/currency'
|
||||
import { ReactNode, useEffect, useMemo, useState } from 'react'
|
||||
import { useQuery } from 'react-query'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
import { ClockIcon, TrendingArrow } from '../../nft/components/icons'
|
||||
import * as styles from './SearchBar.css'
|
||||
import { CollectionRow, SkeletonRow, TokenRow } from './SuggestionRow'
|
||||
|
||||
function isCollection(suggestion: GenieCollection | FungibleToken | TrendingCollection) {
|
||||
return (suggestion as FungibleToken).decimals === undefined
|
||||
}
|
||||
|
||||
interface SearchBarDropdownSectionProps {
|
||||
toggleOpen: () => void
|
||||
suggestions: (GenieCollection | FungibleToken)[]
|
||||
header: JSX.Element
|
||||
headerIcon?: JSX.Element
|
||||
hoveredIndex: number | undefined
|
||||
startingIndex: number
|
||||
setHoveredIndex: (index: number | undefined) => void
|
||||
isLoading?: boolean
|
||||
}
|
||||
|
||||
export const SearchBarDropdownSection = ({
|
||||
toggleOpen,
|
||||
suggestions,
|
||||
header,
|
||||
headerIcon = undefined,
|
||||
hoveredIndex,
|
||||
startingIndex,
|
||||
setHoveredIndex,
|
||||
isLoading,
|
||||
}: SearchBarDropdownSectionProps) => {
|
||||
return (
|
||||
<Column gap="12">
|
||||
<Row paddingX="16" paddingY="4" gap="8" color="grey300" className={subheadSmall} style={{ lineHeight: '20px' }}>
|
||||
{headerIcon ? headerIcon : null}
|
||||
<Box>{header}</Box>
|
||||
</Row>
|
||||
<Column gap="12">
|
||||
{suggestions.map((suggestion, index) =>
|
||||
isLoading ? (
|
||||
<SkeletonRow key={index} />
|
||||
) : isCollection(suggestion) ? (
|
||||
<CollectionRow
|
||||
key={suggestion.address}
|
||||
collection={suggestion as GenieCollection}
|
||||
isHovered={hoveredIndex === index + startingIndex}
|
||||
setHoveredIndex={setHoveredIndex}
|
||||
toggleOpen={toggleOpen}
|
||||
traceEvent={() =>
|
||||
sendAnalyticsEvent(EventName.NAVBAR_SEARCH_EXITED, {
|
||||
position: index,
|
||||
selected_type: 'collection',
|
||||
suggestion_count: suggestions.length,
|
||||
selected_name: suggestion.name,
|
||||
selected_address: suggestion.address,
|
||||
})
|
||||
}
|
||||
index={index + startingIndex}
|
||||
/>
|
||||
) : (
|
||||
<TokenRow
|
||||
key={suggestion.address}
|
||||
token={suggestion as FungibleToken}
|
||||
isHovered={hoveredIndex === index + startingIndex}
|
||||
setHoveredIndex={setHoveredIndex}
|
||||
toggleOpen={toggleOpen}
|
||||
traceEvent={() =>
|
||||
sendAnalyticsEvent(EventName.NAVBAR_SEARCH_EXITED, {
|
||||
position: index,
|
||||
selected_type: 'token',
|
||||
suggestion_count: suggestions.length,
|
||||
selected_name: suggestion.name,
|
||||
selected_address: suggestion.address,
|
||||
})
|
||||
}
|
||||
index={index + startingIndex}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</Column>
|
||||
</Column>
|
||||
)
|
||||
}
|
||||
|
||||
interface SearchBarDropdownProps {
|
||||
toggleOpen: () => void
|
||||
tokens: FungibleToken[]
|
||||
collections: GenieCollection[]
|
||||
hasInput: boolean
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
export const SearchBarDropdown = ({ toggleOpen, tokens, collections, hasInput, isLoading }: SearchBarDropdownProps) => {
|
||||
const [hoveredIndex, setHoveredIndex] = useState<number | undefined>(0)
|
||||
const { history: searchHistory, updateItem: updateSearchHistory } = useSearchHistory()
|
||||
const shortenedHistory = useMemo(() => searchHistory.slice(0, 2), [searchHistory])
|
||||
const { pathname } = useLocation()
|
||||
const isNFTPage = pathname.includes('/nfts')
|
||||
const isTokenPage = pathname.includes('/tokens')
|
||||
const phase1Flag = useNftFlag()
|
||||
const [resultsState, setResultsState] = useState<ReactNode>()
|
||||
|
||||
const { data: trendingCollectionResults, isLoading: trendingCollectionsAreLoading } = useQuery(
|
||||
['trendingCollections', 'eth', 'twenty_four_hours'],
|
||||
() => fetchTrendingCollections({ volumeType: 'eth', timePeriod: 'ONE_DAY' as TimePeriod, size: 3 })
|
||||
)
|
||||
|
||||
const trendingCollections = useMemo(
|
||||
() =>
|
||||
trendingCollectionResults
|
||||
? trendingCollectionResults
|
||||
.map((collection) => ({
|
||||
...collection,
|
||||
collectionAddress: collection.address,
|
||||
floorPrice: formatEthPrice(collection.floor?.toString()),
|
||||
stats: {
|
||||
total_supply: collection.totalSupply,
|
||||
one_day_change: collection.floorChange,
|
||||
},
|
||||
}))
|
||||
.slice(0, isNFTPage ? 3 : 2)
|
||||
: [...Array<GenieCollection>(isNFTPage ? 3 : 2)],
|
||||
[isNFTPage, trendingCollectionResults]
|
||||
)
|
||||
|
||||
const { data: trendingTokenResults, isLoading: trendingTokensAreLoading } = useQuery(
|
||||
['trendingTokens'],
|
||||
() => fetchTrendingTokens(4),
|
||||
{
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: false,
|
||||
refetchOnReconnect: false,
|
||||
}
|
||||
)
|
||||
useEffect(() => {
|
||||
trendingTokenResults?.forEach(updateSearchHistory)
|
||||
}, [trendingTokenResults, updateSearchHistory])
|
||||
|
||||
const trendingTokensLength = phase1Flag === NftVariant.Enabled ? (isTokenPage ? 3 : 2) : 4
|
||||
const trendingTokens = useMemo(
|
||||
() =>
|
||||
trendingTokenResults
|
||||
? trendingTokenResults.slice(0, trendingTokensLength)
|
||||
: [...Array<FungibleToken>(trendingTokensLength)],
|
||||
[trendingTokenResults, trendingTokensLength]
|
||||
)
|
||||
|
||||
const totalSuggestions = hasInput
|
||||
? tokens.length + collections.length
|
||||
: Math.min(shortenedHistory.length, 2) +
|
||||
(isNFTPage || !isTokenPage ? trendingCollections?.length ?? 0 : 0) +
|
||||
(isTokenPage || !isNFTPage ? trendingTokens?.length ?? 0 : 0)
|
||||
|
||||
// Navigate search results via arrow keys
|
||||
useEffect(() => {
|
||||
const keyDownHandler = (event: KeyboardEvent) => {
|
||||
if (event.key === 'ArrowUp') {
|
||||
event.preventDefault()
|
||||
if (!hoveredIndex) {
|
||||
setHoveredIndex(totalSuggestions - 1)
|
||||
} else {
|
||||
setHoveredIndex(hoveredIndex - 1)
|
||||
}
|
||||
} else if (event.key === 'ArrowDown') {
|
||||
event.preventDefault()
|
||||
if (hoveredIndex && hoveredIndex === totalSuggestions - 1) {
|
||||
setHoveredIndex(0)
|
||||
} else {
|
||||
setHoveredIndex((hoveredIndex ?? -1) + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', keyDownHandler)
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('keydown', keyDownHandler)
|
||||
}
|
||||
}, [toggleOpen, hoveredIndex, totalSuggestions])
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading) {
|
||||
const tokenSearchResults =
|
||||
tokens.length > 0 ? (
|
||||
<SearchBarDropdownSection
|
||||
hoveredIndex={hoveredIndex}
|
||||
startingIndex={isNFTPage ? collections.length : 0}
|
||||
setHoveredIndex={setHoveredIndex}
|
||||
toggleOpen={toggleOpen}
|
||||
suggestions={tokens}
|
||||
header={<Trans>Tokens</Trans>}
|
||||
/>
|
||||
) : (
|
||||
<Box className={styles.notFoundContainer}>
|
||||
<Trans>No tokens found.</Trans>
|
||||
</Box>
|
||||
)
|
||||
|
||||
const collectionSearchResults =
|
||||
phase1Flag === NftVariant.Enabled ? (
|
||||
collections.length > 0 ? (
|
||||
<SearchBarDropdownSection
|
||||
hoveredIndex={hoveredIndex}
|
||||
startingIndex={isNFTPage ? 0 : tokens.length}
|
||||
setHoveredIndex={setHoveredIndex}
|
||||
toggleOpen={toggleOpen}
|
||||
suggestions={collections}
|
||||
header={<Trans>NFT Collections</Trans>}
|
||||
/>
|
||||
) : (
|
||||
<Box className={styles.notFoundContainer}>No NFT collections found.</Box>
|
||||
)
|
||||
) : null
|
||||
|
||||
const currentState = () =>
|
||||
hasInput ? (
|
||||
// Empty or Up to 8 combined tokens and nfts
|
||||
<Column gap="20">
|
||||
{isNFTPage ? (
|
||||
<>
|
||||
{collectionSearchResults}
|
||||
{tokenSearchResults}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{tokenSearchResults}
|
||||
{collectionSearchResults}
|
||||
</>
|
||||
)}
|
||||
</Column>
|
||||
) : (
|
||||
// Recent Searches, Trending Tokens, Trending Collections
|
||||
<Column gap="20">
|
||||
{shortenedHistory.length > 0 && (
|
||||
<SearchBarDropdownSection
|
||||
hoveredIndex={hoveredIndex}
|
||||
startingIndex={0}
|
||||
setHoveredIndex={setHoveredIndex}
|
||||
toggleOpen={toggleOpen}
|
||||
suggestions={shortenedHistory}
|
||||
header={<Trans>Recent searches</Trans>}
|
||||
headerIcon={<ClockIcon />}
|
||||
/>
|
||||
)}
|
||||
{!isNFTPage && (
|
||||
<SearchBarDropdownSection
|
||||
hoveredIndex={hoveredIndex}
|
||||
startingIndex={shortenedHistory.length}
|
||||
setHoveredIndex={setHoveredIndex}
|
||||
toggleOpen={toggleOpen}
|
||||
suggestions={trendingTokens}
|
||||
header={<Trans>Popular tokens</Trans>}
|
||||
headerIcon={<TrendingArrow />}
|
||||
isLoading={trendingTokensAreLoading}
|
||||
/>
|
||||
)}
|
||||
{!isTokenPage && phase1Flag === NftVariant.Enabled && (
|
||||
<SearchBarDropdownSection
|
||||
hoveredIndex={hoveredIndex}
|
||||
startingIndex={shortenedHistory.length + (isNFTPage ? 0 : trendingTokens?.length ?? 0)}
|
||||
setHoveredIndex={setHoveredIndex}
|
||||
toggleOpen={toggleOpen}
|
||||
suggestions={trendingCollections as unknown as GenieCollection[]}
|
||||
header={<Trans>Popular NFT collections</Trans>}
|
||||
headerIcon={<TrendingArrow />}
|
||||
isLoading={trendingCollectionsAreLoading}
|
||||
/>
|
||||
)}
|
||||
</Column>
|
||||
)
|
||||
|
||||
setResultsState(currentState)
|
||||
}
|
||||
}, [
|
||||
isLoading,
|
||||
tokens,
|
||||
collections,
|
||||
trendingCollections,
|
||||
trendingCollectionsAreLoading,
|
||||
trendingTokens,
|
||||
trendingTokensAreLoading,
|
||||
hoveredIndex,
|
||||
phase1Flag,
|
||||
toggleOpen,
|
||||
shortenedHistory,
|
||||
hasInput,
|
||||
isNFTPage,
|
||||
isTokenPage,
|
||||
])
|
||||
|
||||
return (
|
||||
<Box className={styles.searchBarDropdown}>
|
||||
<Box opacity={isLoading ? '0.3' : '1'} transition="125">
|
||||
{resultsState}
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
22
src/components/NavBar/ShoppingBag.css.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { style } from '@vanilla-extract/css'
|
||||
import { sprinkles } from 'nft/css/sprinkles.css'
|
||||
|
||||
export const bagQuantity = style([
|
||||
sprinkles({
|
||||
position: 'absolute',
|
||||
top: '4',
|
||||
right: '4',
|
||||
backgroundColor: 'accentAction',
|
||||
borderRadius: 'round',
|
||||
color: 'explicitWhite',
|
||||
textAlign: 'center',
|
||||
fontWeight: 'semibold',
|
||||
paddingY: '1',
|
||||
paddingX: '4',
|
||||
}),
|
||||
{
|
||||
fontSize: '8px',
|
||||
lineHeight: '12px',
|
||||
minWidth: '14px',
|
||||
},
|
||||
])
|
||||
47
src/components/NavBar/ShoppingBag.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import { NavIcon } from 'components/NavBar/NavIcon'
|
||||
import * as styles from 'components/NavBar/ShoppingBag.css'
|
||||
import { Box } from 'nft/components/Box'
|
||||
import { BagIcon, HundredsOverflowIcon, TagIcon } from 'nft/components/icons'
|
||||
import { useBag, useSellAsset } from 'nft/hooks'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
export const ShoppingBag = () => {
|
||||
const itemsInBag = useBag((state) => state.itemsInBag)
|
||||
const sellAssets = useSellAsset((state) => state.sellAssets)
|
||||
const [bagQuantity, setBagQuantity] = useState(0)
|
||||
const [sellQuantity, setSellQuantity] = useState(0)
|
||||
const location = useLocation()
|
||||
|
||||
const toggleBag = useBag((s) => s.toggleBag)
|
||||
|
||||
useEffect(() => {
|
||||
setBagQuantity(itemsInBag.length)
|
||||
}, [itemsInBag])
|
||||
|
||||
useEffect(() => {
|
||||
setSellQuantity(sellAssets.length)
|
||||
}, [sellAssets])
|
||||
|
||||
const isProfilePage = location.pathname === '/profile'
|
||||
|
||||
return (
|
||||
<NavIcon onClick={toggleBag}>
|
||||
{isProfilePage ? (
|
||||
<>
|
||||
<TagIcon width={20} height={20} />
|
||||
{sellQuantity ? (
|
||||
<Box className={styles.bagQuantity}>{sellQuantity > 99 ? <HundredsOverflowIcon /> : sellQuantity}</Box>
|
||||
) : null}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<BagIcon width={20} height={20} />
|
||||
{bagQuantity ? (
|
||||
<Box className={styles.bagQuantity}>{bagQuantity > 99 ? <HundredsOverflowIcon /> : bagQuantity}</Box>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</NavIcon>
|
||||
)
|
||||
}
|
||||
225
src/components/NavBar/SuggestionRow.tsx
Normal file
@@ -0,0 +1,225 @@
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import clsx from 'clsx'
|
||||
import { L2NetworkLogo, LogoContainer } from 'components/Tokens/TokenTable/TokenRow'
|
||||
import { VerifiedIcon } from 'components/TokenSafety/TokenSafetyIcon'
|
||||
import { getChainInfo } from 'constants/chainInfo'
|
||||
import { getTokenDetailsURL } from 'graphql/data/util'
|
||||
import uriToHttp from 'lib/utils/uriToHttp'
|
||||
import { Box } from 'nft/components/Box'
|
||||
import { Column, Row } from 'nft/components/Flex'
|
||||
import { vars } from 'nft/css/sprinkles.css'
|
||||
import { useSearchHistory } from 'nft/hooks'
|
||||
import { FungibleToken, GenieCollection } from 'nft/types'
|
||||
import { ethNumberStandardFormatter } from 'nft/utils/currency'
|
||||
import { putCommas } from 'nft/utils/putCommas'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { Link, useNavigate } from 'react-router-dom'
|
||||
import { formatDollar } from 'utils/formatNumbers'
|
||||
|
||||
import * as styles from './SearchBar.css'
|
||||
|
||||
interface CollectionRowProps {
|
||||
collection: GenieCollection
|
||||
isHovered: boolean
|
||||
setHoveredIndex: (index: number | undefined) => void
|
||||
toggleOpen: () => void
|
||||
traceEvent: () => void
|
||||
index: number
|
||||
}
|
||||
|
||||
export const CollectionRow = ({
|
||||
collection,
|
||||
isHovered,
|
||||
setHoveredIndex,
|
||||
toggleOpen,
|
||||
traceEvent,
|
||||
index,
|
||||
}: CollectionRowProps) => {
|
||||
const [brokenImage, setBrokenImage] = useState(false)
|
||||
const [loaded, setLoaded] = useState(false)
|
||||
const addToSearchHistory = useSearchHistory(
|
||||
(state: { addItem: (item: FungibleToken | GenieCollection) => void }) => state.addItem
|
||||
)
|
||||
const navigate = useNavigate()
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
addToSearchHistory(collection)
|
||||
toggleOpen()
|
||||
traceEvent()
|
||||
}, [addToSearchHistory, collection, toggleOpen, traceEvent])
|
||||
|
||||
useEffect(() => {
|
||||
const keyDownHandler = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Enter' && isHovered) {
|
||||
event.preventDefault()
|
||||
navigate(`/nfts/collection/${collection.address}`)
|
||||
handleClick()
|
||||
}
|
||||
}
|
||||
document.addEventListener('keydown', keyDownHandler)
|
||||
return () => {
|
||||
document.removeEventListener('keydown', keyDownHandler)
|
||||
}
|
||||
}, [toggleOpen, isHovered, collection, navigate, handleClick])
|
||||
|
||||
return (
|
||||
<Link
|
||||
to={`/nfts/collection/${collection.address}`}
|
||||
onClick={handleClick}
|
||||
onMouseEnter={() => !isHovered && setHoveredIndex(index)}
|
||||
onMouseLeave={() => isHovered && setHoveredIndex(undefined)}
|
||||
className={styles.suggestionRow}
|
||||
style={{ background: isHovered ? vars.color.lightGrayOverlay : 'none' }}
|
||||
>
|
||||
<Row style={{ width: '60%' }}>
|
||||
{!brokenImage && collection.imageUrl ? (
|
||||
<Box
|
||||
as="img"
|
||||
src={collection.imageUrl}
|
||||
alt={collection.name}
|
||||
className={clsx(loaded ? styles.suggestionImage : styles.imageHolder)}
|
||||
onError={() => setBrokenImage(true)}
|
||||
onLoad={() => setLoaded(true)}
|
||||
/>
|
||||
) : (
|
||||
<Box className={styles.imageHolder} />
|
||||
)}
|
||||
<Column className={styles.suggestionPrimaryContainer}>
|
||||
<Row gap="4" width="full">
|
||||
<Box className={styles.primaryText}>{collection.name}</Box>
|
||||
{collection.isVerified && <VerifiedIcon className={styles.suggestionIcon} />}
|
||||
</Row>
|
||||
<Box className={styles.secondaryText}>{putCommas(collection.stats.total_supply)} items</Box>
|
||||
</Column>
|
||||
</Row>
|
||||
{collection.floorPrice ? (
|
||||
<Column className={styles.suggestionSecondaryContainer}>
|
||||
<Row gap="4">
|
||||
<Box className={styles.primaryText}>{ethNumberStandardFormatter(collection.floorPrice)} ETH</Box>
|
||||
</Row>
|
||||
<Box className={styles.secondaryText}>Floor</Box>
|
||||
</Column>
|
||||
) : null}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
function useBridgedAddress(token: FungibleToken): [string | undefined, number | undefined, string | undefined] {
|
||||
const { chainId: connectedChainId } = useWeb3React()
|
||||
const bridgedAddress = connectedChainId ? token.extensions?.bridgeInfo?.[connectedChainId]?.tokenAddress : undefined
|
||||
if (bridgedAddress && connectedChainId) {
|
||||
return [bridgedAddress, connectedChainId, getChainInfo(connectedChainId)?.circleLogoUrl]
|
||||
}
|
||||
return [undefined, undefined, undefined]
|
||||
}
|
||||
|
||||
interface TokenRowProps {
|
||||
token: FungibleToken
|
||||
isHovered: boolean
|
||||
setHoveredIndex: (index: number | undefined) => void
|
||||
toggleOpen: () => void
|
||||
traceEvent: () => void
|
||||
index: number
|
||||
}
|
||||
|
||||
export const TokenRow = ({ token, isHovered, setHoveredIndex, toggleOpen, traceEvent, index }: TokenRowProps) => {
|
||||
const [brokenImage, setBrokenImage] = useState(false)
|
||||
const [loaded, setLoaded] = useState(false)
|
||||
const addToSearchHistory = useSearchHistory(
|
||||
(state: { addItem: (item: FungibleToken | GenieCollection) => void }) => state.addItem
|
||||
)
|
||||
const navigate = useNavigate()
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
addToSearchHistory(token)
|
||||
toggleOpen()
|
||||
traceEvent()
|
||||
}, [addToSearchHistory, toggleOpen, token, traceEvent])
|
||||
|
||||
const [bridgedAddress, bridgedChain, L2Icon] = useBridgedAddress(token)
|
||||
const tokenDetailsPath = getTokenDetailsURL(bridgedAddress ?? token.address, undefined, bridgedChain ?? token.chainId)
|
||||
// Close the modal on escape
|
||||
useEffect(() => {
|
||||
const keyDownHandler = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Enter' && isHovered) {
|
||||
event.preventDefault()
|
||||
navigate(tokenDetailsPath)
|
||||
handleClick()
|
||||
}
|
||||
}
|
||||
document.addEventListener('keydown', keyDownHandler)
|
||||
return () => {
|
||||
document.removeEventListener('keydown', keyDownHandler)
|
||||
}
|
||||
}, [toggleOpen, isHovered, token, navigate, handleClick, tokenDetailsPath])
|
||||
|
||||
return (
|
||||
<Link
|
||||
to={tokenDetailsPath}
|
||||
onClick={handleClick}
|
||||
onMouseEnter={() => !isHovered && setHoveredIndex(index)}
|
||||
onMouseLeave={() => isHovered && setHoveredIndex(undefined)}
|
||||
className={styles.suggestionRow}
|
||||
style={{ background: isHovered ? vars.color.lightGrayOverlay : 'none' }}
|
||||
>
|
||||
<Row style={{ width: '65%' }}>
|
||||
{!brokenImage && token.logoURI ? (
|
||||
<LogoContainer>
|
||||
<Box
|
||||
as="img"
|
||||
src={token.logoURI.includes('ipfs://') ? uriToHttp(token.logoURI)[0] : token.logoURI}
|
||||
alt={token.name}
|
||||
className={clsx(loaded ? styles.suggestionImage : styles.imageHolder)}
|
||||
onError={() => setBrokenImage(true)}
|
||||
onLoad={() => setLoaded(true)}
|
||||
/>
|
||||
<L2NetworkLogo networkUrl={L2Icon} size="16px" />
|
||||
</LogoContainer>
|
||||
) : (
|
||||
<Box className={styles.imageHolder} />
|
||||
)}
|
||||
<Column className={styles.suggestionPrimaryContainer}>
|
||||
<Row gap="4" width="full">
|
||||
<Box className={styles.primaryText}>{token.name}</Box>
|
||||
{token.onDefaultList && <VerifiedIcon className={styles.suggestionIcon} />}
|
||||
</Row>
|
||||
<Box className={styles.secondaryText}>{token.symbol}</Box>
|
||||
</Column>
|
||||
</Row>
|
||||
|
||||
<Column className={styles.suggestionSecondaryContainer}>
|
||||
{token.priceUsd && (
|
||||
<Row gap="4">
|
||||
<Box className={styles.primaryText}>{formatDollar({ num: token.priceUsd, isPrice: true })}</Box>
|
||||
</Row>
|
||||
)}
|
||||
{token.price24hChange && (
|
||||
<Box className={styles.secondaryText} color={token.price24hChange >= 0 ? 'green400' : 'red400'}>
|
||||
{token.price24hChange.toFixed(2)}%
|
||||
</Box>
|
||||
)}
|
||||
</Column>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
export const SkeletonRow = () => {
|
||||
return (
|
||||
<Row className={styles.suggestionRow}>
|
||||
<Row width="full">
|
||||
<Box className={styles.imageHolder} />
|
||||
<Column gap="4" width="full">
|
||||
<Row justifyContent="space-between">
|
||||
<Box borderRadius="round" height="20" background="backgroundModule" style={{ width: '180px' }} />
|
||||
<Box borderRadius="round" height="20" width="48" background="backgroundModule" />
|
||||
</Row>
|
||||
|
||||
<Row justifyContent="space-between">
|
||||
<Box borderRadius="round" height="16" width="120" background="backgroundModule" />
|
||||
<Box borderRadius="round" height="16" width="48" background="backgroundModule" />
|
||||
</Row>
|
||||
</Column>
|
||||
</Row>
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
import Navbar from './Navbar'
|
||||
|
||||
export default Navbar
|
||||
121
src/components/NavBar/index.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
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 { 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 { ChainSelector } from './ChainSelector'
|
||||
import { MenuDropdown } from './MenuDropdown'
|
||||
import { SearchBar } from './SearchBar'
|
||||
import { ShoppingBag } from './ShoppingBag'
|
||||
import * as styles from './style.css'
|
||||
|
||||
interface MenuItemProps {
|
||||
href: string
|
||||
id?: NavLinkProps['id']
|
||||
isActive?: boolean
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
const MenuItem = ({ href, id, isActive, children }: MenuItemProps) => {
|
||||
return (
|
||||
<NavLink
|
||||
to={href}
|
||||
className={isActive ? styles.activeMenuItem : styles.menuItem}
|
||||
id={id}
|
||||
style={{ textDecoration: 'none' }}
|
||||
>
|
||||
{children}
|
||||
</NavLink>
|
||||
)
|
||||
}
|
||||
|
||||
const PageTabs = () => {
|
||||
const { pathname } = useLocation()
|
||||
const nftFlag = useNftFlag()
|
||||
const { chainId: connectedChainId } = useWeb3React()
|
||||
const chainName = chainIdToBackendName(connectedChainId)
|
||||
|
||||
const isPoolActive =
|
||||
pathname.startsWith('/pool') ||
|
||||
pathname.startsWith('/add') ||
|
||||
pathname.startsWith('/remove') ||
|
||||
pathname.startsWith('/increase') ||
|
||||
pathname.startsWith('/find')
|
||||
|
||||
return (
|
||||
<>
|
||||
<MenuItem href="/swap" isActive={pathname.startsWith('/swap')}>
|
||||
<Trans>Swap</Trans>
|
||||
</MenuItem>
|
||||
<MenuItem href={`/tokens/${chainName.toLowerCase()}`} isActive={pathname.startsWith('/tokens')}>
|
||||
<Trans>Tokens</Trans>
|
||||
</MenuItem>
|
||||
{nftFlag === NftVariant.Enabled && (
|
||||
<MenuItem href="/nfts" isActive={pathname.startsWith('/nfts')}>
|
||||
<Trans>NFTs</Trans>
|
||||
</MenuItem>
|
||||
)}
|
||||
<MenuItem href="/pool" id={'pool-nav-link'} isActive={isPoolActive}>
|
||||
<Trans>Pool</Trans>
|
||||
</MenuItem>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const Navbar = () => {
|
||||
const { pathname } = useLocation()
|
||||
const showShoppingBag = pathname.startsWith('/nfts') || pathname.startsWith('/profile')
|
||||
|
||||
return (
|
||||
<>
|
||||
<nav className={styles.nav}>
|
||||
<Box display="flex" height="full" flexWrap="nowrap" alignItems="stretch">
|
||||
<Box className={styles.leftSideContainer}>
|
||||
<Box as="a" href="#/swap" className={styles.logoContainer}>
|
||||
<UniIcon width="48" height="48" className={styles.logo} />
|
||||
</Box>
|
||||
<Box display={{ sm: 'flex', lg: 'none' }}>
|
||||
<ChainSelector leftAlign={true} />
|
||||
</Box>
|
||||
<Row gap="8" display={{ sm: 'none', lg: 'flex' }}>
|
||||
<PageTabs />
|
||||
</Row>
|
||||
</Box>
|
||||
<Box className={styles.middleContainer}>
|
||||
<SearchBar />
|
||||
</Box>
|
||||
<Box className={styles.rightSideContainer}>
|
||||
<Row gap="12">
|
||||
<Box display={{ sm: 'flex', xl: 'none' }}>
|
||||
<SearchBar />
|
||||
</Box>
|
||||
<Box display={{ sm: 'none', lg: 'flex' }}>
|
||||
<MenuDropdown />
|
||||
</Box>
|
||||
{showShoppingBag && <ShoppingBag />}
|
||||
<Box display={{ sm: 'none', lg: 'flex' }}>
|
||||
<ChainSelector />
|
||||
</Box>
|
||||
|
||||
<Web3Status />
|
||||
</Row>
|
||||
</Box>
|
||||
</Box>
|
||||
</nav>
|
||||
<Box className={styles.mobileBottomBar}>
|
||||
<PageTabs />
|
||||
<Box marginY="4">
|
||||
<MenuDropdown />
|
||||
</Box>
|
||||
</Box>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Navbar
|
||||
@@ -1,7 +1,7 @@
|
||||
import { style } from '@vanilla-extract/css'
|
||||
|
||||
import { subhead } from '../../nft/css/common.css'
|
||||
import { sprinkles } from '../../nft/css/sprinkles.css'
|
||||
import { sprinkles, vars } from '../../nft/css/sprinkles.css'
|
||||
|
||||
export const nav = style([
|
||||
sprinkles({
|
||||
@@ -10,7 +10,7 @@ export const nav = style([
|
||||
width: 'full',
|
||||
height: '72',
|
||||
zIndex: '2',
|
||||
background: 'white08',
|
||||
background: 'backgroundFloating',
|
||||
}),
|
||||
{
|
||||
backdropFilter: 'blur(24px)',
|
||||
@@ -20,7 +20,7 @@ export const nav = style([
|
||||
export const logoContainer = style([
|
||||
sprinkles({
|
||||
display: 'flex',
|
||||
marginRight: { mobile: '12', desktopXl: '20' },
|
||||
marginRight: { sm: '12', xxl: '20' },
|
||||
alignItems: 'center',
|
||||
}),
|
||||
])
|
||||
@@ -28,29 +28,20 @@ export const logoContainer = style([
|
||||
export const logo = style([
|
||||
sprinkles({
|
||||
display: 'block',
|
||||
color: 'blackBlue',
|
||||
color: 'textPrimary',
|
||||
}),
|
||||
])
|
||||
|
||||
export const baseContainer = style([
|
||||
sprinkles({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
}),
|
||||
])
|
||||
|
||||
export const baseMobileContainer = style([
|
||||
sprinkles({
|
||||
display: 'flex',
|
||||
width: 'full',
|
||||
alignItems: 'center',
|
||||
marginY: '2',
|
||||
}),
|
||||
])
|
||||
|
||||
export const baseSideContainer = style([
|
||||
baseContainer,
|
||||
sprinkles({
|
||||
display: 'flex',
|
||||
width: 'full',
|
||||
flex: '1',
|
||||
flexShrink: '2',
|
||||
@@ -64,19 +55,13 @@ export const leftSideContainer = style([
|
||||
}),
|
||||
])
|
||||
|
||||
export const leftSideMobileContainer = style([
|
||||
baseMobileContainer,
|
||||
sprinkles({
|
||||
justifyContent: 'flex-start',
|
||||
}),
|
||||
])
|
||||
|
||||
export const middleContainer = style([
|
||||
baseContainer,
|
||||
sprinkles({
|
||||
flex: '1',
|
||||
flexShrink: '1',
|
||||
justifyContent: 'center',
|
||||
display: { sm: 'none', xl: 'flex' },
|
||||
}),
|
||||
])
|
||||
|
||||
@@ -94,44 +79,46 @@ const baseMenuItem = style([
|
||||
paddingX: '16',
|
||||
marginY: '4',
|
||||
borderRadius: '12',
|
||||
transition: '250',
|
||||
height: 'min',
|
||||
width: 'full',
|
||||
textAlign: 'center',
|
||||
}),
|
||||
{
|
||||
lineHeight: '24px',
|
||||
textDecoration: 'none',
|
||||
':hover': {
|
||||
background: vars.color.lightGrayOverlay,
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
export const menuItem = style([
|
||||
baseMenuItem,
|
||||
sprinkles({
|
||||
color: 'darkGray',
|
||||
}),
|
||||
])
|
||||
|
||||
export const rightSideMobileContainer = style([
|
||||
baseMobileContainer,
|
||||
sprinkles({
|
||||
justifyContent: 'flex-end',
|
||||
color: 'textSecondary',
|
||||
}),
|
||||
])
|
||||
|
||||
export const activeMenuItem = style([
|
||||
baseMenuItem,
|
||||
sprinkles({
|
||||
color: 'blackBlue',
|
||||
color: 'textPrimary',
|
||||
background: 'backgroundFloating',
|
||||
}),
|
||||
])
|
||||
|
||||
export const mobileWalletContainer = style([
|
||||
export const mobileBottomBar = style([
|
||||
sprinkles({
|
||||
position: 'fixed',
|
||||
display: 'flex',
|
||||
display: { sm: 'flex', lg: 'none' },
|
||||
bottom: '0',
|
||||
right: '1/2',
|
||||
marginY: '0',
|
||||
marginX: 'auto',
|
||||
right: '0',
|
||||
left: '0',
|
||||
justifyContent: 'space-between',
|
||||
paddingY: '4',
|
||||
paddingX: '8',
|
||||
height: '56',
|
||||
background: 'backgroundSurface',
|
||||
}),
|
||||
{
|
||||
transform: 'translate(50%,-50%)',
|
||||
},
|
||||
])
|
||||
@@ -23,7 +23,7 @@ const Tabs = styled.div`
|
||||
const StyledHistoryLink = styled(HistoryLink)<{ flex: string | undefined }>`
|
||||
flex: ${({ flex }) => flex ?? 'none'};
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
|
||||
flex: none;
|
||||
margin-right: 10px;
|
||||
`};
|
||||
|
||||
@@ -37,7 +37,7 @@ const RootWrapper = styled.div`
|
||||
|
||||
const SHOULD_SHOW_ALERT = {
|
||||
[SupportedChainId.OPTIMISM]: true,
|
||||
[SupportedChainId.OPTIMISTIC_KOVAN]: true,
|
||||
[SupportedChainId.OPTIMISM_GOERLI]: true,
|
||||
[SupportedChainId.ARBITRUM_ONE]: true,
|
||||
[SupportedChainId.ARBITRUM_RINKEBY]: true,
|
||||
[SupportedChainId.POLYGON]: true,
|
||||
@@ -62,7 +62,7 @@ const BG_COLORS_BY_DARK_MODE_AND_CHAIN_ID: {
|
||||
'radial-gradient(182.71% 150.59% at 2.81% 7.69%, rgba(90, 190, 170, 0.15) 0%, rgba(80, 160, 40, 0.15) 100%)',
|
||||
[SupportedChainId.OPTIMISM]:
|
||||
'radial-gradient(948% 292% at 42% 0%, rgba(255, 58, 212, 0.01) 0%, rgba(255, 255, 255, 0.04) 100%),radial-gradient(98% 96% at 2% 0%, rgba(255, 39, 39, 0.01) 0%, rgba(235, 0, 255, 0.01) 96%)',
|
||||
[SupportedChainId.OPTIMISTIC_KOVAN]:
|
||||
[SupportedChainId.OPTIMISM_GOERLI]:
|
||||
'radial-gradient(948% 292% at 42% 0%, rgba(255, 58, 212, 0.04) 0%, rgba(255, 255, 255, 0.04) 100%),radial-gradient(98% 96% at 2% 0%, rgba(255, 39, 39, 0.04) 0%, rgba(235, 0, 255, 0.01 96%)',
|
||||
[SupportedChainId.ARBITRUM_ONE]:
|
||||
'radial-gradient(285% 8200% at 30% 50%, rgba(40, 160, 240, 0.01) 0%, rgba(219, 255, 0, 0) 100%),radial-gradient(75% 75% at 0% 0%, rgba(150, 190, 220, 0.05) 0%, rgba(33, 114, 229, 0.05) 100%), hsla(0, 0%, 100%, 0.05)',
|
||||
@@ -80,7 +80,7 @@ const BG_COLORS_BY_DARK_MODE_AND_CHAIN_ID: {
|
||||
'radial-gradient(182.71% 150.59% at 2.81% 7.69%, rgba(63, 208, 137, 0.15) 0%, rgba(49, 205, 50, 0.15) 100%)',
|
||||
[SupportedChainId.OPTIMISM]:
|
||||
'radial-gradient(92% 105% at 50% 7%, rgba(255, 58, 212, 0.04) 0%, rgba(255, 255, 255, 0.03) 100%),radial-gradient(100% 97% at 0% 12%, rgba(235, 0, 255, 0.1) 0%, rgba(243, 19, 19, 0.1) 100%), hsla(0, 0%, 100%, 0.1)',
|
||||
[SupportedChainId.OPTIMISTIC_KOVAN]:
|
||||
[SupportedChainId.OPTIMISM_GOERLI]:
|
||||
'radial-gradient(92% 105% at 50% 7%, rgba(255, 58, 212, 0.04) 0%, rgba(255, 255, 255, 0.03) 100%),radial-gradient(100% 97% at 0% 12%, rgba(235, 0, 255, 0.1) 0%, rgba(243, 19, 19, 0.1) 100%), hsla(0, 0%, 100%, 0.1)',
|
||||
[SupportedChainId.ARBITRUM_ONE]:
|
||||
'radial-gradient(285% 8200% at 30% 50%, rgba(40, 160, 240, 0.1) 0%, rgba(219, 255, 0, 0) 100%),radial-gradient(circle at top left, hsla(206, 50%, 75%, 0.01), hsla(215, 79%, 51%, 0.12)), hsla(0, 0%, 100%, 0.1)',
|
||||
@@ -142,7 +142,7 @@ const TEXT_COLORS: { [chainId in NetworkAlertChains]: string } = {
|
||||
[SupportedChainId.CELO]: 'rgba(53, 178, 97)',
|
||||
[SupportedChainId.CELO_ALFAJORES]: 'rgba(53, 178, 97)',
|
||||
[SupportedChainId.OPTIMISM]: '#ff3856',
|
||||
[SupportedChainId.OPTIMISTIC_KOVAN]: '#ff3856',
|
||||
[SupportedChainId.OPTIMISM_GOERLI]: '#ff3856',
|
||||
[SupportedChainId.ARBITRUM_ONE]: '#0490ed',
|
||||
[SupportedChainId.ARBITRUM_RINKEBY]: '#0490ed',
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ const StyledInput = styled.input<{ error?: boolean; fontSize?: string; align?: s
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
color: ${({ theme, redesignFlag }) => (redesignFlag ? theme.textSecondary : theme.deprecated_text4)};
|
||||
color: ${({ theme, redesignFlag }) => (redesignFlag ? theme.textTertiary : theme.deprecated_text4)};
|
||||
}
|
||||
`
|
||||
|
||||
|
||||
@@ -4,9 +4,10 @@ import useInterval from 'lib/hooks/useInterval'
|
||||
import React, { useCallback, useMemo, useState } from 'react'
|
||||
import { usePopper } from 'react-popper'
|
||||
import styled from 'styled-components/macro'
|
||||
import { Z_INDEX } from 'theme/zIndex'
|
||||
|
||||
const PopoverContainer = styled.div<{ show: boolean }>`
|
||||
z-index: 9999;
|
||||
z-index: ${Z_INDEX.popover};
|
||||
visibility: ${(props) => (props.show ? 'visible' : 'hidden')};
|
||||
opacity: ${(props) => (props.show ? 1 : 0)};
|
||||
transition: visibility 150ms linear, opacity 150ms linear;
|
||||
@@ -15,6 +16,7 @@ const PopoverContainer = styled.div<{ show: boolean }>`
|
||||
|
||||
const ReferenceElement = styled.div`
|
||||
display: inline-block;
|
||||
height: inherit;
|
||||
`
|
||||
|
||||
const Arrow = styled.div`
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { NavBarVariant, useNavBarFlag } from 'featureFlags/flags/navBar'
|
||||
import { useCallback, useEffect } from 'react'
|
||||
import { X } from 'react-feather'
|
||||
import { animated } from 'react-spring'
|
||||
import { useSpring } from 'react-spring/web'
|
||||
import { useSpring } from 'react-spring'
|
||||
import styled, { useTheme } from 'styled-components/macro'
|
||||
|
||||
import { useRemovePopup } from '../../state/application/hooks'
|
||||
@@ -29,7 +30,7 @@ const Popup = styled.div`
|
||||
padding-right: 35px;
|
||||
overflow: hidden;
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
|
||||
min-width: 290px;
|
||||
&:not(:last-of-type) {
|
||||
margin-right: 20px;
|
||||
@@ -57,6 +58,7 @@ export default function PopupItem({
|
||||
popKey: string
|
||||
}) {
|
||||
const removePopup = useRemovePopup()
|
||||
const navbarFlag = useNavBarFlag()
|
||||
const removeThisPopup = useCallback(() => removePopup(popKey), [popKey, removePopup])
|
||||
useEffect(() => {
|
||||
if (removeAfterMs === null) return undefined
|
||||
@@ -71,23 +73,24 @@ export default function PopupItem({
|
||||
}, [removeAfterMs, removeThisPopup])
|
||||
|
||||
const theme = useTheme()
|
||||
const faderStyle = useSpring({
|
||||
from: { width: '100%' },
|
||||
to: { width: '0%' },
|
||||
config: { duration: removeAfterMs ?? undefined },
|
||||
})
|
||||
|
||||
let popupContent
|
||||
if ('txn' in content) {
|
||||
const {
|
||||
txn: { hash },
|
||||
} = content
|
||||
if (navbarFlag === NavBarVariant.Enabled) return null
|
||||
|
||||
popupContent = <TransactionPopup hash={hash} />
|
||||
} else if ('failedSwitchNetwork' in content) {
|
||||
popupContent = <FailedNetworkSwitchPopup chainId={content.failedSwitchNetwork} />
|
||||
}
|
||||
|
||||
const faderStyle = useSpring({
|
||||
from: { width: '100%' },
|
||||
to: { width: '0%' },
|
||||
config: { duration: removeAfterMs ?? undefined },
|
||||
})
|
||||
|
||||
return (
|
||||
<Popup>
|
||||
<StyledClose color={theme.deprecated_text2} onClick={removeThisPopup} />
|
||||
|
||||
@@ -7,7 +7,8 @@ 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, Z_INDEX } from 'theme'
|
||||
import { ExternalLink, ThemedText } from 'theme'
|
||||
import { Z_INDEX } from 'theme/zIndex'
|
||||
|
||||
import BGImage from '../../assets/images/survey-orb.svg'
|
||||
|
||||
@@ -21,7 +22,7 @@ const Wrapper = styled(AutoColumn)`
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
overflow: hidden;
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
|
||||
max-width: 100%;
|
||||
`}
|
||||
`
|
||||
|
||||
@@ -17,7 +17,7 @@ const MobilePopupWrapper = styled.div<{ height: string | number }>`
|
||||
margin-bottom: ${({ height }) => (height ? '20px' : 0)};
|
||||
|
||||
display: none;
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
|
||||
display: block;
|
||||
padding-top: 20px;
|
||||
`};
|
||||
@@ -35,24 +35,24 @@ const MobilePopupInner = styled.div`
|
||||
}
|
||||
`
|
||||
|
||||
const StopOverflowQuery = `@media screen and (min-width: ${MEDIA_WIDTHS.upToMedium + 1}px) and (max-width: ${
|
||||
MEDIA_WIDTHS.upToMedium + 500
|
||||
const StopOverflowQuery = `@media screen and (min-width: ${MEDIA_WIDTHS.deprecated_upToMedium + 1}px) and (max-width: ${
|
||||
MEDIA_WIDTHS.deprecated_upToMedium + 500
|
||||
}px)`
|
||||
|
||||
const FixedPopupColumn = styled(AutoColumn)<{ extraPadding: boolean; xlPadding: boolean }>`
|
||||
position: fixed;
|
||||
top: ${({ extraPadding }) => (extraPadding ? '64px' : '56px')};
|
||||
top: ${({ extraPadding }) => (extraPadding ? '72px' : '64px')};
|
||||
right: 1rem;
|
||||
max-width: 355px !important;
|
||||
width: 100%;
|
||||
z-index: 3;
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
|
||||
display: none;
|
||||
`};
|
||||
|
||||
${StopOverflowQuery} {
|
||||
top: ${({ extraPadding, xlPadding }) => (xlPadding ? '64px' : extraPadding ? '64px' : '56px')};
|
||||
top: ${({ extraPadding, xlPadding }) => (xlPadding ? '72px' : extraPadding ? '72px' : '64px')};
|
||||
}
|
||||
`
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ const DesktopHeader = styled.div`
|
||||
font-weight: 500;
|
||||
padding: 8px;
|
||||
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.deprecated_upToSmall}px) {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -32,11 +32,11 @@ const MobileHeader = styled.div`
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.deprecated_upToSmall}px) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: ${MEDIA_WIDTHS.upToExtraSmall}px) {
|
||||
@media screen and (max-width: ${MEDIA_WIDTHS.deprecated_upToExtraSmall}px) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
@@ -50,12 +50,12 @@ const ToggleWrap = styled.div`
|
||||
`
|
||||
|
||||
const ToggleLabel = styled.div`
|
||||
opacity: 0.6;
|
||||
opacity: ${({ theme }) => theme.opacity.hover};
|
||||
margin-right: 10px;
|
||||
`
|
||||
|
||||
const MobileTogglePosition = styled.div`
|
||||
@media screen and (max-width: ${MEDIA_WIDTHS.upToExtraSmall}px) {
|
||||
@media screen and (max-width: ${MEDIA_WIDTHS.deprecated_upToExtraSmall}px) {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
}
|
||||
|
||||
@@ -48,11 +48,11 @@ const LinkRow = styled(Link)`
|
||||
background-color: ${({ theme }) => theme.deprecated_bg2};
|
||||
}
|
||||
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.deprecated_upToSmall}px) {
|
||||
/* flex-direction: row; */
|
||||
}
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
|
||||
flex-direction: column;
|
||||
row-gap: 12px;
|
||||
`};
|
||||
@@ -61,7 +61,7 @@ const LinkRow = styled(Link)`
|
||||
const BadgeText = styled.div`
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
|
||||
font-size: 12px;
|
||||
`};
|
||||
`
|
||||
@@ -78,7 +78,7 @@ const RangeLineItem = styled(DataLineItem)`
|
||||
margin-top: 4px;
|
||||
width: 100%;
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
|
||||
background-color: ${({ theme }) => theme.deprecated_bg2};
|
||||
border-radius: 12px;
|
||||
padding: 8px 0;
|
||||
@@ -88,7 +88,7 @@ const RangeLineItem = styled(DataLineItem)`
|
||||
const DoubleArrow = styled.span`
|
||||
margin: 0 2px;
|
||||
color: ${({ theme }) => theme.deprecated_text3};
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
|
||||
margin: 4px;
|
||||
padding: 20px;
|
||||
`};
|
||||
@@ -104,7 +104,7 @@ const ExtentsText = styled.span`
|
||||
color: ${({ theme }) => theme.deprecated_text3};
|
||||
font-size: 14px;
|
||||
margin-right: 4px;
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
|
||||
display: none;
|
||||
`};
|
||||
`
|
||||
@@ -122,7 +122,7 @@ const DataText = styled.div`
|
||||
font-weight: 600;
|
||||
font-size: 18px;
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
|
||||
font-size: 14px;
|
||||
`};
|
||||
`
|
||||
|
||||
@@ -10,7 +10,8 @@ import { RoutingDiagramEntry } from 'components/swap/SwapRoute'
|
||||
import { useTokenInfoFromActiveList } from 'hooks/useTokenInfoFromActiveList'
|
||||
import { Box } from 'rebass'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ThemedText, Z_INDEX } from 'theme'
|
||||
import { ThemedText } from 'theme'
|
||||
import { Z_INDEX } from 'theme/zIndex'
|
||||
|
||||
import { ReactComponent as DotLine } from '../../assets/svg/dot_line.svg'
|
||||
import { MouseoverTooltip } from '../Tooltip'
|
||||
|
||||