Compare commits
26
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9b0a0be02 | ||
|
|
9689b5a449 | ||
|
|
80f7a08765 | ||
|
|
3e627ff849 | ||
|
|
d2854b6e90 | ||
|
|
6d130c9cfc | ||
|
|
ab4f4e9084 | ||
|
|
fa1825ca64 | ||
|
|
206c6c7207 | ||
|
|
4f15a0ef06 | ||
|
|
72a008a69d | ||
|
|
0c5b5d8e7b | ||
|
|
3be1738ecb | ||
|
|
5b0bd69710 | ||
|
|
4de59363f4 | ||
|
|
de6fce0ade | ||
|
|
546b710093 | ||
|
|
574f73506b | ||
|
|
759e11cff1 | ||
|
|
2f08bac47a | ||
|
|
1fdc00fc3b | ||
|
|
4b347c571d | ||
|
|
7e5f90cca2 | ||
|
|
3090ee88d7 | ||
|
|
35f3d52282 | ||
|
|
e5b74a141c |
@@ -48,9 +48,11 @@ query ExplorerPartyAssets($partyId: ID!) {
|
||||
}
|
||||
stakingSummary {
|
||||
currentStakeAvailable
|
||||
linkings(pagination: { first: 100 }) {
|
||||
linkings(pagination: { last: 100 }) {
|
||||
edges {
|
||||
node {
|
||||
type
|
||||
status
|
||||
amount
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ export type ExplorerPartyAssetsQueryVariables = Types.Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ExplorerPartyAssetsQuery = { __typename?: 'Query', partiesConnection?: { __typename?: 'PartyConnection', edges: Array<{ __typename?: 'PartyEdge', node: { __typename?: 'Party', id: string, delegationsConnection?: { __typename?: 'DelegationsConnection', edges?: Array<{ __typename?: 'DelegationEdge', node: { __typename?: 'Delegation', amount: string, epoch: number, node: { __typename?: 'Node', id: string, name: string } } } | null> | null } | null, stakingSummary: { __typename?: 'StakingSummary', currentStakeAvailable: string, linkings: { __typename?: 'StakesConnection', edges?: Array<{ __typename?: 'StakeLinkingEdge', node: { __typename?: 'StakeLinking', amount: string } } | null> | null } }, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', name: string, id: string, decimals: number, symbol: string, source: { __typename: 'BuiltinAsset' } | { __typename: 'ERC20', contractAddress: string } }, market?: { __typename?: 'Market', id: string, decimalPlaces: number, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } } } } | null } } | null> | null } | null } }> } | null };
|
||||
export type ExplorerPartyAssetsQuery = { __typename?: 'Query', partiesConnection?: { __typename?: 'PartyConnection', edges: Array<{ __typename?: 'PartyEdge', node: { __typename?: 'Party', id: string, delegationsConnection?: { __typename?: 'DelegationsConnection', edges?: Array<{ __typename?: 'DelegationEdge', node: { __typename?: 'Delegation', amount: string, epoch: number, node: { __typename?: 'Node', id: string, name: string } } } | null> | null } | null, stakingSummary: { __typename?: 'StakingSummary', currentStakeAvailable: string, linkings: { __typename?: 'StakesConnection', edges?: Array<{ __typename?: 'StakeLinkingEdge', node: { __typename?: 'StakeLinking', type: Types.StakeLinkingType, status: Types.StakeLinkingStatus, amount: string } } | null> | null } }, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', name: string, id: string, decimals: number, symbol: string, source: { __typename: 'BuiltinAsset' } | { __typename: 'ERC20', contractAddress: string } }, market?: { __typename?: 'Market', id: string, decimalPlaces: number, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } } } } | null } } | null> | null } | null } }> } | null };
|
||||
|
||||
export const ExplorerPartyAssetsAccountsFragmentDoc = gql`
|
||||
fragment ExplorerPartyAssetsAccounts on AccountBalance {
|
||||
@@ -64,9 +64,11 @@ export const ExplorerPartyAssetsDocument = gql`
|
||||
}
|
||||
stakingSummary {
|
||||
currentStakeAvailable
|
||||
linkings(pagination: {first: 100}) {
|
||||
linkings(pagination: {last: 100}) {
|
||||
edges {
|
||||
node {
|
||||
type
|
||||
status
|
||||
amount
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,9 +42,15 @@ export const PartyBlockStake = ({
|
||||
linkedLength && linkedLength > 0
|
||||
? p?.stakingSummary?.linkings?.edges
|
||||
?.reduce((total, e) => {
|
||||
return new BigNumber(total).plus(
|
||||
new BigNumber(e?.node.amount || 0)
|
||||
);
|
||||
const accumulator = new BigNumber(total);
|
||||
const diff = new BigNumber(e?.node.amount || 0);
|
||||
if (e?.node.type === 'TYPE_LINK') {
|
||||
return accumulator.plus(diff);
|
||||
} else if (e?.node.type === 'TYPE_UNLINK') {
|
||||
return accumulator.minus(diff);
|
||||
} else {
|
||||
return accumulator;
|
||||
}
|
||||
}, new BigNumber(0))
|
||||
.toString()
|
||||
: '0';
|
||||
|
||||
@@ -361,6 +361,34 @@ describe(
|
||||
stakingPageDisassociateAllTokens();
|
||||
});
|
||||
|
||||
it('Error message should be displayed if error returned from wallet when voting', function () {
|
||||
const errorMsg =
|
||||
'Application error: party has already submitted the maximum number of transactions of this type per epoch (3)';
|
||||
|
||||
createRawProposal();
|
||||
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
|
||||
getProposalFromTitle(rawProposal.rationale.title).within(() =>
|
||||
cy.getByTestId(viewProposalButton).click()
|
||||
);
|
||||
cy.intercept('POST', '/api/v2/requests', {
|
||||
jsonrpc: '2.0',
|
||||
error: {
|
||||
code: 2001,
|
||||
message: 'Application error',
|
||||
data: 'party has already submitted the maximum number of transactions of this type per epoch (3)',
|
||||
},
|
||||
id: '-PK5EGmErnjLhAmzMeclC',
|
||||
});
|
||||
cy.contains('Vote breakdown').should('be.visible', { timeout: 10000 });
|
||||
cy.getByTestId('vote-buttons').contains('for').click();
|
||||
cy.getByTestId('dialog-title').should(
|
||||
'have.text',
|
||||
'Transaction failed'
|
||||
);
|
||||
cy.getByTestId('Error').should('have.text', errorMsg);
|
||||
});
|
||||
});
|
||||
|
||||
it('Able to see successor market details with new and updated values', function () {
|
||||
cy.createMarket();
|
||||
cy.reload();
|
||||
|
||||
@@ -119,6 +119,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
||||
});
|
||||
});
|
||||
|
||||
// 0006-NETW-001 0006-NETW-002
|
||||
it('should display network data', function () {
|
||||
cy.getByTestId('git-network-data')
|
||||
.should('contain.text', 'Reading network data from')
|
||||
@@ -130,6 +131,37 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
||||
});
|
||||
});
|
||||
|
||||
// 0006-NETW-003 0006-NETW-008 0006-NETW-009 0006-NETW-010 0006-NETW-012 0006-NETW-013 0006-NETW-017 0006-NETW-018 0006-NETW-019 0006-NETW-020
|
||||
it('should have option to switch to different network node', function () {
|
||||
cy.getByTestId('git-network-data').within(() => {
|
||||
cy.getByTestId('link').click();
|
||||
});
|
||||
cy.getByTestId('node-row').within(() => {
|
||||
cy.getByTestId('node-url-0')
|
||||
.parent()
|
||||
.should('have.text', 'http://localhost:3008/graphql');
|
||||
cy.getByTestId('response-time-cell')
|
||||
.invoke('text')
|
||||
.should('not.be.empty')
|
||||
.and('not.eq', 'Checking');
|
||||
cy.getByTestId('block-height-cell')
|
||||
.invoke('text')
|
||||
.should('not.be.empty')
|
||||
.then((currentBlockHeight) => {
|
||||
// Check that block height updates automatically
|
||||
cy.getByTestId('block-height-cell')
|
||||
.invoke('text')
|
||||
.should('not.eq', currentBlockHeight);
|
||||
});
|
||||
cy.getByTestId('subscription-cell').should('have.text', 'Yes');
|
||||
});
|
||||
cy.getByTestId('connect').should('be.disabled');
|
||||
cy.getByTestId('node-url-custom').click();
|
||||
cy.get('input').should('exist');
|
||||
cy.getByTestId('connect').should('be.disabled');
|
||||
cy.getByTestId('icon-cross').click();
|
||||
});
|
||||
|
||||
it('should display eth data', function () {
|
||||
cy.getByTestId('git-eth-data')
|
||||
.should('contain.text', 'Reading Ethereum data from')
|
||||
@@ -138,6 +170,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
||||
});
|
||||
});
|
||||
|
||||
// 0006-NETW-011
|
||||
it('should contain link for known issues on Github', function () {
|
||||
cy.getByTestId('git-info').within(() => {
|
||||
cy.contains('Known issues and feedback on')
|
||||
|
||||
@@ -183,9 +183,8 @@ export function clickOnValidatorFromList(
|
||||
cy.get(`[row-id="${validatorNumber}"]`)
|
||||
.should('be.visible')
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(stakeValidatorListName).click();
|
||||
});
|
||||
.as('validatorOnList');
|
||||
cy.get('@validatorOnList').click();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,8 +22,6 @@ NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
|
||||
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
|
||||
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
#Test configuration variables
|
||||
CYPRESS_FAIRGROUND=false
|
||||
|
||||
@@ -19,6 +19,9 @@ NX_DELEGATIONS_PAGINATION=50
|
||||
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
||||
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/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_TENDERMINT_URL=http://localhost:26617
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
|
||||
|
||||
@@ -26,4 +29,4 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
|
||||
CYPRESS_FAIRGROUND=false
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
|
||||
@@ -14,8 +14,11 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
|
||||
NX_VEGA_REST_URL=https://api.n00.devnet1.vega.xyz/api/v2/
|
||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
|
||||
@@ -14,9 +14,11 @@ NX_DELEGATIONS_PAGINATION=50
|
||||
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-k8s.ops.vega.xyz
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||
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/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_TENDERMINT_URL=https://be.vega.community
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
|
||||
@@ -13,9 +13,11 @@ NX_DELEGATIONS_PAGINATION=50
|
||||
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-mirror-k8s.ops.vega.xyz
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||
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/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_TENDERMINT_URL=https://be.mainnet-mirror.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
|
||||
@@ -10,8 +10,11 @@ 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_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
|
||||
@@ -15,8 +15,11 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
|
||||
NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/
|
||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
|
||||
@@ -12,8 +12,11 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
|
||||
NX_VEGA_REST_URL=https://api-validators-testnet.vega.rocks/api/v2/
|
||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
|
||||
@@ -55,7 +55,7 @@ export const Proposal = ({
|
||||
mostRecentlyEnactedAssociatedMarketProposal,
|
||||
}: ProposalProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { submit, Dialog, finalizedVote } = useVoteSubmit();
|
||||
const { submit, Dialog, finalizedVote, transaction } = useVoteSubmit();
|
||||
const { voteState, voteDatetime } = useUserVote(proposal?.id, finalizedVote);
|
||||
|
||||
if (!proposal) {
|
||||
@@ -215,6 +215,7 @@ export const Proposal = ({
|
||||
}
|
||||
submit={submit}
|
||||
dialog={Dialog}
|
||||
transaction={transaction}
|
||||
voteState={voteState}
|
||||
voteDatetime={voteDatetime}
|
||||
/>
|
||||
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { VoteTransactionDialog } from './vote-transaction-dialog';
|
||||
import { VoteState } from './use-user-vote';
|
||||
import { VegaTxStatus } from '@vegaprotocol/wallet';
|
||||
|
||||
describe('VoteTransactionDialog', () => {
|
||||
const mockTransactionDialog = jest.fn(({ title, content }) => (
|
||||
<div>
|
||||
<div>{title}</div>
|
||||
<div>{content?.Complete}</div>
|
||||
</div>
|
||||
));
|
||||
|
||||
it('renders without crashing', () => {
|
||||
render(
|
||||
<VoteTransactionDialog
|
||||
voteState={VoteState.Yes}
|
||||
transaction={null}
|
||||
TransactionDialog={mockTransactionDialog}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('vote-transaction-dialog')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders with txRequested title when voteState is Requested', () => {
|
||||
render(
|
||||
<VoteTransactionDialog
|
||||
voteState={VoteState.Requested}
|
||||
transaction={null}
|
||||
TransactionDialog={mockTransactionDialog}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText('txRequested')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders with votePending title when voteState is Pending', () => {
|
||||
render(
|
||||
<VoteTransactionDialog
|
||||
voteState={VoteState.Pending}
|
||||
transaction={null}
|
||||
TransactionDialog={mockTransactionDialog}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText('votePending')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders with no title when voteState is neither Requested nor Pending', () => {
|
||||
render(
|
||||
<VoteTransactionDialog
|
||||
voteState={VoteState.Yes} // or any other state other than Requested or Pending
|
||||
transaction={null}
|
||||
TransactionDialog={mockTransactionDialog}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.queryByText('txRequested')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('votePending')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders custom error message when voteState is Failed and error message exists', () => {
|
||||
render(
|
||||
<VoteTransactionDialog
|
||||
voteState={VoteState.Failed}
|
||||
transaction={{
|
||||
error: { message: 'Custom error test message', name: 'blah' },
|
||||
txHash: null,
|
||||
signature: null,
|
||||
status: VegaTxStatus.Error,
|
||||
dialogOpen: false,
|
||||
}}
|
||||
TransactionDialog={mockTransactionDialog}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText('Custom error test message')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders default error message when voteState is failed and no error message exists on the tx', () => {
|
||||
render(
|
||||
<VoteTransactionDialog
|
||||
voteState={VoteState.Failed}
|
||||
transaction={{
|
||||
error: null,
|
||||
txHash: null,
|
||||
signature: null,
|
||||
status: VegaTxStatus.Error,
|
||||
dialogOpen: false,
|
||||
}}
|
||||
TransactionDialog={mockTransactionDialog}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText('voteError')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders default ui (i.e. not error) when not in a failed state', () => {
|
||||
render(
|
||||
<VoteTransactionDialog
|
||||
voteState={VoteState.Yes}
|
||||
transaction={null}
|
||||
TransactionDialog={mockTransactionDialog}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.queryByText('voteError')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { VoteButtons } from './vote-buttons';
|
||||
import { VoteState } from './use-user-vote';
|
||||
@@ -24,6 +24,7 @@ describe('Vote buttons', () => {
|
||||
currentStakeAvailable={new BigNumber(1)}
|
||||
dialog={() => <div>Blah</div>}
|
||||
submit={() => Promise.resolve()}
|
||||
transaction={null}
|
||||
/>
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
@@ -47,6 +48,7 @@ describe('Vote buttons', () => {
|
||||
currentStakeAvailable={new BigNumber(1)}
|
||||
dialog={() => <div>Blah</div>}
|
||||
submit={() => Promise.resolve()}
|
||||
transaction={null}
|
||||
/>
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
@@ -81,6 +83,7 @@ describe('Vote buttons', () => {
|
||||
currentStakeAvailable={new BigNumber(1)}
|
||||
dialog={() => <div>Blah</div>}
|
||||
submit={() => Promise.resolve()}
|
||||
transaction={null}
|
||||
/>
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
@@ -105,6 +108,7 @@ describe('Vote buttons', () => {
|
||||
currentStakeAvailable={new BigNumber(0)}
|
||||
dialog={() => <div>Blah</div>}
|
||||
submit={() => Promise.resolve()}
|
||||
transaction={null}
|
||||
/>
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
@@ -132,6 +136,7 @@ describe('Vote buttons', () => {
|
||||
currentStakeAvailable={new BigNumber(1)}
|
||||
dialog={() => <div>Blah</div>}
|
||||
submit={() => Promise.resolve()}
|
||||
transaction={null}
|
||||
/>
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
@@ -159,6 +164,7 @@ describe('Vote buttons', () => {
|
||||
currentStakeAvailable={new BigNumber(10)}
|
||||
dialog={() => <div>Blah</div>}
|
||||
submit={() => Promise.resolve()}
|
||||
transaction={null}
|
||||
/>
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
@@ -183,6 +189,7 @@ describe('Vote buttons', () => {
|
||||
currentStakeAvailable={new BigNumber(10)}
|
||||
dialog={() => <div>Blah</div>}
|
||||
submit={() => Promise.resolve()}
|
||||
transaction={null}
|
||||
/>
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
|
||||
@@ -17,7 +17,7 @@ import { VoteState } from './use-user-vote';
|
||||
import { ProposalMinRequirements, ProposalUserAction } from '../shared';
|
||||
import { VoteTransactionDialog } from './vote-transaction-dialog';
|
||||
import { useVoteButtonsQuery } from './__generated__/Stake';
|
||||
import type { DialogProps } from '@vegaprotocol/wallet';
|
||||
import type { DialogProps, VegaTxState } from '@vegaprotocol/wallet';
|
||||
|
||||
interface VoteButtonsContainerProps {
|
||||
voteState: VoteState | null;
|
||||
@@ -27,6 +27,7 @@ interface VoteButtonsContainerProps {
|
||||
minVoterBalance: string | null | undefined;
|
||||
spamProtectionMinTokens: string | null | undefined;
|
||||
submit: (voteValue: VoteValue, proposalId: string | null) => Promise<void>;
|
||||
transaction: VegaTxState | null;
|
||||
dialog: (props: DialogProps) => JSX.Element;
|
||||
className?: string;
|
||||
}
|
||||
@@ -67,6 +68,7 @@ export const VoteButtons = ({
|
||||
minVoterBalance,
|
||||
spamProtectionMinTokens,
|
||||
submit,
|
||||
transaction,
|
||||
dialog: Dialog,
|
||||
}: VoteButtonsProps) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -208,7 +210,11 @@ export const VoteButtons = ({
|
||||
</p>
|
||||
)
|
||||
)}
|
||||
<VoteTransactionDialog voteState={voteState} TransactionDialog={Dialog} />
|
||||
<VoteTransactionDialog
|
||||
voteState={voteState}
|
||||
transaction={transaction}
|
||||
TransactionDialog={Dialog}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -12,7 +12,7 @@ import { VoteButtonsContainer } from './vote-buttons';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import { ProposalType } from '../proposal/proposal';
|
||||
import type { VoteValue } from '@vegaprotocol/types';
|
||||
import type { DialogProps } from '@vegaprotocol/wallet';
|
||||
import type { DialogProps, VegaTxState } from '@vegaprotocol/wallet';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import type { VoteState } from './use-user-vote';
|
||||
@@ -22,6 +22,7 @@ interface VoteDetailsProps {
|
||||
minVoterBalance: string | null | undefined;
|
||||
spamProtectionMinTokens: string | null | undefined;
|
||||
proposalType: ProposalType | null;
|
||||
transaction: VegaTxState | null;
|
||||
submit: (voteValue: VoteValue, proposalId: string | null) => Promise<void>;
|
||||
dialog: (props: DialogProps) => JSX.Element;
|
||||
voteState: VoteState | null;
|
||||
@@ -34,6 +35,7 @@ export const VoteDetails = ({
|
||||
spamProtectionMinTokens,
|
||||
proposalType,
|
||||
submit,
|
||||
transaction,
|
||||
dialog,
|
||||
voteState,
|
||||
voteDatetime,
|
||||
@@ -228,6 +230,7 @@ export const VoteDetails = ({
|
||||
spamProtectionMinTokens={spamProtectionMinTokens}
|
||||
className="flex"
|
||||
submit={submit}
|
||||
transaction={transaction}
|
||||
dialog={dialog}
|
||||
/>
|
||||
)
|
||||
|
||||
+6
-2
@@ -1,9 +1,10 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { VoteState } from './use-user-vote';
|
||||
import type { DialogProps } from '@vegaprotocol/wallet';
|
||||
import type { DialogProps, VegaTxState } from '@vegaprotocol/wallet';
|
||||
|
||||
interface VoteTransactionDialogProps {
|
||||
voteState: VoteState;
|
||||
transaction: VegaTxState | null;
|
||||
TransactionDialog: (props: DialogProps) => JSX.Element;
|
||||
}
|
||||
|
||||
@@ -20,12 +21,15 @@ const dialogTitle = (voteState: VoteState): string | undefined => {
|
||||
|
||||
export const VoteTransactionDialog = ({
|
||||
voteState,
|
||||
transaction,
|
||||
TransactionDialog,
|
||||
}: VoteTransactionDialogProps) => {
|
||||
// Render a custom message if the voting fails otherwise
|
||||
// pass undefined so that the default vega transaction dialog UI gets used
|
||||
const customMessage =
|
||||
voteState === VoteState.Failed ? <p>{t('voteError')}</p> : undefined;
|
||||
voteState === VoteState.Failed ? (
|
||||
<p>{transaction?.error?.message || t('voteError')}</p>
|
||||
) : undefined;
|
||||
|
||||
return (
|
||||
<div data-testid="vote-transaction-dialog">
|
||||
|
||||
@@ -36,4 +36,4 @@ CYPRESS_VEGA_WALLET_API_TOKEN=
|
||||
|
||||
# Cosmic elevator flags (MUST be doubled with CYPRESS_ prefix)
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
CYPRESS_NX_SUCCESSOR_MARKETS=true
|
||||
CYPRESS_NX_SUCCESSOR_MARKETS=true
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
describe('Settings page', { tags: '@smoke' }, () => {
|
||||
beforeEach(() => {
|
||||
cy.clearLocalStorage();
|
||||
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setOnBoardingViewed();
|
||||
cy.visit('/');
|
||||
|
||||
// Only click if not already active otherwise sidebar will close
|
||||
cy.get('[data-testid="sidebar-content"]').then(($sidebarContent) => {
|
||||
if ($sidebarContent.find('h2').text() !== 'Settings') {
|
||||
cy.get('[data-testid="sidebar"] [data-testid="Settings"]').click();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('telemetry checkbox should work well', () => {
|
||||
const telemetrySwitch = '#switch-settings-telemetry-switch';
|
||||
cy.get(telemetrySwitch).should('have.attr', 'data-state', 'unchecked');
|
||||
cy.get(telemetrySwitch).click();
|
||||
cy.get(telemetrySwitch).should('have.attr', 'data-state', 'checked');
|
||||
cy.reload();
|
||||
cy.get(telemetrySwitch).should('have.attr', 'data-state', 'checked');
|
||||
cy.get(telemetrySwitch).click();
|
||||
cy.get(telemetrySwitch).should('have.attr', 'data-state', 'unchecked');
|
||||
cy.reload();
|
||||
cy.get(telemetrySwitch).should('have.attr', 'data-state', 'unchecked');
|
||||
});
|
||||
});
|
||||
@@ -1,186 +0,0 @@
|
||||
interface ItemInfoType {
|
||||
name: string;
|
||||
infoText: string;
|
||||
}
|
||||
|
||||
type CheckMenuItemsFnType = (
|
||||
triggerSelector: string,
|
||||
validTexts: string[],
|
||||
clickItem?: string
|
||||
) => void;
|
||||
type CheckMenuItemCheckboxFnType = (
|
||||
buttonText: string,
|
||||
items: ItemInfoType[]
|
||||
) => void;
|
||||
|
||||
const menuItemRadio = 'div[role="menuitemradio"]';
|
||||
const menuItemCheckbox = 'div[role="menuitemcheckbox"]';
|
||||
const button = 'button';
|
||||
const indicatorInfo = '.indicator-info-wrapper';
|
||||
|
||||
const checkMenuItems: CheckMenuItemsFnType = (
|
||||
triggerSelector,
|
||||
validTexts,
|
||||
clickItem
|
||||
) => {
|
||||
cy.get(triggerSelector).click();
|
||||
|
||||
cy.get(menuItemRadio)
|
||||
.should('have.length', validTexts.length)
|
||||
.each(($el, index) => {
|
||||
const text = $el.text().trim();
|
||||
expect(text).to.equal(validTexts[index]);
|
||||
});
|
||||
|
||||
if (clickItem) {
|
||||
cy.contains(menuItemRadio, clickItem).click();
|
||||
cy.get(triggerSelector).click();
|
||||
cy.get(`${menuItemRadio}[data-state="checked"]`)
|
||||
.invoke('text')
|
||||
.then((text: string) => {
|
||||
expect(text.trim()).to.equal(clickItem);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const checkMenuItemCheckbox: CheckMenuItemCheckboxFnType = (
|
||||
buttonText,
|
||||
items
|
||||
) => {
|
||||
items.forEach((item) => {
|
||||
cy.contains(button, buttonText).click();
|
||||
cy.contains(menuItemCheckbox, item.name).click();
|
||||
});
|
||||
|
||||
cy.contains(button, buttonText).click();
|
||||
cy.get(menuItemCheckbox)
|
||||
.should('have.length', items.length)
|
||||
.each(($el, index) => {
|
||||
const text = $el.text();
|
||||
expect(text).to.equal(items[index].name);
|
||||
});
|
||||
|
||||
items.forEach((item, index) => {
|
||||
cy.get(indicatorInfo)
|
||||
.eq(index + 1)
|
||||
.invoke('text')
|
||||
.should('eq', item.infoText);
|
||||
});
|
||||
|
||||
cy.contains(button, buttonText).click({ force: true });
|
||||
};
|
||||
|
||||
function getButtonSelectorByText(text: string): string {
|
||||
return `${button}[aria-haspopup="menu"]:contains(${text})`;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setOnBoardingViewed();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
});
|
||||
|
||||
describe(
|
||||
'chart display options',
|
||||
{ tags: '@smoke', testIsolation: true },
|
||||
() => {
|
||||
it('change time interval', () => {
|
||||
// 6004-CHAR-001
|
||||
checkMenuItems(
|
||||
getButtonSelectorByText('Interval:'),
|
||||
['1m', '5m', '15m', '1H', '6H', '1D'],
|
||||
'1m'
|
||||
);
|
||||
});
|
||||
|
||||
it('change display type', () => {
|
||||
// 6004-CHAR-002
|
||||
// 6004-CHAR-003
|
||||
checkMenuItems(
|
||||
'[aria-label$="chart icon"]',
|
||||
['Mountain', 'Candlestick', 'Line', 'OHLC'],
|
||||
'Mountain'
|
||||
);
|
||||
});
|
||||
|
||||
it('Overlays', () => {
|
||||
// 6004-CHAR-004
|
||||
// 6004-CHAR-008
|
||||
// 6004-CHAR-009
|
||||
// 6004-CHAR-034
|
||||
// 6004-CHAR-037
|
||||
// 6004-CHAR-039
|
||||
// 6004-CHAR-041
|
||||
|
||||
const overlayInfo: ItemInfoType[] = [
|
||||
{
|
||||
name: 'Bollinger bands',
|
||||
infoText: 'Bollinger: Upper 174.78590Lower 173.38014',
|
||||
},
|
||||
{
|
||||
name: 'Envelope',
|
||||
infoText: 'Envelope: Upper 191.29000Lower 156.51000',
|
||||
},
|
||||
{ name: 'EMA', infoText: 'EMA: 174.06793' },
|
||||
{ name: 'Moving average', infoText: 'Moving average: 174.08302' },
|
||||
{
|
||||
name: 'Price monitoring bounds',
|
||||
infoText:
|
||||
'Price Monitoring Bounds 1: Min 162.56291Max 182.96869Reference 172.47489',
|
||||
},
|
||||
];
|
||||
|
||||
checkMenuItemCheckbox('Overlays', overlayInfo);
|
||||
});
|
||||
|
||||
it('Studies', () => {
|
||||
// 6004-CHAR-005
|
||||
// 6004-CHAR-006
|
||||
// 6004-CHAR-007
|
||||
// 6004-CHAR-042
|
||||
// 6004-CHAR-045
|
||||
// 6004-CHAR-047
|
||||
// 6004-CHAR-049
|
||||
// 6004-CHAR-051
|
||||
const studyInfo: ItemInfoType[] = [
|
||||
{
|
||||
name: 'Eldar-ray',
|
||||
infoText: 'Eldar-ray: Bull -0.08376Bear -0.58376',
|
||||
},
|
||||
{ name: 'Force index', infoText: 'Force index: 987.48858' },
|
||||
{ name: 'MACD', infoText: 'MACD: S -0.06420D 0.00359MACD -0.06062' },
|
||||
{ name: 'RSI', infoText: 'RSI: 47.08648' },
|
||||
{ name: 'Volume', infoText: 'Volume: 55,000' },
|
||||
];
|
||||
cy.get(indicatorInfo).eq(1).realHover();
|
||||
cy.get('.chart__wrapper [data-testid="split-view-view"]')
|
||||
.last()
|
||||
.find('[role="button"][title="Close"]')
|
||||
.click({ force: true });
|
||||
cy.get(indicatorInfo).should('have.length', 1);
|
||||
|
||||
checkMenuItemCheckbox('Studies', studyInfo);
|
||||
});
|
||||
|
||||
it('price details', () => {
|
||||
// 6004-CHAR-010
|
||||
const expectedDateRegex = new RegExp(
|
||||
/^\d{2}:\d{2} \d{2} [A-Za-z]{3} \d{4}$/
|
||||
);
|
||||
const expectedOhlc = `O 173.60000H 174.00000L 173.50000C 173.90000Change −0.60000(−0.34%)`;
|
||||
cy.get(indicatorInfo)
|
||||
.eq(0)
|
||||
.invoke('text')
|
||||
.then((text) => {
|
||||
const actualDate = text.slice(0, -67);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(actualDate);
|
||||
const actualOhlc = text.slice(-67);
|
||||
assert.isTrue(expectedDateRegex.test(actualDate));
|
||||
assert.strictEqual(actualOhlc, expectedOhlc);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -3,7 +3,7 @@ import { testOrderSubmission } from '../support/order-validation';
|
||||
import type { OrderSubmission } from '@vegaprotocol/wallet';
|
||||
import { createOrder } from '../support/create-order';
|
||||
|
||||
describe('must submit order', { tags: '@smoke' }, () => {
|
||||
describe.skip('must submit order', { tags: '@smoke' }, () => {
|
||||
// 7002-SORD-039
|
||||
before(() => {
|
||||
cy.setVegaWallet();
|
||||
|
||||
@@ -23,7 +23,7 @@ describe('deal ticket basics', { tags: '@smoke' }, () => {
|
||||
// 0003-WTXN-001
|
||||
cy.getByTestId('connect-vega-wallet'); // Not connected
|
||||
cy.getByTestId(placeOrderBtn).should('exist');
|
||||
cy.getByTestId('get-started-button').should('exist');
|
||||
cy.getByTestId('order-connect-wallet').should('exist');
|
||||
});
|
||||
|
||||
it('must be able to select order direction - long/short', function () {
|
||||
@@ -44,7 +44,7 @@ describe('deal ticket basics', { tags: '@smoke' }, () => {
|
||||
mockConnectWallet();
|
||||
cy.getByTestId(toggleLimit).click();
|
||||
cy.getByTestId(orderPriceField).clear().type('101');
|
||||
cy.getByTestId('get-started-button').click();
|
||||
cy.getByTestId('order-connect-wallet').click();
|
||||
cy.getByTestId('dialog-content').should('be.visible');
|
||||
cy.getByTestId('connectors-list')
|
||||
.find('[data-testid="connector-jsonRpc"]')
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import {
|
||||
accountsQuery,
|
||||
amendGeneralAccountBalance,
|
||||
amendMarginAccountBalance,
|
||||
} from '@vegaprotocol/mock';
|
||||
|
||||
describe.skip(
|
||||
'account validation',
|
||||
{ tags: '@regression', testIsolation: true },
|
||||
() => {
|
||||
describe('zero balance error', () => {
|
||||
beforeEach(() => {
|
||||
cy.setVegaWallet();
|
||||
cy.mockTradingPage();
|
||||
let accounts = accountsQuery();
|
||||
accounts = amendMarginAccountBalance(accounts, 'market-0', '1000');
|
||||
accounts = amendGeneralAccountBalance(accounts, 'market-0', '0');
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'Accounts', accounts);
|
||||
});
|
||||
cy.mockSubscription();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
});
|
||||
|
||||
it('should show an error if your balance is zero', () => {
|
||||
const accounts = accountsQuery();
|
||||
amendMarginAccountBalance(accounts, 'market-0', '0');
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'Accounts', accounts);
|
||||
});
|
||||
// 7002-SORD-060
|
||||
cy.getByTestId('place-order').should('be.enabled');
|
||||
// 7002-SORD-003
|
||||
cy.getByTestId('deal-ticket-error-message-zero-balance').should(
|
||||
'have.text',
|
||||
'You need ' +
|
||||
'tDAI' +
|
||||
' in your wallet to trade in this market. See all your collateral.Make a deposit'
|
||||
);
|
||||
cy.getByTestId('deal-ticket-deposit-dialog-button').should('exist');
|
||||
});
|
||||
});
|
||||
|
||||
describe('not enough balance warning', () => {
|
||||
beforeEach(() => {
|
||||
cy.setVegaWallet();
|
||||
cy.mockTradingPage();
|
||||
let accounts = accountsQuery();
|
||||
accounts = amendMarginAccountBalance(accounts, 'market-0', '1000');
|
||||
accounts = amendGeneralAccountBalance(accounts, 'market-0', '1');
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'Accounts', accounts);
|
||||
});
|
||||
cy.mockSubscription();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
|
||||
cy.get('[data-testid="deal-ticket-form"]').then(($form) => {
|
||||
if (!$form.length) {
|
||||
cy.getByTestId('Order').click();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should display info and button for deposit', () => {
|
||||
// 7002-SORD-003
|
||||
|
||||
// warning should show immediately
|
||||
cy.getByTestId('deal-ticket-warning-margin').should(
|
||||
'contain.text',
|
||||
'You may not have enough margin available to open this position'
|
||||
);
|
||||
cy.getByTestId('deal-ticket-warning-margin').should(
|
||||
'contain.text',
|
||||
'You may not have enough margin available to open this position. 5.00 tDAI is currently required. You have only 0.01001 tDAI available.'
|
||||
);
|
||||
cy.getByTestId('deal-ticket-deposit-dialog-button').click();
|
||||
cy.getByTestId('sidebar-content')
|
||||
.find('h2')
|
||||
.eq(0)
|
||||
.should('have.text', 'Deposit');
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
import type { OrderSubmission } from '@vegaprotocol/wallet';
|
||||
import { createOrder } from '../support/create-order';
|
||||
|
||||
describe(
|
||||
describe.skip(
|
||||
'account validation',
|
||||
{ tags: '@regression', testIsolation: true },
|
||||
() => {
|
||||
|
||||
+1
-2
@@ -12,8 +12,7 @@ NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json
|
||||
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
|
||||
@@ -12,6 +12,8 @@ NX_VEGA_WALLET_URL=http://localhost:1789
|
||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/
|
||||
NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supply green clown session"
|
||||
|
||||
@@ -13,6 +13,8 @@ NX_VEGA_DOCS_URL=#
|
||||
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega-dev-releases/releases
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
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/pl/firefox/addon/vega-wallet
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
|
||||
@@ -13,6 +13,8 @@ NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||
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/pl/firefox/addon/vega-wallet-mainnet
|
||||
# TAG name of the current app version - TODO: bump to the latest upon release
|
||||
NX_APP_VERSION=v0.20.21-core-0.71.6
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||
NX_VEGA_CONSOLE_URL=https://console.mainnet-mirror.vega.rocks
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-mainnet/codfcglpplgmmlokgilfkpcjnmkbfiel
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet-mainnet
|
||||
# TAG name of the current app version - TODO: bump to the latest upon release
|
||||
NX_APP_VERSION=v0.20.19-core-0.71.6
|
||||
|
||||
|
||||
@@ -13,9 +13,11 @@ NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
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/pl/firefox/addon/vega-wallet
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
# NX_ICEBERG_ORDERS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
|
||||
@@ -14,6 +14,8 @@ NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
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/pl/firefox/addon/vega-wallet
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
@@ -22,4 +24,4 @@ NX_ICEBERG_ORDERS=true
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
||||
|
||||
@@ -15,6 +15,9 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
|
||||
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||
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/pl/firefox/addon/vega-wallet
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_STOP_ORDERS=false
|
||||
|
||||
@@ -94,7 +94,11 @@ const MainGrid = memo(
|
||||
>
|
||||
<TradeGridChild>
|
||||
<Tabs storageKey="console-trade-grid-bottom">
|
||||
<Tab id="positions" name={t('Positions')}>
|
||||
<Tab
|
||||
id="positions"
|
||||
name={t('Positions')}
|
||||
menu={<TradingViews.positions.menu />}
|
||||
>
|
||||
<TradingViews.positions.component />
|
||||
</Tab>
|
||||
<Tab
|
||||
|
||||
@@ -17,6 +17,7 @@ import type { OrderContainerProps } from '../../components/orders-container';
|
||||
import { OrdersContainer } from '../../components/orders-container';
|
||||
import { StopOrdersContainer } from '../../components/stop-orders-container';
|
||||
import { AccountsMenu } from '../../components/accounts-menu';
|
||||
import { PositionsMenu } from '../../components/positions-menu';
|
||||
|
||||
type MarketDependantView =
|
||||
| typeof CandlesChartContainer
|
||||
@@ -57,7 +58,11 @@ export const TradingViews = {
|
||||
label: 'Trades',
|
||||
component: requiresMarket(TradesContainer),
|
||||
},
|
||||
positions: { label: 'Positions', component: PositionsContainer },
|
||||
positions: {
|
||||
label: 'Positions',
|
||||
component: PositionsContainer,
|
||||
menu: PositionsMenu,
|
||||
},
|
||||
activeOrders: {
|
||||
label: 'Active',
|
||||
component: (props: OrderContainerProps) => (
|
||||
|
||||
@@ -1,10 +1,2 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
|
||||
export const THROTTLE_UPDATE_TIME = 500;
|
||||
export const ONBOARDING_VIEWED_KEY = 'vega_onboarding_viewed';
|
||||
export const MAINNET_WELCOME_HEADER = t(
|
||||
'Trade cash settled futures on the fully decentralised Vega network.'
|
||||
);
|
||||
export const TESTNET_WELCOME_HEADER = t(
|
||||
'Try out trading cash settled futures on the fully decentralised Vega network (Testnet).'
|
||||
);
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import type { DataGridSlice } from '../../stores/datagrid-store-slice';
|
||||
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
|
||||
import type { StateCreator } from 'zustand';
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
||||
@@ -13,6 +14,7 @@ export const PositionsContainer = ({ allKeys }: { allKeys?: boolean }) => {
|
||||
const onMarketClick = useMarketClickHandler(true);
|
||||
const { pubKey, pubKeys, isReadOnly } = useVegaWallet();
|
||||
|
||||
const showClosed = usePositionsStore((store) => store.showClosedMarkets);
|
||||
const gridStore = usePositionsStore((store) => store.gridStore);
|
||||
const updateGridStore = usePositionsStore((store) => store.updateGridStore);
|
||||
const gridStoreCallbacks = useDataGridEvents(gridStore, updateGridStore);
|
||||
@@ -40,12 +42,35 @@ export const PositionsContainer = ({ allKeys }: { allKeys?: boolean }) => {
|
||||
onMarketClick={onMarketClick}
|
||||
isReadOnly={isReadOnly}
|
||||
gridProps={gridStoreCallbacks}
|
||||
showClosed={showClosed}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const usePositionsStore = create<DataGridSlice>()(
|
||||
persist(createDataGridSlice, {
|
||||
name: 'vega_positions_store',
|
||||
})
|
||||
type PositionsStoreSlice = {
|
||||
showClosedMarkets: boolean;
|
||||
toggleClosedMarkets: () => void;
|
||||
};
|
||||
|
||||
const createPositionStoreSlice: StateCreator<PositionsStoreSlice> = (set) => ({
|
||||
showClosedMarkets: false,
|
||||
toggleClosedMarkets: () => {
|
||||
set((curr) => {
|
||||
return {
|
||||
showClosedMarkets: !curr.showClosedMarkets,
|
||||
};
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const usePositionsStore = create<PositionsStoreSlice & DataGridSlice>()(
|
||||
persist(
|
||||
(...args) => ({
|
||||
...createPositionStoreSlice(...args),
|
||||
...createDataGridSlice(...args),
|
||||
}),
|
||||
{
|
||||
name: 'vega_positions_store',
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from './positions-menu';
|
||||
@@ -0,0 +1,18 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Intent, TradingButton } from '@vegaprotocol/ui-toolkit';
|
||||
import { usePositionsStore } from '../positions-container';
|
||||
|
||||
export const PositionsMenu = () => {
|
||||
const showClosed = usePositionsStore((store) => store.showClosedMarkets);
|
||||
const toggle = usePositionsStore((store) => store.toggleClosedMarkets);
|
||||
return (
|
||||
<TradingButton
|
||||
intent={Intent.Primary}
|
||||
size="extra-small"
|
||||
data-testid="open-transfer"
|
||||
onClick={toggle}
|
||||
>
|
||||
{showClosed ? t('Hide closed markets') : t('Show closed markets')}
|
||||
</TradingButton>
|
||||
);
|
||||
};
|
||||
@@ -1,16 +1,36 @@
|
||||
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';
|
||||
|
||||
let mockStep = 1;
|
||||
jest.mock('./use-get-onboarding-step', () => ({
|
||||
...jest.requireActual('./use-get-onboarding-step'),
|
||||
useGetOnboardingStep: jest.fn(() => mockStep),
|
||||
}));
|
||||
|
||||
describe('GetStarted', () => {
|
||||
const renderComponent = (context: Partial<VegaWalletContextShape> = {}) => {
|
||||
return render(
|
||||
<VegaWalletContext.Provider value={context as VegaWalletContextShape}>
|
||||
<GetStarted />
|
||||
</VegaWalletContext.Provider>
|
||||
<MemoryRouter>
|
||||
<VegaWalletContext.Provider value={context as VegaWalletContextShape}>
|
||||
<GetStarted />
|
||||
</VegaWalletContext.Provider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
};
|
||||
const checkTicks = (elements: Element[]) => {
|
||||
elements.forEach((item, i) => {
|
||||
if (i + 1 < mockStep) {
|
||||
expect(item.querySelector('[data-testid="icon-tick"]')).toBeTruthy();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders full get started content if not connected and no browser wallet detected', () => {
|
||||
renderComponent();
|
||||
@@ -20,12 +40,71 @@ describe('GetStarted', () => {
|
||||
it('renders connect prompt if no pubKey but wallet installed', () => {
|
||||
globalThis.window.vega = {} as Vega;
|
||||
renderComponent();
|
||||
expect(screen.getByTestId('order-connect-wallet')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('get-started-banner')).toBeInTheDocument();
|
||||
globalThis.window.vega = undefined as unknown as Vega;
|
||||
});
|
||||
|
||||
it('renders nothing if connected', () => {
|
||||
mockStep = 0;
|
||||
const { container } = renderComponent({ pubKey: 'my-pubkey' });
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
it('steps should be ticked', () => {
|
||||
const navigatorGetter: jest.SpyInstance = jest.spyOn(
|
||||
window.navigator,
|
||||
'userAgent',
|
||||
'get'
|
||||
);
|
||||
navigatorGetter.mockReturnValue('Chrome');
|
||||
mockStep = 1;
|
||||
const { rerender, container } = renderComponent();
|
||||
expect(screen.queryByTestId('icon-tick')).not.toBeInTheDocument();
|
||||
expect(screen.getByTestId('get-wallet-button')).toBeInTheDocument();
|
||||
|
||||
mockStep = 2;
|
||||
rerender(
|
||||
<MemoryRouter>
|
||||
<VegaWalletContext.Provider value={{} as VegaWalletContextShape}>
|
||||
<GetStarted />
|
||||
</VegaWalletContext.Provider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
checkTicks(screen.getAllByRole('listitem'));
|
||||
expect(screen.getByRole('button', { name: 'Connect' })).toBeInTheDocument();
|
||||
|
||||
mockStep = 3;
|
||||
rerender(
|
||||
<MemoryRouter>
|
||||
<VegaWalletContext.Provider value={{} as VegaWalletContextShape}>
|
||||
<GetStarted />
|
||||
</VegaWalletContext.Provider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
checkTicks(screen.getAllByRole('listitem'));
|
||||
expect(screen.getByRole('button', { name: 'Deposit' })).toBeInTheDocument();
|
||||
|
||||
mockStep = 4;
|
||||
rerender(
|
||||
<MemoryRouter>
|
||||
<VegaWalletContext.Provider value={{} as VegaWalletContextShape}>
|
||||
<GetStarted />
|
||||
</VegaWalletContext.Provider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
checkTicks(screen.getAllByRole('listitem'));
|
||||
expect(screen.getByRole('button', { name: 'Dismiss' })).toBeInTheDocument();
|
||||
|
||||
mockStep = 5;
|
||||
rerender(
|
||||
<MemoryRouter>
|
||||
<VegaWalletContext.Provider
|
||||
value={{ pubKey: 'my-pubkey' } as VegaWalletContextShape}
|
||||
>
|
||||
<GetStarted />
|
||||
</VegaWalletContext.Provider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,36 +1,96 @@
|
||||
import classNames from 'classnames';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { ExternalLink, Intent, TradingButton } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
ExternalLink,
|
||||
Intent,
|
||||
TradingButton,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
GetWalletButton,
|
||||
useVegaWallet,
|
||||
useVegaWalletDialogStore,
|
||||
isBrowserWalletInstalled,
|
||||
} from '@vegaprotocol/wallet';
|
||||
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import {
|
||||
OnboardingStep,
|
||||
useGetOnboardingStep,
|
||||
} 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';
|
||||
|
||||
interface Props {
|
||||
lead?: string;
|
||||
}
|
||||
|
||||
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 GetStartedButton = ({ step }: { step: OnboardingStep }) => {
|
||||
const navigate = useNavigate();
|
||||
const [, setOnboardingViewed] = useLocalStorage(
|
||||
constants.ONBOARDING_VIEWED_KEY
|
||||
);
|
||||
|
||||
const update = useGlobalStore((store) => store.update);
|
||||
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();
|
||||
};
|
||||
if (step === OnboardingStep.ONBOARDING_WALLET_STEP) {
|
||||
return <GetWalletButton className="justify-between" />;
|
||||
} else if (step === OnboardingStep.ONBOARDING_CONNECT_STEP) {
|
||||
buttonText = t('Connect');
|
||||
} else if (step === OnboardingStep.ONBOARDING_DEPOSIT_STEP) {
|
||||
buttonText = t('Deposit');
|
||||
onClickHandle = () => {
|
||||
navigate(link);
|
||||
setView({ type: ViewType.Deposit });
|
||||
update({ onBoardingDismissed: true });
|
||||
};
|
||||
} else if (step === OnboardingStep.ONBOARDING_ORDER_STEP) {
|
||||
buttonText = t('Dismiss');
|
||||
onClickHandle = () => {
|
||||
navigate(link);
|
||||
setView({ type: ViewType.Order });
|
||||
setOnboardingViewed('true');
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<TradingButton
|
||||
onClick={onClickHandle}
|
||||
size="small"
|
||||
data-testid="get-started-button"
|
||||
intent={Intent.Info}
|
||||
>
|
||||
{buttonText}
|
||||
</TradingButton>
|
||||
);
|
||||
};
|
||||
|
||||
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 onButtonClick = () => {
|
||||
openVegaWalletDialog();
|
||||
setOnboardingViewed('true');
|
||||
};
|
||||
const getStartedNeeded =
|
||||
onBoardingViewed !== 'true' &&
|
||||
currentStep &&
|
||||
currentStep < OnboardingStep.ONBOARDING_COMPLETE_STEP;
|
||||
|
||||
const wrapperClasses = classNames(
|
||||
'flex flex-col py-4 px-6 gap-4 rounded',
|
||||
@@ -39,27 +99,48 @@ export const GetStarted = ({ lead }: Props) => {
|
||||
{ 'mt-8': !lead }
|
||||
);
|
||||
|
||||
if (!pubKey && !isBrowserWalletInstalled()) {
|
||||
if (getStartedNeeded) {
|
||||
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-decimal list-inside">
|
||||
<li>{t('Get a Vega wallet')}</li>
|
||||
<li>{t('Connect')}</li>
|
||||
<li>{t('Deposit funds')}</li>
|
||||
<li>{t('Open a position')}</li>
|
||||
<ul className="list-inside -ml-5" role="list">
|
||||
<li className="flex">
|
||||
<div className="w-5">
|
||||
{currentStep > OnboardingStep.ONBOARDING_WALLET_STEP && (
|
||||
<VegaIcon name={VegaIconNames.TICK} size={20} />
|
||||
)}
|
||||
</div>
|
||||
<div className="ml-1">1. {t('Get a Vega wallet')}</div>
|
||||
</li>
|
||||
<li className="flex">
|
||||
<div className="w-5">
|
||||
{(currentStep > OnboardingStep.ONBOARDING_CONNECT_STEP ||
|
||||
pubKey) && <VegaIcon name={VegaIconNames.TICK} size={20} />}
|
||||
</div>
|
||||
<div className="ml-1">2. {t('Connect')}</div>
|
||||
</li>
|
||||
<li className="flex">
|
||||
<div className="w-5">
|
||||
{currentStep > OnboardingStep.ONBOARDING_DEPOSIT_STEP && (
|
||||
<VegaIcon name={VegaIconNames.TICK} size={20} />
|
||||
)}
|
||||
</div>
|
||||
<div className="ml-1">3. {t('Deposit funds')}</div>
|
||||
</li>
|
||||
<li className="flex">
|
||||
<div className="w-5">
|
||||
{currentStep > OnboardingStep.ONBOARDING_ORDER_STEP && (
|
||||
<VegaIcon name={VegaIconNames.TICK} size={20} />
|
||||
)}
|
||||
</div>
|
||||
<div className="ml-1">4. {t('Open a position')}</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<TradingButton
|
||||
intent={Intent.Info}
|
||||
onClick={onButtonClick}
|
||||
data-testid="get-started-button"
|
||||
>
|
||||
{t('Get started')}
|
||||
</TradingButton>
|
||||
<GetStartedButton step={currentStep} />
|
||||
</div>
|
||||
{VEGA_ENV === Networks.MAINNET && (
|
||||
<p className="text-sm">
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import {
|
||||
useGetOnboardingStep,
|
||||
OnboardingStep,
|
||||
} from './use-get-onboarding-step';
|
||||
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { depositsProvider } from '@vegaprotocol/deposits';
|
||||
import { aggregatedAccountsDataProvider } from '@vegaprotocol/accounts';
|
||||
import { ordersWithMarketProvider } from '@vegaprotocol/orders';
|
||||
import { positionsDataProvider } from '@vegaprotocol/positions';
|
||||
|
||||
let mockData: object[] | null = [{ id: 'item-id' }];
|
||||
jest.mock('@vegaprotocol/data-provider', () => ({
|
||||
...jest.requireActual('@vegaprotocol/data-provider'),
|
||||
useDataProvider: jest.fn(() => ({ data: mockData })),
|
||||
}));
|
||||
|
||||
let mockContext: Partial<VegaWalletContextShape> = { pubKey: 'test-pubkey' };
|
||||
|
||||
describe('useGetOnboardingStep', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockData = [{ id: 'item-id' }];
|
||||
mockContext = { pubKey: 'test-pubkey' };
|
||||
globalThis.window.vega = {} as Vega;
|
||||
});
|
||||
|
||||
const wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<VegaWalletContext.Provider
|
||||
value={mockContext as unknown as VegaWalletContextShape}
|
||||
>
|
||||
{children}
|
||||
</VegaWalletContext.Provider>
|
||||
);
|
||||
|
||||
it('should return properly ONBOARDING_UNKNOWN_STEP', () => {
|
||||
mockData = null;
|
||||
const { result } = renderHook(() => useGetOnboardingStep(), { wrapper });
|
||||
expect(result.current).toEqual(OnboardingStep.ONBOARDING_UNKNOWN_STEP);
|
||||
});
|
||||
|
||||
it('should return properly ONBOARDING_WALLET_STEP', () => {
|
||||
// @ts-ignore test only purpose
|
||||
globalThis.window.vega = undefined;
|
||||
const { result } = renderHook(() => useGetOnboardingStep(), { wrapper });
|
||||
expect(result.current).toEqual(OnboardingStep.ONBOARDING_WALLET_STEP);
|
||||
});
|
||||
|
||||
it('should return properly ONBOARDING_CONNECT_STEP', () => {
|
||||
mockContext = { pubKey: null };
|
||||
const { result } = renderHook(() => useGetOnboardingStep(), { wrapper });
|
||||
expect(result.current).toEqual(OnboardingStep.ONBOARDING_CONNECT_STEP);
|
||||
});
|
||||
|
||||
it('should return properly ONBOARDING_DEPOSIT_STEP', async () => {
|
||||
(useDataProvider as jest.Mock).mockImplementation((args) => {
|
||||
if (
|
||||
args.dataProvider === depositsProvider ||
|
||||
args.dataProvider === aggregatedAccountsDataProvider
|
||||
) {
|
||||
return { data: [] };
|
||||
}
|
||||
return { data: mockData };
|
||||
});
|
||||
const { result } = renderHook(() => useGetOnboardingStep(), { wrapper });
|
||||
await expect(result.current).toEqual(
|
||||
OnboardingStep.ONBOARDING_DEPOSIT_STEP
|
||||
);
|
||||
});
|
||||
|
||||
it('should return properly ONBOARDING_ORDER_STEP', async () => {
|
||||
(useDataProvider as jest.Mock).mockImplementation((args) => {
|
||||
if (
|
||||
args.dataProvider === ordersWithMarketProvider ||
|
||||
args.dataProvider === positionsDataProvider
|
||||
) {
|
||||
return { data: [] };
|
||||
}
|
||||
return { data: mockData };
|
||||
});
|
||||
const { result } = renderHook(() => useGetOnboardingStep(), { wrapper });
|
||||
await expect(result.current).toEqual(OnboardingStep.ONBOARDING_ORDER_STEP);
|
||||
});
|
||||
|
||||
it('should return properly ONBOARDING_COMPLETE_STEP', async () => {
|
||||
(useDataProvider as jest.Mock).mockImplementation(() => {
|
||||
return { data: mockData };
|
||||
});
|
||||
const { result } = renderHook(() => useGetOnboardingStep(), { wrapper });
|
||||
await expect(result.current).toEqual(
|
||||
OnboardingStep.ONBOARDING_COMPLETE_STEP
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,82 @@
|
||||
import { isBrowserWalletInstalled, useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { depositsProvider } from '@vegaprotocol/deposits';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { ordersWithMarketProvider } from '@vegaprotocol/orders';
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
import { aggregatedAccountsDataProvider } from '@vegaprotocol/accounts';
|
||||
import { positionsDataProvider } from '@vegaprotocol/positions';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
|
||||
export enum OnboardingStep {
|
||||
ONBOARDING_UNKNOWN_STEP,
|
||||
ONBOARDING_WALLET_STEP,
|
||||
ONBOARDING_CONNECT_STEP,
|
||||
ONBOARDING_DEPOSIT_STEP,
|
||||
ONBOARDING_ORDER_STEP,
|
||||
ONBOARDING_COMPLETE_STEP,
|
||||
}
|
||||
|
||||
export const useGetOnboardingStep = () => {
|
||||
const connecting = useGlobalStore((store) => store.eagerConnecting);
|
||||
const { pubKey = '', pubKeys } = useVegaWallet();
|
||||
const { data: depositsData } = useDataProvider({
|
||||
dataProvider: depositsProvider,
|
||||
variables: { partyId: pubKey || '' },
|
||||
skip: !pubKey,
|
||||
});
|
||||
const { data: collateralData } = useDataProvider({
|
||||
dataProvider: aggregatedAccountsDataProvider,
|
||||
variables: { partyId: pubKey || '' },
|
||||
skip: !pubKey,
|
||||
});
|
||||
const collaterals = Boolean(collateralData?.length);
|
||||
const deposits =
|
||||
depositsData?.some(
|
||||
(item) => item.status === Types.DepositStatus.STATUS_FINALIZED
|
||||
) || false;
|
||||
const { data: ordersData } = useDataProvider({
|
||||
dataProvider: ordersWithMarketProvider,
|
||||
variables: {
|
||||
partyId: pubKey || '',
|
||||
pagination: {
|
||||
first: 1,
|
||||
},
|
||||
},
|
||||
skip: !pubKey,
|
||||
});
|
||||
const orders = Boolean(ordersData?.length);
|
||||
|
||||
const partyIds = pubKeys?.map((item) => item.publicKey) || [];
|
||||
const { data: positionsData } = useDataProvider({
|
||||
dataProvider: positionsDataProvider,
|
||||
variables: {
|
||||
partyIds,
|
||||
},
|
||||
skip: !partyIds?.length,
|
||||
});
|
||||
const positions = Boolean(positionsData?.length);
|
||||
|
||||
const isLoading = Boolean(
|
||||
(connecting || pubKey) &&
|
||||
(depositsData === null ||
|
||||
ordersData === null ||
|
||||
collateralData === null ||
|
||||
positionsData === null)
|
||||
);
|
||||
if (isLoading) {
|
||||
return OnboardingStep.ONBOARDING_UNKNOWN_STEP;
|
||||
}
|
||||
if (!isBrowserWalletInstalled()) {
|
||||
return OnboardingStep.ONBOARDING_WALLET_STEP;
|
||||
}
|
||||
if (!pubKey) {
|
||||
return OnboardingStep.ONBOARDING_CONNECT_STEP;
|
||||
}
|
||||
if (!deposits && !collaterals) {
|
||||
return OnboardingStep.ONBOARDING_DEPOSIT_STEP;
|
||||
}
|
||||
if (!orders && !positions) {
|
||||
return OnboardingStep.ONBOARDING_ORDER_STEP;
|
||||
}
|
||||
return OnboardingStep.ONBOARDING_COMPLETE_STEP;
|
||||
};
|
||||
@@ -3,21 +3,19 @@ import { GetStarted } from './get-started';
|
||||
import { TradingButton } from '@vegaprotocol/ui-toolkit';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Links, Routes } from '../../pages/client-router';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import * as constants from '../constants';
|
||||
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
|
||||
export const WelcomeDialogContent = () => {
|
||||
const { VEGA_ENV } = useEnvironment();
|
||||
const [, setOnboardingViewed] = useLocalStorage(
|
||||
constants.ONBOARDING_VIEWED_KEY
|
||||
);
|
||||
|
||||
const update = useGlobalStore((store) => store.update);
|
||||
const navigate = useNavigate();
|
||||
const browseMarkets = () => {
|
||||
const link = Links[Routes.MARKETS]();
|
||||
navigate(link);
|
||||
setOnboardingViewed('true');
|
||||
update({ onBoardingDismissed: true });
|
||||
};
|
||||
const lead =
|
||||
VEGA_ENV === Networks.MAINNET
|
||||
@@ -57,7 +55,7 @@ export const WelcomeDialogContent = () => {
|
||||
{t('Browse the markets')}
|
||||
</TradingButton>
|
||||
</div>
|
||||
<div className="sm:w-1/2">
|
||||
<div className="sm:w-1/2 flex grow">
|
||||
<GetStarted lead={lead} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,31 +2,38 @@ import React from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Dialog, Intent } from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { isBrowserWalletInstalled } from '@vegaprotocol/wallet';
|
||||
import * as constants from '../constants';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import { WelcomeDialogContent } from './welcome-dialog-content';
|
||||
import { getConfig } from '@vegaprotocol/wallet';
|
||||
import { Links, Routes } from '../../pages/client-router';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
import {
|
||||
useGetOnboardingStep,
|
||||
OnboardingStep,
|
||||
} from './use-get-onboarding-step';
|
||||
import * as constants from '../constants';
|
||||
|
||||
export const WelcomeDialog = () => {
|
||||
const { VEGA_ENV } = useEnvironment();
|
||||
const [onBoardingViewed, setOnboardingViewed] = useLocalStorage(
|
||||
constants.ONBOARDING_VIEWED_KEY
|
||||
);
|
||||
const [onBoardingViewed] = useLocalStorage(constants.ONBOARDING_VIEWED_KEY);
|
||||
const update = useGlobalStore((store) => store.update);
|
||||
const dismissed = useGlobalStore((store) => store.onBoardingDismissed);
|
||||
const currentStep = useGetOnboardingStep();
|
||||
|
||||
const navigate = useNavigate();
|
||||
const isOnboardingDialogNeeded =
|
||||
onBoardingViewed !== 'true' && !isBrowserWalletInstalled() && !getConfig();
|
||||
onBoardingViewed !== 'true' &&
|
||||
currentStep &&
|
||||
currentStep < OnboardingStep.ONBOARDING_COMPLETE_STEP &&
|
||||
!dismissed;
|
||||
const marketId = useGlobalStore((store) => store.marketId);
|
||||
|
||||
const onClose = () => {
|
||||
setOnboardingViewed('true');
|
||||
const link = marketId
|
||||
? Links[Routes.MARKET](marketId)
|
||||
: Links[Routes.HOME]();
|
||||
navigate(link);
|
||||
update({ onBoardingDismissed: true });
|
||||
};
|
||||
const title = (
|
||||
<span className="font-alpha calt" data-testid="welcome-title">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import Head from 'next/head';
|
||||
import type { AppProps } from 'next/app';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
useNodeSwitcherStore,
|
||||
} from '@vegaprotocol/environment';
|
||||
import './styles.css';
|
||||
import { usePageTitleStore } from '../stores';
|
||||
import { useGlobalStore, usePageTitleStore } from '../stores';
|
||||
import DialogsContainer from './dialogs-container';
|
||||
import ToastsManager from './toasts-manager';
|
||||
import {
|
||||
@@ -170,7 +170,8 @@ const PartyData = () => {
|
||||
|
||||
const MaybeConnectEagerly = () => {
|
||||
const { VEGA_ENV, SENTRY_DSN } = useEnvironment();
|
||||
useVegaEagerConnect(Connectors);
|
||||
const update = useGlobalStore((store) => store.update);
|
||||
const eagerConnecting = useVegaEagerConnect(Connectors);
|
||||
const [isTelemetryApproved] = useTelemetryApproval();
|
||||
useEthereumEagerConnect(
|
||||
isTelemetryApproved ? { dsn: SENTRY_DSN, env: VEGA_ENV } : {}
|
||||
@@ -182,5 +183,8 @@ const MaybeConnectEagerly = () => {
|
||||
if (query && !pubKey) {
|
||||
connect(Connectors['view']);
|
||||
}
|
||||
useEffect(() => {
|
||||
update({ eagerConnecting });
|
||||
}, [update, eagerConnecting]);
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -4,6 +4,8 @@ import produce from 'immer';
|
||||
|
||||
interface GlobalStore {
|
||||
marketId: string | null;
|
||||
onBoardingDismissed: boolean;
|
||||
eagerConnecting: boolean;
|
||||
update: (store: Partial<Omit<GlobalStore, 'update'>>) => void;
|
||||
}
|
||||
|
||||
@@ -14,6 +16,8 @@ interface PageTitleStore {
|
||||
|
||||
export const useGlobalStore = create<GlobalStore>()((set) => ({
|
||||
marketId: LocalStorage.getItem('marketId') || null,
|
||||
onBoardingDismissed: false,
|
||||
eagerConnecting: false,
|
||||
update: (newState) => {
|
||||
set(
|
||||
produce((state: GlobalStore) => {
|
||||
|
||||
+3
-3
@@ -34,21 +34,21 @@ export type OrdersUpdateSubscription = { __typename?: 'Subscription', orders?: A
|
||||
|
||||
export type OrderSubmissionFieldsFragment = { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null };
|
||||
|
||||
export type StopOrderFieldsFragment = { __typename?: 'StopOrder', id: string, ocoLinkId?: string | null, expiresAt?: any | null, expiryStrategy?: Types.StopOrderExpiryStrategy | null, triggerDirection: Types.StopOrderTriggerDirection, status: Types.StopOrderStatus, createdAt: any, updatedAt?: any | null, partyId: string, marketId: string, trigger?: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string } | null, submission: { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null } };
|
||||
export type StopOrderFieldsFragment = { __typename?: 'StopOrder', id: string, ocoLinkId?: string | null, expiresAt?: any | null, expiryStrategy?: Types.StopOrderExpiryStrategy | null, triggerDirection: Types.StopOrderTriggerDirection, status: Types.StopOrderStatus, createdAt: any, updatedAt?: any | null, partyId: string, marketId: string, trigger: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string }, submission: { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null } };
|
||||
|
||||
export type StopOrdersQueryVariables = Types.Exact<{
|
||||
partyId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type StopOrdersQuery = { __typename?: 'Query', stopOrders?: { __typename?: 'StopOrderConnection', edges?: Array<{ __typename?: 'StopOrderEdge', node?: { __typename?: 'StopOrder', id: string, ocoLinkId?: string | null, expiresAt?: any | null, expiryStrategy?: Types.StopOrderExpiryStrategy | null, triggerDirection: Types.StopOrderTriggerDirection, status: Types.StopOrderStatus, createdAt: any, updatedAt?: any | null, partyId: string, marketId: string, trigger?: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string } | null, submission: { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null } } | null }> | null } | null };
|
||||
export type StopOrdersQuery = { __typename?: 'Query', stopOrders?: { __typename?: 'StopOrderConnection', edges?: Array<{ __typename?: 'StopOrderEdge', node?: { __typename?: 'StopOrder', id: string, ocoLinkId?: string | null, expiresAt?: any | null, expiryStrategy?: Types.StopOrderExpiryStrategy | null, triggerDirection: Types.StopOrderTriggerDirection, status: Types.StopOrderStatus, createdAt: any, updatedAt?: any | null, partyId: string, marketId: string, trigger: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string }, submission: { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null } } | null }> | null } | null };
|
||||
|
||||
export type StopOrderByIdQueryVariables = Types.Exact<{
|
||||
stopOrderId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type StopOrderByIdQuery = { __typename?: 'Query', stopOrder?: { __typename?: 'StopOrder', id: string, ocoLinkId?: string | null, expiresAt?: any | null, expiryStrategy?: Types.StopOrderExpiryStrategy | null, triggerDirection: Types.StopOrderTriggerDirection, status: Types.StopOrderStatus, createdAt: any, updatedAt?: any | null, partyId: string, marketId: string, trigger?: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string } | null, submission: { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null } } | null };
|
||||
export type StopOrderByIdQuery = { __typename?: 'Query', stopOrder?: { __typename?: 'StopOrder', id: string, ocoLinkId?: string | null, expiresAt?: any | null, expiryStrategy?: Types.StopOrderExpiryStrategy | null, triggerDirection: Types.StopOrderTriggerDirection, status: Types.StopOrderStatus, createdAt: any, updatedAt?: any | null, partyId: string, marketId: string, trigger: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string }, submission: { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null } } | null };
|
||||
|
||||
export const OrderFieldsFragmentDoc = gql`
|
||||
fragment OrderFields on Order {
|
||||
|
||||
@@ -2,7 +2,12 @@ import * as Schema from '@vegaprotocol/types';
|
||||
import type { Account } from '@vegaprotocol/accounts';
|
||||
import type { MarketWithData } from '@vegaprotocol/markets';
|
||||
import type { PositionFieldsFragment } from './__generated__/Positions';
|
||||
import { getMetrics, rejoinPositionData } from './positions-data-providers';
|
||||
import type { Position } from './positions-data-providers';
|
||||
import {
|
||||
getMetrics,
|
||||
preparePositions,
|
||||
rejoinPositionData,
|
||||
} from './positions-data-providers';
|
||||
import { PositionStatus } from '@vegaprotocol/types';
|
||||
|
||||
const accounts = [
|
||||
@@ -223,4 +228,29 @@ describe('getMetrics && rejoinPositionData', () => {
|
||||
);
|
||||
expect(metrics[1].status).toEqual(positions[1].positionStatus);
|
||||
});
|
||||
|
||||
it('sorts and filters positions', () => {
|
||||
const createPosition = (override?: Partial<Position>) =>
|
||||
({
|
||||
marketState: Schema.MarketState.STATE_ACTIVE,
|
||||
marketCode: 'a',
|
||||
...override,
|
||||
} as Position);
|
||||
|
||||
const data = [
|
||||
createPosition(),
|
||||
createPosition({
|
||||
marketCode: 'c',
|
||||
marketState: Schema.MarketState.STATE_CANCELLED,
|
||||
}),
|
||||
createPosition({ marketCode: 'd' }),
|
||||
createPosition({ marketCode: 'b' }),
|
||||
];
|
||||
|
||||
const withoutClosed = preparePositions(data, false);
|
||||
expect(withoutClosed.map((p) => p.marketCode)).toEqual(['a', 'b', 'd']);
|
||||
|
||||
const withClosed = preparePositions(data, true);
|
||||
expect(withClosed.map((p) => p.marketCode)).toEqual(['a', 'b', 'c', 'd']);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -41,6 +41,7 @@ export interface Position {
|
||||
marketId: string;
|
||||
marketCode: string;
|
||||
marketTradingMode: Schema.MarketTradingMode;
|
||||
marketState: Schema.MarketState;
|
||||
markPrice: string | undefined;
|
||||
notional: string | undefined;
|
||||
openVolume: string;
|
||||
@@ -119,6 +120,7 @@ export const getMetrics = (
|
||||
marketId: market.id,
|
||||
marketCode: market.tradableInstrument.instrument.code,
|
||||
marketTradingMode: market.tradingMode,
|
||||
marketState: market.state,
|
||||
markPrice: marketData ? marketData.markPrice : undefined,
|
||||
notional: notional
|
||||
? notional.multipliedBy(10 ** marketDecimalPlaces).toFixed(0)
|
||||
@@ -179,7 +181,7 @@ const getSubscriptionVariables = (
|
||||
): PositionsSubscriptionSubscriptionVariables[] =>
|
||||
([] as string[]).concat(variables.partyIds).map((partyId) => ({ partyId }));
|
||||
|
||||
const positionsDataProvider = makeDataProvider<
|
||||
export const positionsDataProvider = makeDataProvider<
|
||||
PositionsQuery,
|
||||
PositionFieldsFragment[],
|
||||
PositionsSubscriptionSubscription,
|
||||
@@ -248,6 +250,26 @@ export const rejoinPositionData = (
|
||||
return null;
|
||||
};
|
||||
|
||||
export const preparePositions = (metrics: Position[], showClosed: boolean) => {
|
||||
return sortBy(metrics, 'marketCode').filter((p) => {
|
||||
if (showClosed) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
[
|
||||
Schema.MarketState.STATE_ACTIVE,
|
||||
Schema.MarketState.STATE_PENDING,
|
||||
Schema.MarketState.STATE_SUSPENDED,
|
||||
].includes(p.marketState)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
export const positionsMarketsProvider = makeDerivedDataProvider<
|
||||
string[],
|
||||
never,
|
||||
@@ -265,7 +287,7 @@ export const positionsMarketsProvider = makeDerivedDataProvider<
|
||||
export const positionsMetricsProvider = makeDerivedDataProvider<
|
||||
Position[],
|
||||
Position[],
|
||||
PositionsQueryVariables & { marketIds: string[] }
|
||||
PositionsQueryVariables & { marketIds: string[]; showClosed: boolean }
|
||||
>(
|
||||
[
|
||||
(callback, client, variables) =>
|
||||
@@ -281,10 +303,10 @@ export const positionsMetricsProvider = makeDerivedDataProvider<
|
||||
marketIds: variables.marketIds,
|
||||
}),
|
||||
],
|
||||
([positions, accounts, marketsData]) => {
|
||||
([positions, accounts, marketsData], variables) => {
|
||||
const positionsData = rejoinPositionData(positions, marketsData);
|
||||
const metrics = getMetrics(positionsData, accounts as Account[] | null);
|
||||
return sortBy(metrics, 'marketCode');
|
||||
return preparePositions(metrics, variables.showClosed);
|
||||
},
|
||||
(data, delta, previousData) =>
|
||||
data.filter((row) => {
|
||||
|
||||
@@ -16,6 +16,7 @@ interface PositionsManagerProps {
|
||||
onMarketClick?: (marketId: string) => void;
|
||||
isReadOnly: boolean;
|
||||
gridProps?: ReturnType<typeof useDataGridEvents>;
|
||||
showClosed?: boolean;
|
||||
}
|
||||
|
||||
export const PositionsManager = ({
|
||||
@@ -23,6 +24,7 @@ export const PositionsManager = ({
|
||||
onMarketClick,
|
||||
isReadOnly,
|
||||
gridProps,
|
||||
showClosed = false,
|
||||
}: PositionsManagerProps) => {
|
||||
const { pubKeys, pubKey } = useVegaWallet();
|
||||
const create = useVegaTransactionStore((store) => store.create);
|
||||
@@ -60,7 +62,7 @@ export const PositionsManager = ({
|
||||
|
||||
const { data, error } = useDataProvider({
|
||||
dataProvider: positionsMetricsProvider,
|
||||
variables: { partyIds, marketIds: marketIds || [] },
|
||||
variables: { partyIds, marketIds: marketIds || [], showClosed },
|
||||
skip: !marketIds,
|
||||
});
|
||||
|
||||
@@ -68,7 +70,7 @@ export const PositionsManager = ({
|
||||
<PositionsTable
|
||||
pubKey={pubKey}
|
||||
pubKeys={pubKeys}
|
||||
rowData={error ? [] : data}
|
||||
rowData={data}
|
||||
onMarketClick={onMarketClick}
|
||||
onClose={onClose}
|
||||
isReadOnly={isReadOnly}
|
||||
|
||||
@@ -27,6 +27,7 @@ const singleRow: Position = {
|
||||
marketId: 'string',
|
||||
marketCode: 'ETHBTC.QM21',
|
||||
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
|
||||
marketState: Schema.MarketState.STATE_ACTIVE,
|
||||
markPrice: '123',
|
||||
notional: '12300',
|
||||
openVolume: '100',
|
||||
|
||||
@@ -473,8 +473,8 @@ export const PositionsTable = ({
|
||||
</div>
|
||||
);
|
||||
},
|
||||
minWidth: 75,
|
||||
maxWidth: 75,
|
||||
minWidth: 55,
|
||||
maxWidth: 55,
|
||||
}
|
||||
: null,
|
||||
];
|
||||
|
||||
Generated
+3
-1
@@ -4364,6 +4364,8 @@ export type StopOrder = {
|
||||
marketId: Scalars['ID'];
|
||||
/** If OCO (one-cancels-other) order, the ID of the associated order. */
|
||||
ocoLinkId?: Maybe<Scalars['ID']>;
|
||||
/** The order that was created when triggered. */
|
||||
order?: Maybe<Order>;
|
||||
/** Party that submitted the stop order. */
|
||||
partyId: Scalars['ID'];
|
||||
/** Status of the stop order */
|
||||
@@ -4371,7 +4373,7 @@ export type StopOrder = {
|
||||
/** Order to submit when the stop order is triggered. */
|
||||
submission: OrderSubmission;
|
||||
/** Price movement that will trigger the stop order */
|
||||
trigger?: Maybe<StopOrderTrigger>;
|
||||
trigger: StopOrderTrigger;
|
||||
/** Direction the price is moving to trigger the stop order. */
|
||||
triggerDirection: StopOrderTriggerDirection;
|
||||
/** Time the stop order was last updated. */
|
||||
|
||||
@@ -204,6 +204,7 @@ const ConnectorList = ({
|
||||
setWalletUrl: (value: string) => void;
|
||||
isDesktopWalletRunning: boolean | null;
|
||||
}) => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const title = isBrowserWalletInstalled()
|
||||
? t('Connect Vega wallet')
|
||||
: t('Get a Vega wallet');
|
||||
@@ -234,7 +235,7 @@ const ConnectorList = ({
|
||||
onClick={() => onSelect('injected')}
|
||||
/>
|
||||
) : (
|
||||
<GetWallet />
|
||||
<GetWalletButton />
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
@@ -242,6 +243,7 @@ const ConnectorList = ({
|
||||
type="view"
|
||||
text={t('View as party')}
|
||||
onClick={() => onSelect('view')}
|
||||
disabled={Boolean(pubKey)}
|
||||
/>
|
||||
</div>
|
||||
<div className="last:mb-0">
|
||||
@@ -318,7 +320,7 @@ const SelectedForm = ({
|
||||
throw new Error('No connector selected');
|
||||
};
|
||||
|
||||
const GetWallet = () => {
|
||||
export const GetWalletButton = ({ className }: { className?: string }) => {
|
||||
const { MOZILLA_EXTENSION_URL, CHROME_EXTENSION_URL } = useEnvironment();
|
||||
const isItChrome = window.navigator.userAgent.includes('Chrome');
|
||||
const isItMozilla =
|
||||
@@ -347,10 +349,13 @@ const GetWallet = () => {
|
||||
|
||||
return !isItChrome && !isItMozilla ? (
|
||||
<div
|
||||
className={classNames([
|
||||
'bg-vega-blue-350 hover:bg-vega-blue-400 dark:bg-vega-blue-650 dark:hover:bg-vega-blue-600',
|
||||
'flex gap-2 items-center justify-center rounded h-8 px-3 relative',
|
||||
])}
|
||||
className={classNames(
|
||||
[
|
||||
'bg-vega-blue-350 hover:bg-vega-blue-400 dark:bg-vega-blue-650 dark:hover:bg-vega-blue-600',
|
||||
'flex gap-2 items-center justify-center rounded h-8 px-3 relative',
|
||||
],
|
||||
className
|
||||
)}
|
||||
data-testid="get-wallet-button"
|
||||
>
|
||||
{buttonContent}
|
||||
@@ -360,7 +365,7 @@ const GetWallet = () => {
|
||||
onClick={onClick}
|
||||
intent={Intent.Info}
|
||||
data-testid="get-wallet-button"
|
||||
className="relative"
|
||||
className={classNames('relative', className)}
|
||||
size="small"
|
||||
fill
|
||||
>
|
||||
@@ -409,6 +414,7 @@ const CustomUrlInput = ({
|
||||
isDesktopWalletRunning: boolean | null;
|
||||
onSelect: (type: WalletType) => void;
|
||||
}) => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const [urlInputExpanded, setUrlInputExpanded] = useState(false);
|
||||
return urlInputExpanded ? (
|
||||
<>
|
||||
@@ -433,7 +439,7 @@ const CustomUrlInput = ({
|
||||
/>
|
||||
</FormGroup>
|
||||
<ConnectionOption
|
||||
disabled={!isDesktopWalletRunning}
|
||||
disabled={!isDesktopWalletRunning || Boolean(pubKey)}
|
||||
type="jsonRpc"
|
||||
text={t('Connect the App/CLI')}
|
||||
onClick={() => onSelect('jsonRpc')}
|
||||
@@ -442,7 +448,7 @@ const CustomUrlInput = ({
|
||||
) : (
|
||||
<>
|
||||
<ConnectionOption
|
||||
disabled={!isDesktopWalletRunning}
|
||||
disabled={!isDesktopWalletRunning || Boolean(pubKey)}
|
||||
type="jsonRpc"
|
||||
text={t('Use the Desktop App/CLI')}
|
||||
onClick={() => onSelect('jsonRpc')}
|
||||
@@ -453,6 +459,7 @@ const CustomUrlInput = ({
|
||||
<button
|
||||
className="underline text-default"
|
||||
onClick={() => setUrlInputExpanded(true)}
|
||||
disabled={Boolean(pubKey)}
|
||||
>
|
||||
{t('Enter a custom wallet location')}{' '}
|
||||
<VegaIcon name={VegaIconNames.ARROW_RIGHT} />
|
||||
@@ -467,6 +474,7 @@ const CustomUrlInput = ({
|
||||
<button
|
||||
className="underline"
|
||||
onClick={() => setUrlInputExpanded(true)}
|
||||
disabled={Boolean(pubKey)}
|
||||
>
|
||||
{t('custom wallet location')}
|
||||
</button>
|
||||
|
||||
@@ -12,7 +12,9 @@ export const useIsWalletServiceRunning = (
|
||||
|
||||
const checkState = useCallback(async () => {
|
||||
const connector = connectors['jsonRpc'] as JsonRpcConnector;
|
||||
connector.url = url;
|
||||
if (url && url !== connector.url) {
|
||||
connector.url = url;
|
||||
}
|
||||
try {
|
||||
await connector.checkCompat();
|
||||
const chainIdResult = await connector.getChainId();
|
||||
|
||||
@@ -21,8 +21,7 @@
|
||||
"echo $NX_TENDERMINT_URL",
|
||||
"echo $NX_TENDERMINT_WEBSOCKET_URL",
|
||||
"echo $NX_ETHEREUM_PROVIDER_URL"
|
||||
],
|
||||
"url": "https://cloud.nx.app"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,22 +1,34 @@
|
||||
# First use & get started steps
|
||||
|
||||
## When first enter the app
|
||||
## "Onboarding" state is gradable and has following steps: (<a name="0007-FUGS-002" href="#0007-FUGS-002">0007-FUGS-003</a>)
|
||||
|
||||
- New visitor - has no wallet nor any dapps running.
|
||||
- I **must** see CTA button "Get started", which clicking launches the get wallet flow (wallet connection window with links to the Chrome and FF stores)
|
||||
- Has wallet - Once wallet detected set to this.
|
||||
- I **must** see CTA button "Connect", which clicking launches the wallet connection
|
||||
- Has connected - Once user has connected set to this.
|
||||
- I **must** see CTA button "Deposit", which clicking launches the deposit ticket.
|
||||
- Has deposited - Once user has made AT LEAST one deposit of ANY settlement asset set to this.
|
||||
- I **must** see CTA button "Dismiss", which clicking updates the state to Ready to trade - Get started box should now disappear forever.
|
||||
- Ready to trade - Once user has made at least one deposit AND has dismissed the "Get Started" box in the ticket.
|
||||
- Onboarding window nor contextual "Get started" banner should be not displayed anymore.
|
||||
|
||||
## When first enter the app or next times, but I didn't accomplish all onboarding steps.
|
||||
|
||||
- **Must** When I open Console for the first time I can see what it is i.e. a short description and key features in auto opened dialog window (first use popup) (<a name="0007-FUGS-001" href="#0007-FUGS-001">0007-FUGS-001</a>)
|
||||
- **Must** If my wallet is already connected I don't see the first use popup (<a name="0007-FUGS-002" href="#0007-FUGS-002">0007-FUGS-002</a>)
|
||||
- - **Must** If window.vega is detected (browser wallet is installed), don't open first use popup (<a name="0007-FUGS-003" href="#0007-FUGS-003">0007-FUGS-003</a>)
|
||||
- - **Must** If we detect previous connection using localStorage for desktop/cli wallet, don't open first use popup (<a name="0007-FUGS-004" href="#0007-FUGS-004">0007-FUGS-004</a>)
|
||||
- If full "onboarding" hasn't been accomplished yet, I **must** see the popup with my progress marked. (<a name="0007-FUGS-002" href="#0007-FUGS-002">0007-FUGS-002</a>)
|
||||
- **Must** There is a call to action to browse markets, linking to the market view market/all (<a name="0007-FUGS-005" href="#0007-FUGS-005">0007-FUGS-005</a>)
|
||||
- **Must** I can see the steps I need to take to get started trading (<a name="0007-FUGS-007" href="#0007-FUGS-006">0007-FUGS-006</a>)
|
||||
- **Must** There is a call to action to get started, triggering the connect modal (<a name="0007-FUGS-007" href="#0007-FUGS-007">0007-FUGS-007</a>)
|
||||
- **Must** There is a link to try out trading on Fairground when I'm on Mainnet (<a name="0007-FUGS-008" href="#0007-FUGS-008">0007-FUGS-008</a>)
|
||||
- **Must** There is a link to trade with real funds on Mainnet when I am on Fairground (<a name="0007-FUGS-010" href="#0007-FUGS-010">0007-FUGS-010</a>)
|
||||
- **Must** When I am on the Fairground version, I can see a warning / call out that this is Fairground meaning I can try out with virtual assets at no risk (<a name="0007-FUGS-011" href="#0007-FUGS-011">0007-FUGS-011</a>)
|
||||
- If I dismiss the popup, I **must** not see it unless I NOT accomplish full "onboarding"
|
||||
- If I dismiss the popup, I land on the default market (<a name="0007-FUGS-012" href="#0007-FUGS-012">0007-FUGS-012</a>)
|
||||
|
||||
## When first use popup has been seen, but no browser wallet is installed
|
||||
## When the popup has been dismissed:
|
||||
|
||||
- **Must** I can see the steps to get started with a visible call to action to "get started" in the context of the deal ticket, deposit, withdraw, transfer components in the sidebar (<a name="0007-FUGS-013" href="#0007-FUGS-013">0007-FUGS-013</a>)
|
||||
- **Must** I can see the steps to get started with a visible call to action (according to my progress) in the context of the deal ticket, deposit, withdraw, transfer components in the sidebar (<a name="0007-FUGS-013" href="#0007-FUGS-013">0007-FUGS-013</a>)
|
||||
- **Must** Remove buttons from pane containers that prompt to connect wallet (<a name="0007-FUGS-014" href="#0007-FUGS-014">0007-FUGS-014</a>)
|
||||
- **Must** We've replaced "connect wallet" in the top right with "get started" (<a name="0007-FUGS-015" href="#0007-FUGS-015">0007-FUGS-015</a>)
|
||||
- **Must** When I press the get started CTA, I see the wallet connect popup (<a name="0007-FUGS-016" href="#0007-FUGS-016">0007-FUGS-016</a>)
|
||||
|
||||
@@ -53,7 +53,6 @@ for the a given Ethereum wallet/address/key:
|
||||
- **must** see how many tokens in each tranche are locked <a name="1005-VEST-025" href="#1005-VEST-025">1005-VEST-025</a>
|
||||
- **must** see how many tokens in each tranche are redeemable <a name="1005-VEST-026" href="#1005-VEST-026">1005-VEST-026</a>
|
||||
- **must** see an option to redeem from tranche <a name="1005-VEST-027" href="#1005-VEST-027">1005-VEST-027</a>
|
||||
- **must** be warned if amount that can be redeemed from that tranche is greater than the un-associated balance for that Eth key (because this will cause the redeem function to fail) <a name="1005-VEST-028" href="#1005-VEST-028">1005-VEST-028</a>
|
||||
- **should** see how many tokens I'd need to disassociate to be able to run the redeem function (this should be rounded up to avoid the transaction failing due to more tokens having unlocked since the user looked at the form)
|
||||
- **should** see link to [disassociate](1004-ASSO-associate.md)
|
||||
|
||||
|
||||
@@ -51,4 +51,8 @@
|
||||
|
||||
- **Must** be able to see if your realised PnL was affected by loss socialisation (<a name="7004-POSI-018" href="#7004-POSI-018">7004-POSI-018</a>)
|
||||
|
||||
- **Must** Must be able to see what type of product the position was opened on (<a name="7004-POSI-019" href="#7004-POSI-019">7004-POSI-019</a>)
|
||||
- **Must** be able to see what type of product the position was opened on (<a name="7004-POSI-019" href="#7004-POSI-019">7004-POSI-019</a>)
|
||||
|
||||
- **Must** not see positions on markets which are closed (<a name="7004-POSI-020" href="#7004-POSI-020">7004-POSI-020</a>)
|
||||
|
||||
- **Must** be able to show closed markets (<a name="7004-POSI-021" href="#7004-POSI-021">7004-POSI-021</a>)
|
||||
|
||||
Reference in New Issue
Block a user