Compare commits
29
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c3f14c4959 | ||
|
|
f2c1904e93 | ||
|
|
f86b0706fe | ||
|
|
9d8e37192f | ||
|
|
8bb52ff0cf | ||
|
|
0131612414 | ||
|
|
a6672d213f | ||
|
|
62f368da10 | ||
|
|
f3e2fe746d | ||
|
|
8b19572dc9 | ||
|
|
80399d04f7 | ||
|
|
8322fc7edd | ||
|
|
46752816ec | ||
|
|
f99f78780c | ||
|
|
f7037bca80 | ||
|
|
ba39720f05 | ||
|
|
6d35f2b39d | ||
|
|
78afecc2e5 | ||
|
|
3de5b07495 | ||
|
|
e944d3e37c | ||
|
|
5596392835 | ||
|
|
5c57106c04 | ||
|
|
2fc688f477 | ||
|
|
9ea8c839db | ||
|
|
d7e2da7a54 | ||
|
|
b29c268257 | ||
|
|
01ca05a313 | ||
|
|
e3c6dd41c9 | ||
|
|
fbd01dc1bd |
@@ -1,5 +1,8 @@
|
||||
name: (CI) Console tests
|
||||
|
||||
env:
|
||||
VEGA_VERSION: v0.72.14
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
@@ -11,7 +14,7 @@ jobs:
|
||||
run-tests:
|
||||
name: run-tests
|
||||
runs-on: console-test
|
||||
timeout-minutes: 20
|
||||
timeout-minutes: 40
|
||||
steps:
|
||||
#----------------------------------------------
|
||||
# check-out frontend-monorepo
|
||||
@@ -67,23 +70,39 @@ jobs:
|
||||
repository: vegaprotocol/console-test
|
||||
path: './console-test'
|
||||
#----------------------------------------------
|
||||
# install dependencies if cache does not exist
|
||||
# install dependencies
|
||||
#----------------------------------------------
|
||||
- name: Install dependencies
|
||||
working-directory: ./console-test
|
||||
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
|
||||
run: poetry install --no-interaction --no-root
|
||||
#----------------------------------------------
|
||||
# install vega binaries
|
||||
# find vega binaries path
|
||||
#----------------------------------------------
|
||||
- name: Find vega binaries path
|
||||
id: vega_bin_path
|
||||
working-directory: ./console-test
|
||||
run: echo path=$(poetry run python -c "import vega_sim; print(vega_sim.vega_bin_path)") >> $GITHUB_OUTPUT
|
||||
#----------------------------------------------
|
||||
# vega binaries cache
|
||||
#----------------------------------------------
|
||||
- name: Vega binaries cache
|
||||
uses: actions/cache@v3
|
||||
id: vega_binaries_cache
|
||||
with:
|
||||
path: ${{ steps.vega_bin_path.outputs.path }}
|
||||
key: ${{ runner.os }}-vega-binaries-${{ env.VEGA_VERSION }}
|
||||
#----------------------------------------------
|
||||
# install vega binaries
|
||||
#----------------------------------------------
|
||||
- name: Install vega binaries
|
||||
working-directory: ./console-test
|
||||
run: poetry run python -m vega_sim.tools.load_binaries --force
|
||||
if: steps.vega_binaries_cache.outputs.cache-hit != 'true'
|
||||
run: poetry run python -m vega_sim.tools.load_binaries --force --version $VEGA_VERSION
|
||||
#----------------------------------------------
|
||||
# install playwright
|
||||
#----------------------------------------------
|
||||
- name: install playwright
|
||||
run: poetry run playwright install
|
||||
run: poetry run playwright install --with-deps chromium
|
||||
working-directory: ./console-test
|
||||
#----------------------------------------------
|
||||
# run tests
|
||||
|
||||
@@ -155,10 +155,10 @@ jobs:
|
||||
- name: Sanity check docker image
|
||||
run: |
|
||||
echo "Check ipfs-hash"
|
||||
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat /ipfs-hash
|
||||
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat /ipfs-hash > ${{ matrix.app }}-ipfs-hash
|
||||
docker run --rm --entrypoint /bin/sh ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local -c 'cat /ipfs-hash'
|
||||
docker run --rm --entrypoint /bin/sh ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local -c 'cat /ipfs-hash' > ${{ matrix.app }}-ipfs-hash
|
||||
echo "List html directory"
|
||||
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local sh -c 'apk add --update tree; tree /usr/share/nginx/html'
|
||||
docker run --rm --entrypoint /bin/sh ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local -c 'apk add --update tree; tree /usr/share/nginx/html'
|
||||
|
||||
- name: Publish dist as docker image (ghcr)
|
||||
uses: docker/build-push-action@v3
|
||||
|
||||
@@ -113,6 +113,20 @@ In order to run a container on port 3000:
|
||||
docker run -p 3000:80 [TAG]
|
||||
```
|
||||
|
||||
On top of that there are two possible scenarios for running docker image - using nginx server (default) of ipfs daemon.
|
||||
|
||||
to run ipfs on port 3000:
|
||||
|
||||
```bash
|
||||
docker run -p 3000:80 [TAG] ipfs
|
||||
```
|
||||
|
||||
to run nginx on port 3000:
|
||||
|
||||
```bash
|
||||
docker run -p 3000:80 [TAG]
|
||||
```
|
||||
|
||||
## Build instructions
|
||||
|
||||
The [`docker`](./docker) subfolder has some docker configurations for easily setting up your own hosted version of Console either for the web, or ready for pinning on IPFS.
|
||||
@@ -150,7 +164,7 @@ As a prerequisite you need to perform build of `dist` directory and move its con
|
||||
You can build any of the containers locally with the following command:
|
||||
|
||||
```bash
|
||||
docker build --dockerfile docker/node-outside-docker.Dockerfile . --tag=[TAG]
|
||||
docker build -f docker/node-outside-docker.Dockerfile . --tag=[TAG]
|
||||
```
|
||||
|
||||
### Verifying ipfs-hash of existing current application version
|
||||
|
||||
@@ -4,6 +4,8 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://n04.d.vega.xyz/tm/websocket
|
||||
NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql
|
||||
NX_VEGA_ENV=DEVNET
|
||||
NX_BLOCK_EXPLORER=https://be.devnet1.vega.xyz/rest
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
|
||||
# App flags
|
||||
NX_EXPLORER_ASSETS=1
|
||||
|
||||
@@ -4,6 +4,7 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://mainnet-observer-proxy01.ops.vega.xyz/websocke
|
||||
NX_VEGA_URL=https://api.vega.community/graphql
|
||||
NX_VEGA_ENV=MAINNET
|
||||
NX_BLOCK_EXPLORER=https://be.explorer.vega.xyz/rest
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
# App flags
|
||||
NX_EXPLORER_ASSETS=1
|
||||
|
||||
@@ -4,6 +4,7 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://lb.testnet.vega.xyz/tm/websocket
|
||||
NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
|
||||
NX_VEGA_ENV=TESTNET
|
||||
NX_BLOCK_EXPLORER=https://be.testnet.vega.xyz/rest
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
# App flags
|
||||
NX_EXPLORER_ASSETS=1
|
||||
|
||||
@@ -3,6 +3,7 @@ NX_VEGA_ENV=CUSTOM
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
|
||||
NX_VEGA_EXPLORER_URL=/
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
# App flags
|
||||
NX_EXPLORER_TXS_LIST=0
|
||||
|
||||
@@ -8,6 +8,7 @@ NX_VEGA_URL=https://api.devnet1.vega.xyz/graphql
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_VEGA_EXPLORER_URL=/
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
|
||||
|
||||
@@ -9,6 +9,7 @@ NX_VEGA_GOVERNANCE_URL=https://governance.vega.xyz
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz/
|
||||
NX_VEGA_CONSOLE_URL=https://console.vega.xyz
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
NX_TENDERMINT_URL=https://be.vega.community
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
||||
|
||||
@@ -9,6 +9,7 @@ NX_VEGA_GOVERNANCE_URL=https://governance.mainnet-mirror.vega.rocks
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.mainnet-mirror.vega.rocks/
|
||||
NX_VEGA_CONSOLE_URL=https://console.mainnet-mirror.vega.rocks
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
NX_TENDERMINT_URL=https://be.mainnet-mirror.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
# .env is stagnet1, so there are no overrides required
|
||||
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz","STAGNET1":"https://stagnet1.explorer.vega.xyz"}'
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
@@ -9,6 +9,7 @@ NX_VEGA_GOVERNANCE_URL=https://governance.fairground.wtf
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
||||
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
||||
|
||||
@@ -11,6 +11,7 @@ NX_BLOCK_EXPLORER=https://be.validators-testnet.vega.rocks/rest
|
||||
NX_VEGA_GOVERNANCE_URL=https://governance.validators-testnet.vega.rocks
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.validators-testnet.vega.rocks/
|
||||
NX_VEGA_CONSOLE_URL=https://trading.validators-testnet.vega.rocks/
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.xyz/websocket
|
||||
|
||||
@@ -5,4 +5,5 @@ NX_VEGA_ENV=CUSTOM
|
||||
NX_BLOCK_EXPLORER=
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
|
||||
NX_VEGA_EXPLORER_URL=/
|
||||
NX_VEGA_EXPLORER_URL=/
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
@@ -14,6 +14,8 @@ NX_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/
|
||||
NX_VEGA_WALLET_URL=http://localhost:1789
|
||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
|
||||
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
|
||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
||||
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||
|
||||
@@ -3,3 +3,4 @@ NX_VEGA_ENV=DEVNET
|
||||
NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql
|
||||
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
@@ -3,3 +3,4 @@ NX_VEGA_ENV=MAINNET
|
||||
NX_VEGA_URL=https://api.vega.community/graphql
|
||||
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://etherscan.io
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
@@ -3,3 +3,4 @@ NX_VEGA_ENV=TESTNET
|
||||
NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
|
||||
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
@@ -212,7 +212,7 @@ context(
|
||||
closeStakingDialog();
|
||||
navigateTo(navigation.validators);
|
||||
cy.get(`[row-id="${0}"]`)
|
||||
.eq(1)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.getByTestId(stakeValidatorListTotalStake)
|
||||
.should('have.text', '3,002.00')
|
||||
@@ -222,7 +222,7 @@ context(
|
||||
.and('be.visible');
|
||||
});
|
||||
cy.get(`[row-id="${1}"]`)
|
||||
.eq(1)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.getByTestId(stakeValidatorListTotalStake)
|
||||
.scrollIntoView()
|
||||
|
||||
@@ -22,6 +22,8 @@ NX_VEGA_REST_URL=http://localhost:3008/api/v2/
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
||||
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
NX_TENDERMINT_URL=http://localhost:26617
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fa
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
|
||||
@@ -16,6 +16,7 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
|
||||
NX_VEGA_REST_URL=https://api.vega.community/api/v2/
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-mainnet
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
NX_TENDERMINT_URL=https://be.vega.community
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
||||
|
||||
@@ -15,6 +15,7 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
|
||||
NX_VEGA_REST_URL=https://api.mainnet-mirror.vega.rocks/api/v2/
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-mainnet
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
NX_TENDERMINT_URL=https://be.mainnet-mirror.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
|
||||
|
||||
@@ -9,6 +9,7 @@ NX_DELEGATIONS_PAGINATION=50
|
||||
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
||||
|
||||
@@ -14,6 +14,7 @@ NX_TRANCHES_SERVICE_URL=https://tranches-testnet-k8s.ops.vega.xyz
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/
|
||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
||||
|
||||
@@ -11,6 +11,7 @@ NX_VEGA_EXPLORER_URL=https://explorer.validators-testnet.vega.rocks/
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_VEGA_REST_URL=https://api-validators-testnet.vega.rocks/api/v2/
|
||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
||||
|
||||
@@ -15,6 +15,8 @@ NX_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/
|
||||
NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supply green clown session"
|
||||
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||
NX_SENTRY_DSN=https://dummy@o999999.ingest.sentry.io/9999999
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
|
||||
# Expose some env vars to cypress environment for market setup
|
||||
CYPRESS_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit supply green clown session
|
||||
|
||||
@@ -12,6 +12,8 @@ NX_VEGA_URL=http://localhost:3008/graphql
|
||||
NX_VEGA_WALLET_URL=http://localhost:1789
|
||||
NX_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/
|
||||
NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supply green clown session"
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
|
||||
# Expose some env vars to cypress environment for market setup
|
||||
CYPRESS_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit supply green clown session
|
||||
|
||||
@@ -104,7 +104,7 @@ describe('deposit actions', { tags: '@smoke' }, () => {
|
||||
cy.visit('/#/markets/market-1');
|
||||
});
|
||||
|
||||
it('Deposit to trade is visible', () => {
|
||||
it.skip('Deposit to trade is visible', () => {
|
||||
cy.getByTestId('Collateral').click();
|
||||
cy.get('[row-id="asset-id"]').contains('tEURO').should('be.visible');
|
||||
cy.contains('[data-testid="deposit"]', 'Deposit').should('be.visible');
|
||||
|
||||
@@ -1,256 +0,0 @@
|
||||
import { MarketTradingModeMapping } from '@vegaprotocol/types';
|
||||
import { MarketState } from '@vegaprotocol/types';
|
||||
import compact from 'lodash/compact';
|
||||
|
||||
const accordionContent = 'accordion-content';
|
||||
const blockExplorerLink = 'block-explorer-link';
|
||||
const dialogClose = 'dialog-close';
|
||||
const dialogContent = 'dialog-content';
|
||||
const externalLink = 'external-link';
|
||||
const githubLink = 'github-link';
|
||||
const liquidityLink = 'view-liquidity-link';
|
||||
const marketInfoBtn = 'Info';
|
||||
const marketTitle = 'accordion-title';
|
||||
const providerName = 'provider-name';
|
||||
const row = 'key-value-table-row';
|
||||
const verifiedProofs = 'verified-proofs';
|
||||
|
||||
describe('market info is displayed', { tags: '@smoke' }, () => {
|
||||
beforeEach(() => {
|
||||
cy.mockTradingPage();
|
||||
});
|
||||
|
||||
before(() => {
|
||||
cy.setOnBoardingViewed();
|
||||
cy.mockTradingPage(MarketState.STATE_ACTIVE);
|
||||
cy.mockSubscription();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
cy.getByTestId(marketInfoBtn).click();
|
||||
cy.wait('@MarketInfo');
|
||||
});
|
||||
|
||||
it('current fees displayed', () => {
|
||||
// 6002-MDET-101
|
||||
cy.getByTestId(marketTitle).contains('Current fees').click();
|
||||
validateMarketDataRow(0, 'Maker Fee', '0.02%');
|
||||
validateMarketDataRow(1, 'Infrastructure Fee', '0.05%');
|
||||
validateMarketDataRow(2, 'Liquidity Fee', '1.00%');
|
||||
validateMarketDataRow(3, 'Total Fees', '1.07%');
|
||||
});
|
||||
|
||||
it('market price', () => {
|
||||
// 6002-MDET-102
|
||||
cy.getByTestId(marketTitle).contains('Market price').click();
|
||||
validateMarketDataRow(0, 'Mark Price', '46,126.90058');
|
||||
validateMarketDataRow(1, 'Best Bid Price', '44,126.90058 ');
|
||||
validateMarketDataRow(2, 'Best Offer Price', '48,126.90058 ');
|
||||
validateMarketDataRow(3, 'Quote Unit', 'BTC');
|
||||
});
|
||||
|
||||
it('market volume displayed', () => {
|
||||
// 6002-MDET-103
|
||||
cy.getByTestId(marketTitle).contains('Market volume').click();
|
||||
validateMarketDataRow(1, 'Open Interest', '-');
|
||||
validateMarketDataRow(2, 'Best Bid Volume', '1');
|
||||
validateMarketDataRow(3, 'Best Offer Volume', '3');
|
||||
validateMarketDataRow(4, 'Best Static Bid Volume', '2');
|
||||
validateMarketDataRow(5, 'Best Static Offer Volume', '4');
|
||||
});
|
||||
|
||||
it('insurance pool displayed', () => {
|
||||
// 6002-MDET-104
|
||||
cy.getByTestId(marketTitle).contains('Insurance pool').click();
|
||||
validateMarketDataRow(0, 'Balance', '0');
|
||||
});
|
||||
|
||||
it('key details displayed', () => {
|
||||
// 6002-MDET-201
|
||||
cy.getByTestId(marketTitle).contains('Key details').click();
|
||||
|
||||
const rows: [string, string][] = compact([
|
||||
['Name', 'BTCUSD Monthly (30 Jun 2022)'],
|
||||
['Market ID', 'market-0'],
|
||||
Cypress.env('NX_SUCCESSOR_MARKETS') && ['Parent Market ID', 'PARENT-A'],
|
||||
Cypress.env('NX_SUCCESSOR_MARKETS') && [
|
||||
'Insurance Pool Fraction',
|
||||
'0.75',
|
||||
],
|
||||
['Trading Mode', MarketTradingModeMapping.TRADING_MODE_CONTINUOUS],
|
||||
['Market Decimal Places', '5'],
|
||||
['Position Decimal Places', '0'],
|
||||
['Settlement Asset Decimal Places', '5'],
|
||||
]);
|
||||
|
||||
for (const rowNumber in rows) {
|
||||
const [name, value] = rows[rowNumber];
|
||||
validateMarketDataRow(Number(rowNumber), name, value);
|
||||
}
|
||||
});
|
||||
|
||||
it('instrument displayed', () => {
|
||||
// 6002-MDET-202
|
||||
cy.getByTestId(marketTitle).contains('Instrument').click();
|
||||
|
||||
validateMarketDataRow(0, 'Market Name', 'BTCUSD Monthly (30 Jun 2022)');
|
||||
validateMarketDataRow(1, 'Code', 'BTCUSD.MF21');
|
||||
validateMarketDataRow(2, 'Product Type', 'Future');
|
||||
validateMarketDataRow(3, 'Quote Name', 'BTC');
|
||||
});
|
||||
|
||||
it('oracle displayed', () => {
|
||||
// 6002-MDET-203
|
||||
cy.getByTestId(marketTitle).contains('Oracle').click();
|
||||
|
||||
cy.getByTestId(accordionContent)
|
||||
.getByTestId(providerName)
|
||||
.and('contain', 'Another oracle');
|
||||
|
||||
cy.getByTestId(providerName).should('be.visible').click();
|
||||
cy.getByTestId(dialogContent)
|
||||
.eq(1)
|
||||
.within(() => {
|
||||
cy.getByTestId(blockExplorerLink).contains('Block explorer');
|
||||
cy.getByTestId(githubLink).contains('Oracle repository');
|
||||
});
|
||||
cy.getByTestId(dialogClose).click();
|
||||
|
||||
cy.getByTestId(accordionContent)
|
||||
.getByTestId(verifiedProofs)
|
||||
.and('contain', '1');
|
||||
});
|
||||
|
||||
it('settlement asset displayed', () => {
|
||||
// 6002-MDET-206
|
||||
cy.getByTestId(marketTitle).contains('Settlement asset').click();
|
||||
cy.window().then((win) => {
|
||||
cy.stub(win, 'prompt').returns('DISABLED WINDOW PROMPT');
|
||||
});
|
||||
validateMarketDataRow(0, 'ID', 'asset-id');
|
||||
validateMarketDataRow(1, 'Type', 'ERC20');
|
||||
validateMarketDataRow(2, 'Name', 'Euro');
|
||||
validateMarketDataRow(3, 'Symbol', 'tEURO');
|
||||
validateMarketDataRow(4, 'Decimals', '5');
|
||||
validateMarketDataRow(5, 'Quantum', '1');
|
||||
validateMarketDataRow(6, 'Status', 'Enabled');
|
||||
validateMarketDataRow(7, 'Contract address', '0x0158…78a4');
|
||||
validateMarketDataRow(8, 'Withdrawal threshold', '0.0005');
|
||||
validateMarketDataRow(9, 'Lifetime limit', '1,230');
|
||||
validateMarketDataRow(10, 'Infrastructure fee account balance', '0.00001');
|
||||
validateMarketDataRow(11, 'Global reward pool account balance', '0.00002');
|
||||
});
|
||||
|
||||
it('metadata displayed', () => {
|
||||
// 6002-MDET-207
|
||||
cy.getByTestId(marketTitle).contains('Metadata').click();
|
||||
|
||||
validateMarketDataRow(0, 'Formerly', '076BB86A5AA41E3E');
|
||||
validateMarketDataRow(1, 'Base', 'BTC');
|
||||
validateMarketDataRow(2, 'Quote', 'USD');
|
||||
validateMarketDataRow(3, 'Class', 'fx/crypto');
|
||||
validateMarketDataRow(4, 'Sector', 'crypto');
|
||||
});
|
||||
|
||||
it('risk model displayed', () => {
|
||||
// 6002-MDET-208
|
||||
cy.getByTestId(marketTitle).contains('Risk model').click();
|
||||
validateMarketDataRow(0, 'Tau', '0.0001140771161');
|
||||
validateMarketDataRow(1, 'Risk Aversion Parameter', '0.01');
|
||||
});
|
||||
|
||||
it('risk parameters displayed', () => {
|
||||
// 6002-MDET-209
|
||||
cy.getByTestId(marketTitle).contains('Risk parameters').click();
|
||||
validateMarketDataRow(0, 'R', '0.016');
|
||||
validateMarketDataRow(1, 'Sigma', '0.3');
|
||||
});
|
||||
|
||||
it('risk factors displayed', () => {
|
||||
// 6002-MDET-210
|
||||
cy.getByTestId(marketTitle).contains('Risk factors').click();
|
||||
|
||||
validateMarketDataRow(0, 'Short', '0.008571790367285281');
|
||||
validateMarketDataRow(1, 'Long', '0.008508132993273576');
|
||||
});
|
||||
|
||||
it('price monitoring bounds displayed', () => {
|
||||
// 6002-MDET-211
|
||||
cy.getByTestId(marketTitle).contains('Price monitoring bounds 1').click();
|
||||
cy.get('p.col-span-1').contains('99.99999% probability price bounds');
|
||||
cy.get('p.col-span-1').contains('Within 43,200 seconds');
|
||||
validateMarketDataRow(0, 'Highest Price', '7.97323 ');
|
||||
validateMarketDataRow(1, 'Lowest Price', '6.54701 ');
|
||||
});
|
||||
|
||||
it('liquidity monitoring parameters displayed', () => {
|
||||
// 6002-MDET-212
|
||||
cy.getByTestId(marketTitle)
|
||||
.contains('Liquidity monitoring parameters')
|
||||
.click();
|
||||
|
||||
validateMarketDataRow(0, 'Triggering Ratio', '0.7');
|
||||
validateMarketDataRow(1, 'Time Window', '3,600');
|
||||
validateMarketDataRow(2, 'Scaling Factor', '10');
|
||||
});
|
||||
|
||||
it('liquidity displayed', () => {
|
||||
// 6002-MDET-213
|
||||
cy.getByTestId(marketTitle)
|
||||
.contains(/Liquidity(?! m)/)
|
||||
.click();
|
||||
|
||||
validateMarketDataRow(0, 'Target Stake', '10.00 tBTC');
|
||||
validateMarketDataRow(1, 'Supplied Stake', '0.01 tBTC');
|
||||
validateMarketDataRow(2, 'Market Value Proxy', '20.00 tBTC');
|
||||
cy.getByTestId(liquidityLink).should(
|
||||
'have.text',
|
||||
'View liquidity provision table'
|
||||
);
|
||||
});
|
||||
|
||||
it('liquidity price range displayed', () => {
|
||||
// 6002-MDET-214
|
||||
cy.getByTestId(marketTitle).contains('Liquidity price range').click();
|
||||
|
||||
validateMarketDataRow(0, 'Liquidity Price Range', '2.00% of mid price');
|
||||
validateMarketDataRow(1, 'Lowest Price', '45,204.362 BTC');
|
||||
validateMarketDataRow(2, 'Highest Price', '47,049.438 BTC');
|
||||
});
|
||||
|
||||
it('proposal displayed', () => {
|
||||
// 6002-MDET-301
|
||||
cy.getByTestId(marketTitle).contains('Proposal').click();
|
||||
|
||||
cy.getByTestId(accordionContent)
|
||||
.find(`[data-testid="${externalLink}"]`)
|
||||
.first()
|
||||
.should('have.text', 'View governance proposal')
|
||||
.and('have.attr', 'href')
|
||||
.and('contain', '/proposals/market-0');
|
||||
cy.getByTestId(accordionContent)
|
||||
.find(`[data-testid="${externalLink}"]`)
|
||||
.eq(1)
|
||||
.should('have.text', 'Propose a change to market')
|
||||
.and('have.attr', 'href')
|
||||
.and('contain', '/proposals/propose/update-market');
|
||||
});
|
||||
|
||||
afterEach('close toggle', () => {
|
||||
cy.get('[data-state="open"]').then((tab) => {
|
||||
if (tab) tab.find('button').trigger('click');
|
||||
});
|
||||
});
|
||||
|
||||
function validateMarketDataRow(
|
||||
rowNumber: number,
|
||||
name: string,
|
||||
value: string
|
||||
) {
|
||||
cy.getByTestId(row)
|
||||
.eq(rowNumber)
|
||||
.within(() => {
|
||||
cy.get('dt').should('contain.text', name);
|
||||
cy.get('dd').should('contain.text', value);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -24,9 +24,7 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => {
|
||||
|
||||
beforeEach(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.getByTestId('deal-ticket-fee-margin-required').within(() => {
|
||||
cy.get('button').click();
|
||||
});
|
||||
cy.getByTestId('deal-ticket-fee-margin-required').click();
|
||||
});
|
||||
|
||||
describe('limit order', () => {
|
||||
@@ -111,6 +109,7 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => {
|
||||
'Total margin available100.01 tDAI'
|
||||
);
|
||||
});
|
||||
cy.getByTestId('deal-ticket-fee-margin-required').click();
|
||||
});
|
||||
|
||||
it('must have current margin allocation', () => {
|
||||
@@ -120,6 +119,7 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => {
|
||||
'Current margin allocation'
|
||||
);
|
||||
});
|
||||
cy.getByTestId('deal-ticket-fee-margin-required').click();
|
||||
});
|
||||
|
||||
it('should open usage breakdown dialog when clicked on current margin allocation', () => {
|
||||
@@ -128,6 +128,7 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => {
|
||||
});
|
||||
cy.getByTestId('usage-breakdown').should('exist');
|
||||
cy.getByTestId('dialog-close').click();
|
||||
cy.getByTestId('deal-ticket-fee-margin-required').click();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -86,8 +86,8 @@ describe('trades', { tags: '@smoke' }, () => {
|
||||
});
|
||||
|
||||
it('copy price to deal ticket form', () => {
|
||||
cy.getByTestId('Order').click();
|
||||
// 6005-THIS-007
|
||||
cy.getByTestId('order-type-Limit').click();
|
||||
cy.get(colIdPrice).last().should('be.visible').click();
|
||||
cy.getByTestId('order-price').should('have.value', '171.16898');
|
||||
});
|
||||
|
||||
@@ -10,10 +10,10 @@ const dialogContent = 'dialog-content';
|
||||
describe('connect vega wallet', { tags: '@smoke', testIsolation: true }, () => {
|
||||
beforeEach(() => {
|
||||
// Using portfolio page as it requires vega wallet connection
|
||||
cy.visit('/#/portfolio');
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setOnBoardingViewed();
|
||||
cy.visit('/#/portfolio');
|
||||
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
||||
});
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
|
||||
@@ -15,8 +15,11 @@ NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||
NX_VEGA_CONSOLE_URL=https://console.vega.xyz
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-mainnet/codfcglpplgmmlokgilfkpcjnmkbfiel
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-mainnet
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
|
||||
# TAG name of the current app version - TODO: bump to the latest upon release
|
||||
NX_APP_VERSION=v0.21.1-core-0.72.14
|
||||
NX_APP_VERSION=v0.21.2-core-0.72.14
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
|
||||
@@ -15,6 +15,8 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
|
||||
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
|
||||
@@ -16,6 +16,7 @@ NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
|
||||
@@ -17,6 +17,7 @@ NX_VEGA_CONSOLE_URL=https://trading.validators-testnet.vega.rocks
|
||||
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 947 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 947 KiB |
@@ -1,18 +1,58 @@
|
||||
import { DepositContainer } from '@vegaprotocol/deposits';
|
||||
import { GetStarted } from '../../components/welcome-dialog';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Intent, TradingAnchorButton } from '@vegaprotocol/ui-toolkit';
|
||||
import { GetStartedCheckList } from '../../components/welcome-dialog';
|
||||
import {
|
||||
useGetOnboardingStep,
|
||||
useOnboardingStore,
|
||||
OnboardingStep,
|
||||
} from '../../components/welcome-dialog/use-get-onboarding-step';
|
||||
import { Links, Routes } from '../../pages/client-router';
|
||||
import classNames from 'classnames';
|
||||
|
||||
export const Deposit = () => {
|
||||
return (
|
||||
<div className="py-16 px-8 flex w-full justify-center">
|
||||
<div className="lg:min-w-[700px] min-w-[300px] max-w-[700px]">
|
||||
<h1 className="text-4xl xl:text-5xl uppercase font-alpha calt">
|
||||
{t('Deposit')}
|
||||
</h1>
|
||||
<div className="mt-10">
|
||||
<DepositContainer />
|
||||
<GetStarted />
|
||||
</div>
|
||||
<div className="max-w-[600px] px-4 py-8 mx-auto lg:px-8">
|
||||
<h1 className="mb-6 text-4xl uppercase xl:text-5xl font-alpha calt">
|
||||
{t('Deposit')}
|
||||
</h1>
|
||||
<div className="flex flex-col gap-6">
|
||||
<DepositContainer />
|
||||
<DepositGetStarted />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const DepositGetStarted = () => {
|
||||
const onboardingDismissed = useOnboardingStore((store) => store.dismissed);
|
||||
const dismiss = useOnboardingStore((store) => store.dismiss);
|
||||
const step = useGetOnboardingStep();
|
||||
const wrapperClasses = classNames(
|
||||
'flex flex-col py-4 px-6 gap-4 rounded',
|
||||
'bg-vega-blue-300 dark:bg-vega-blue-700',
|
||||
'border border-vega-blue-350 dark:border-vega-blue-650'
|
||||
);
|
||||
|
||||
// Dont show unless still onboarding
|
||||
if (onboardingDismissed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="pt-6 border-t border-default">
|
||||
<div className={wrapperClasses}>
|
||||
<h3 className="text-lg">{t('Get started')}</h3>
|
||||
<GetStartedCheckList />
|
||||
{step > OnboardingStep.ONBOARDING_DEPOSIT_STEP && (
|
||||
<TradingAnchorButton
|
||||
href={Links[Routes.HOME]()}
|
||||
onClick={() => dismiss()}
|
||||
intent={Intent.Info}
|
||||
>
|
||||
{t('Start trading')}
|
||||
</TradingAnchorButton>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,20 +1,16 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { marketsWithDataProvider } from '@vegaprotocol/markets';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { Links, Routes } from '../../pages/client-router';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
import { useTopTradedMarkets } from '../../lib/hooks/use-top-traded-markets';
|
||||
|
||||
// The home pages only purpose is to redirect to the users last market,
|
||||
// the top traded if they are new, or fall back to the list of markets.
|
||||
// Thats why we just render a loader here
|
||||
export const Home = () => {
|
||||
const navigate = useNavigate();
|
||||
// The default market selected in the platform behind the overlay
|
||||
// should be the oldest market that is currently trading in continuous mode(i.e. not in auction).
|
||||
const { data, error, loading } = useDataProvider({
|
||||
dataProvider: marketsWithDataProvider,
|
||||
variables: undefined,
|
||||
});
|
||||
const update = useGlobalStore((store) => store.update);
|
||||
const { data } = useTopTradedMarkets();
|
||||
const marketId = useGlobalStore((store) => store.marketId);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -32,12 +28,11 @@ export const Home = () => {
|
||||
navigate(Links[Routes.MARKETS]());
|
||||
}
|
||||
}
|
||||
}, [marketId, data, navigate, update]);
|
||||
}, [marketId, data, navigate]);
|
||||
|
||||
return (
|
||||
<AsyncRenderer data={data} loading={loading} error={error}>
|
||||
{/* Render a loading and error state but we will redirect if markets are found */}
|
||||
{null}
|
||||
</AsyncRenderer>
|
||||
<Splash>
|
||||
<Loader />
|
||||
</Splash>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@ import { TradePanels } from './trade-panels';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { Links, Routes } from '../../pages/client-router';
|
||||
import { ViewType, useSidebar } from '../../components/sidebar';
|
||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||
|
||||
const calculatePrice = (markPrice?: string, decimalPlaces?: number) => {
|
||||
return markPrice && decimalPlaces
|
||||
@@ -58,7 +59,9 @@ const TitleUpdater = ({
|
||||
export const MarketPage = () => {
|
||||
const { marketId } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const { init, view, setView } = useSidebar();
|
||||
const currentRouteId = useGetCurrentRouteId();
|
||||
const { setViews, getView } = useSidebar();
|
||||
const view = getView(currentRouteId);
|
||||
const { screenSize } = useScreenDimensions();
|
||||
const largeScreen = ['lg', 'xl', 'xxl', 'xxxl'].includes(screenSize);
|
||||
const update = useGlobalStore((store) => store.update);
|
||||
@@ -69,15 +72,14 @@ export const MarketPage = () => {
|
||||
useEffect(() => {
|
||||
if (data?.id && data.id !== lastMarketId) {
|
||||
update({ marketId: data.id });
|
||||
// make sidebar open on market id change
|
||||
setView({ type: ViewType.Order });
|
||||
}
|
||||
}, [update, lastMarketId, data?.id]);
|
||||
|
||||
// make sidebar open on deal ticket by default
|
||||
if (view === null) {
|
||||
setView({ type: ViewType.Order });
|
||||
useEffect(() => {
|
||||
if (largeScreen && view === undefined) {
|
||||
setViews({ type: ViewType.Order }, currentRouteId);
|
||||
}
|
||||
}, [update, lastMarketId, data?.id, setView, init, view]);
|
||||
}, [setViews, view, currentRouteId, largeScreen]);
|
||||
|
||||
const tradeView = useMemo(() => {
|
||||
if (largeScreen) {
|
||||
|
||||
@@ -56,6 +56,7 @@ const MainGrid = memo(
|
||||
<Tabs storageKey="console-trade-grid-main-left">
|
||||
<Tab
|
||||
id="chart"
|
||||
overflowHidden
|
||||
name={t('Chart')}
|
||||
menu={<TradingViews.candles.menu />}
|
||||
>
|
||||
@@ -72,7 +73,7 @@ const MainGrid = memo(
|
||||
</ResizableGridPanel>
|
||||
<ResizableGridPanel
|
||||
minSize={200}
|
||||
preferredSize={sizesMiddle[1] || 300}
|
||||
preferredSize={sizesMiddle[1] || 275}
|
||||
>
|
||||
<TradeGridChild>
|
||||
<Tabs storageKey="console-trade-grid-main-right">
|
||||
@@ -180,7 +181,7 @@ const TradeGridChild = ({ children }: TradeGridChildProps) => {
|
||||
{({ width, height }) => (
|
||||
<div
|
||||
style={{ width, height }}
|
||||
className="border border-default rounded-sm"
|
||||
className="border rounded-sm border-default"
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@@ -23,6 +23,7 @@ import { ViewType, useSidebar } from '../../components/sidebar';
|
||||
import { AccountsMenu } from '../../components/accounts-menu';
|
||||
import { DepositsMenu } from '../../components/deposits-menu';
|
||||
import { WithdrawalsMenu } from '../../components/withdrawals-menu';
|
||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||
|
||||
const WithdrawalsIndicator = () => {
|
||||
const { ready } = useIncompleteWithdrawals();
|
||||
@@ -37,7 +38,10 @@ const WithdrawalsIndicator = () => {
|
||||
};
|
||||
|
||||
export const Portfolio = () => {
|
||||
const { init, view, setView } = useSidebar();
|
||||
const currentRouteId = useGetCurrentRouteId();
|
||||
const { getView, setViews } = useSidebar();
|
||||
const view = getView(currentRouteId);
|
||||
|
||||
const { updateTitle } = usePageTitleStore((store) => ({
|
||||
updateTitle: store.updateTitle,
|
||||
}));
|
||||
@@ -48,10 +52,10 @@ export const Portfolio = () => {
|
||||
|
||||
// Make transfer sidebar open by default
|
||||
useEffect(() => {
|
||||
if (init && view === null) {
|
||||
setView({ type: ViewType.Transfer });
|
||||
if (view === undefined) {
|
||||
setViews({ type: ViewType.Transfer }, currentRouteId);
|
||||
}
|
||||
}, [init, view, setView]);
|
||||
}, [view, setViews, currentRouteId]);
|
||||
|
||||
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'portfolio' });
|
||||
const wrapperClasses = 'p-0.5 h-full max-h-full flex flex-col';
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
import { Input, InputError } from '@vegaprotocol/ui-toolkit';
|
||||
import type { FieldValues } from 'react-hook-form';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import classNames from 'classnames';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { useEffect } from 'react';
|
||||
import { Button } from './buttons';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
|
||||
export const ApplyCodeForm = () => {
|
||||
const { isReadOnly, pubKey, sendTx } = useVegaWallet();
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
setValue,
|
||||
setError,
|
||||
} = useForm();
|
||||
|
||||
const [params] = useSearchParams();
|
||||
useEffect(() => {
|
||||
const code = params.get('code');
|
||||
if (code) setValue('code', code);
|
||||
}, [params, setValue]);
|
||||
|
||||
const onSubmit = ({ code }: FieldValues) => {
|
||||
if (isReadOnly || !pubKey || !code || code.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
sendTx(pubKey, {
|
||||
applyReferralCode: {
|
||||
id: code as string,
|
||||
},
|
||||
})
|
||||
.then((res) => {
|
||||
// TODO: Do something with response
|
||||
})
|
||||
.catch((err) => {
|
||||
setError('code', {
|
||||
type: 'required',
|
||||
message: 'Your code has been rejected',
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mx-auto lg:w-1/2">
|
||||
<h3 className="mb-5 text-xl text-center uppercase calt">
|
||||
Apply a referral code
|
||||
</h3>
|
||||
<p className="mb-6 text-center">Enter a referral code</p>
|
||||
<form
|
||||
className={classNames('w-full flex flex-col gap-3', {
|
||||
'animate-shake': Boolean(errors.code),
|
||||
})}
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
>
|
||||
<label className="flex-grow">
|
||||
<span className="block mb-1 text-sm text-vega-clight-100 dark:text-vega-cdark-100">
|
||||
Your referral code
|
||||
</span>
|
||||
<Input
|
||||
hasError={Boolean(errors.code)}
|
||||
{...register('code', {
|
||||
required: 'You have to provide a code to apply it.',
|
||||
})}
|
||||
/>
|
||||
</label>
|
||||
<Button
|
||||
disabled={isReadOnly || !pubKey}
|
||||
className="w-full"
|
||||
type="submit"
|
||||
>
|
||||
Apply
|
||||
</Button>
|
||||
</form>
|
||||
{errors.code && (
|
||||
<InputError>{errors.code.message?.toString()}</InputError>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,83 @@
|
||||
import { Intent, TradingButton } from '@vegaprotocol/ui-toolkit';
|
||||
import classNames from 'classnames';
|
||||
import type { ComponentProps, HTMLAttributes } from 'react';
|
||||
import { forwardRef } from 'react';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
|
||||
type RainbowButtonProps = {
|
||||
variant?: 'full' | 'border';
|
||||
};
|
||||
|
||||
export const RainbowButton = ({
|
||||
variant = 'full',
|
||||
children,
|
||||
className,
|
||||
...props
|
||||
}: RainbowButtonProps & HTMLAttributes<HTMLButtonElement>) => (
|
||||
<button
|
||||
className={classNames(
|
||||
'bg-rainbow hover:bg-none hover:bg-vega-pink-500 rounded-lg overflow-hidden',
|
||||
{
|
||||
'px-5 py-3 text-white': variant === 'full',
|
||||
'p-[0.125rem]': variant === 'border',
|
||||
},
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div
|
||||
className={classNames({
|
||||
'bg-white dark:bg-vega-cdark-900 text-black dark:text-white px-5 py-3 rounded-[0.35rem] overflow-hidden':
|
||||
variant === 'border',
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
|
||||
const RAINBOW_TAB_STYLE = classNames(
|
||||
'inline-block',
|
||||
'bg-vega-clight-500 dark:bg-vega-cdark-500 hover:bg-vega-clight-400 dark:hover:bg-vega-cdark-400',
|
||||
'data-[state="active"]:text-white data-[state="active"]:bg-rainbow data-[state="active"]:hover:bg-none data-[state="active"]:hover:bg-vega-pink-500 dark:data-[state="active"]:hover:bg-vega-pink-500',
|
||||
'[&.active]:text-white [&.active]:bg-rainbow [&.active]:hover:bg-none [&.active]:hover:bg-vega-pink-500 dark:[&.active]:hover:bg-vega-pink-500',
|
||||
'px-5 py-3',
|
||||
'first:rounded-tl-lg last:rounded-tr-lg'
|
||||
);
|
||||
|
||||
export const RainbowTabButton = forwardRef<
|
||||
HTMLButtonElement,
|
||||
HTMLAttributes<HTMLButtonElement>
|
||||
>(({ children, ...props }, ref) => (
|
||||
<button ref={ref} className={RAINBOW_TAB_STYLE} {...props}>
|
||||
{children}
|
||||
</button>
|
||||
));
|
||||
RainbowTabButton.displayName = 'RainbowTabButton';
|
||||
|
||||
export const RainbowTabLink = ({
|
||||
to,
|
||||
children,
|
||||
...props
|
||||
}: ComponentProps<typeof NavLink>) => (
|
||||
<NavLink to={to} className={RAINBOW_TAB_STYLE} {...props}>
|
||||
{children}
|
||||
</NavLink>
|
||||
);
|
||||
|
||||
export const Button = forwardRef<
|
||||
HTMLButtonElement,
|
||||
ComponentProps<typeof TradingButton>
|
||||
>(({ children, intent, type, ...props }, ref) => {
|
||||
return (
|
||||
<TradingButton
|
||||
ref={ref}
|
||||
intent={intent || type === 'submit' ? Intent.Primary : Intent.None}
|
||||
type={type}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</TradingButton>
|
||||
);
|
||||
});
|
||||
Button.displayName = 'TradingButton';
|
||||
@@ -0,0 +1,6 @@
|
||||
export const BORDER_COLOR = 'border-vega-clight-500 dark:border-vega-cdark-500';
|
||||
export const GRADIENT =
|
||||
'bg-gradient-to-b from-vega-clight-800 dark:from-vega-cdark-800 to-transparent';
|
||||
|
||||
export const SKY_BACKGROUND =
|
||||
'bg-[url(/assets/sky-light.png)] dark:bg-[url(/assets/sky-dark.png)] bg-[40%_0px] bg-[length:1440px] bg-no-repeat';
|
||||
@@ -0,0 +1,61 @@
|
||||
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||
import { RainbowButton } from './buttons';
|
||||
import { useState } from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
export const CreateCodeForm = () => {
|
||||
const [err, setErr] = useState<boolean | null>(null);
|
||||
const openWalletDialog = useVegaWalletDialogStore(
|
||||
(store) => store.openVegaWalletDialog
|
||||
);
|
||||
const { isReadOnly, pubKey, sendTx } = useVegaWallet();
|
||||
const onSubmit = () => {
|
||||
if (isReadOnly || !pubKey) {
|
||||
setErr(true);
|
||||
} else {
|
||||
setErr(false);
|
||||
sendTx(pubKey, {
|
||||
createReferralSet: {
|
||||
isTeam: false,
|
||||
},
|
||||
})
|
||||
.then((res) => {
|
||||
// TODO: Do something with response
|
||||
})
|
||||
.catch((err) => {
|
||||
// TODO: Do something with rejection
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mx-auto lg:w-1/2">
|
||||
<h3 className="mb-5 text-xl text-center uppercase calt">
|
||||
Create a referral code
|
||||
</h3>
|
||||
<p className="mb-6 text-center">
|
||||
Generate a referral code to share with your friends and start earning
|
||||
commission.
|
||||
</p>
|
||||
{pubKey ? (
|
||||
<div className="text-center">
|
||||
<RainbowButton
|
||||
className={classNames({
|
||||
'animate-shake': err,
|
||||
})}
|
||||
variant="border"
|
||||
onClick={() => onSubmit()}
|
||||
>
|
||||
Create a referral code
|
||||
</RainbowButton>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-center">
|
||||
<RainbowButton variant="border" onClick={() => openWalletDialog()}>
|
||||
Connect wallet
|
||||
</RainbowButton>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
import { isRouteErrorResponse, useNavigate, useRouteError } from 'react-router';
|
||||
import { RainbowButton } from './buttons';
|
||||
import { AnimatedDudeWithWire } from './graphics/dude';
|
||||
import { LayoutWithSky } from './layout';
|
||||
|
||||
export const ErrorBoundary = () => {
|
||||
const error = useRouteError();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const title = isRouteErrorResponse(error)
|
||||
? `${error.status} ${error.statusText}`
|
||||
: 'Something went wrong';
|
||||
|
||||
const code = isRouteErrorResponse(error) ? error.status : 0;
|
||||
|
||||
const messages: Record<number, string> = {
|
||||
0: 'An unknown error occurred.',
|
||||
404: "The page you're looking for doesn't exists.",
|
||||
};
|
||||
|
||||
return (
|
||||
<LayoutWithSky className="pt-32">
|
||||
<div
|
||||
aria-hidden
|
||||
className="absolute top-64 right-[220px] md:right-[340px] max-sm:hidden"
|
||||
>
|
||||
<AnimatedDudeWithWire className="animate-spin" />
|
||||
</div>
|
||||
<h1 className="text-6xl font-alpha calt mb-10">{title}</h1>
|
||||
|
||||
{Object.keys(messages).includes(code.toString()) ? (
|
||||
<p className="text-lg mb-10">{messages[code]}</p>
|
||||
) : null}
|
||||
|
||||
<p className="text-lg mb-10">
|
||||
<RainbowButton
|
||||
onClick={() => navigate('..')}
|
||||
variant="border"
|
||||
className="text-xs"
|
||||
>
|
||||
Go back and try again
|
||||
</RainbowButton>
|
||||
</p>
|
||||
</LayoutWithSky>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,70 @@
|
||||
import classNames from 'classnames';
|
||||
import type { HTMLAttributes } from 'react';
|
||||
|
||||
export const Dude = ({ className }: HTMLAttributes<SVGElement>) => {
|
||||
return (
|
||||
<svg
|
||||
width="41"
|
||||
height="47"
|
||||
viewBox="0 0 41 47"
|
||||
fill="none"
|
||||
className={className}
|
||||
>
|
||||
<path
|
||||
d="M21.1895 0.298767L5.08827 27.4101L8.96133 29.7103L4.36099 37.4564L8.23404 39.7566L12.8344 32.0105L16.7074 34.3107L12.1071 42.0568L15.9801 44.3569L20.5805 36.6108L24.4535 38.911L40.5547 11.7996L21.1895 0.298767Z"
|
||||
className="fill-black dark:fill-white"
|
||||
/>
|
||||
<path
|
||||
d="M35.9346 15.1683L20.4424 5.96765L14.3086 16.2958L29.8008 25.4965L35.9346 15.1683Z"
|
||||
className="fill-white dark:fill-black"
|
||||
/>
|
||||
<path
|
||||
d="M25.646 17.7895L23.064 16.2561L21.5305 18.8381L24.1126 20.3716L25.646 17.7895Z"
|
||||
className="fill-black dark:fill-white"
|
||||
/>
|
||||
<path
|
||||
d="M29.7612 16.7412L27.1792 15.2077L25.6458 17.7898L28.2278 19.3232L29.7612 16.7412Z"
|
||||
className="fill-black dark:fill-white"
|
||||
/>
|
||||
<path
|
||||
d="M33.877 15.6925L31.2949 14.159L29.7615 16.7411L32.3435 18.2745L33.877 15.6925Z"
|
||||
className="fill-black dark:fill-white"
|
||||
/>
|
||||
<path
|
||||
d="M29.0342 26.7874L26.4521 25.2539L24.9187 27.836L27.5007 29.3694L29.0342 26.7874Z"
|
||||
fill="#FF077F"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const Wire = ({ className }: HTMLAttributes<SVGElement>) => {
|
||||
return (
|
||||
<svg
|
||||
width="157"
|
||||
height="88"
|
||||
viewBox="0 0 157 88"
|
||||
fill="none"
|
||||
className={className}
|
||||
>
|
||||
<path
|
||||
d="M109.398 6.12235C127.37 -3.81898 146.791 1.45045 153.465 14.307C160.138 27.1636 154.195 43.9948 140.438 52.1164C126.68 60.238 105.767 54.9998 84.9212 43.464C64.0752 31.9281 32.2412 6.42016 18.8175 24.185C6.90871 40.719 41.9332 68.4495 29.2664 82.7049C23.187 88.4974 11.1379 88.2645 0.968295 80.3398"
|
||||
className="stroke-black dark:stroke-white"
|
||||
strokeWidth="1.5"
|
||||
strokeMiterlimit="10"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const AnimatedDudeWithWire = ({ className }: { className?: string }) => (
|
||||
<div className="relative">
|
||||
<Wire className="absolute top-[25px]" />
|
||||
<Dude
|
||||
className={classNames(
|
||||
'absolute left-[96px] animate-[wave_20s_ease-in-out_infinite]',
|
||||
className
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@@ -0,0 +1,52 @@
|
||||
import { Table } from './table';
|
||||
|
||||
export const HowItWorksTable = () => (
|
||||
<Table
|
||||
className="bg-none bg-vega-clight-800 dark:bg-vega-cdark-800"
|
||||
noHeader
|
||||
noCollapse
|
||||
columns={[{ name: 'number', className: 'px-0 pl-5' }, { name: 'step' }]}
|
||||
data={[
|
||||
{
|
||||
number: (
|
||||
<span className="text-2xl calt text-vega-clight-100 dark:text-vega-cdark-100">
|
||||
1
|
||||
</span>
|
||||
),
|
||||
step: 'Referrers generate a code assigned to their key via an on chain transaction',
|
||||
},
|
||||
{
|
||||
number: (
|
||||
<span className="text-2xl calt text-vega-clight-100 dark:text-vega-cdark-100">
|
||||
2
|
||||
</span>
|
||||
),
|
||||
step: 'Anyone with the referral link can apply it to their key(s) of choice via an on chain transaction',
|
||||
},
|
||||
{
|
||||
number: (
|
||||
<span className="text-2xl calt text-vega-clight-100 dark:text-vega-cdark-100">
|
||||
3
|
||||
</span>
|
||||
),
|
||||
step: 'Discounts are applied automatically during trading based on the key(s) used',
|
||||
},
|
||||
{
|
||||
number: (
|
||||
<span className="text-2xl calt text-vega-clight-100 dark:text-vega-cdark-100">
|
||||
4
|
||||
</span>
|
||||
),
|
||||
step: 'Referrers earn commission based on a percentage of the taker fees their referees pay',
|
||||
},
|
||||
{
|
||||
number: (
|
||||
<span className="text-2xl calt text-vega-clight-100 dark:text-vega-cdark-100">
|
||||
5
|
||||
</span>
|
||||
),
|
||||
step: 'The commission is taken from the infrastructure fee, maker fee, and liquidity provider fee, not from the referee',
|
||||
},
|
||||
]}
|
||||
></Table>
|
||||
);
|
||||
@@ -0,0 +1,31 @@
|
||||
import classNames from 'classnames';
|
||||
import { AnimatedDudeWithWire } from './graphics/dude';
|
||||
|
||||
export const LandingBanner = () => {
|
||||
return (
|
||||
<div className={classNames('relative mb-20')}>
|
||||
<div className="">
|
||||
<div
|
||||
aria-hidden
|
||||
className="absolute top-64 right-[220px] md:right-[340px] max-sm:hidden"
|
||||
>
|
||||
<AnimatedDudeWithWire />
|
||||
</div>
|
||||
<div className="pt-32 sm:w-[50%]">
|
||||
<h1 className="text-6xl font-alpha calt mb-10">
|
||||
Earn commission & stake rewards
|
||||
</h1>
|
||||
<p className="text-lg mb-10">
|
||||
Invite friends and earn commission in the form of Vega rewards from
|
||||
the trading fees they pay. Stake those rewards to earn multipliers
|
||||
on future rewards.
|
||||
</p>
|
||||
<p className="text-lg">
|
||||
Any friends that join using the code will receive discounts off
|
||||
trading fees.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
import classNames from 'classnames';
|
||||
import type { HTMLAttributes } from 'react';
|
||||
import { SKY_BACKGROUND } from './constants';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
|
||||
export const Layout = ({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: HTMLAttributes<HTMLDivElement>) => {
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'max-w-[1440px]',
|
||||
'mx-auto px-4 md:px-32 lg:px-16 pb-32',
|
||||
'relative z-0',
|
||||
'h-full overflow-auto',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children || <Outlet />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const LayoutWithSky = ({
|
||||
className,
|
||||
...props
|
||||
}: HTMLAttributes<HTMLDivElement>) => {
|
||||
return (
|
||||
<div className={classNames('h-full', SKY_BACKGROUND)}>
|
||||
<Layout className={className} {...props} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,79 @@
|
||||
import { Tile } from './tile';
|
||||
import {
|
||||
CopyWithTooltip,
|
||||
Input,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { Button } from './buttons';
|
||||
import { Tag } from './tag';
|
||||
|
||||
export const ReferralStatistics = () => {
|
||||
return (
|
||||
<div className="grid grid-cols-1 grid-rows-1 gap-5">
|
||||
<div className="grid lg:grid-cols-3 grid-rows-3 lg:grid-rows-1 gap-5">
|
||||
<Tile>
|
||||
<h3 className="mb-1 text-4xl text-center">3</h3>
|
||||
<p className="mb-3 text-sm text-center text-vega-clight-100 dark:text-vega-cdark-100">
|
||||
Your referral tier
|
||||
</p>
|
||||
<Tag className="mx-auto" color="purple">
|
||||
10,000 until Tier 2
|
||||
</Tag>
|
||||
</Tile>
|
||||
<Tile>
|
||||
<h3 className="mb-1 text-4xl text-center">0.1%</h3>
|
||||
<p className="mb-3 text-sm text-center text-vega-clight-100 dark:text-vega-cdark-100">
|
||||
Your commission
|
||||
</p>
|
||||
<Tag className="mx-auto" color="purple">
|
||||
10,000 until 0.5%x
|
||||
</Tag>
|
||||
</Tile>
|
||||
<Tile variant="rainbow">
|
||||
<h3 className="mb-1 text-lg calt">Your referral code</h3>
|
||||
<p className="mb-3 text-sm text-vega-clight-100 dark:text-vega-cdark-100">
|
||||
Share this code with friends
|
||||
</p>
|
||||
<div className="flex gap-2">
|
||||
<Input size={1} readOnly value="1234567890" />
|
||||
<CopyWithTooltip text="1234567890">
|
||||
<Button
|
||||
className="text-sm no-underline"
|
||||
icon={<VegaIcon name={VegaIconNames.COPY} />}
|
||||
>
|
||||
<span>Copy</span>
|
||||
</Button>
|
||||
</CopyWithTooltip>
|
||||
</div>
|
||||
</Tile>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 grid-rows-1 gap-5">
|
||||
<Tile className="py-3">
|
||||
<h3 className="mb-1 text-2xl text-center">10,000</h3>
|
||||
<p className="text-sm text-center text-vega-clight-100 dark:text-vega-cdark-100">
|
||||
Your total trading volume
|
||||
</p>
|
||||
</Tile>
|
||||
<Tile className="py-3">
|
||||
<h3 className="mb-1 text-2xl text-center">7</h3>
|
||||
<p className="text-sm text-center text-vega-clight-100 dark:text-vega-cdark-100">
|
||||
Total traders referred
|
||||
</p>
|
||||
</Tile>
|
||||
<Tile className="py-3">
|
||||
<h3 className="mb-1 text-2xl text-center">0.1%</h3>
|
||||
<p className="text-sm text-center text-vega-clight-100 dark:text-vega-cdark-100">
|
||||
Maximum trader discount
|
||||
</p>
|
||||
</Tile>
|
||||
<Tile className="py-3">
|
||||
<h3 className="mb-1 text-2xl text-center">10,000</h3>
|
||||
<p className="text-sm text-center text-vega-clight-100 dark:text-vega-cdark-100">
|
||||
Your commission (last 30d)
|
||||
</p>
|
||||
</Tile>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,148 @@
|
||||
import {
|
||||
TradingAnchorButton,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import classNames from 'classnames';
|
||||
import { HowItWorksTable } from './how-it-works-table';
|
||||
import { LandingBanner } from './landing-banner';
|
||||
import { TiersTable } from './tiers-table';
|
||||
import { BORDER_COLOR, GRADIENT } from './constants';
|
||||
|
||||
import { RainbowTabLink } from './buttons';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
import { Tag } from './tag';
|
||||
import { Routes } from '../../pages/client-router';
|
||||
|
||||
export const Referrals = () => {
|
||||
return (
|
||||
<>
|
||||
<LandingBanner />
|
||||
<div>
|
||||
<div className="flex justify-center">
|
||||
<RainbowTabLink end to={Routes.REFERRALS}>
|
||||
Your referrals
|
||||
</RainbowTabLink>
|
||||
<RainbowTabLink to={Routes.REFERRALS_CREATE_CODE}>
|
||||
Your referrals
|
||||
</RainbowTabLink>
|
||||
<RainbowTabLink to={Routes.REFERRALS_APPLY_CODE}>
|
||||
Apply a code
|
||||
</RainbowTabLink>
|
||||
</div>
|
||||
<div className="py-16 border-t border-b border-vega-cdark-500">
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row items-baseline justify-between mt-10 mb-5">
|
||||
<h2 className="text-2xl">Referral tiers</h2>
|
||||
<span className="text-base">
|
||||
<span className="text-vega-clight-200 dark:text-vega-cdark-200">
|
||||
Program ends:
|
||||
</span>{' '}
|
||||
16 epochs
|
||||
</span>
|
||||
</div>
|
||||
<div className="mb-20">
|
||||
<TiersTable />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row items-baseline justify-between mb-5">
|
||||
<h2 className="text-2xl">Staking multipliers</h2>
|
||||
</div>
|
||||
<div className="flex flex-col mb-20 jjustify-items-stretch md:flex-row gap-5">
|
||||
<div
|
||||
className={classNames(
|
||||
'overflow-hidden',
|
||||
'border rounded-md w-full',
|
||||
BORDER_COLOR
|
||||
)}
|
||||
>
|
||||
<div aria-hidden>
|
||||
<img
|
||||
src="/1x.png"
|
||||
alt="1x multiplier"
|
||||
width={768}
|
||||
height={400}
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
<div className={classNames('p-3', GRADIENT)}>
|
||||
<h3 className="mb-3 text-xl">Tradestarter</h3>
|
||||
<p className="text-base text-vega-clight-100 dark:text-vega-cdark-100">
|
||||
Stake a minimum of 100 $VEGA tokens
|
||||
</p>
|
||||
<Tag color="green">Reward multiplier 1x</Tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={classNames(
|
||||
'overflow-hidden',
|
||||
'border rounded-md w-full',
|
||||
BORDER_COLOR
|
||||
)}
|
||||
>
|
||||
<div aria-hidden>
|
||||
<img
|
||||
src="/2x.png"
|
||||
alt="2x multiplier"
|
||||
width={768}
|
||||
height={400}
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
<div className={classNames('p-3', GRADIENT)}>
|
||||
<h3 className="mb-3 text-xl">Mid level degen</h3>
|
||||
<p className="text-base text-vega-clight-100 dark:text-vega-cdark-100">
|
||||
Stake a minimum of 1000 $VEGA tokens
|
||||
</p>
|
||||
<Tag color="blue">Reward multiplier 2x</Tag>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={classNames(
|
||||
'overflow-hidden',
|
||||
'border rounded-md w-full',
|
||||
BORDER_COLOR
|
||||
)}
|
||||
>
|
||||
<div aria-hidden>
|
||||
<img
|
||||
src="/3x.png"
|
||||
alt="3x multiplier"
|
||||
width={768}
|
||||
height={400}
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
<div className={classNames('p-3', GRADIENT)}>
|
||||
<h3 className="mb-3 text-xl">Reward hoarder</h3>
|
||||
<p className="text-base text-vega-clight-100 dark:text-vega-cdark-100">
|
||||
Stake a minimum of 10,000 $VEGA tokens
|
||||
</p>
|
||||
<Tag color="pink">Reward multiplier 3x</Tag>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-10 mb-5 text-center">
|
||||
<h2 className="text-2xl">How it works</h2>
|
||||
</div>
|
||||
<div className="md:w-[60%] mx-auto">
|
||||
<HowItWorksTable />
|
||||
<div className="mt-5">
|
||||
<TradingAnchorButton
|
||||
className="mx-auto w-max"
|
||||
href="https://docs.vega.xyz/"
|
||||
target="_blank"
|
||||
>
|
||||
Read the terms <VegaIcon name={VegaIconNames.OPEN_EXTERNAL} />
|
||||
</TradingAnchorButton>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Referrals;
|
||||
@@ -0,0 +1,116 @@
|
||||
import { Tooltip, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
||||
import classNames from 'classnames';
|
||||
import type { HTMLAttributes } from 'react';
|
||||
import { BORDER_COLOR, GRADIENT } from './constants';
|
||||
|
||||
type TableColumnDefinition = {
|
||||
displayName?: string;
|
||||
name: string;
|
||||
tooltip?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
type TableProps = {
|
||||
columns: TableColumnDefinition[];
|
||||
data: Record<TableColumnDefinition['name'] | 'className', React.ReactNode>[];
|
||||
noHeader?: boolean;
|
||||
noCollapse?: boolean;
|
||||
};
|
||||
|
||||
const INNER_BORDER_STYLE = `border-b ${BORDER_COLOR}`;
|
||||
|
||||
export const Table = ({
|
||||
columns,
|
||||
data,
|
||||
noHeader = false,
|
||||
noCollapse = false,
|
||||
className,
|
||||
...props
|
||||
}: TableProps & HTMLAttributes<HTMLTableElement>) => {
|
||||
const header = (
|
||||
<thead className={classNames({ 'max-md:hidden': !noCollapse })}>
|
||||
<tr>
|
||||
{columns.map(({ displayName, name, tooltip }) => (
|
||||
<th
|
||||
key={name}
|
||||
col-id={name}
|
||||
className={classNames(
|
||||
'px-5 py-3 text-sm text-vega-clight-100 dark:text-vega-cdark-100',
|
||||
INNER_BORDER_STYLE
|
||||
)}
|
||||
>
|
||||
<span className="flex flex-row gap-2 items-center">
|
||||
<span>{displayName}</span>
|
||||
{tooltip ? (
|
||||
<Tooltip description={tooltip}>
|
||||
<button className="text-vega-clight-400 dark:text-vega-cdark-400 no-underline decoration-transparent w-[12px] h-[12px] inline-flex">
|
||||
<VegaIcon size={12} name={VegaIconNames.INFO} />
|
||||
</button>
|
||||
</Tooltip>
|
||||
) : null}
|
||||
</span>
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
);
|
||||
return (
|
||||
<table
|
||||
className={classNames(
|
||||
'w-full',
|
||||
'border-separate border rounded-md border-spacing-0',
|
||||
BORDER_COLOR,
|
||||
GRADIENT,
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{!noHeader && header}
|
||||
<tbody>
|
||||
{data.map((d, i) => (
|
||||
<tr
|
||||
key={i}
|
||||
className={classNames(d['className'] as string, {
|
||||
'max-md:flex flex-col w-full': !noCollapse,
|
||||
})}
|
||||
>
|
||||
{columns.map(({ name, displayName, className }, j) => (
|
||||
<td
|
||||
className={classNames(
|
||||
'px-5 py-3 text-base',
|
||||
{
|
||||
'max-md:flex max-md:flex-col max-md:justify-between':
|
||||
!noCollapse,
|
||||
},
|
||||
INNER_BORDER_STYLE,
|
||||
{
|
||||
'border-none': i === data.length - 1 && noCollapse,
|
||||
'md:border-none': i === data.length - 1,
|
||||
'max-md:border-none':
|
||||
i === data.length - 1 && j === columns.length - 1,
|
||||
},
|
||||
className
|
||||
)}
|
||||
key={`${i}-${name}`}
|
||||
>
|
||||
{/** display column name in mobile view */}
|
||||
{!noCollapse &&
|
||||
!noHeader &&
|
||||
displayName &&
|
||||
displayName.length > 0 && (
|
||||
<span
|
||||
aria-hidden
|
||||
className="md:hidden font-mono text-xs px-0 text-vega-clight-100 dark:text-vega-cdark-100"
|
||||
>
|
||||
{displayName}
|
||||
</span>
|
||||
)}
|
||||
<span>{d[name]}</span>
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
import type { HTMLAttributes } from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
type TagProps = {
|
||||
color?: 'yellow' | 'green' | 'blue' | 'purple' | 'pink' | 'orange' | 'none';
|
||||
};
|
||||
export const Tag = ({
|
||||
color = 'none',
|
||||
children,
|
||||
className,
|
||||
...props
|
||||
}: TagProps & HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={classNames(
|
||||
'mt-3 w-max border rounded-[1rem] py-[0.125rem] px-2 text-xs',
|
||||
{
|
||||
'border-vega-yellow-500 text-vega-yellow-500': color === 'yellow',
|
||||
'border-vega-green-500 text-vega-green-500': color === 'green',
|
||||
'border-vega-blue-500 text-vega-blue-500': color === 'blue',
|
||||
'border-vega-purple-500 text-vega-purple-500': color === 'purple',
|
||||
'border-vega-pink-500 text-vega-pink-500': color === 'pink',
|
||||
'border-vega-orange-500 text-vega-orange-500': color === 'orange',
|
||||
'border-vega-clight-100 text-vega-clight-100 dark:border-vega-cdark-100 dark:text-vega-cdark-100':
|
||||
color === 'none',
|
||||
},
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
@@ -0,0 +1,42 @@
|
||||
import { Table } from './table';
|
||||
|
||||
export const TiersTable = () => (
|
||||
<Table
|
||||
columns={[
|
||||
{ name: 'tier', displayName: 'Tier' },
|
||||
{
|
||||
name: 'commission',
|
||||
displayName: 'Referrer commission',
|
||||
tooltip: 'A percentage of commission earned by the referrer',
|
||||
},
|
||||
{ name: 'discount', displayName: 'Referrer trading discount' },
|
||||
{ name: 'volume', displayName: 'Min. trading volume' },
|
||||
]}
|
||||
data={[
|
||||
{
|
||||
tier: '1',
|
||||
commission: '1.0%',
|
||||
discount: '1.0%',
|
||||
volume: '€30,000',
|
||||
className:
|
||||
'from-vega-pink-400 dark:from-vega-pink-600 to-20% bg-highlight',
|
||||
},
|
||||
{
|
||||
tier: '2',
|
||||
commission: '0.5%',
|
||||
discount: '0.5%',
|
||||
volume: '€20,000',
|
||||
className:
|
||||
'from-vega-purple-400 dark:from-vega-purple-600 to-20% bg-highlight',
|
||||
},
|
||||
{
|
||||
tier: '3',
|
||||
commission: '0.1%',
|
||||
discount: '0.1%',
|
||||
volume: '€10,000',
|
||||
className:
|
||||
'from-vega-blue-400 dark:from-vega-blue-600 to-20% bg-highlight',
|
||||
},
|
||||
]}
|
||||
></Table>
|
||||
);
|
||||
@@ -0,0 +1,39 @@
|
||||
import classNames from 'classnames';
|
||||
import type { HTMLAttributes } from 'react';
|
||||
import { BORDER_COLOR, GRADIENT } from './constants';
|
||||
|
||||
type TileProps = {
|
||||
variant?: 'rainbow' | 'default';
|
||||
};
|
||||
|
||||
export const Tile = ({
|
||||
variant = 'default',
|
||||
className,
|
||||
children,
|
||||
}: TileProps & HTMLAttributes<HTMLDivElement>) => {
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
{
|
||||
'bg-rainbow p-[0.125rem]': variant === 'rainbow',
|
||||
[`border-2 ${BORDER_COLOR} p-0`]: variant === 'default',
|
||||
},
|
||||
'rounded-lg overflow-hidden'
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={classNames(
|
||||
{
|
||||
'bg-white dark:bg-vega-cdark-900 text-black dark:text-white rounded-[0.35rem] overflow-hidden':
|
||||
variant === 'rainbow',
|
||||
},
|
||||
'p-6',
|
||||
GRADIENT,
|
||||
className
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -12,6 +12,7 @@ import type { DataGridSlice } from '../../stores/datagrid-store-slice';
|
||||
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
|
||||
import { ViewType, useSidebar } from '../sidebar';
|
||||
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||
|
||||
export const AccountsContainer = ({
|
||||
pinnedAsset,
|
||||
@@ -21,7 +22,8 @@ export const AccountsContainer = ({
|
||||
const onMarketClick = useMarketClickHandler(true);
|
||||
const { pubKey, isReadOnly } = useVegaWallet();
|
||||
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
const currentRouteId = useGetCurrentRouteId();
|
||||
const setViews = useSidebar((store) => store.setViews);
|
||||
|
||||
const gridStore = useAccountStore((store) => store.gridStore);
|
||||
const updateGridStore = useAccountStore((store) => store.updateGridStore);
|
||||
@@ -49,13 +51,13 @@ export const AccountsContainer = ({
|
||||
partyId={pubKey}
|
||||
onClickAsset={onClickAsset}
|
||||
onClickWithdraw={(assetId) => {
|
||||
setView({ type: ViewType.Withdraw, assetId });
|
||||
setViews({ type: ViewType.Withdraw, assetId }, currentRouteId);
|
||||
}}
|
||||
onClickDeposit={(assetId) => {
|
||||
setView({ type: ViewType.Deposit, assetId });
|
||||
setViews({ type: ViewType.Deposit, assetId }, currentRouteId);
|
||||
}}
|
||||
onClickTransfer={(assetId) => {
|
||||
setView({ type: ViewType.Transfer, assetId });
|
||||
setViews({ type: ViewType.Transfer, assetId }, currentRouteId);
|
||||
}}
|
||||
onMarketClick={onMarketClick}
|
||||
isReadOnly={isReadOnly}
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { TradingButton } from '@vegaprotocol/ui-toolkit';
|
||||
import { ViewType, useSidebar } from '../sidebar';
|
||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||
|
||||
export const AccountsMenu = () => {
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
const currentRouteId = useGetCurrentRouteId();
|
||||
const setViews = useSidebar((store) => store.setViews);
|
||||
|
||||
return (
|
||||
<>
|
||||
<TradingButton
|
||||
size="extra-small"
|
||||
data-testid="open-transfer"
|
||||
onClick={() => setView({ type: ViewType.Transfer })}
|
||||
onClick={() => setViews({ type: ViewType.Transfer }, currentRouteId)}
|
||||
>
|
||||
{t('Transfer')}
|
||||
</TradingButton>
|
||||
<TradingButton
|
||||
size="extra-small"
|
||||
onClick={() => setView({ type: ViewType.Deposit })}
|
||||
onClick={() => setViews({ type: ViewType.Deposit }, currentRouteId)}
|
||||
>
|
||||
{t('Deposit')}
|
||||
</TradingButton>
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
export const THROTTLE_UPDATE_TIME = 500;
|
||||
export const ONBOARDING_VIEWED_KEY = 'vega_onboarding_viewed';
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { TradingButton } from '@vegaprotocol/ui-toolkit';
|
||||
import { ViewType, useSidebar } from '../sidebar';
|
||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||
|
||||
export const DepositsMenu = () => {
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
const currentRouteId = useGetCurrentRouteId();
|
||||
const setViews = useSidebar((store) => store.setViews);
|
||||
|
||||
return (
|
||||
<TradingButton
|
||||
size="extra-small"
|
||||
onClick={() => setView({ type: ViewType.Deposit })}
|
||||
onClick={() => setViews({ type: ViewType.Deposit }, currentRouteId)}
|
||||
data-testid="deposit-button"
|
||||
>
|
||||
{t('Deposit')}
|
||||
|
||||
@@ -4,16 +4,19 @@ import classNames from 'classnames';
|
||||
import { Routes as AppRoutes } from '../../pages/client-router';
|
||||
import { MarketHeader } from '../market-header';
|
||||
import { LiquidityHeader } from '../liquidity-header';
|
||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||
|
||||
export const LayoutWithSidebar = () => {
|
||||
const sidebarView = useSidebar((store) => store.view);
|
||||
const currentRouteId = useGetCurrentRouteId();
|
||||
const views = useSidebar((store) => store.views);
|
||||
const sidebarView = views[currentRouteId] || null;
|
||||
const sidebarOpen = sidebarView !== null;
|
||||
|
||||
const gridClasses = classNames(
|
||||
'h-full relative z-0 grid',
|
||||
'grid-rows-[min-content_1fr_40px]',
|
||||
'lg:grid-rows-[min-content_1fr]',
|
||||
'lg:grid-cols-[1fr_350px_40px]'
|
||||
'lg:grid-cols-[1fr_280px_40px]',
|
||||
'xxxl:grid-cols-[1fr_320px_40px]'
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -180,6 +180,11 @@ const NavbarMenu = ({ onClick }: { onClick: () => void }) => {
|
||||
{t('Trading')}
|
||||
</NavbarLink>
|
||||
</NavbarItem>
|
||||
<NavbarItem>
|
||||
<NavbarLink to={Links[Routes.REFERRALS]()} onClick={onClick}>
|
||||
{t('Referrals')}
|
||||
</NavbarLink>
|
||||
</NavbarItem>
|
||||
<NavbarItem>
|
||||
<NavbarLink to={Links[Routes.PORTFOLIO]()} onClick={onClick}>
|
||||
{t('Portfolio')}
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import { OrderbookManager } from '@vegaprotocol/market-depth';
|
||||
import { ViewType, useSidebar } from '../sidebar';
|
||||
import { useDealTicketFormValues } from '@vegaprotocol/deal-ticket';
|
||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||
|
||||
export const OrderbookContainer = ({ marketId }: { marketId: string }) => {
|
||||
const currentRouteId = useGetCurrentRouteId();
|
||||
const update = useDealTicketFormValues((state) => state.updateAll);
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
const setViews = useSidebar((store) => store.setViews);
|
||||
return (
|
||||
<OrderbookManager
|
||||
marketId={marketId}
|
||||
onClick={(values) => {
|
||||
update(marketId, values);
|
||||
setView({ type: ViewType.Order });
|
||||
setViews({ type: ViewType.Order }, currentRouteId);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -11,6 +11,7 @@ import { MemoryRouter, Route, Routes } from 'react-router-dom';
|
||||
import { VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
||||
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { Routes as AppRoutes } from '../../pages/client-router';
|
||||
|
||||
jest.mock('../node-health', () => ({
|
||||
NodeHealthContainer: () => <span data-testid="node-health" />,
|
||||
@@ -115,7 +116,11 @@ describe('SidebarContent', () => {
|
||||
<VegaWalletContext.Provider value={walletContext}>
|
||||
<MemoryRouter initialEntries={['/markets/ABC']}>
|
||||
<Routes>
|
||||
<Route path="/markets/:marketId" element={<SidebarContent />} />
|
||||
<Route
|
||||
path="/markets/:marketId"
|
||||
id={AppRoutes.MARKET}
|
||||
element={<SidebarContent />}
|
||||
/>
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
</VegaWalletContext.Provider>
|
||||
@@ -124,13 +129,17 @@ describe('SidebarContent', () => {
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
|
||||
act(() => {
|
||||
useSidebar.setState({ view: { type: ViewType.Transfer } });
|
||||
useSidebar.setState({
|
||||
views: { [AppRoutes.MARKET]: { type: ViewType.Transfer } },
|
||||
});
|
||||
});
|
||||
|
||||
expect(screen.getByTestId('transfer')).toBeInTheDocument();
|
||||
|
||||
act(() => {
|
||||
useSidebar.setState({ view: { type: ViewType.Deposit } });
|
||||
useSidebar.setState({
|
||||
views: { [AppRoutes.MARKET]: { type: ViewType.Deposit } },
|
||||
});
|
||||
});
|
||||
|
||||
expect(screen.getByTestId('deposit')).toBeInTheDocument();
|
||||
@@ -141,26 +150,36 @@ describe('SidebarContent', () => {
|
||||
<VegaWalletContext.Provider value={walletContext}>
|
||||
<MemoryRouter initialEntries={['/portfolio']}>
|
||||
<Routes>
|
||||
<Route path="/portfolio" element={<SidebarContent />} />
|
||||
<Route
|
||||
path="/portfolio"
|
||||
id={AppRoutes.PORTFOLIO}
|
||||
element={<SidebarContent />}
|
||||
/>
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
</VegaWalletContext.Provider>
|
||||
);
|
||||
|
||||
act(() => {
|
||||
useSidebar.setState({ view: { type: ViewType.Order } });
|
||||
useSidebar.setState({
|
||||
views: { [AppRoutes.PORTFOLIO]: { type: ViewType.Order } },
|
||||
});
|
||||
});
|
||||
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
|
||||
act(() => {
|
||||
useSidebar.setState({ view: { type: ViewType.Settings } });
|
||||
useSidebar.setState({
|
||||
views: { [AppRoutes.PORTFOLIO]: { type: ViewType.Settings } },
|
||||
});
|
||||
});
|
||||
|
||||
expect(screen.getByTestId('settings')).toBeInTheDocument();
|
||||
|
||||
act(() => {
|
||||
useSidebar.setState({ view: { type: ViewType.Info } });
|
||||
useSidebar.setState({
|
||||
views: { [AppRoutes.PORTFOLIO]: { type: ViewType.Info } },
|
||||
});
|
||||
});
|
||||
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
@@ -178,6 +197,7 @@ describe('SidebarButton', () => {
|
||||
tooltip="INFO"
|
||||
onClick={onClick}
|
||||
view={view}
|
||||
routeId="current-route-id"
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
@@ -14,8 +14,9 @@ import { Settings } from '../settings';
|
||||
import { Tooltip } from '../../components/tooltip';
|
||||
import { WithdrawContainer } from '../withdraw-container';
|
||||
import { Routes as AppRoutes } from '../../pages/client-router';
|
||||
import { GetStarted } from '../welcome-dialog';
|
||||
import { useVegaWallet, useViewAsDialog } from '@vegaprotocol/wallet';
|
||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||
import { GetStarted } from '../welcome-dialog';
|
||||
|
||||
export enum ViewType {
|
||||
Order = 'Order',
|
||||
@@ -51,27 +52,31 @@ type SidebarView =
|
||||
};
|
||||
|
||||
export const Sidebar = () => {
|
||||
const currentRouteId = useGetCurrentRouteId();
|
||||
const navClasses = 'flex lg:flex-col items-center gap-2 lg:gap-4 p-1';
|
||||
const setViewAsDialogOpen = useViewAsDialog((state) => state.setOpen);
|
||||
const { pubKeys } = useVegaWallet();
|
||||
return (
|
||||
<div className="flex lg:flex-col gap-2 h-full p-1" data-testid="sidebar">
|
||||
<div className="flex h-full p-1 lg:flex-col gap-2" data-testid="sidebar">
|
||||
<nav className={navClasses}>
|
||||
{/* sidebar options that always show */}
|
||||
<SidebarButton
|
||||
view={ViewType.Deposit}
|
||||
icon={VegaIconNames.DEPOSIT}
|
||||
tooltip={t('Deposit')}
|
||||
routeId={currentRouteId}
|
||||
/>
|
||||
<SidebarButton
|
||||
view={ViewType.Withdraw}
|
||||
icon={VegaIconNames.WITHDRAW}
|
||||
tooltip={t('Withdraw')}
|
||||
routeId={currentRouteId}
|
||||
/>
|
||||
<SidebarButton
|
||||
view={ViewType.Transfer}
|
||||
icon={VegaIconNames.TRANSFER}
|
||||
tooltip={t('Transfer')}
|
||||
routeId={currentRouteId}
|
||||
/>
|
||||
{/* buttons for specific routes */}
|
||||
<Routes>
|
||||
@@ -94,11 +99,13 @@ export const Sidebar = () => {
|
||||
view={ViewType.Order}
|
||||
icon={VegaIconNames.TICKET}
|
||||
tooltip={t('Order')}
|
||||
routeId={currentRouteId}
|
||||
/>
|
||||
<SidebarButton
|
||||
view={ViewType.Info}
|
||||
icon={VegaIconNames.BREAKDOWN}
|
||||
tooltip={t('Market specification')}
|
||||
routeId={currentRouteId}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
@@ -114,12 +121,13 @@ export const Sidebar = () => {
|
||||
icon={VegaIconNames.EYE}
|
||||
tooltip={t('View as party')}
|
||||
disabled={Boolean(pubKeys)}
|
||||
routeId={currentRouteId}
|
||||
/>
|
||||
|
||||
<SidebarButton
|
||||
view={ViewType.Settings}
|
||||
icon={VegaIconNames.COG}
|
||||
tooltip={t('Settings')}
|
||||
routeId={currentRouteId}
|
||||
/>
|
||||
<NodeHealthContainer />
|
||||
</nav>
|
||||
@@ -133,23 +141,25 @@ export const SidebarButton = ({
|
||||
tooltip,
|
||||
disabled = false,
|
||||
onClick,
|
||||
routeId,
|
||||
}: {
|
||||
view?: ViewType;
|
||||
icon: VegaIconNames;
|
||||
tooltip: string;
|
||||
disabled?: boolean;
|
||||
onClick?: () => void;
|
||||
routeId: string;
|
||||
}) => {
|
||||
const { currView, setView } = useSidebar((store) => ({
|
||||
currView: store.view,
|
||||
setView: store.setView,
|
||||
const { setViews, getView } = useSidebar((store) => ({
|
||||
setViews: store.setViews,
|
||||
getView: store.getView,
|
||||
}));
|
||||
|
||||
const currView = getView(routeId);
|
||||
const onSelect = (view: SidebarView['type']) => {
|
||||
if (view === currView?.type) {
|
||||
setView(null);
|
||||
setViews(null, routeId);
|
||||
} else {
|
||||
setView({ type: view });
|
||||
setViews({ type: view }, routeId);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -187,7 +197,7 @@ export const SidebarButton = ({
|
||||
const SidebarDivider = () => {
|
||||
return (
|
||||
<div
|
||||
className="bg-vega-clight-600 dark:bg-vega-cdark-600 w-px h-4 lg:w-4 lg:h-px"
|
||||
className="w-px h-4 bg-vega-clight-600 dark:bg-vega-cdark-600 lg:w-4 lg:h-px"
|
||||
role="separator"
|
||||
/>
|
||||
);
|
||||
@@ -195,8 +205,10 @@ const SidebarDivider = () => {
|
||||
|
||||
export const SidebarContent = () => {
|
||||
const params = useParams();
|
||||
const { view, setView } = useSidebar();
|
||||
const currentRouteId = useGetCurrentRouteId();
|
||||
|
||||
const { setViews, getView } = useSidebar();
|
||||
const view = getView(currentRouteId);
|
||||
if (!view) return null;
|
||||
|
||||
if (view.type === ViewType.Order) {
|
||||
@@ -206,7 +218,7 @@ export const SidebarContent = () => {
|
||||
<DealTicketContainer
|
||||
marketId={params.marketId}
|
||||
onDeposit={(assetId) =>
|
||||
setView({ type: ViewType.Deposit, assetId })
|
||||
setViews({ type: ViewType.Deposit, assetId }, currentRouteId)
|
||||
}
|
||||
/>
|
||||
<GetStarted />
|
||||
@@ -233,7 +245,6 @@ export const SidebarContent = () => {
|
||||
return (
|
||||
<ContentWrapper title={t('Deposit')}>
|
||||
<DepositContainer assetId={view.assetId} />
|
||||
<GetStarted />
|
||||
</ContentWrapper>
|
||||
);
|
||||
}
|
||||
@@ -242,7 +253,6 @@ export const SidebarContent = () => {
|
||||
return (
|
||||
<ContentWrapper title={t('Withdraw')}>
|
||||
<WithdrawContainer assetId={view.assetId} />
|
||||
<GetStarted />
|
||||
</ContentWrapper>
|
||||
);
|
||||
}
|
||||
@@ -251,7 +261,6 @@ export const SidebarContent = () => {
|
||||
return (
|
||||
<ContentWrapper title={t('Transfer')}>
|
||||
<TransferContainer assetId={view.assetId} />
|
||||
<GetStarted />
|
||||
</ContentWrapper>
|
||||
);
|
||||
}
|
||||
@@ -276,7 +285,7 @@ const ContentWrapper = ({
|
||||
}) => {
|
||||
return (
|
||||
<TinyScroll
|
||||
className="h-full overflow-auto py-4 pl-3 pr-4"
|
||||
className="h-full py-4 pl-3 pr-4 overflow-auto"
|
||||
// panes have p-1, since sidebar is on the right make pl less to account for additional pane space
|
||||
data-testid="sidebar-content"
|
||||
>
|
||||
@@ -288,25 +297,21 @@ const ContentWrapper = ({
|
||||
|
||||
/** If rendered will close sidebar */
|
||||
const CloseSidebar = () => {
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
const currentRouteId = useGetCurrentRouteId();
|
||||
const setViews = useSidebar((store) => store.setViews);
|
||||
useEffect(() => {
|
||||
setView(null);
|
||||
}, [setView]);
|
||||
setViews(null, currentRouteId);
|
||||
}, [setViews, currentRouteId]);
|
||||
return null;
|
||||
};
|
||||
|
||||
export const useSidebar = create<{
|
||||
init: boolean;
|
||||
view: SidebarView | null;
|
||||
setView: (view: SidebarView | null) => void;
|
||||
}>()((set) => ({
|
||||
init: true,
|
||||
view: null,
|
||||
setView: (x) =>
|
||||
set(() => {
|
||||
if (x == null) {
|
||||
return { view: null, init: false };
|
||||
}
|
||||
return { view: x, init: false };
|
||||
}),
|
||||
views: { [key: string]: SidebarView | null };
|
||||
setViews: (view: SidebarView | null, routeId: string) => void;
|
||||
getView: (routeId: string) => SidebarView | null | undefined;
|
||||
}>()((set, get) => ({
|
||||
views: {},
|
||||
setViews: (x, routeId) =>
|
||||
set(({ views }) => ({ views: { ...views, [routeId]: x } })),
|
||||
getView: (routeId) => get().views[routeId],
|
||||
}));
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { Telemetry } from './telemetry';
|
||||
@@ -0,0 +1,69 @@
|
||||
import type { Toast } from '@vegaprotocol/ui-toolkit';
|
||||
import { Intent, useToasts } from '@vegaprotocol/ui-toolkit';
|
||||
import { useTelemetryApproval } from '../../lib/hooks/use-telemetry-approval';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { TelemetryApproval } from './telemetry-approval';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useOnboardingStore } from '../welcome-dialog/use-get-onboarding-step';
|
||||
|
||||
const TELEMETRY_APPROVAL_TOAST_ID = 'telemetry_tost_id';
|
||||
|
||||
export const Telemetry = () => {
|
||||
const onboardingDissmissed = useOnboardingStore((store) => store.dismissed);
|
||||
const [telemetryValue, setTelemetryValue, isTelemetryNeeded, closeTelemetry] =
|
||||
useTelemetryApproval();
|
||||
|
||||
const [setToast, hasToast, removeToast] = useToasts((store) => [
|
||||
store.setToast,
|
||||
store.hasToast,
|
||||
store.remove,
|
||||
]);
|
||||
|
||||
const onApprovalClose = useCallback(() => {
|
||||
closeTelemetry();
|
||||
removeToast(TELEMETRY_APPROVAL_TOAST_ID);
|
||||
}, [closeTelemetry, removeToast]);
|
||||
|
||||
const setTelemetryApprovalAndClose = useCallback(
|
||||
(value: string) => {
|
||||
setTelemetryValue(value);
|
||||
onApprovalClose();
|
||||
},
|
||||
[onApprovalClose, setTelemetryValue]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (isTelemetryNeeded && onboardingDissmissed) {
|
||||
const toast: Toast = {
|
||||
id: TELEMETRY_APPROVAL_TOAST_ID,
|
||||
intent: Intent.Primary,
|
||||
content: (
|
||||
<>
|
||||
<h3 className="mb-1 text-sm uppercase">
|
||||
{t('Improve vega console')}
|
||||
</h3>
|
||||
<TelemetryApproval
|
||||
telemetryValue={telemetryValue}
|
||||
setTelemetryValue={setTelemetryApprovalAndClose}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
onClose: onApprovalClose,
|
||||
};
|
||||
if (!hasToast(TELEMETRY_APPROVAL_TOAST_ID)) {
|
||||
setToast(toast);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}, [
|
||||
telemetryValue,
|
||||
isTelemetryNeeded,
|
||||
onboardingDissmissed,
|
||||
setToast,
|
||||
hasToast,
|
||||
onApprovalClose,
|
||||
setTelemetryApprovalAndClose,
|
||||
]);
|
||||
|
||||
return null;
|
||||
};
|
||||
@@ -11,6 +11,10 @@ jest.mock('@vegaprotocol/wallet', () => ({
|
||||
useVegaWalletDialogStore: () => mockUpdateDialogOpen,
|
||||
}));
|
||||
|
||||
jest.mock('../../lib/hooks/use-get-current-route-id', () => ({
|
||||
useGetCurrentRouteId: jest.fn().mockReturnValue('current-route-id'),
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
@@ -22,13 +22,15 @@ import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||
import { useCopyTimeout } from '@vegaprotocol/react-helpers';
|
||||
import { ViewType, useSidebar } from '../sidebar';
|
||||
import classNames from 'classnames';
|
||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||
|
||||
export const VegaWalletConnectButton = () => {
|
||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
||||
const openVegaWalletDialog = useVegaWalletDialogStore(
|
||||
(store) => store.openVegaWalletDialog
|
||||
);
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
const currentRouteId = useGetCurrentRouteId();
|
||||
const setViews = useSidebar((store) => store.setViews);
|
||||
const {
|
||||
pubKey,
|
||||
pubKeys,
|
||||
@@ -95,7 +97,7 @@ export const VegaWalletConnectButton = () => {
|
||||
<TradingDropdownItem
|
||||
data-testid="wallet-transfer"
|
||||
onClick={() => {
|
||||
setView({ type: ViewType.Transfer });
|
||||
setViews({ type: ViewType.Transfer }, currentRouteId);
|
||||
setDropdownOpen(false);
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -10,6 +10,7 @@ import { useVegaWallet, type PubKey } from '@vegaprotocol/wallet';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import CopyToClipboard from 'react-copy-to-clipboard';
|
||||
import { ViewType, useSidebar } from '../sidebar';
|
||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||
|
||||
export const VegaWalletMenu = ({
|
||||
setMenu,
|
||||
@@ -17,7 +18,8 @@ export const VegaWalletMenu = ({
|
||||
setMenu: (open: 'nav' | 'wallet' | null) => void;
|
||||
}) => {
|
||||
const { pubKey, pubKeys, selectPubKey, disconnect } = useVegaWallet();
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
const currentRouteId = useGetCurrentRouteId();
|
||||
const setViews = useSidebar((store) => store.setViews);
|
||||
|
||||
const activeKey = useMemo(() => {
|
||||
return pubKeys?.find((pk) => pk.publicKey === pubKey);
|
||||
@@ -46,7 +48,7 @@ export const VegaWalletMenu = ({
|
||||
<div className="flex flex-col gap-2 m-4">
|
||||
<Button
|
||||
onClick={() => {
|
||||
setView({ type: ViewType.Transfer });
|
||||
setViews({ type: ViewType.Transfer }, currentRouteId);
|
||||
setMenu(null);
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -2,7 +2,8 @@ import { MemoryRouter } from 'react-router-dom';
|
||||
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { GetStarted } from './get-started';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import { useOnboardingStore } from './use-get-onboarding-step';
|
||||
|
||||
let mockStep = 1;
|
||||
jest.mock('./use-get-onboarding-step', () => ({
|
||||
@@ -44,13 +45,15 @@ describe('GetStarted', () => {
|
||||
globalThis.window.vega = undefined as unknown as Vega;
|
||||
});
|
||||
|
||||
it('renders nothing if connected', () => {
|
||||
it('renders nothing if dismissed', () => {
|
||||
useOnboardingStore.setState({ dismissed: true });
|
||||
mockStep = 0;
|
||||
const { container } = renderComponent({ pubKey: 'my-pubkey' });
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
it('steps should be ticked', () => {
|
||||
useOnboardingStore.setState({ dismissed: false });
|
||||
const navigatorGetter: jest.SpyInstance = jest.spyOn(
|
||||
window.navigator,
|
||||
'userAgent',
|
||||
@@ -72,7 +75,7 @@ describe('GetStarted', () => {
|
||||
</MemoryRouter>
|
||||
);
|
||||
checkTicks(screen.getAllByRole('listitem'));
|
||||
expect(screen.getByRole('button', { name: 'Deposit' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: 'Deposit' })).toBeInTheDocument();
|
||||
|
||||
mockStep = 4;
|
||||
rerender(
|
||||
@@ -84,9 +87,11 @@ describe('GetStarted', () => {
|
||||
);
|
||||
checkTicks(screen.getAllByRole('listitem'));
|
||||
expect(
|
||||
screen.getByRole('button', { name: 'Ready to trade' })
|
||||
screen.getByRole('link', { name: 'Ready to trade' })
|
||||
).toBeInTheDocument();
|
||||
|
||||
fireEvent.click(screen.getByRole('link', { name: 'Ready to trade' }));
|
||||
|
||||
mockStep = 5;
|
||||
rerender(
|
||||
<MemoryRouter>
|
||||
|
||||
@@ -3,89 +3,113 @@ import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
ExternalLink,
|
||||
Intent,
|
||||
TradingAnchorButton,
|
||||
TradingButton,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import {
|
||||
OnboardingStep,
|
||||
useGetOnboardingStep,
|
||||
useOnboardingStore,
|
||||
} from './use-get-onboarding-step';
|
||||
import { Links, Routes } from '../../pages/client-router';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
import { useSidebar, ViewType } from '../sidebar';
|
||||
import * as constants from '../constants';
|
||||
import { useOnboardingStore } from './welcome-dialog';
|
||||
|
||||
interface Props {
|
||||
lead?: string;
|
||||
}
|
||||
|
||||
const GetStartedButton = ({ step }: { step: OnboardingStep }) => {
|
||||
const navigate = useNavigate();
|
||||
const [, setOnboardingViewed] = useLocalStorage(
|
||||
constants.ONBOARDING_VIEWED_KEY
|
||||
);
|
||||
|
||||
const dismiss = useOnboardingStore((store) => store.dismiss);
|
||||
const setDialogOpen = useOnboardingStore((store) => store.setDialogOpen);
|
||||
const marketId = useGlobalStore((store) => store.marketId);
|
||||
const link = marketId ? Links[Routes.MARKET](marketId) : Links[Routes.HOME]();
|
||||
const openVegaWalletDialog = useVegaWalletDialogStore(
|
||||
(store) => store.openVegaWalletDialog
|
||||
);
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
let buttonText = t('Get started');
|
||||
let onClickHandle = () => {
|
||||
openVegaWalletDialog();
|
||||
const setViews = useSidebar((store) => store.setViews);
|
||||
|
||||
const buttonProps = {
|
||||
size: 'small' as const,
|
||||
'data-testid': 'get-started-button',
|
||||
intent: Intent.Info,
|
||||
};
|
||||
|
||||
if (step <= OnboardingStep.ONBOARDING_CONNECT_STEP) {
|
||||
buttonText = t('Connect');
|
||||
return (
|
||||
<TradingButton {...buttonProps} onClick={() => openVegaWalletDialog()}>
|
||||
{t('Connect')}
|
||||
</TradingButton>
|
||||
);
|
||||
} else if (step === OnboardingStep.ONBOARDING_DEPOSIT_STEP) {
|
||||
buttonText = t('Deposit');
|
||||
onClickHandle = () => {
|
||||
navigate(link);
|
||||
setView({ type: ViewType.Deposit });
|
||||
dismiss();
|
||||
};
|
||||
} else if (step === OnboardingStep.ONBOARDING_ORDER_STEP) {
|
||||
buttonText = t('Ready to trade');
|
||||
onClickHandle = () => {
|
||||
navigate(link);
|
||||
setView({ type: ViewType.Order });
|
||||
setOnboardingViewed('true');
|
||||
};
|
||||
return (
|
||||
<TradingAnchorButton
|
||||
{...buttonProps}
|
||||
href={Links[Routes.DEPOSIT]()}
|
||||
onClick={() => setDialogOpen(false)}
|
||||
>
|
||||
{t('Deposit')}
|
||||
</TradingAnchorButton>
|
||||
);
|
||||
} else if (step >= OnboardingStep.ONBOARDING_ORDER_STEP) {
|
||||
return (
|
||||
<TradingAnchorButton
|
||||
{...buttonProps}
|
||||
href={marketId ? Links[Routes.MARKET](marketId) : Links[Routes.HOME]()}
|
||||
onClick={() => {
|
||||
setViews({ type: ViewType.Order }, Routes.MARKET);
|
||||
dismiss();
|
||||
}}
|
||||
>
|
||||
{t('Ready to trade')}
|
||||
</TradingAnchorButton>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<TradingButton
|
||||
onClick={onClickHandle}
|
||||
size="small"
|
||||
data-testid="get-started-button"
|
||||
intent={Intent.Info}
|
||||
>
|
||||
{buttonText}
|
||||
<TradingButton {...buttonProps} onClick={() => openVegaWalletDialog()}>
|
||||
{t('Get started')}
|
||||
</TradingButton>
|
||||
);
|
||||
};
|
||||
|
||||
export const GetStartedCheckList = () => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const currentStep = useGetOnboardingStep();
|
||||
return (
|
||||
<ul className="list-none">
|
||||
<Step
|
||||
step={1}
|
||||
text={t('Connect')}
|
||||
complete={Boolean(
|
||||
currentStep > OnboardingStep.ONBOARDING_CONNECT_STEP || pubKey
|
||||
)}
|
||||
/>
|
||||
<Step
|
||||
step={2}
|
||||
text={t('Deposit funds')}
|
||||
complete={currentStep > OnboardingStep.ONBOARDING_DEPOSIT_STEP}
|
||||
/>
|
||||
<Step
|
||||
step={3}
|
||||
text={t('Open a position')}
|
||||
complete={currentStep > OnboardingStep.ONBOARDING_ORDER_STEP}
|
||||
/>
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
|
||||
export const GetStarted = ({ lead }: Props) => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const { VEGA_ENV, VEGA_NETWORKS } = useEnvironment();
|
||||
const CANONICAL_URL = VEGA_NETWORKS[VEGA_ENV] || 'https://console.vega.xyz';
|
||||
const [onBoardingViewed] = useLocalStorage(constants.ONBOARDING_VIEWED_KEY);
|
||||
const currentStep = useGetOnboardingStep();
|
||||
const openVegaWalletDialog = useVegaWalletDialogStore(
|
||||
(store) => store.openVegaWalletDialog
|
||||
);
|
||||
|
||||
const getStartedNeeded =
|
||||
onBoardingViewed !== 'true' &&
|
||||
currentStep &&
|
||||
currentStep < OnboardingStep.ONBOARDING_COMPLETE_STEP;
|
||||
const currentStep = useGetOnboardingStep();
|
||||
const dismissed = useOnboardingStore((store) => store.dismissed);
|
||||
|
||||
const wrapperClasses = classNames(
|
||||
'flex flex-col py-4 px-6 gap-4 rounded',
|
||||
@@ -94,31 +118,13 @@ export const GetStarted = ({ lead }: Props) => {
|
||||
{ 'mt-8': !lead }
|
||||
);
|
||||
|
||||
if (getStartedNeeded) {
|
||||
if (!dismissed) {
|
||||
return (
|
||||
<div className={wrapperClasses} data-testid="get-started-banner">
|
||||
{lead && <h2>{lead}</h2>}
|
||||
<h3 className="text-lg">{t('Get started')}</h3>
|
||||
<div>
|
||||
<ul className="list-none">
|
||||
<Step
|
||||
step={1}
|
||||
text={t('Connect')}
|
||||
complete={Boolean(
|
||||
currentStep > OnboardingStep.ONBOARDING_CONNECT_STEP || pubKey
|
||||
)}
|
||||
/>
|
||||
<Step
|
||||
step={2}
|
||||
text={t('Deposit funds')}
|
||||
complete={currentStep > OnboardingStep.ONBOARDING_DEPOSIT_STEP}
|
||||
/>
|
||||
<Step
|
||||
step={3}
|
||||
text={t('Open a position')}
|
||||
complete={currentStep > OnboardingStep.ONBOARDING_ORDER_STEP}
|
||||
/>
|
||||
</ul>
|
||||
<GetStartedCheckList />
|
||||
</div>
|
||||
<div>
|
||||
<GetStartedButton step={currentStep} />
|
||||
@@ -126,7 +132,7 @@ export const GetStarted = ({ lead }: Props) => {
|
||||
{VEGA_ENV === Networks.MAINNET && (
|
||||
<p className="text-sm">
|
||||
{t('Experiment for free with virtual assets on')}{' '}
|
||||
<ExternalLink href={CANONICAL_URL}>
|
||||
<ExternalLink href={VEGA_NETWORKS.TESTNET}>
|
||||
{t('Fairground Testnet')}
|
||||
</ExternalLink>
|
||||
</p>
|
||||
@@ -134,7 +140,7 @@ export const GetStarted = ({ lead }: Props) => {
|
||||
{VEGA_ENV === Networks.TESTNET && (
|
||||
<p className="text-sm">
|
||||
{t('Ready to trade with real funds?')}{' '}
|
||||
<ExternalLink href={CANONICAL_URL}>
|
||||
<ExternalLink href={VEGA_NETWORKS.MAINNET}>
|
||||
{t('Switch to Mainnet')}
|
||||
</ExternalLink>
|
||||
</p>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { depositsProvider } from '@vegaprotocol/deposits';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
@@ -7,6 +9,29 @@ import { aggregatedAccountsDataProvider } from '@vegaprotocol/accounts';
|
||||
import { positionsDataProvider } from '@vegaprotocol/positions';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
|
||||
const ONBOARDING_STORAGE_KEY = 'vega_onboarding';
|
||||
export const useOnboardingStore = create<{
|
||||
dialogOpen: boolean;
|
||||
dismissed: boolean;
|
||||
dismiss: () => void;
|
||||
setDialogOpen: (isOpen: boolean) => void;
|
||||
}>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
dialogOpen: true,
|
||||
dismissed: false,
|
||||
dismiss: () => set({ dismissed: true }),
|
||||
setDialogOpen: (isOpen) => set({ dialogOpen: isOpen }),
|
||||
}),
|
||||
{
|
||||
name: ONBOARDING_STORAGE_KEY,
|
||||
partialize: (state) => ({
|
||||
dismissed: state.dismissed,
|
||||
}),
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
export enum OnboardingStep {
|
||||
ONBOARDING_UNKNOWN_STEP,
|
||||
ONBOARDING_WALLET_STEP,
|
||||
|
||||
@@ -1,40 +1,24 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { GetStarted } from './get-started';
|
||||
import { TradingButton } from '@vegaprotocol/ui-toolkit';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { TradingAnchorButton } from '@vegaprotocol/ui-toolkit';
|
||||
import { Links, Routes } from '../../pages/client-router';
|
||||
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useOnboardingStore } from './welcome-dialog';
|
||||
import { useMarketList } from '@vegaprotocol/markets';
|
||||
import { isMarketActive } from '../../lib/utils';
|
||||
import orderBy from 'lodash/orderBy';
|
||||
import { priceChangePercentage } from '@vegaprotocol/utils';
|
||||
import { useTopTradedMarkets } from '../../lib/hooks/use-top-traded-markets';
|
||||
import { useOnboardingStore } from './use-get-onboarding-step';
|
||||
|
||||
export const WelcomeDialogContent = () => {
|
||||
const { VEGA_ENV } = useEnvironment();
|
||||
|
||||
const dismiss = useOnboardingStore((store) => store.dismiss);
|
||||
const navigate = useNavigate();
|
||||
const { data } = useMarketList();
|
||||
const markets = orderBy(
|
||||
data?.filter((m) => isMarketActive(m.state)) || [],
|
||||
[
|
||||
(m) => {
|
||||
if (!m.candles?.length) return 0;
|
||||
return Number(priceChangePercentage(m.candles.map((c) => c.close)));
|
||||
},
|
||||
],
|
||||
['desc']
|
||||
const setOnboardingDialog = useOnboardingStore(
|
||||
(store) => store.setDialogOpen
|
||||
);
|
||||
const explore = () => {
|
||||
const marketId = markets?.[0].id ?? '';
|
||||
const link = marketId
|
||||
? Links[Routes.MARKET](marketId)
|
||||
: Links[Routes.MARKETS]();
|
||||
navigate(link);
|
||||
dismiss();
|
||||
};
|
||||
|
||||
const { data } = useTopTradedMarkets();
|
||||
const marketId = data && data[0]?.id;
|
||||
const link = marketId
|
||||
? Links[Routes.MARKET](marketId)
|
||||
: Links[Routes.MARKETS]();
|
||||
|
||||
const lead =
|
||||
VEGA_ENV === Networks.MAINNET
|
||||
? t('Start trading on the worlds most advanced decentralised exchange.')
|
||||
@@ -43,7 +27,7 @@ export const WelcomeDialogContent = () => {
|
||||
);
|
||||
return (
|
||||
<div className="flex flex-col sm:flex-row gap-8">
|
||||
<div className="sm:w-1/2 flex flex-col justify-between pt-3">
|
||||
<div className="flex flex-col justify-between pt-3 sm:w-1/2">
|
||||
<ul className="ml-0">
|
||||
<ListItemContent
|
||||
icon={<NonCustodialIcon />}
|
||||
@@ -65,15 +49,16 @@ export const WelcomeDialogContent = () => {
|
||||
)}
|
||||
/>
|
||||
</ul>
|
||||
<TradingButton
|
||||
onClick={explore}
|
||||
<TradingAnchorButton
|
||||
href={link}
|
||||
onClick={() => setOnboardingDialog(false)}
|
||||
className="block w-full"
|
||||
data-testid="browse-markets-button"
|
||||
>
|
||||
{t('Explore')}
|
||||
</TradingButton>
|
||||
</TradingAnchorButton>
|
||||
</div>
|
||||
<div className="sm:w-1/2 flex grow">
|
||||
<div className="flex sm:w-1/2 grow">
|
||||
<GetStarted lead={lead} />
|
||||
</div>
|
||||
</div>
|
||||
@@ -90,10 +75,10 @@ const ListItemContent = ({
|
||||
text: string;
|
||||
}) => {
|
||||
return (
|
||||
<li className="my-4 flex gap-3">
|
||||
<div className="shrink-0 pt-1">{icon}</div>
|
||||
<li className="flex my-4 gap-3">
|
||||
<div className="pt-1 shrink-0">{icon}</div>
|
||||
<div>
|
||||
<h3 className="text-lg leading-snug mb-2">{title}</h3>
|
||||
<h3 className="mb-2 text-lg leading-snug">{title}</h3>
|
||||
<p className="text-sm text-secondary">{text}</p>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
@@ -1,133 +1,32 @@
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import type { Toast } from '@vegaprotocol/ui-toolkit';
|
||||
import { Dialog, Intent, useToasts } from '@vegaprotocol/ui-toolkit';
|
||||
import { Dialog, Intent } from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import { WelcomeDialogContent } from './welcome-dialog-content';
|
||||
import { Links, Routes } from '../../pages/client-router';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
import {
|
||||
useGetOnboardingStep,
|
||||
OnboardingStep,
|
||||
} from './use-get-onboarding-step';
|
||||
import * as constants from '../constants';
|
||||
import { TelemetryApproval } from './telemetry-approval';
|
||||
import { useTelemetryApproval } from '../../lib/hooks/use-telemetry-approval';
|
||||
import { useCallback } from 'react';
|
||||
import { useOnboardingStore } from './use-get-onboarding-step';
|
||||
|
||||
const ONBOARDING_STORAGE_KEY = 'vega_onboarding_dismiss_store';
|
||||
export const useOnboardingStore = create<{
|
||||
dismissed: boolean;
|
||||
dismiss: () => void;
|
||||
}>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
dismissed: false,
|
||||
dismiss: () => set(() => ({ dismissed: true })),
|
||||
}),
|
||||
{
|
||||
name: ONBOARDING_STORAGE_KEY,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
const TELEMETRY_APPROVAL_TOAST_ID = 'telemetry_tost_id';
|
||||
export const WelcomeDialog = () => {
|
||||
const { VEGA_ENV } = useEnvironment();
|
||||
const navigate = useNavigate();
|
||||
const [telemetryValue, setTelemetryValue, isTelemetryNeeded, closeTelemetry] =
|
||||
useTelemetryApproval();
|
||||
const [onBoardingViewed] = useLocalStorage(constants.ONBOARDING_VIEWED_KEY);
|
||||
const dismiss = useOnboardingStore((store) => store.dismiss);
|
||||
const dismissed = useOnboardingStore((store) => store.dismissed);
|
||||
const currentStep = useGetOnboardingStep();
|
||||
const isTelemetryPopupNeeded =
|
||||
isTelemetryNeeded &&
|
||||
(onBoardingViewed === 'true' ||
|
||||
currentStep > OnboardingStep.ONBOARDING_ORDER_STEP);
|
||||
const dialogOpen = useOnboardingStore((store) => store.dialogOpen);
|
||||
const dismiss = useOnboardingStore((store) => store.dismiss);
|
||||
|
||||
const isOnboardingDialogNeeded =
|
||||
onBoardingViewed !== 'true' &&
|
||||
currentStep &&
|
||||
currentStep < OnboardingStep.ONBOARDING_COMPLETE_STEP &&
|
||||
!dismissed;
|
||||
const marketId = useGlobalStore((store) => store.marketId);
|
||||
|
||||
const onClose = () => {
|
||||
if (isTelemetryPopupNeeded) {
|
||||
closeTelemetry();
|
||||
} else {
|
||||
const link = marketId
|
||||
? Links[Routes.MARKET](marketId)
|
||||
: Links[Routes.HOME]();
|
||||
navigate(link);
|
||||
dismiss();
|
||||
}
|
||||
};
|
||||
|
||||
const [setToast, hasToast, removeToast] = useToasts((store) => [
|
||||
store.setToast,
|
||||
store.hasToast,
|
||||
store.remove,
|
||||
]);
|
||||
const onApprovalClose = useCallback(() => {
|
||||
closeTelemetry();
|
||||
removeToast(TELEMETRY_APPROVAL_TOAST_ID);
|
||||
}, [removeToast, closeTelemetry]);
|
||||
|
||||
const setTelemetryApprovalAndClose = useCallback(
|
||||
(value: string) => {
|
||||
setTelemetryValue(value);
|
||||
onApprovalClose();
|
||||
},
|
||||
[setTelemetryValue, onApprovalClose]
|
||||
);
|
||||
|
||||
if (isTelemetryPopupNeeded) {
|
||||
const toast: Toast = {
|
||||
id: TELEMETRY_APPROVAL_TOAST_ID,
|
||||
intent: Intent.Primary,
|
||||
content: (
|
||||
<>
|
||||
<h3 className="mb-1 text-sm uppercase">
|
||||
{t('Improve vega console')}
|
||||
</h3>
|
||||
<TelemetryApproval
|
||||
telemetryValue={telemetryValue}
|
||||
setTelemetryValue={setTelemetryApprovalAndClose}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
onClose: onApprovalClose,
|
||||
};
|
||||
if (!hasToast(TELEMETRY_APPROVAL_TOAST_ID)) {
|
||||
setToast(toast);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const title = (
|
||||
<span className="font-alpha calt" data-testid="welcome-title">
|
||||
{t('Console')}{' '}
|
||||
<span className="text-vega-clight-100 dark:text-vega-cdark-100">
|
||||
{VEGA_ENV}
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
|
||||
return isOnboardingDialogNeeded ? (
|
||||
return (
|
||||
<Dialog
|
||||
open
|
||||
title={title}
|
||||
open={dismissed ? false : dialogOpen}
|
||||
title={
|
||||
<span className="font-alpha calt" data-testid="welcome-title">
|
||||
{t('Console')}{' '}
|
||||
<span className="text-vega-clight-100 dark:text-vega-cdark-100">
|
||||
{VEGA_ENV}
|
||||
</span>
|
||||
</span>
|
||||
}
|
||||
size="medium"
|
||||
onChange={onClose}
|
||||
onChange={() => dismiss()}
|
||||
intent={Intent.None}
|
||||
dataTestId="welcome-dialog"
|
||||
>
|
||||
<WelcomeDialogContent />
|
||||
</Dialog>
|
||||
) : null;
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { TradingButton } from '@vegaprotocol/ui-toolkit';
|
||||
import { ViewType, useSidebar } from '../sidebar';
|
||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||
|
||||
export const WithdrawalsMenu = () => {
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
|
||||
const setViews = useSidebar((store) => store.setViews);
|
||||
const currentRouteId = useGetCurrentRouteId();
|
||||
return (
|
||||
<TradingButton
|
||||
size="extra-small"
|
||||
onClick={() => setView({ type: ViewType.Withdraw })}
|
||||
onClick={() => setViews({ type: ViewType.Withdraw }, currentRouteId)}
|
||||
data-testid="withdraw-dialog-button"
|
||||
>
|
||||
{t('Make withdrawal')}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { routerConfig } from '../../pages/client-router';
|
||||
import { matchRoutes, useLocation } from 'react-router-dom';
|
||||
|
||||
export const useGetCurrentRouteId = () => {
|
||||
const location = useLocation();
|
||||
const currentRoute = matchRoutes(routerConfig, location);
|
||||
const lastRoute = currentRoute?.pop();
|
||||
if (lastRoute) {
|
||||
const {
|
||||
route: { id },
|
||||
} = lastRoute;
|
||||
return id || '';
|
||||
}
|
||||
return '';
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
import orderBy from 'lodash/orderBy';
|
||||
import { calcTradedFactor, useMarketList } from '@vegaprotocol/markets';
|
||||
import { isMarketActive } from '../utils';
|
||||
|
||||
export const useTopTradedMarkets = () => {
|
||||
const { data, loading, error } = useMarketList();
|
||||
|
||||
const activeMarkets = data?.filter((m) => isMarketActive(m.state));
|
||||
const marketsByTopTraded = data
|
||||
? orderBy(activeMarkets, (m) => calcTradedFactor(m), 'desc')
|
||||
: undefined;
|
||||
return { data: marketsByTopTraded, loading, error };
|
||||
};
|
||||
@@ -49,6 +49,7 @@ import {
|
||||
import { ViewingBanner } from '../components/viewing-banner';
|
||||
import { NavHeader } from '../components/navbar/nav-header';
|
||||
import { Routes as AppRoutes } from './client-router';
|
||||
import { Telemetry } from '../components/telemetry';
|
||||
|
||||
const DEFAULT_TITLE = t('Welcome to Vega trading!');
|
||||
|
||||
@@ -125,6 +126,7 @@ function AppBody({ Component }: AppProps) {
|
||||
<InitializeHandlers />
|
||||
<MaybeConnectEagerly />
|
||||
<PartyData />
|
||||
<Telemetry />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,10 @@ import { t } from '@vegaprotocol/i18n';
|
||||
import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import trimEnd from 'lodash/trimEnd';
|
||||
import { LayoutWithSidebar } from '../components/layouts';
|
||||
import { LayoutWithSky } from '../client-pages/referrals/layout';
|
||||
import { ReferralStatistics } from '../client-pages/referrals/referral-statistics';
|
||||
import { ApplyCodeForm } from '../client-pages/referrals/apply-code-form';
|
||||
import { CreateCodeForm } from '../client-pages/referrals/create-code-form';
|
||||
|
||||
const LazyHome = dynamic(() => import('../client-pages/home'), {
|
||||
ssr: false,
|
||||
@@ -35,6 +39,13 @@ const LazyDeposit = dynamic(() => import('../client-pages/deposit'), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
const LazyReferrals = dynamic(
|
||||
() => import('../client-pages/referrals/referrals'),
|
||||
{
|
||||
ssr: false,
|
||||
}
|
||||
);
|
||||
|
||||
export enum Routes {
|
||||
HOME = '/',
|
||||
MARKET = '/markets/:marketId',
|
||||
@@ -43,6 +54,10 @@ export enum Routes {
|
||||
LIQUIDITY = '/liquidity/:marketId',
|
||||
DISCLAIMER = '/disclaimer',
|
||||
DEPOSIT = '/deposit',
|
||||
REFERRALS = '/referrals',
|
||||
REFERRALS_APPLY_CODE = '/referrals/apply-code',
|
||||
REFERRALS_CREATE_CODE = '/referrals/create-code',
|
||||
TEAMS = '/teams',
|
||||
}
|
||||
|
||||
type ConsoleLinks = { [r in Routes]: (...args: string[]) => string };
|
||||
@@ -57,9 +72,13 @@ export const Links: ConsoleLinks = {
|
||||
trimEnd(Routes.LIQUIDITY.replace(':marketId', marketId)),
|
||||
[Routes.DISCLAIMER]: () => Routes.DISCLAIMER,
|
||||
[Routes.DEPOSIT]: () => Routes.DEPOSIT,
|
||||
[Routes.REFERRALS]: () => Routes.REFERRALS,
|
||||
[Routes.REFERRALS_APPLY_CODE]: () => Routes.REFERRALS_APPLY_CODE,
|
||||
[Routes.REFERRALS_CREATE_CODE]: () => Routes.REFERRALS_CREATE_CODE,
|
||||
[Routes.TEAMS]: () => Routes.TEAMS,
|
||||
};
|
||||
|
||||
const routerConfig: RouteObject[] = [
|
||||
export const routerConfig: RouteObject[] = [
|
||||
{
|
||||
path: '/*',
|
||||
element: <LayoutWithSidebar />,
|
||||
@@ -68,6 +87,7 @@ const routerConfig: RouteObject[] = [
|
||||
{
|
||||
index: true,
|
||||
element: <LazyHome />,
|
||||
id: Routes.HOME,
|
||||
},
|
||||
{
|
||||
path: 'markets',
|
||||
@@ -76,16 +96,19 @@ const routerConfig: RouteObject[] = [
|
||||
{
|
||||
path: 'all',
|
||||
element: <LazyMarkets />,
|
||||
id: Routes.MARKETS,
|
||||
},
|
||||
{
|
||||
path: ':marketId',
|
||||
element: <LazyMarket />,
|
||||
id: Routes.MARKET,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'portfolio',
|
||||
element: <LazyPortfolio />,
|
||||
id: Routes.PORTFOLIO,
|
||||
},
|
||||
{
|
||||
path: 'liquidity',
|
||||
@@ -94,6 +117,7 @@ const routerConfig: RouteObject[] = [
|
||||
{
|
||||
path: ':marketId',
|
||||
element: <LazyLiquidity />,
|
||||
id: Routes.LIQUIDITY,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -104,6 +128,30 @@ const routerConfig: RouteObject[] = [
|
||||
element: <LazyDisclaimer />,
|
||||
},
|
||||
{ path: Routes.DEPOSIT, element: <LazyDeposit /> },
|
||||
// Referrals routing:
|
||||
{
|
||||
path: Routes.REFERRALS,
|
||||
element: <LayoutWithSky />,
|
||||
children: [
|
||||
{
|
||||
element: <LazyReferrals />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <ReferralStatistics />,
|
||||
},
|
||||
{
|
||||
path: Routes.REFERRALS_APPLY_CODE,
|
||||
element: <ApplyCodeForm />,
|
||||
},
|
||||
{
|
||||
path: Routes.REFERRALS_CREATE_CODE,
|
||||
element: <CreateCodeForm />,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
element: (
|
||||
|
||||
@@ -83,8 +83,7 @@ html [data-theme='light'] {
|
||||
--pennant-color-volume-sell: theme(colors.market.red.DEFAULT);
|
||||
|
||||
/* reduce space between candles */
|
||||
--pennant-candlestick-inner-padding: 0.175;
|
||||
--pennant-candlestick-stroke-width: 0.5;
|
||||
--pennant-candlestick-inner-padding: 0.25;
|
||||
}
|
||||
|
||||
html [data-theme='light'] {
|
||||
@@ -162,11 +161,22 @@ html [data-theme='dark'] {
|
||||
@apply font-normal font-alpha;
|
||||
}
|
||||
|
||||
/* Light variables */
|
||||
.ag-theme-balham {
|
||||
.ag-theme-balham,
|
||||
.ag-theme-balham-dark {
|
||||
--ag-grid-size: 2px; /* Used for compactness */
|
||||
--ag-row-height: 36px;
|
||||
--ag-header-height: 36px;
|
||||
--ag-header-height: 28px;
|
||||
}
|
||||
|
||||
@media (min-width: theme(screens.xxl)) {
|
||||
.ag-theme-balham,
|
||||
.ag-theme-balham-dark {
|
||||
--ag-header-height: 36px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Light variables */
|
||||
.ag-theme-balham {
|
||||
--ag-background-color: theme(colors.white);
|
||||
--ag-border-color: theme(colors.vega.clight.600);
|
||||
--ag-header-background-color: theme(colors.vega.clight.700);
|
||||
@@ -179,9 +189,6 @@ html [data-theme='dark'] {
|
||||
|
||||
/* Dark variables */
|
||||
.ag-theme-balham-dark {
|
||||
--ag-grid-size: 2px; /* Used for compactness */
|
||||
--ag-row-height: 36px;
|
||||
--ag-header-height: 36px;
|
||||
--ag-background-color: theme(colors.vega.cdark.900);
|
||||
--ag-border-color: theme(colors.vega.cdark.600);
|
||||
--ag-header-background-color: theme(colors.vega.cdark.700);
|
||||
@@ -191,6 +198,7 @@ html [data-theme='dark'] {
|
||||
--ag-row-hover-color: theme(colors.vega.cdark.800);
|
||||
--ag-modal-overlay-background-color: rgb(9 11 16 / 50%);
|
||||
}
|
||||
|
||||
.ag-theme-balham-dark .ag-row.no-hover,
|
||||
.ag-theme-balham-dark .ag-row.no-hover:hover,
|
||||
.ag-theme-balham .ag-row.no-hover,
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 614 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 572 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 574 KiB |
@@ -13,7 +13,37 @@ module.exports = {
|
||||
],
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
extend: theme,
|
||||
extend: {
|
||||
...theme,
|
||||
colors: {
|
||||
transparent: 'transparent',
|
||||
current: 'currentColor',
|
||||
...theme.colors,
|
||||
},
|
||||
backgroundImage: {
|
||||
...theme.backgroundImage,
|
||||
rainbow:
|
||||
'linear-gradient(103.47deg, #FF077F 1.68%, #8028FF 47.49%, #0075FF 100%)',
|
||||
'rainbow-shifted':
|
||||
'linear-gradient(103.47deg, #0075FF 1.68%, #8028FF 47.49%, #FF077F 100%)',
|
||||
highlight:
|
||||
'linear-gradient(170deg, var(--tw-gradient-from), transparent var(--tw-gradient-to-position))',
|
||||
},
|
||||
keyframes: {
|
||||
...theme.keyframes,
|
||||
shake: {
|
||||
'0%': { transform: 'translateX(0)' },
|
||||
'25%': { transform: 'translateX(5px)' },
|
||||
'50%': { transform: 'translateX(-5px)' },
|
||||
'75%': { transform: 'translateX(5px)' },
|
||||
'100%': { transform: 'translateX(0)' },
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
...theme.animation,
|
||||
shake: 'shake 200ms linear',
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [vegaCustomClasses],
|
||||
};
|
||||
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
|
||||
entrypoint="${1:-nginx}"
|
||||
|
||||
if [[ "$entrypoint" = "nginx" ]]; then
|
||||
nginx -g 'daemon off;'
|
||||
elif [[ "$entrypoint" = "ipfs" ]]; then
|
||||
ipfs config profile apply server
|
||||
ipfs config --json Addresses.Gateway '"/ip4/127.0.0.1/tcp/80"'
|
||||
ipfs daemon
|
||||
elif [[ "-c" ]]; then
|
||||
shift
|
||||
/bin/sh -c "$@"
|
||||
fi
|
||||
@@ -20,6 +20,8 @@ RUN sh docker/docker-build.sh
|
||||
FROM --platform=amd64 nginx:1.23-alpine@sha256:6318314189b40e73145a48060bff4783a116c34cc7241532d0d94198fb2c9629
|
||||
# configuration of system
|
||||
EXPOSE 80
|
||||
COPY docker/entrypoint.sh /entrypoint.sh
|
||||
ENTRYPOINT [ "/entrypoint.sh" ]
|
||||
# Copy dist
|
||||
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
RUN rm -rf /usr/share/nginx/html/*
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
FROM --platform=amd64 nginx:1.23-alpine@sha256:6318314189b40e73145a48060bff4783a116c34cc7241532d0d94198fb2c9629
|
||||
EXPOSE 80
|
||||
COPY docker/entrypoint.sh /entrypoint.sh
|
||||
ENTRYPOINT [ "/entrypoint.sh" ]
|
||||
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
RUN rm -rf /usr/share/nginx/html/*
|
||||
COPY ./dist-result/ /usr/share/nginx/html/
|
||||
|
||||
@@ -8,13 +8,16 @@ import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useCandlesChartSettings } from './use-candles-chart-settings';
|
||||
import {
|
||||
STUDY_SIZE,
|
||||
useCandlesChartSettings,
|
||||
} from './use-candles-chart-settings';
|
||||
|
||||
export type CandlesChartContainerProps = {
|
||||
marketId: string;
|
||||
};
|
||||
|
||||
const CANDLES_TO_WIDTH_FACTOR = 0.15;
|
||||
const CANDLES_TO_WIDTH_FACTOR = 0.2;
|
||||
|
||||
export const CandlesChartContainer = ({
|
||||
marketId,
|
||||
@@ -49,33 +52,34 @@ export const CandlesChartContainer = ({
|
||||
|
||||
return (
|
||||
<AutoSizer>
|
||||
{({ width, height }) => (
|
||||
<div style={{ width, height }}>
|
||||
<CandlestickChart
|
||||
dataSource={dataSource}
|
||||
options={{
|
||||
chartType,
|
||||
overlays,
|
||||
studies,
|
||||
notEnoughDataText: (
|
||||
<span className="text-xs text-center">{t('No data')}</span>
|
||||
),
|
||||
initialNumCandlesToDisplay: Math.floor(
|
||||
width * CANDLES_TO_WIDTH_FACTOR
|
||||
),
|
||||
studySize: 150, // default size
|
||||
studySizes,
|
||||
}}
|
||||
interval={interval}
|
||||
theme={theme}
|
||||
onOptionsChanged={(options) => {
|
||||
setStudies(options.studies);
|
||||
setOverlays(options.overlays);
|
||||
}}
|
||||
onPaneChanged={handlePaneChange}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{({ width, height }) => {
|
||||
const candlesCount = Math.floor(width * CANDLES_TO_WIDTH_FACTOR);
|
||||
return (
|
||||
<div style={{ width, height }}>
|
||||
<CandlestickChart
|
||||
dataSource={dataSource}
|
||||
options={{
|
||||
chartType,
|
||||
overlays,
|
||||
studies,
|
||||
notEnoughDataText: (
|
||||
<span className="text-xs text-center">{t('No data')}</span>
|
||||
),
|
||||
initialNumCandlesToDisplay: candlesCount,
|
||||
studySize: STUDY_SIZE,
|
||||
studySizes,
|
||||
}}
|
||||
interval={interval}
|
||||
theme={theme}
|
||||
onOptionsChanged={(options) => {
|
||||
setStudies(options.studies);
|
||||
setOverlays(options.overlays);
|
||||
}}
|
||||
onPaneChanged={handlePaneChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</AutoSizer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@ interface StoredSettings {
|
||||
studySizes: StudySizes;
|
||||
}
|
||||
|
||||
export const STUDY_SIZE = 100;
|
||||
export const STUDY_SIZE = 90;
|
||||
const STUDY_ORDER: Study[] = [
|
||||
Study.FORCE_INDEX,
|
||||
Study.RELATIVE_STRENGTH_INDEX,
|
||||
|
||||
@@ -61,10 +61,15 @@ export function addVegaWalletConnect() {
|
||||
});
|
||||
}
|
||||
|
||||
const onboardingViewedState = { state: { dismissed: true }, version: 0 };
|
||||
|
||||
export function addSetVegaWallet() {
|
||||
Cypress.Commands.add('setVegaWallet', () => {
|
||||
cy.window().then((win) => {
|
||||
win.localStorage.setItem('vega_onboarding_viewed', 'true');
|
||||
win.localStorage.setItem(
|
||||
'vega_onboarding',
|
||||
JSON.stringify(onboardingViewedState)
|
||||
);
|
||||
win.localStorage.setItem('vega_telemetry_approval', 'false');
|
||||
win.localStorage.setItem('vega_telemetry_viewed', 'true');
|
||||
win.localStorage.setItem(
|
||||
@@ -82,7 +87,10 @@ export function addSetVegaWallet() {
|
||||
export function addSetOnBoardingViewed() {
|
||||
Cypress.Commands.add('setOnBoardingViewed', () => {
|
||||
cy.window().then((win) => {
|
||||
win.localStorage.setItem('vega_onboarding_viewed', 'true');
|
||||
win.localStorage.setItem(
|
||||
'vega_onboarding',
|
||||
JSON.stringify(onboardingViewedState)
|
||||
);
|
||||
win.localStorage.setItem('vega_telemetry_approval', 'false');
|
||||
win.localStorage.setItem('vega_telemetry_viewed', 'true');
|
||||
});
|
||||
|
||||
@@ -84,6 +84,7 @@ const ocoPostfix = (id: string, postfix = true) => (postfix ? `${id}-oco` : id);
|
||||
|
||||
const mockDataProvider = jest.fn((...args) => ({
|
||||
data: Array(0),
|
||||
reload: jest.fn(),
|
||||
}));
|
||||
jest.mock('@vegaprotocol/data-provider', () => ({
|
||||
...jest.requireActual('@vegaprotocol/data-provider'),
|
||||
@@ -473,6 +474,7 @@ describe('StopOrder', () => {
|
||||
|
||||
it('shows limit of active stop orders number', async () => {
|
||||
mockDataProvider.mockReturnValue({
|
||||
reload: jest.fn(),
|
||||
data: Array(4),
|
||||
});
|
||||
render(generateJsx());
|
||||
@@ -484,6 +486,7 @@ describe('StopOrder', () => {
|
||||
|
||||
it('counts oco as two orders', async () => {
|
||||
mockDataProvider.mockReturnValue({
|
||||
reload: jest.fn(),
|
||||
data: Array(3),
|
||||
});
|
||||
render(generateJsx());
|
||||
|
||||
@@ -26,10 +26,7 @@ import {
|
||||
Pill,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
import {
|
||||
useEstimatePositionQuery,
|
||||
useOpenVolume,
|
||||
} from '@vegaprotocol/positions';
|
||||
import { useOpenVolume } from '@vegaprotocol/positions';
|
||||
import {
|
||||
toBigNum,
|
||||
removeDecimal,
|
||||
@@ -63,13 +60,14 @@ import {
|
||||
useAccountBalance,
|
||||
} from '@vegaprotocol/accounts';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import type { OrderFormValues } from '../../hooks';
|
||||
import {
|
||||
DealTicketType,
|
||||
dealTicketTypeToOrderType,
|
||||
isStopOrderType,
|
||||
} from '../../hooks/use-form-values';
|
||||
import type { OrderFormValues } from '../../hooks/use-form-values';
|
||||
import { useDealTicketFormValues } from '../../hooks/use-form-values';
|
||||
useDealTicketFormValues,
|
||||
usePositionEstimate,
|
||||
} from '../../hooks';
|
||||
import { DealTicketSizeIceberg } from './deal-ticket-size-iceberg';
|
||||
import noop from 'lodash/noop';
|
||||
import { isNonPersistentOrder } from '../../utils/time-in-force-persistance';
|
||||
@@ -253,16 +251,14 @@ export const DealTicket = ({
|
||||
side: normalizedOrder.side,
|
||||
});
|
||||
}
|
||||
const { data: positionEstimate } = useEstimatePositionQuery({
|
||||
variables: {
|
||||
marketId: market.id,
|
||||
openVolume,
|
||||
orders,
|
||||
collateralAvailable:
|
||||
marginAccountBalance || generalAccountBalance ? balance : undefined,
|
||||
},
|
||||
|
||||
const positionEstimate = usePositionEstimate({
|
||||
marketId: market.id,
|
||||
openVolume,
|
||||
orders,
|
||||
collateralAvailable:
|
||||
marginAccountBalance || generalAccountBalance ? balance : undefined,
|
||||
skip: !normalizedOrder,
|
||||
fetchPolicy: 'no-cache',
|
||||
});
|
||||
|
||||
const assetSymbol =
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './__generated__/EstimateOrder';
|
||||
export * from './use-estimate-fees';
|
||||
export * from './use-form-values';
|
||||
export * from './use-position-estimate';
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user