Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79ce55f160 | ||
|
|
5ffc67aff3 | ||
|
|
9880aea753 | ||
|
|
c2901bb27d | ||
|
|
b3b10c83fb | ||
|
|
38904b6442 | ||
|
|
98ed5778a3 |
+2
-1
@@ -1,2 +1,3 @@
|
|||||||
* @vegaprotocol/frontend @vegaprotocol/frontend-qa
|
* @vegaprotocol/frontend
|
||||||
|
* @vegaprotocol/frontend-qa
|
||||||
*.graphql @vegaprotocol/core
|
*.graphql @vegaprotocol/core
|
||||||
|
|||||||
@@ -718,8 +718,6 @@
|
|||||||
"UpdateAssetProposal": "Update asset proposal",
|
"UpdateAssetProposal": "Update asset proposal",
|
||||||
"NewFreeformProposal": "New freeform proposal",
|
"NewFreeformProposal": "New freeform proposal",
|
||||||
"NewRawProposal": "New proposal",
|
"NewRawProposal": "New proposal",
|
||||||
"NewTransferProposal": "New transfer proposal",
|
|
||||||
"CancelTransferProposal": "Cancel transfer proposal",
|
|
||||||
"MARKET_STATE_UPDATE_TYPE_RESUME": "Resume market",
|
"MARKET_STATE_UPDATE_TYPE_RESUME": "Resume market",
|
||||||
"MARKET_STATE_UPDATE_TYPE_SUSPEND": "Suspend market",
|
"MARKET_STATE_UPDATE_TYPE_SUSPEND": "Suspend market",
|
||||||
"MARKET_STATE_UPDATE_TYPE_TERMINATE": "Terminate market",
|
"MARKET_STATE_UPDATE_TYPE_TERMINATE": "Terminate market",
|
||||||
@@ -740,8 +738,6 @@
|
|||||||
"UpdateVolumeDiscountProgram": "Update volume discount program",
|
"UpdateVolumeDiscountProgram": "Update volume discount program",
|
||||||
"NewAsset": "New asset",
|
"NewAsset": "New asset",
|
||||||
"UpdateAsset": "Update asset",
|
"UpdateAsset": "Update asset",
|
||||||
"NewTransfer": "New transfer",
|
|
||||||
"CancelTransfer": "Cancel transfer",
|
|
||||||
"AssetID": "Asset ID",
|
"AssetID": "Asset ID",
|
||||||
"Freeform": "Freeform",
|
"Freeform": "Freeform",
|
||||||
"RawProposal": "Let me choose (raw proposal)",
|
"RawProposal": "Let me choose (raw proposal)",
|
||||||
@@ -918,31 +914,5 @@
|
|||||||
"EndOfProgramTimestamp": "End of program",
|
"EndOfProgramTimestamp": "End of program",
|
||||||
"EndOfProgramTimestampDescription": "Time after which when the current epoch ends, the programs will end and benefits will be disabled.",
|
"EndOfProgramTimestampDescription": "Time after which when the current epoch ends, the programs will end and benefits will be disabled.",
|
||||||
"BenefitTierVolumeDiscountFactor": "Volume discount factor",
|
"BenefitTierVolumeDiscountFactor": "Volume discount factor",
|
||||||
"BenefitTierVolumeDiscountFactorDescription": "Discount given to those in this benefit tier",
|
"BenefitTierVolumeDiscountFactorDescription": "Discount given to those in this benefit tier"
|
||||||
"ACCOUNT_TYPE_INSURANCE": "Insurance account",
|
|
||||||
"ACCOUNT_TYPE_GLOBAL_INSURANCE": "Global insurance account",
|
|
||||||
"ACCOUNT_TYPE_SETTLEMENT": "Settlement account",
|
|
||||||
"ACCOUNT_TYPE_MARGIN": "Margin account ",
|
|
||||||
"ACCOUNT_TYPE_GENERAL": "General account",
|
|
||||||
"ACCOUNT_TYPE_FEES_INFRASTRUCTURE": "Infrastructure fees account",
|
|
||||||
"ACCOUNT_TYPE_FEES_LIQUIDITY": "Liquidity fees account",
|
|
||||||
"ACCOUNT_TYPE_FEES_MAKER": "Maker fees account",
|
|
||||||
"ACCOUNT_TYPE_BOND": "Bond account",
|
|
||||||
"ACCOUNT_TYPE_EXTERNAL": "External account",
|
|
||||||
"ACCOUNT_TYPE_GLOBAL_REWARD": "Global reward account",
|
|
||||||
"ACCOUNT_TYPE_PENDING_TRANSFERS": "Pending transfers account",
|
|
||||||
"ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES": "Maker paid fees reward account",
|
|
||||||
"ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES": "Maker received fees reward account",
|
|
||||||
"ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES": "Liquidity provider received fees reward account",
|
|
||||||
"ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS": "Market proposers reward account",
|
|
||||||
"ACCOUNT_TYPE_HOLDING": "Holding account",
|
|
||||||
"ACCOUNT_TYPE_LP_LIQUIDITY_FEES": "Liquidity provider fees account",
|
|
||||||
"ACCOUNT_TYPE_NETWORK_TREASURY": "Network treasury account",
|
|
||||||
"ACCOUNT_TYPE_VESTING_REWARDS": "Vesting rewards account",
|
|
||||||
"ACCOUNT_TYPE_VESTED_REWARDS": "Vested rewards account",
|
|
||||||
"ACCOUNT_TYPE_REWARD_AVERAGE_POSITION": "Average position reward account",
|
|
||||||
"ACCOUNT_TYPE_REWARD_RELATIVE_RETURN": "Relative return reward account",
|
|
||||||
"ACCOUNT_TYPE_REWARD_RETURN_VOLATILITY": "Return volatility reward account",
|
|
||||||
"ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING": "Validator ranking reward account",
|
|
||||||
"ACCOUNT_TYPE_PENDING_FEE_REFERRAL_REWARD": "Pending fee referral reward account"
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-4
@@ -263,10 +263,7 @@ const NewTransferSummary = ({ proposalId }: { proposalId?: string | null }) => {
|
|||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
{GovernanceTransferKindMapping[details.kind.__typename]}{' '}
|
{GovernanceTransferKindMapping[details.kind.__typename]}{' '}
|
||||||
{t('transfer from')}{' '}
|
{t('transfer from')} <Lozenge>{truncateMiddle(details.source)}</Lozenge>{' '}
|
||||||
<Lozenge>
|
|
||||||
{truncateMiddle(details.source) || t(details.sourceType)}
|
|
||||||
</Lozenge>{' '}
|
|
||||||
{t('to')} <Lozenge>{truncateMiddle(details.destination)}</Lozenge>
|
{t('to')} <Lozenge>{truncateMiddle(details.destination)}</Lozenge>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|||||||
+39
-36
@@ -63,42 +63,44 @@ const mockReferralProposal = generateProposal({
|
|||||||
terms: {
|
terms: {
|
||||||
change: {
|
change: {
|
||||||
__typename: 'UpdateReferralProgram',
|
__typename: 'UpdateReferralProgram',
|
||||||
benefitTiers: [
|
changes: {
|
||||||
{
|
benefitTiers: [
|
||||||
minimumEpochs: 6,
|
{
|
||||||
minimumRunningNotionalTakerVolume: '10000',
|
minimumEpochs: 6,
|
||||||
referralDiscountFactor: '0.001',
|
minimumRunningNotionalTakerVolume: '10000',
|
||||||
referralRewardFactor: '0.001',
|
referralDiscountFactor: '0.001',
|
||||||
},
|
referralRewardFactor: '0.001',
|
||||||
{
|
},
|
||||||
minimumEpochs: 24,
|
{
|
||||||
minimumRunningNotionalTakerVolume: '500000',
|
minimumEpochs: 24,
|
||||||
referralDiscountFactor: '0.005',
|
minimumRunningNotionalTakerVolume: '500000',
|
||||||
referralRewardFactor: '0.005',
|
referralDiscountFactor: '0.005',
|
||||||
},
|
referralRewardFactor: '0.005',
|
||||||
{
|
},
|
||||||
minimumEpochs: 48,
|
{
|
||||||
minimumRunningNotionalTakerVolume: '1000000',
|
minimumEpochs: 48,
|
||||||
referralDiscountFactor: '0.01',
|
minimumRunningNotionalTakerVolume: '1000000',
|
||||||
referralRewardFactor: '0.01',
|
referralDiscountFactor: '0.01',
|
||||||
},
|
referralRewardFactor: '0.01',
|
||||||
],
|
},
|
||||||
endOfProgram: '2026-10-03T10:34:34Z',
|
],
|
||||||
windowLength: 3,
|
endOfProgramTimestamp: '2026-10-03T10:34:34Z',
|
||||||
stakingTiers: [
|
windowLength: 3,
|
||||||
{
|
stakingTiers: [
|
||||||
minimumStakedTokens: '1',
|
{
|
||||||
referralRewardMultiplier: '1',
|
minimumStakedTokens: '1',
|
||||||
},
|
referralRewardMultiplier: '1',
|
||||||
{
|
},
|
||||||
minimumStakedTokens: '2',
|
{
|
||||||
referralRewardMultiplier: '2',
|
minimumStakedTokens: '2',
|
||||||
},
|
referralRewardMultiplier: '2',
|
||||||
{
|
},
|
||||||
minimumStakedTokens: '5',
|
{
|
||||||
referralRewardMultiplier: '3',
|
minimumStakedTokens: '5',
|
||||||
},
|
referralRewardMultiplier: '3',
|
||||||
],
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -130,6 +132,7 @@ describe('<ProposalReferralProgramDetails />', () => {
|
|||||||
terms: {
|
terms: {
|
||||||
change: {
|
change: {
|
||||||
__typename: 'UpdateReferralProgram',
|
__typename: 'UpdateReferralProgram',
|
||||||
|
changes: {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
+5
-4
@@ -54,10 +54,11 @@ export const ProposalReferralProgramDetails = ({
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const benefitTiers = proposal?.terms?.change?.benefitTiers;
|
const benefitTiers = proposal?.terms?.change?.changes?.benefitTiers;
|
||||||
const stakingTiers = proposal?.terms?.change?.stakingTiers;
|
const stakingTiers = proposal?.terms?.change?.changes?.stakingTiers;
|
||||||
const windowLength = proposal?.terms?.change?.windowLength;
|
const windowLength = proposal?.terms?.change?.changes?.windowLength;
|
||||||
const endOfProgramTimestamp = proposal?.terms?.change?.endOfProgram;
|
const endOfProgramTimestamp =
|
||||||
|
proposal?.terms?.change?.changes?.endOfProgramTimestamp;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!benefitTiers &&
|
!benefitTiers &&
|
||||||
|
|||||||
@@ -30,8 +30,6 @@ export const useProposalNetworkParams = ({
|
|||||||
NetworkParams.governance_proposal_freeform_requiredParticipation,
|
NetworkParams.governance_proposal_freeform_requiredParticipation,
|
||||||
NetworkParams.governance_proposal_VolumeDiscountProgram_requiredMajority,
|
NetworkParams.governance_proposal_VolumeDiscountProgram_requiredMajority,
|
||||||
NetworkParams.governance_proposal_VolumeDiscountProgram_requiredParticipation,
|
NetworkParams.governance_proposal_VolumeDiscountProgram_requiredParticipation,
|
||||||
NetworkParams.governance_proposal_transfer_requiredParticipation,
|
|
||||||
NetworkParams.governance_proposal_transfer_requiredMajority,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const fallback = {
|
const fallback = {
|
||||||
@@ -113,14 +111,6 @@ export const useProposalNetworkParams = ({
|
|||||||
params.governance_proposal_VolumeDiscountProgram_requiredParticipation
|
params.governance_proposal_VolumeDiscountProgram_requiredParticipation
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
case 'NewTransfer':
|
|
||||||
case 'CancelTransfer':
|
|
||||||
return {
|
|
||||||
requiredMajority: params.governance_proposal_transfer_requiredMajority,
|
|
||||||
requiredParticipation: new BigNumber(
|
|
||||||
params.governance_proposal_transfer_requiredParticipation
|
|
||||||
),
|
|
||||||
};
|
|
||||||
default:
|
default:
|
||||||
return fallback;
|
return fallback;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,17 +47,19 @@ fragment UpdateReferralProgram on Proposal {
|
|||||||
terms {
|
terms {
|
||||||
change {
|
change {
|
||||||
... on UpdateReferralProgram {
|
... on UpdateReferralProgram {
|
||||||
benefitTiers {
|
changes {
|
||||||
minimumEpochs
|
benefitTiers {
|
||||||
minimumRunningNotionalTakerVolume
|
minimumEpochs
|
||||||
referralDiscountFactor
|
minimumRunningNotionalTakerVolume
|
||||||
referralRewardFactor
|
referralDiscountFactor
|
||||||
}
|
referralRewardFactor
|
||||||
endOfProgram: endOfProgramTimestamp
|
}
|
||||||
windowLength
|
endOfProgramTimestamp
|
||||||
stakingTiers {
|
windowLength
|
||||||
minimumStakedTokens
|
stakingTiers {
|
||||||
referralRewardMultiplier
|
minimumStakedTokens
|
||||||
|
referralRewardMultiplier
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -47,17 +47,19 @@ fragment UpdateReferralPrograms on Proposal {
|
|||||||
terms {
|
terms {
|
||||||
change {
|
change {
|
||||||
... on UpdateReferralProgram {
|
... on UpdateReferralProgram {
|
||||||
benefitTiers {
|
changes {
|
||||||
minimumEpochs
|
benefitTiers {
|
||||||
minimumRunningNotionalTakerVolume
|
minimumEpochs
|
||||||
referralDiscountFactor
|
minimumRunningNotionalTakerVolume
|
||||||
referralRewardFactor
|
referralDiscountFactor
|
||||||
}
|
referralRewardFactor
|
||||||
endOfProgram: endOfProgramTimestamp
|
}
|
||||||
windowLength
|
endOfProgramTimestamp
|
||||||
stakingTiers {
|
windowLength
|
||||||
minimumStakedTokens
|
stakingTiers {
|
||||||
referralRewardMultiplier
|
minimumStakedTokens
|
||||||
|
referralRewardMultiplier
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-13
@@ -7,7 +7,7 @@ export type NewMarketProductFieldsFragment = { __typename?: 'Proposal', terms: {
|
|||||||
|
|
||||||
export type UpdateMarketStatesFragment = { __typename?: 'Proposal', terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState', updateType: Types.MarketUpdateType, price?: string | null, market: { __typename?: 'Market', decimalPlaces: number, id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string, product: { __typename: 'Future', quoteName: string } | { __typename: 'Perpetual', quoteName: string } | { __typename: 'Spot' } } } } } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } } };
|
export type UpdateMarketStatesFragment = { __typename?: 'Proposal', terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState', updateType: Types.MarketUpdateType, price?: string | null, market: { __typename?: 'Market', decimalPlaces: number, id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string, product: { __typename: 'Future', quoteName: string } | { __typename: 'Perpetual', quoteName: string } | { __typename: 'Spot' } } } } } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } } };
|
||||||
|
|
||||||
export type UpdateReferralProgramsFragment = { __typename?: 'Proposal', terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram', windowLength: number, endOfProgram: string, benefitTiers: Array<{ __typename?: 'BenefitTier', minimumEpochs: number, minimumRunningNotionalTakerVolume: string, referralDiscountFactor: string, referralRewardFactor: string }>, stakingTiers: Array<{ __typename?: 'StakingTier', minimumStakedTokens: string, referralRewardMultiplier: string }> } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } } };
|
export type UpdateReferralProgramsFragment = { __typename?: 'Proposal', terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram', changes: { __typename?: 'ReferralProgram', endOfProgramTimestamp: string, windowLength: number, benefitTiers: Array<{ __typename?: 'BenefitTier', minimumEpochs: number, minimumRunningNotionalTakerVolume: string, referralDiscountFactor: string, referralRewardFactor: string }>, stakingTiers: Array<{ __typename?: 'StakingTier', minimumStakedTokens: string, referralRewardMultiplier: string }> } } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } } };
|
||||||
|
|
||||||
export type UpdateVolumeDiscountProgramsFragment = { __typename?: 'Proposal', terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram', endOfProgramTimestamp: any, windowLength: number, benefitTiers: Array<{ __typename?: 'VolumeBenefitTier', minimumRunningNotionalTakerVolume: string, volumeDiscountFactor: string }> } } };
|
export type UpdateVolumeDiscountProgramsFragment = { __typename?: 'Proposal', terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram', endOfProgramTimestamp: any, windowLength: number, benefitTiers: Array<{ __typename?: 'VolumeBenefitTier', minimumRunningNotionalTakerVolume: string, volumeDiscountFactor: string }> } } };
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ export type ProposalsQueryVariables = Types.Exact<{
|
|||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
export type ProposalsQuery = { __typename?: 'Query', proposalsConnection?: { __typename?: 'ProposalsConnection', edges?: Array<{ __typename?: 'ProposalEdge', node: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename?: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | null, product?: { __typename: 'FutureProduct' } | { __typename: 'PerpetualProduct' } | { __typename: 'SpotProduct' } | null } } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateMarketState', updateType: Types.MarketUpdateType, price?: string | null, market: { __typename?: 'Market', decimalPlaces: number, id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string, product: { __typename: 'Future', quoteName: string } | { __typename: 'Perpetual', quoteName: string } | { __typename: 'Spot' } } } } } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } | { __typename?: 'UpdateReferralProgram', windowLength: number, endOfProgram: string, benefitTiers: Array<{ __typename?: 'BenefitTier', minimumEpochs: number, minimumRunningNotionalTakerVolume: string, referralDiscountFactor: string, referralRewardFactor: string }>, stakingTiers: Array<{ __typename?: 'StakingTier', minimumStakedTokens: string, referralRewardMultiplier: string }> } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram', endOfProgramTimestamp: any, windowLength: number, benefitTiers: Array<{ __typename?: 'VolumeBenefitTier', minimumRunningNotionalTakerVolume: string, volumeDiscountFactor: string }> } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } } } | null> | null } | null };
|
export type ProposalsQuery = { __typename?: 'Query', proposalsConnection?: { __typename?: 'ProposalsConnection', edges?: Array<{ __typename?: 'ProposalEdge', node: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename?: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | null, product?: { __typename: 'FutureProduct' } | { __typename: 'PerpetualProduct' } | { __typename: 'SpotProduct' } | null } } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateMarketState', updateType: Types.MarketUpdateType, price?: string | null, market: { __typename?: 'Market', decimalPlaces: number, id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string, product: { __typename: 'Future', quoteName: string } | { __typename: 'Perpetual', quoteName: string } | { __typename: 'Spot' } } } } } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } | { __typename?: 'UpdateReferralProgram', changes: { __typename?: 'ReferralProgram', endOfProgramTimestamp: string, windowLength: number, benefitTiers: Array<{ __typename?: 'BenefitTier', minimumEpochs: number, minimumRunningNotionalTakerVolume: string, referralDiscountFactor: string, referralRewardFactor: string }>, stakingTiers: Array<{ __typename?: 'StakingTier', minimumStakedTokens: string, referralRewardMultiplier: string }> } } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram', endOfProgramTimestamp: any, windowLength: number, benefitTiers: Array<{ __typename?: 'VolumeBenefitTier', minimumRunningNotionalTakerVolume: string, volumeDiscountFactor: string }> } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } } } | null> | null } | null };
|
||||||
|
|
||||||
export const NewMarketProductFieldsFragmentDoc = gql`
|
export const NewMarketProductFieldsFragmentDoc = gql`
|
||||||
fragment NewMarketProductFields on Proposal {
|
fragment NewMarketProductFields on Proposal {
|
||||||
@@ -75,17 +75,19 @@ export const UpdateReferralProgramsFragmentDoc = gql`
|
|||||||
terms {
|
terms {
|
||||||
change {
|
change {
|
||||||
... on UpdateReferralProgram {
|
... on UpdateReferralProgram {
|
||||||
benefitTiers {
|
changes {
|
||||||
minimumEpochs
|
benefitTiers {
|
||||||
minimumRunningNotionalTakerVolume
|
minimumEpochs
|
||||||
referralDiscountFactor
|
minimumRunningNotionalTakerVolume
|
||||||
referralRewardFactor
|
referralDiscountFactor
|
||||||
}
|
referralRewardFactor
|
||||||
endOfProgram: endOfProgramTimestamp
|
}
|
||||||
windowLength
|
endOfProgramTimestamp
|
||||||
stakingTiers {
|
windowLength
|
||||||
minimumStakedTokens
|
stakingTiers {
|
||||||
referralRewardMultiplier
|
minimumStakedTokens
|
||||||
|
referralRewardMultiplier
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,201 @@
|
|||||||
|
import { removeDecimal } from '@vegaprotocol/cypress';
|
||||||
|
import { ethers } from 'ethers';
|
||||||
|
import { connectEthereumWallet } from '../support/ethereum-wallet';
|
||||||
|
import { selectAsset } from '../support/helpers';
|
||||||
|
|
||||||
|
const assetSelectField = 'select[name="asset"]';
|
||||||
|
const toAddressField = 'input[name="to"]';
|
||||||
|
const amountField = 'input[name="amount"]';
|
||||||
|
const formFieldError = 'input-error-text';
|
||||||
|
|
||||||
|
const ASSET_EURO = 1;
|
||||||
|
|
||||||
|
describe('deposit form validation', { tags: '@smoke' }, () => {
|
||||||
|
function openDepositForm() {
|
||||||
|
cy.mockWeb3Provider();
|
||||||
|
cy.mockSubscription();
|
||||||
|
cy.mockTradingPage();
|
||||||
|
cy.setVegaWallet();
|
||||||
|
cy.visit('/#/portfolio');
|
||||||
|
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
||||||
|
cy.getByTestId('Deposits').click();
|
||||||
|
cy.getByTestId('deposit-button').click();
|
||||||
|
connectEthereumWallet('MetaMask');
|
||||||
|
cy.wait('@Assets');
|
||||||
|
}
|
||||||
|
|
||||||
|
before(() => {
|
||||||
|
openDepositForm();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles empty fields', () => {
|
||||||
|
cy.getByTestId('deposit-submit').click();
|
||||||
|
cy.getByTestId(formFieldError).should('contain.text', 'Required');
|
||||||
|
// once Ethereum wallet is connected and key selected the only field that will
|
||||||
|
// error is the asset select
|
||||||
|
cy.getByTestId(formFieldError).should('have.length', 1);
|
||||||
|
cy.get('[data-testid="input-error-text"][aria-describedby="asset"]').should(
|
||||||
|
'have.length',
|
||||||
|
1
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('unable to select assets not enabled', () => {
|
||||||
|
// Assets not enabled in mocks
|
||||||
|
cy.get(assetSelectField + ' option:contains(Asset 2)').should('not.exist');
|
||||||
|
cy.get(assetSelectField + ' option:contains(Asset 3)').should('not.exist');
|
||||||
|
cy.get(assetSelectField + ' option:contains(Asset 4)').should('not.exist');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('invalid public key when entering address manually', () => {
|
||||||
|
cy.getByTestId('enter-pubkey-manually').click();
|
||||||
|
cy.get(toAddressField).clear().type('INVALID_DEPOSIT_TO_ADDRESS');
|
||||||
|
cy.get(`[data-testid="${formFieldError}"][aria-describedby="to"]`).should(
|
||||||
|
'have.text',
|
||||||
|
'Invalid Vega key'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('invalid amount', () => {
|
||||||
|
mockWeb3DepositCalls({
|
||||||
|
allowance: '1000',
|
||||||
|
depositLifetimeLimit: '1000',
|
||||||
|
balance: '800',
|
||||||
|
deposited: '0',
|
||||||
|
dps: 5,
|
||||||
|
});
|
||||||
|
// Deposit amount smaller than minimum viable for selected asset
|
||||||
|
// Select an amount so that we have a known decimal places value to work with
|
||||||
|
selectAsset(ASSET_EURO);
|
||||||
|
|
||||||
|
cy.get(amountField)
|
||||||
|
.clear()
|
||||||
|
.type('0.00000000000000000000000000000000001')
|
||||||
|
.next(`[data-testid="${formFieldError}"]`)
|
||||||
|
.should('have.text', 'Value is below minimum');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('insufficient funds', () => {
|
||||||
|
// 1001-DEPO-004
|
||||||
|
mockWeb3DepositCalls({
|
||||||
|
allowance: '1000',
|
||||||
|
depositLifetimeLimit: '1000',
|
||||||
|
balance: '800',
|
||||||
|
deposited: '0',
|
||||||
|
dps: 5,
|
||||||
|
});
|
||||||
|
cy.get(amountField)
|
||||||
|
.clear()
|
||||||
|
.type('850')
|
||||||
|
.next(`[data-testid="${formFieldError}"]`)
|
||||||
|
.should(
|
||||||
|
'have.text',
|
||||||
|
"You can't deposit more than you have in your Ethereum wallet, 800 tEURO"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('deposit actions', { tags: '@smoke' }, () => {
|
||||||
|
before(() => {
|
||||||
|
cy.mockWeb3Provider();
|
||||||
|
cy.mockTradingPage();
|
||||||
|
cy.mockSubscription();
|
||||||
|
cy.setVegaWallet();
|
||||||
|
cy.visit('/#/markets/market-1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.skip('Deposit to trade is visible', () => {
|
||||||
|
cy.getByTestId('Collateral').click();
|
||||||
|
cy.get('[row-id="asset-id"]').contains('tEURO').should('be.visible');
|
||||||
|
cy.contains('[data-testid="deposit"]', 'Deposit').should('be.visible');
|
||||||
|
cy.contains('[data-testid="deposit"]', 'Deposit').click();
|
||||||
|
cy.getByTestId('deposit-submit').should('be.visible');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function mockWeb3DepositCalls({
|
||||||
|
allowance,
|
||||||
|
depositLifetimeLimit,
|
||||||
|
balance,
|
||||||
|
deposited,
|
||||||
|
dps,
|
||||||
|
}: {
|
||||||
|
allowance: string;
|
||||||
|
depositLifetimeLimit: string;
|
||||||
|
balance: string;
|
||||||
|
deposited: string;
|
||||||
|
dps: number;
|
||||||
|
}) {
|
||||||
|
const assetContractAddress = '0x0158031158bb4df2ad02eaa31e8963e84ea978a4';
|
||||||
|
const collateralBridgeAddress = '0x7fe27d970bc8afc3b11cc8d9737bfb66b1efd799';
|
||||||
|
const toResult = (value: string, dps: number) => {
|
||||||
|
const rawValue = removeDecimal(value, dps);
|
||||||
|
return ethers.utils.hexZeroPad(
|
||||||
|
ethers.utils.hexlify(parseInt(rawValue)),
|
||||||
|
32
|
||||||
|
);
|
||||||
|
};
|
||||||
|
cy.intercept('POST', 'http://localhost:8545', (req) => {
|
||||||
|
// Mock chainId call
|
||||||
|
if (req.body.method === 'eth_chainId') {
|
||||||
|
req.alias = 'eth_chainId';
|
||||||
|
req.reply({
|
||||||
|
id: req.body.id,
|
||||||
|
jsonrpc: req.body.jsonrpc,
|
||||||
|
result: '0xaa36a7', // 11155111 for sepolia chain id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mock deposited amount
|
||||||
|
if (req.body.method === 'eth_getStorageAt') {
|
||||||
|
req.alias = 'eth_getStorageAt';
|
||||||
|
req.reply({
|
||||||
|
id: req.body.id,
|
||||||
|
jsonrpc: req.body.jsonrpc,
|
||||||
|
result: toResult(deposited, dps),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.body.method === 'eth_call') {
|
||||||
|
// Mock approved amount for asset on collateral bridge
|
||||||
|
if (
|
||||||
|
req.body.params[0].to === assetContractAddress &&
|
||||||
|
req.body.params[0].data ===
|
||||||
|
'0xdd62ed3e000000000000000000000000ee7d375bcb50c26d52e1a4a472d8822a2a22d94f0000000000000000000000007fe27d970bc8afc3b11cc8d9737bfb66b1efd799'
|
||||||
|
) {
|
||||||
|
req.alias = 'eth_call_allowance';
|
||||||
|
req.reply({
|
||||||
|
id: req.body.id,
|
||||||
|
jsonrpc: req.body.jsonrpc,
|
||||||
|
result: toResult(allowance, dps),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Mock balance of asset in Ethereum wallet
|
||||||
|
else if (
|
||||||
|
req.body.params[0].to === assetContractAddress &&
|
||||||
|
req.body.params[0].data ===
|
||||||
|
'0x70a08231000000000000000000000000ee7d375bcb50c26d52e1a4a472d8822a2a22d94f'
|
||||||
|
) {
|
||||||
|
req.alias = 'eth_call_balanceOf';
|
||||||
|
req.reply({
|
||||||
|
id: req.body.id,
|
||||||
|
jsonrpc: req.body.jsonrpc,
|
||||||
|
result: toResult(balance, dps),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Mock deposit lifetime limit
|
||||||
|
else if (
|
||||||
|
req.body.params[0].to === collateralBridgeAddress &&
|
||||||
|
req.body.params[0].data ===
|
||||||
|
'0x354a897a0000000000000000000000000158031158bb4df2ad02eaa31e8963e84ea978a4'
|
||||||
|
) {
|
||||||
|
req.alias = 'eth_call_get_deposit_maximum'; // deposit lifetime limit
|
||||||
|
req.reply({
|
||||||
|
id: req.body.id,
|
||||||
|
jsonrpc: req.body.jsonrpc,
|
||||||
|
result: toResult(depositLifetimeLimit, dps),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import { selectAsset } from '../support/helpers';
|
||||||
|
|
||||||
|
const amountField = 'input[name="amount"]';
|
||||||
|
const transferText = 'transfer-intro-text';
|
||||||
|
const errorText = 'input-error-text';
|
||||||
|
const formFieldError = 'input-error-text';
|
||||||
|
const keyID = `[data-testid="${transferText}"] > .rounded-md`;
|
||||||
|
const manageVegaWallet = 'manage-vega-wallet';
|
||||||
|
const submitTransferBtn = '[type="submit"]';
|
||||||
|
const toAddressField = '[name="toAddress"]';
|
||||||
|
const transferForm = 'transfer-form';
|
||||||
|
const walletTransfer = 'wallet-transfer';
|
||||||
|
|
||||||
|
const ASSET_EURO = 1;
|
||||||
|
const ASSET_SEPOLIA_TBTC = 2;
|
||||||
|
|
||||||
|
describe(
|
||||||
|
'transfer form validation',
|
||||||
|
{ tags: '@regression', testIsolation: true },
|
||||||
|
() => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.mockWeb3Provider();
|
||||||
|
cy.mockTradingPage();
|
||||||
|
cy.mockSubscription();
|
||||||
|
cy.setVegaWallet();
|
||||||
|
|
||||||
|
cy.visit('/#/portfolio');
|
||||||
|
cy.getByTestId(manageVegaWallet).click();
|
||||||
|
cy.getByTestId(walletTransfer).click();
|
||||||
|
|
||||||
|
cy.wait('@Accounts');
|
||||||
|
cy.wait('@Assets');
|
||||||
|
|
||||||
|
cy.mockVegaWalletTransaction();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('transfer Text', () => {
|
||||||
|
// 1003-TRAN-003
|
||||||
|
cy.getByTestId(transferText)
|
||||||
|
.should('exist')
|
||||||
|
.get(keyID)
|
||||||
|
.invoke('text')
|
||||||
|
.should('match', /[\w.]{6}…[\w.]{6}/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('invalid vega key validation', () => {
|
||||||
|
//1003-TRAN-013
|
||||||
|
//1003-TRAN-004
|
||||||
|
cy.getByTestId(transferForm).should('be.visible');
|
||||||
|
cy.contains('Enter manually').click();
|
||||||
|
cy.getByTestId(transferForm).find(toAddressField).type('asd');
|
||||||
|
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||||
|
cy.getByTestId(formFieldError).should('contain.text', 'Invalid Vega key');
|
||||||
|
cy.contains('label', 'Vega key').should('be.visible');
|
||||||
|
cy.contains('label', 'Asset').should('be.visible');
|
||||||
|
cy.contains('label', 'Amount').should('be.visible');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('empty fields', () => {
|
||||||
|
// 1003-TRAN-012
|
||||||
|
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||||
|
cy.getByTestId(formFieldError).should('contain.text', 'Required');
|
||||||
|
cy.getByTestId(formFieldError).should('have.length', 3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('min amount', () => {
|
||||||
|
// 1002-WITH-010
|
||||||
|
// 1003-TRAN-014
|
||||||
|
selectAsset(ASSET_SEPOLIA_TBTC);
|
||||||
|
cy.get(amountField).clear().type('0');
|
||||||
|
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||||
|
cy.getByTestId(errorText).should(
|
||||||
|
'contain.text',
|
||||||
|
'Value is below minimum'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('max amount', () => {
|
||||||
|
// 1003-TRAN-002
|
||||||
|
// 1003-TRAN-011
|
||||||
|
// 1003-TRAN-002
|
||||||
|
selectAsset(ASSET_EURO); // Will be above maximum because the vega wallet doesn't have any collateral
|
||||||
|
cy.get(amountField).clear().type('1001', { delay: 100 });
|
||||||
|
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||||
|
cy.getByTestId(errorText).should(
|
||||||
|
'contain.text',
|
||||||
|
'You cannot transfer more than your available collateral'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import MarketPage from '../market';
|
||||||
|
|
||||||
|
export const ClosedMarketPage = () => {
|
||||||
|
return <MarketPage closed />;
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { ClosedMarketPage as default } from './closed-market';
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
import { VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import {
|
|
||||||
SidebarButton,
|
|
||||||
SidebarDivider,
|
|
||||||
ViewType,
|
|
||||||
} from '../../components/sidebar';
|
|
||||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
|
||||||
|
|
||||||
export const LiquiditySidebar = () => {
|
|
||||||
const currentRouteId = useGetCurrentRouteId();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<SidebarButton
|
|
||||||
view={ViewType.Deposit}
|
|
||||||
icon={VegaIconNames.DEPOSIT}
|
|
||||||
tooltip={t('Deposit')}
|
|
||||||
routeId={currentRouteId}
|
|
||||||
/>
|
|
||||||
<SidebarButton
|
|
||||||
view={ViewType.Withdraw}
|
|
||||||
icon={VegaIconNames.WITHDRAW}
|
|
||||||
tooltip={t('Withdraw')}
|
|
||||||
routeId={currentRouteId}
|
|
||||||
/>
|
|
||||||
<SidebarButton
|
|
||||||
view={ViewType.Transfer}
|
|
||||||
icon={VegaIconNames.TRANSFER}
|
|
||||||
tooltip={t('Transfer')}
|
|
||||||
routeId={currentRouteId}
|
|
||||||
/>
|
|
||||||
<SidebarDivider />
|
|
||||||
<SidebarButton
|
|
||||||
view={ViewType.Order}
|
|
||||||
icon={VegaIconNames.TICKET}
|
|
||||||
tooltip={t('Order')}
|
|
||||||
routeId={currentRouteId}
|
|
||||||
/>
|
|
||||||
<SidebarButton
|
|
||||||
view={ViewType.Info}
|
|
||||||
icon={VegaIconNames.BREAKDOWN}
|
|
||||||
tooltip={t('Market specification')}
|
|
||||||
routeId={currentRouteId}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -43,7 +43,7 @@ export const MarketHeaderStats = ({ market }: MarketHeaderStatsProps) => {
|
|||||||
const asset = getAsset(market);
|
const asset = getAsset(market);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="flex gap-8">
|
||||||
<HeaderStat heading={t('Mark Price')} testId="market-price">
|
<HeaderStat heading={t('Mark Price')} testId="market-price">
|
||||||
<MarketMarkPrice
|
<MarketMarkPrice
|
||||||
marketId={market.id}
|
marketId={market.id}
|
||||||
@@ -144,7 +144,7 @@ export const MarketHeaderStats = ({ market }: MarketHeaderStatsProps) => {
|
|||||||
</HeaderStat>
|
</HeaderStat>
|
||||||
)}
|
)}
|
||||||
<MarketProposalNotification marketId={market.id} />
|
<MarketProposalNotification marketId={market.id} />
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,10 @@ import { useGlobalStore, usePageTitleStore } from '../../stores';
|
|||||||
import { TradeGrid } from './trade-grid';
|
import { TradeGrid } from './trade-grid';
|
||||||
import { TradePanels } from './trade-panels';
|
import { TradePanels } from './trade-panels';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
import { Links } from '../../lib/links';
|
import { Links, Routes } from '../../lib/links';
|
||||||
import { ViewType, useSidebar } from '../../components/sidebar';
|
import { ViewType, useSidebar } from '../../components/sidebar';
|
||||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||||
|
import { MarketState } from '@vegaprotocol/types';
|
||||||
|
|
||||||
const calculatePrice = (markPrice?: string, decimalPlaces?: number) => {
|
const calculatePrice = (markPrice?: string, decimalPlaces?: number) => {
|
||||||
return markPrice && decimalPlaces
|
return markPrice && decimalPlaces
|
||||||
@@ -56,7 +57,7 @@ const TitleUpdater = ({
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MarketPage = () => {
|
export const MarketPage = ({ closed }: { closed?: boolean }) => {
|
||||||
const { marketId } = useParams();
|
const { marketId } = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const currentRouteId = useGetCurrentRouteId();
|
const currentRouteId = useGetCurrentRouteId();
|
||||||
@@ -69,11 +70,25 @@ export const MarketPage = () => {
|
|||||||
|
|
||||||
const { data, loading } = useMarket(marketId);
|
const { data, loading } = useMarket(marketId);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
data?.state &&
|
||||||
|
[
|
||||||
|
MarketState.STATE_SETTLED,
|
||||||
|
MarketState.STATE_TRADING_TERMINATED,
|
||||||
|
].includes(data.state) &&
|
||||||
|
currentRouteId !== Routes.CLOSED_MARKETS &&
|
||||||
|
marketId
|
||||||
|
) {
|
||||||
|
navigate(Links.CLOSED_MARKETS(marketId));
|
||||||
|
}
|
||||||
|
}, [data?.state, currentRouteId, navigate, marketId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (data?.id && data.id !== lastMarketId && !closed) {
|
if (data?.id && data.id !== lastMarketId && !closed) {
|
||||||
update({ marketId: data.id });
|
update({ marketId: data.id });
|
||||||
}
|
}
|
||||||
}, [update, lastMarketId, data?.id]);
|
}, [update, lastMarketId, data?.id, closed]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (largeScreen && view === undefined) {
|
if (largeScreen && view === undefined) {
|
||||||
@@ -82,7 +97,7 @@ export const MarketPage = () => {
|
|||||||
currentRouteId
|
currentRouteId
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [setViews, view, currentRouteId, largeScreen]);
|
}, [setViews, view, currentRouteId, largeScreen, closed]);
|
||||||
|
|
||||||
const pinnedAsset = data && getAsset(data);
|
const pinnedAsset = data && getAsset(data);
|
||||||
|
|
||||||
|
|||||||
@@ -73,19 +73,10 @@ const MainGrid = memo(
|
|||||||
{market &&
|
{market &&
|
||||||
market.tradableInstrument.instrument.product.__typename ===
|
market.tradableInstrument.instrument.product.__typename ===
|
||||||
'Perpetual' ? (
|
'Perpetual' ? (
|
||||||
<Tab id="funding-history" name={t('Funding history')}>
|
<Tab id="funding" name={t('Funding')}>
|
||||||
<TradingViews.funding.component marketId={marketId} />
|
<TradingViews.funding.component marketId={marketId} />
|
||||||
</Tab>
|
</Tab>
|
||||||
) : null}
|
) : null}
|
||||||
{market &&
|
|
||||||
market.tradableInstrument.instrument.product.__typename ===
|
|
||||||
'Perpetual' ? (
|
|
||||||
<Tab id="funding-payments" name={t('Funding payments')}>
|
|
||||||
<TradingViews.fundingPayments.component
|
|
||||||
marketId={marketId}
|
|
||||||
/>
|
|
||||||
</Tab>
|
|
||||||
) : null}
|
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</TradeGridChild>
|
</TradeGridChild>
|
||||||
</ResizableGridPanel>
|
</ResizableGridPanel>
|
||||||
@@ -148,6 +139,9 @@ const MainGrid = memo(
|
|||||||
<Tab id="fills" name={t('Fills')}>
|
<Tab id="fills" name={t('Fills')}>
|
||||||
<TradingViews.fills.component />
|
<TradingViews.fills.component />
|
||||||
</Tab>
|
</Tab>
|
||||||
|
<Tab id="funding-payments" name={t('Funding payments')}>
|
||||||
|
<TradingViews.fundingPayments.component />
|
||||||
|
</Tab>
|
||||||
<Tab
|
<Tab
|
||||||
id="accounts"
|
id="accounts"
|
||||||
name={t('Collateral')}
|
name={t('Collateral')}
|
||||||
|
|||||||
@@ -363,12 +363,12 @@ describe('Closed', () => {
|
|||||||
const container = within(
|
const container = within(
|
||||||
document.querySelector('.ag-center-cols-container') as HTMLElement
|
document.querySelector('.ag-center-cols-container') as HTMLElement
|
||||||
);
|
);
|
||||||
|
const cell = container.getAllByRole('gridcell', {
|
||||||
|
name: (_name, element) => element.getAttribute('col-id') === 'code',
|
||||||
|
})[0];
|
||||||
|
|
||||||
const cells = await container.findAllByRole('gridcell');
|
expect(within(cell).getByTestId('stack-cell-secondary')).toHaveTextContent(
|
||||||
const cell = cells.find((el) => el.getAttribute('col-id') === 'code');
|
'PRNT'
|
||||||
|
);
|
||||||
expect(
|
|
||||||
within(cell as HTMLElement).getByTestId('stack-cell-secondary')
|
|
||||||
).toHaveTextContent('PRNT');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import type { CellClickedEvent } from 'ag-grid-community';
|
|
||||||
import compact from 'lodash/compact';
|
import compact from 'lodash/compact';
|
||||||
import { isAfter } from 'date-fns';
|
import { isAfter } from 'date-fns';
|
||||||
import type {
|
import type {
|
||||||
@@ -6,7 +5,6 @@ import type {
|
|||||||
VegaValueFormatterParams,
|
VegaValueFormatterParams,
|
||||||
} from '@vegaprotocol/datagrid';
|
} from '@vegaprotocol/datagrid';
|
||||||
import { AgGrid, COL_DEFS } from '@vegaprotocol/datagrid';
|
import { AgGrid, COL_DEFS } from '@vegaprotocol/datagrid';
|
||||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import type { Asset } from '@vegaprotocol/types';
|
import type { Asset } from '@vegaprotocol/types';
|
||||||
@@ -19,11 +17,13 @@ import {
|
|||||||
import { closedMarketsWithDataProvider, getAsset } from '@vegaprotocol/markets';
|
import { closedMarketsWithDataProvider, getAsset } from '@vegaprotocol/markets';
|
||||||
import type { DataSourceFilterFragment } from '@vegaprotocol/markets';
|
import type { DataSourceFilterFragment } from '@vegaprotocol/markets';
|
||||||
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
||||||
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
|
||||||
import { SettlementDateCell } from './settlement-date-cell';
|
import { SettlementDateCell } from './settlement-date-cell';
|
||||||
import { SettlementPriceCell } from './settlement-price-cell';
|
import { SettlementPriceCell } from './settlement-price-cell';
|
||||||
|
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||||
import { MarketCodeCell } from './market-code-cell';
|
import { MarketCodeCell } from './market-code-cell';
|
||||||
import { MarketActionsDropdown } from './market-table-actions';
|
import { MarketActionsDropdown } from './market-table-actions';
|
||||||
|
import type { CellClickedEvent } from 'ag-grid-community';
|
||||||
|
import { useClosedMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
||||||
|
|
||||||
type SettlementAsset = Pick<
|
type SettlementAsset = Pick<
|
||||||
Asset,
|
Asset,
|
||||||
@@ -127,7 +127,7 @@ const ClosedMarketsDataGrid = ({
|
|||||||
rowData: Row[];
|
rowData: Row[];
|
||||||
error: Error | undefined;
|
error: Error | undefined;
|
||||||
}) => {
|
}) => {
|
||||||
const handleOnSelect = useMarketClickHandler();
|
const handleOnSelect = useClosedMarketClickHandler();
|
||||||
const openAssetDialog = useAssetDetailsDialogStore((store) => store.open);
|
const openAssetDialog = useAssetDetailsDialogStore((store) => store.open);
|
||||||
|
|
||||||
const colDefs = useMemo(() => {
|
const colDefs = useMemo(() => {
|
||||||
@@ -302,11 +302,8 @@ const ClosedMarketsDataGrid = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleOnSelect(
|
// @ts-ignore metaKey exists
|
||||||
data.id,
|
handleOnSelect(data.id, event ? event.metaKey : false);
|
||||||
// @ts-ignore metaKey exists
|
|
||||||
event ? event.metaKey : false
|
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
import { Route, Routes, useParams } from 'react-router-dom';
|
|
||||||
import { MarketState } from '@vegaprotocol/types';
|
|
||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
import { useMarket } from '@vegaprotocol/markets';
|
|
||||||
import { VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import {
|
|
||||||
SidebarButton,
|
|
||||||
SidebarDivider,
|
|
||||||
ViewType,
|
|
||||||
} from '../../components/sidebar';
|
|
||||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
|
||||||
|
|
||||||
export const MarketsSidebar = () => {
|
|
||||||
const { marketId } = useParams();
|
|
||||||
const currentRouteId = useGetCurrentRouteId();
|
|
||||||
const { data } = useMarket(marketId);
|
|
||||||
const active =
|
|
||||||
data &&
|
|
||||||
[MarketState.STATE_ACTIVE, MarketState.STATE_PENDING].includes(data.state);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<SidebarButton
|
|
||||||
view={ViewType.Deposit}
|
|
||||||
icon={VegaIconNames.DEPOSIT}
|
|
||||||
tooltip={t('Deposit')}
|
|
||||||
routeId={currentRouteId}
|
|
||||||
/>
|
|
||||||
<SidebarButton
|
|
||||||
view={ViewType.Withdraw}
|
|
||||||
icon={VegaIconNames.WITHDRAW}
|
|
||||||
tooltip={t('Withdraw')}
|
|
||||||
routeId={currentRouteId}
|
|
||||||
/>
|
|
||||||
<SidebarButton
|
|
||||||
view={ViewType.Transfer}
|
|
||||||
icon={VegaIconNames.TRANSFER}
|
|
||||||
tooltip={t('Transfer')}
|
|
||||||
routeId={currentRouteId}
|
|
||||||
/>
|
|
||||||
<Routes>
|
|
||||||
<Route
|
|
||||||
path=":marketId"
|
|
||||||
element={
|
|
||||||
<>
|
|
||||||
<SidebarDivider />
|
|
||||||
{active && (
|
|
||||||
<SidebarButton
|
|
||||||
view={ViewType.Order}
|
|
||||||
icon={VegaIconNames.TICKET}
|
|
||||||
tooltip={t('Order')}
|
|
||||||
routeId={currentRouteId}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<SidebarButton
|
|
||||||
view={ViewType.Info}
|
|
||||||
icon={VegaIconNames.BREAKDOWN}
|
|
||||||
tooltip={t('Market specification')}
|
|
||||||
routeId={currentRouteId}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Routes>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
import { VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import { SidebarButton, ViewType } from '../../components/sidebar';
|
|
||||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
|
||||||
|
|
||||||
export const PortfolioSidebar = () => {
|
|
||||||
const currentRouteId = useGetCurrentRouteId();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<SidebarButton
|
|
||||||
view={ViewType.Deposit}
|
|
||||||
icon={VegaIconNames.DEPOSIT}
|
|
||||||
tooltip={t('Deposit')}
|
|
||||||
routeId={currentRouteId}
|
|
||||||
/>
|
|
||||||
<SidebarButton
|
|
||||||
view={ViewType.Withdraw}
|
|
||||||
icon={VegaIconNames.WITHDRAW}
|
|
||||||
tooltip={t('Withdraw')}
|
|
||||||
routeId={currentRouteId}
|
|
||||||
/>
|
|
||||||
<SidebarButton
|
|
||||||
view={ViewType.Transfer}
|
|
||||||
icon={VegaIconNames.TRANSFER}
|
|
||||||
tooltip={t('Transfer')}
|
|
||||||
routeId={currentRouteId}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -9,11 +9,7 @@ import type { DataGridSlice } from '../../stores/datagrid-store-slice';
|
|||||||
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
|
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
|
||||||
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
||||||
|
|
||||||
export const FundingPaymentsContainer = ({
|
export const FundingPaymentsContainer = () => {
|
||||||
marketId,
|
|
||||||
}: {
|
|
||||||
marketId?: string;
|
|
||||||
}) => {
|
|
||||||
const onMarketClick = useMarketClickHandler(true);
|
const onMarketClick = useMarketClickHandler(true);
|
||||||
const { pubKey } = useVegaWallet();
|
const { pubKey } = useVegaWallet();
|
||||||
|
|
||||||
@@ -37,7 +33,6 @@ export const FundingPaymentsContainer = ({
|
|||||||
return (
|
return (
|
||||||
<FundingPaymentsManager
|
<FundingPaymentsManager
|
||||||
partyId={pubKey}
|
partyId={pubKey}
|
||||||
marketId={marketId}
|
|
||||||
onMarketClick={onMarketClick}
|
onMarketClick={onMarketClick}
|
||||||
gridProps={gridStoreCallbacks}
|
gridProps={gridStoreCallbacks}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ export const Header = ({ title, children }: TradeMarketHeaderProps) => {
|
|||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<header className={headerClasses}>
|
<header className={headerClasses}>
|
||||||
<div className="flex-col items-start justify-center hidden pt-2 pb-0 pl-3 lg:flex lg:pl-4 xl:pb-2">
|
<div className="hidden lg:flex flex-col justify-center items-start pl-3 lg:pl-4 pt-2 xl:pb-2 pb-0">
|
||||||
{title}
|
{title}
|
||||||
</div>
|
</div>
|
||||||
<div data-testid="header-summary" className="min-w-0">
|
<div data-testid="header-summary" className="min-w-0">
|
||||||
<div className="flex items-center px-3 py-2 overflow-x-auto text-xs lg:px-4 flex-nowrap gap-6">
|
<div className="px-3 lg:px-4 py-2 flex flex-nowrap gap-4 items-center text-xs overflow-x-auto">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -70,7 +70,7 @@ export const HeaderTitle = ({ children }: { children: ReactNode }) => {
|
|||||||
return (
|
return (
|
||||||
<h1
|
<h1
|
||||||
data-testid="header-title"
|
data-testid="header-title"
|
||||||
className="flex items-center text-lg gap-4 whitespace-nowrap xl:pr-4 xl:border-r border-default"
|
className="flex gap-4 items-center text-lg whitespace-nowrap xl:pr-4 xl:border-r border-default"
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</h1>
|
</h1>
|
||||||
|
|||||||
@@ -1,16 +1,11 @@
|
|||||||
import type { ReactNode } from 'react';
|
import { Outlet, Routes, Route } from 'react-router-dom';
|
||||||
import { Outlet } from 'react-router-dom';
|
|
||||||
import { Sidebar, SidebarContent, useSidebar } from '../sidebar';
|
import { Sidebar, SidebarContent, useSidebar } from '../sidebar';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import { MarketHeader } from '../market-header';
|
||||||
|
import { LiquidityHeader } from '../liquidity-header';
|
||||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||||
|
|
||||||
export const LayoutWithSidebar = ({
|
export const LayoutWithSidebar = () => {
|
||||||
header,
|
|
||||||
sidebar,
|
|
||||||
}: {
|
|
||||||
header?: ReactNode;
|
|
||||||
sidebar?: ReactNode;
|
|
||||||
}) => {
|
|
||||||
const currentRouteId = useGetCurrentRouteId();
|
const currentRouteId = useGetCurrentRouteId();
|
||||||
const views = useSidebar((store) => store.views);
|
const views = useSidebar((store) => store.views);
|
||||||
const sidebarView = views[currentRouteId] || null;
|
const sidebarView = views[currentRouteId] || null;
|
||||||
@@ -25,7 +20,16 @@ export const LayoutWithSidebar = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={gridClasses}>
|
<div className={gridClasses}>
|
||||||
<div className="col-span-full">{header}</div>
|
<div className="col-span-full">
|
||||||
|
<Routes>
|
||||||
|
<Route path="markets/:marketId" element={<MarketHeader />} />
|
||||||
|
<Route
|
||||||
|
path="markets/all/closed/:marketId"
|
||||||
|
element={<MarketHeader />}
|
||||||
|
/>
|
||||||
|
<Route path="liquidity/:marketId" element={<LiquidityHeader />} />
|
||||||
|
</Routes>
|
||||||
|
</div>
|
||||||
<main
|
<main
|
||||||
className={classNames('col-start-1 col-end-1', {
|
className={classNames('col-start-1 col-end-1', {
|
||||||
'lg:col-end-3': !sidebarOpen,
|
'lg:col-end-3': !sidebarOpen,
|
||||||
@@ -36,12 +40,9 @@ export const LayoutWithSidebar = ({
|
|||||||
</main>
|
</main>
|
||||||
<aside
|
<aside
|
||||||
// min-h-0 is needed as this element is part of a grid, we want the content to be scrollable, without it it will push the grid element taller
|
// min-h-0 is needed as this element is part of a grid, we want the content to be scrollable, without it it will push the grid element taller
|
||||||
className={classNames(
|
className={classNames('col-start-1 lg:col-start-2 min-h-0', {
|
||||||
'col-start-1 lg:col-start-2 min-h-0 lg:border-l border-default lg:bg-vega-clight-800 dark:lg:bg-vega-cdark-800',
|
hidden: !sidebarOpen,
|
||||||
{
|
})}
|
||||||
hidden: !sidebarOpen,
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<SidebarContent />
|
<SidebarContent />
|
||||||
</aside>
|
</aside>
|
||||||
@@ -53,7 +54,7 @@ export const LayoutWithSidebar = ({
|
|||||||
'lg:row-start-2 lg:row-span-full lg:col-start-3'
|
'lg:row-start-2 lg:row-span-full lg:col-start-3'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Sidebar options={sidebar} />
|
<Sidebar />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Popover, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
import { Popover, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
||||||
import { Header, HeaderTitle } from '../header';
|
import { Header, HeaderTitle } from '../header';
|
||||||
import { Route, Routes, useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { MarketSelector } from '../../components/market-selector/market-selector';
|
import { MarketSelector } from '../../components/market-selector/market-selector';
|
||||||
import { MarketHeaderStats } from '../../client-pages/market/market-header-stats';
|
import { MarketHeaderStats } from '../../client-pages/market/market-header-stats';
|
||||||
import { useMarket, useMarketList } from '@vegaprotocol/markets';
|
import { useMarket, useMarketList } from '@vegaprotocol/markets';
|
||||||
@@ -19,44 +19,36 @@ export const MarketHeader = () => {
|
|||||||
if (!data) return null;
|
if (!data) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<Header
|
||||||
<Route
|
title={
|
||||||
path=":marketId"
|
<Popover
|
||||||
element={
|
open={open}
|
||||||
<Header
|
onChange={setOpen}
|
||||||
title={
|
trigger={
|
||||||
<Popover
|
<HeaderTitle>
|
||||||
open={open}
|
<span>
|
||||||
onChange={setOpen}
|
{data.tradableInstrument.instrument.code}
|
||||||
trigger={
|
<span className="ml-2 text-xs uppercase text-muted">
|
||||||
<HeaderTitle>
|
{' '}
|
||||||
<span>
|
{data.tradableInstrument.instrument.product.__typename &&
|
||||||
{data.tradableInstrument.instrument.code}
|
ProductTypeShortName[
|
||||||
<span className="ml-1 text-xs uppercase text-muted">
|
data.tradableInstrument.instrument.product.__typename
|
||||||
{data.tradableInstrument.instrument.product
|
]}{' '}
|
||||||
.__typename &&
|
</span>
|
||||||
ProductTypeShortName[
|
</span>
|
||||||
data.tradableInstrument.instrument.product
|
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={14} />
|
||||||
.__typename
|
</HeaderTitle>
|
||||||
]}
|
}
|
||||||
</span>
|
alignOffset={-10}
|
||||||
</span>
|
>
|
||||||
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={14} />
|
<MarketSelector
|
||||||
</HeaderTitle>
|
currentMarketId={marketId}
|
||||||
}
|
onSelect={() => setOpen(false)}
|
||||||
alignOffset={-10}
|
/>
|
||||||
>
|
</Popover>
|
||||||
<MarketSelector
|
}
|
||||||
currentMarketId={marketId}
|
>
|
||||||
onSelect={() => setOpen(false)}
|
<MarketHeaderStats market={data} />
|
||||||
/>
|
</Header>
|
||||||
</Popover>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<MarketHeaderStats market={data} />
|
|
||||||
</Header>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Routes>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ describe('Navbar', () => {
|
|||||||
|
|
||||||
const expectedLinks = [
|
const expectedLinks = [
|
||||||
['/', ''],
|
['/', ''],
|
||||||
['/markets', 'Markets'],
|
['/markets/all', 'Markets'],
|
||||||
[`/markets/${marketId}`, 'Trading'],
|
[`/markets/${marketId}`, 'Trading'],
|
||||||
['/portfolio', 'Portfolio'],
|
['/portfolio', 'Portfolio'],
|
||||||
['/referrals', 'Referrals'],
|
['/referrals', 'Referrals'],
|
||||||
@@ -80,7 +80,7 @@ describe('Navbar', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Markets page route should not match empty market page', () => {
|
it('Markets page route should not match empty market page', () => {
|
||||||
renderComponent(['/markets']);
|
renderComponent(['/markets/all']);
|
||||||
expect(screen.getByRole('link', { name: 'Markets' })).toHaveClass('active');
|
expect(screen.getByRole('link', { name: 'Markets' })).toHaveClass('active');
|
||||||
expect(screen.getByRole('link', { name: 'Trading' })).not.toHaveClass(
|
expect(screen.getByRole('link', { name: 'Trading' })).not.toHaveClass(
|
||||||
'active'
|
'active'
|
||||||
@@ -96,7 +96,7 @@ describe('Navbar', () => {
|
|||||||
const menu = within(menuEl);
|
const menu = within(menuEl);
|
||||||
|
|
||||||
const expectedLinks = [
|
const expectedLinks = [
|
||||||
['/markets', 'Markets'],
|
['/markets/all', 'Markets'],
|
||||||
[`/markets/${marketId}`, 'Trading'],
|
[`/markets/${marketId}`, 'Trading'],
|
||||||
['/portfolio', 'Portfolio'],
|
['/portfolio', 'Portfolio'],
|
||||||
['/referrals', 'Referrals'],
|
['/referrals', 'Referrals'],
|
||||||
|
|||||||
@@ -264,7 +264,6 @@ const NavbarLink = ({
|
|||||||
<N.Link asChild={true}>
|
<N.Link asChild={true}>
|
||||||
<NavLink
|
<NavLink
|
||||||
to={to}
|
to={to}
|
||||||
end={true}
|
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'block lg:flex lg:h-full flex-col justify-center',
|
'block lg:flex lg:h-full flex-col justify-center',
|
||||||
'px-6 py-2 lg:p-0 text-lg lg:text-sm',
|
'px-6 py-2 lg:p-0 text-lg lg:text-sm',
|
||||||
|
|||||||
@@ -3,12 +3,14 @@ import { Filter, OrderListManager } from '@vegaprotocol/orders';
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import { useNavigateWithMeta } from '../../lib/hooks/use-market-click-handler';
|
import {
|
||||||
|
useMarketClickHandler,
|
||||||
|
useMarketLiquidityClickHandler,
|
||||||
|
} from '../../lib/hooks/use-market-click-handler';
|
||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
import { persist } from 'zustand/middleware';
|
import { persist } from 'zustand/middleware';
|
||||||
import type { DataGridStore } from '../../stores/datagrid-store-slice';
|
import type { DataGridStore } from '../../stores/datagrid-store-slice';
|
||||||
import { OrderStatus } from '@vegaprotocol/types';
|
import { OrderStatus } from '@vegaprotocol/types';
|
||||||
import { Links } from '../../lib/links';
|
|
||||||
|
|
||||||
export const FilterStatusValue = {
|
export const FilterStatusValue = {
|
||||||
[Filter.Open]: [OrderStatus.STATUS_ACTIVE, OrderStatus.STATUS_PARKED],
|
[Filter.Open]: [OrderStatus.STATUS_ACTIVE, OrderStatus.STATUS_PARKED],
|
||||||
@@ -30,7 +32,8 @@ const AUTO_SIZE_COLUMNS = ['instrument-code'];
|
|||||||
|
|
||||||
export const OrdersContainer = ({ filter }: OrderContainerProps) => {
|
export const OrdersContainer = ({ filter }: OrderContainerProps) => {
|
||||||
const { pubKey, isReadOnly } = useVegaWallet();
|
const { pubKey, isReadOnly } = useVegaWallet();
|
||||||
const navigate = useNavigateWithMeta();
|
const onMarketClick = useMarketClickHandler(true);
|
||||||
|
const onOrderTypeClick = useMarketLiquidityClickHandler();
|
||||||
const { gridState, updateGridState } = useOrderListGridState(filter);
|
const { gridState, updateGridState } = useOrderListGridState(filter);
|
||||||
const gridStoreCallbacks = useDataGridEvents(
|
const gridStoreCallbacks = useDataGridEvents(
|
||||||
gridState,
|
gridState,
|
||||||
@@ -48,12 +51,8 @@ export const OrdersContainer = ({ filter }: OrderContainerProps) => {
|
|||||||
<OrderListManager
|
<OrderListManager
|
||||||
partyId={pubKey}
|
partyId={pubKey}
|
||||||
filter={filter}
|
filter={filter}
|
||||||
onMarketClick={(marketId, metaKey) => {
|
onMarketClick={onMarketClick}
|
||||||
navigate(Links.MARKET(marketId), metaKey);
|
onOrderTypeClick={onOrderTypeClick}
|
||||||
}}
|
|
||||||
onOrderTypeClick={(marketId, metaKey) =>
|
|
||||||
navigate(Links.LIQUIDITY(marketId), metaKey)
|
|
||||||
}
|
|
||||||
isReadOnly={isReadOnly}
|
isReadOnly={isReadOnly}
|
||||||
gridProps={gridStoreCallbacks}
|
gridProps={gridStoreCallbacks}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -38,68 +38,79 @@ const walletContext = {
|
|||||||
} as VegaWalletContextShape;
|
} as VegaWalletContextShape;
|
||||||
|
|
||||||
describe('Sidebar', () => {
|
describe('Sidebar', () => {
|
||||||
beforeEach(() => {
|
it.each(['/markets/all', '/portfolio'])(
|
||||||
useSidebar.setState({ views: {} });
|
'does not render ticket and info',
|
||||||
});
|
(path) => {
|
||||||
|
render(
|
||||||
|
<VegaWalletContext.Provider value={walletContext}>
|
||||||
|
<MemoryRouter initialEntries={[path]}>
|
||||||
|
<Sidebar />
|
||||||
|
</MemoryRouter>
|
||||||
|
</VegaWalletContext.Provider>
|
||||||
|
);
|
||||||
|
|
||||||
it('renders options prop', () => {
|
expect(screen.getByTestId(ViewType.ViewAs)).toBeInTheDocument();
|
||||||
|
expect(screen.getByTestId(ViewType.Settings)).toBeInTheDocument();
|
||||||
|
expect(screen.getByTestId(ViewType.Transfer)).toBeInTheDocument();
|
||||||
|
expect(screen.getByTestId(ViewType.Deposit)).toBeInTheDocument();
|
||||||
|
expect(screen.getByTestId(ViewType.Withdraw)).toBeInTheDocument();
|
||||||
|
expect(screen.getByTestId('node-health')).toBeInTheDocument();
|
||||||
|
|
||||||
|
// no order or info sidebars
|
||||||
|
expect(screen.queryByTestId(ViewType.Order)).not.toBeInTheDocument();
|
||||||
|
expect(screen.queryByTestId(ViewType.Info)).not.toBeInTheDocument();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it('renders ticket and info on market pages', () => {
|
||||||
render(
|
render(
|
||||||
<VegaWalletContext.Provider value={walletContext}>
|
<VegaWalletContext.Provider value={walletContext}>
|
||||||
<MemoryRouter>
|
<MemoryRouter initialEntries={['/markets/ABC']}>
|
||||||
<Sidebar options={<div data-testid="options" />} />
|
<Sidebar />
|
||||||
</MemoryRouter>
|
</MemoryRouter>
|
||||||
</VegaWalletContext.Provider>
|
</VegaWalletContext.Provider>
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(screen.getByTestId(ViewType.ViewAs)).toBeInTheDocument();
|
expect(screen.getByTestId(ViewType.ViewAs)).toBeInTheDocument();
|
||||||
expect(screen.getByTestId(ViewType.Settings)).toBeInTheDocument();
|
expect(screen.getByTestId(ViewType.Settings)).toBeInTheDocument();
|
||||||
|
expect(screen.getByTestId(ViewType.Transfer)).toBeInTheDocument();
|
||||||
|
expect(screen.getByTestId(ViewType.Deposit)).toBeInTheDocument();
|
||||||
|
expect(screen.getByTestId(ViewType.Withdraw)).toBeInTheDocument();
|
||||||
expect(screen.getByTestId('node-health')).toBeInTheDocument();
|
expect(screen.getByTestId('node-health')).toBeInTheDocument();
|
||||||
expect(screen.getByTestId('options')).toBeInTheDocument();
|
|
||||||
|
// order and info sidebars are shown
|
||||||
|
expect(screen.getByTestId(ViewType.Order)).toBeInTheDocument();
|
||||||
|
expect(screen.getByTestId(ViewType.Info)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders selected state', async () => {
|
it('renders selected state', async () => {
|
||||||
const routeId = '/markets/ABC';
|
|
||||||
render(
|
render(
|
||||||
<VegaWalletContext.Provider value={walletContext}>
|
<VegaWalletContext.Provider value={walletContext}>
|
||||||
<MemoryRouter initialEntries={[routeId]}>
|
<MemoryRouter initialEntries={['/markets/ABC']}>
|
||||||
<Sidebar
|
<Sidebar />
|
||||||
options={
|
|
||||||
<SidebarButton
|
|
||||||
view={ViewType.Deposit}
|
|
||||||
icon={VegaIconNames.DEPOSIT}
|
|
||||||
tooltip="Deposit"
|
|
||||||
routeId={'/markets/:marketId'}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</MemoryRouter>
|
</MemoryRouter>
|
||||||
</VegaWalletContext.Provider>
|
</VegaWalletContext.Provider>
|
||||||
);
|
);
|
||||||
|
|
||||||
const settingsButton = screen.getByTestId(ViewType.Settings);
|
const settingsButton = screen.getByTestId(ViewType.Settings);
|
||||||
const depositButton = screen.getByTestId(ViewType.Deposit);
|
const orderButton = screen.getByTestId(ViewType.Order);
|
||||||
|
|
||||||
// select settings first
|
// select settings first
|
||||||
await userEvent.click(settingsButton);
|
await userEvent.click(settingsButton);
|
||||||
expect(settingsButton).toHaveClass('bg-vega-yellow text-black');
|
expect(settingsButton).toHaveClass('bg-vega-yellow text-black');
|
||||||
|
|
||||||
// switch to deposit
|
// switch to order
|
||||||
await userEvent.click(depositButton);
|
await userEvent.click(orderButton);
|
||||||
|
|
||||||
expect(settingsButton).not.toHaveClass('bg-vega-yellow text-black');
|
expect(settingsButton).not.toHaveClass('bg-vega-yellow text-black');
|
||||||
expect(depositButton).toHaveClass('bg-vega-yellow text-black');
|
expect(orderButton).toHaveClass('bg-vega-yellow text-black');
|
||||||
|
|
||||||
// close order
|
// close order
|
||||||
await userEvent.click(depositButton);
|
await userEvent.click(orderButton);
|
||||||
expect(depositButton).not.toHaveClass('bg-vega-yellow text-black');
|
expect(orderButton).not.toHaveClass('bg-vega-yellow text-black');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('SidebarContent', () => {
|
describe('SidebarContent', () => {
|
||||||
beforeEach(() => {
|
|
||||||
useSidebar.setState({ views: {} });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('renders the correct content', () => {
|
it('renders the correct content', () => {
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
<VegaWalletContext.Provider value={walletContext}>
|
<VegaWalletContext.Provider value={walletContext}>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { Route, Routes, useParams } from 'react-router-dom';
|
||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
import { TransferContainer } from '@vegaprotocol/accounts';
|
import { TransferContainer } from '@vegaprotocol/accounts';
|
||||||
import { DealTicketContainer } from '@vegaprotocol/deal-ticket';
|
import { DealTicketContainer } from '@vegaprotocol/deal-ticket';
|
||||||
@@ -50,14 +50,108 @@ type SidebarView =
|
|||||||
type: ViewType.Settings;
|
type: ViewType.Settings;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Sidebar = ({ options }: { options?: ReactNode }) => {
|
const MarketSidebarButtons = () => {
|
||||||
|
const currentRouteId = useGetCurrentRouteId();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SidebarButton
|
||||||
|
view={ViewType.Order}
|
||||||
|
icon={VegaIconNames.TICKET}
|
||||||
|
tooltip={t('Order')}
|
||||||
|
routeId={currentRouteId}
|
||||||
|
/>
|
||||||
|
<SidebarButton
|
||||||
|
view={ViewType.Info}
|
||||||
|
icon={VegaIconNames.BREAKDOWN}
|
||||||
|
tooltip={t('Market specification')}
|
||||||
|
routeId={currentRouteId}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const AssetSidebarButtons = () => {
|
||||||
|
const currentRouteId = useGetCurrentRouteId();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SidebarButton
|
||||||
|
view={ViewType.Deposit}
|
||||||
|
icon={VegaIconNames.DEPOSIT}
|
||||||
|
tooltip={t('Deposit')}
|
||||||
|
routeId={currentRouteId}
|
||||||
|
/>
|
||||||
|
<SidebarButton
|
||||||
|
view={ViewType.Withdraw}
|
||||||
|
icon={VegaIconNames.WITHDRAW}
|
||||||
|
tooltip={t('Withdraw')}
|
||||||
|
routeId={currentRouteId}
|
||||||
|
/>
|
||||||
|
<SidebarButton
|
||||||
|
view={ViewType.Transfer}
|
||||||
|
icon={VegaIconNames.TRANSFER}
|
||||||
|
tooltip={t('Transfer')}
|
||||||
|
routeId={currentRouteId}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Sidebar = () => {
|
||||||
const currentRouteId = useGetCurrentRouteId();
|
const currentRouteId = useGetCurrentRouteId();
|
||||||
const navClasses = 'flex lg:flex-col items-center gap-2 lg:gap-4 p-1';
|
const navClasses = 'flex lg:flex-col items-center gap-2 lg:gap-4 p-1';
|
||||||
const setViewAsDialogOpen = useViewAsDialog((state) => state.setOpen);
|
const setViewAsDialogOpen = useViewAsDialog((state) => state.setOpen);
|
||||||
const { pubKeys } = useVegaWallet();
|
const { pubKeys } = useVegaWallet();
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full p-1 lg:flex-col gap-2" data-testid="sidebar">
|
<div className="flex h-full p-1 lg:flex-col gap-2" data-testid="sidebar">
|
||||||
{options && <nav className={navClasses}>{options}</nav>}
|
<nav className={navClasses}>
|
||||||
|
<Routes>
|
||||||
|
<Route path="markets/all" element={<AssetSidebarButtons />} />
|
||||||
|
<Route path="portfolio">
|
||||||
|
<Route
|
||||||
|
// Show deposit/withdraw/transfer sidebar options only on portflio dashboard (index)
|
||||||
|
index={true}
|
||||||
|
element={<AssetSidebarButtons />}
|
||||||
|
/>
|
||||||
|
</Route>
|
||||||
|
<Route
|
||||||
|
path="markets/:marketId"
|
||||||
|
element={
|
||||||
|
<>
|
||||||
|
<AssetSidebarButtons />
|
||||||
|
<SidebarDivider />
|
||||||
|
<MarketSidebarButtons />
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="liquidity/:marketId"
|
||||||
|
element={
|
||||||
|
<>
|
||||||
|
<AssetSidebarButtons />
|
||||||
|
<SidebarDivider />
|
||||||
|
<MarketSidebarButtons />
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="markets/all/closed/:marketId"
|
||||||
|
element={
|
||||||
|
<>
|
||||||
|
<AssetSidebarButtons />
|
||||||
|
<SidebarDivider />
|
||||||
|
<SidebarButton
|
||||||
|
view={ViewType.Info}
|
||||||
|
icon={VegaIconNames.BREAKDOWN}
|
||||||
|
tooltip={t('Market specification')}
|
||||||
|
routeId={currentRouteId}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Routes>
|
||||||
|
</nav>
|
||||||
<nav className={classNames(navClasses, 'ml-auto lg:mt-auto lg:ml-0')}>
|
<nav className={classNames(navClasses, 'ml-auto lg:mt-auto lg:ml-0')}>
|
||||||
<SidebarButton
|
<SidebarButton
|
||||||
view={ViewType.ViewAs}
|
view={ViewType.ViewAs}
|
||||||
@@ -140,7 +234,7 @@ export const SidebarButton = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SidebarDivider = () => {
|
const SidebarDivider = () => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="w-px h-4 bg-vega-clight-600 dark:bg-vega-cdark-600 lg:w-4 lg:h-px"
|
className="w-px h-4 bg-vega-clight-600 dark:bg-vega-cdark-600 lg:w-4 lg:h-px"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { TelemetryApproval } from './telemetry-approval';
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { useOnboardingStore } from '../welcome-dialog/use-get-onboarding-step';
|
import { useOnboardingStore } from '../welcome-dialog/use-get-onboarding-step';
|
||||||
|
|
||||||
const TELEMETRY_APPROVAL_TOAST_ID = 'telemetry_toast_id';
|
const TELEMETRY_APPROVAL_TOAST_ID = 'telemetry_tost_id';
|
||||||
|
|
||||||
export const Telemetry = () => {
|
export const Telemetry = () => {
|
||||||
const onboardingDissmissed = useOnboardingStore((store) => store.dismissed);
|
const onboardingDissmissed = useOnboardingStore((store) => store.dismissed);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { useCallback } from 'react';
|
||||||
import { Links } from '../../lib/links';
|
import { Links } from '../../lib/links';
|
||||||
|
|
||||||
export const useMarketClickHandler = (replace = false) => {
|
export const useMarketClickHandler = (replace = false) => {
|
||||||
@@ -14,10 +15,17 @@ export const useMarketClickHandler = (replace = false) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useNavigateWithMeta = (replace = false) => {
|
export const useMarketLiquidityClickHandler = () => {
|
||||||
|
return useCallback((selectedId: string, metaKey?: boolean) => {
|
||||||
|
window.open(`/#/liquidity/${selectedId}`, metaKey ? '_blank' : '_self');
|
||||||
|
}, []);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useClosedMarketClickHandler = (replace = false) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
return (link: string, metaKey?: boolean) => {
|
return (selectedId: string, metaKey?: boolean) => {
|
||||||
|
const link = Links.CLOSED_MARKETS(selectedId);
|
||||||
if (metaKey) {
|
if (metaKey) {
|
||||||
window.open(`/#${link}`, '_blank');
|
window.open(`/#${link}`, '_blank');
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ import trimEnd from 'lodash/trimEnd';
|
|||||||
// href creation
|
// href creation
|
||||||
export const Routes = {
|
export const Routes = {
|
||||||
HOME: '/',
|
HOME: '/',
|
||||||
MARKETS: '/markets',
|
MARKETS: '/markets/all',
|
||||||
|
CLOSED_MARKETS: '/markets/all/closed/:marketId',
|
||||||
MARKET: '/markets/:marketId',
|
MARKET: '/markets/:marketId',
|
||||||
LIQUIDITY: '/liquidity/:marketId',
|
LIQUIDITY: '/liquidity/:marketId',
|
||||||
PORTFOLIO: '/portfolio',
|
PORTFOLIO: '/portfolio',
|
||||||
@@ -28,6 +29,8 @@ export const Links: ConsoleLinks = {
|
|||||||
MARKET: (marketId: string) =>
|
MARKET: (marketId: string) =>
|
||||||
trimEnd(Routes.MARKET.replace(':marketId', marketId)),
|
trimEnd(Routes.MARKET.replace(':marketId', marketId)),
|
||||||
MARKETS: () => Routes.MARKETS,
|
MARKETS: () => Routes.MARKETS,
|
||||||
|
CLOSED_MARKETS: (marketId: string) =>
|
||||||
|
trimEnd(Routes.CLOSED_MARKETS.replace(':marketId', marketId)),
|
||||||
PORTFOLIO: () => Routes.PORTFOLIO,
|
PORTFOLIO: () => Routes.PORTFOLIO,
|
||||||
LIQUIDITY: (marketId: string) =>
|
LIQUIDITY: (marketId: string) =>
|
||||||
trimEnd(Routes.LIQUIDITY.replace(':marketId', marketId)),
|
trimEnd(Routes.LIQUIDITY.replace(':marketId', marketId)),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { RouteObject } from 'react-router-dom';
|
import type { RouteObject } from 'react-router-dom';
|
||||||
import { Navigate, useRoutes } from 'react-router-dom';
|
import { Navigate, Outlet, useRoutes } from 'react-router-dom';
|
||||||
import { lazy, Suspense } from 'react';
|
import { lazy, Suspense } from 'react';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
|
import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
|
||||||
@@ -13,7 +13,7 @@ import { Assets } from '../client-pages/assets';
|
|||||||
import { Deposit } from '../client-pages/deposit';
|
import { Deposit } from '../client-pages/deposit';
|
||||||
import { Withdraw } from '../client-pages/withdraw';
|
import { Withdraw } from '../client-pages/withdraw';
|
||||||
import { Transfer } from '../client-pages/transfer';
|
import { Transfer } from '../client-pages/transfer';
|
||||||
import { Routes as AppRoutes } from '../lib/links';
|
import { Routes } from '../lib/links';
|
||||||
import { LayoutWithSky } from '../client-pages/referrals/layout';
|
import { LayoutWithSky } from '../client-pages/referrals/layout';
|
||||||
import { Referrals } from '../client-pages/referrals/referrals';
|
import { Referrals } from '../client-pages/referrals/referrals';
|
||||||
import { ReferralStatistics } from '../client-pages/referrals/referral-statistics';
|
import { ReferralStatistics } from '../client-pages/referrals/referral-statistics';
|
||||||
@@ -22,15 +22,11 @@ import { CreateCodeContainer } from '../client-pages/referrals/create-code-form'
|
|||||||
import { NotFound as ReferralNotFound } from '../client-pages/referrals/error-boundary';
|
import { NotFound as ReferralNotFound } from '../client-pages/referrals/error-boundary';
|
||||||
import { compact } from 'lodash';
|
import { compact } from 'lodash';
|
||||||
import { FLAGS } from '@vegaprotocol/environment';
|
import { FLAGS } from '@vegaprotocol/environment';
|
||||||
import { LiquidityHeader } from '../components/liquidity-header';
|
|
||||||
import { MarketHeader } from '../components/market-header';
|
|
||||||
import { PortfolioSidebar } from '../client-pages/portfolio/portfolio-sidebar';
|
|
||||||
import { LiquiditySidebar } from '../client-pages/liquidity/liquidity-sidebar';
|
|
||||||
import { MarketsSidebar } from '../client-pages/markets/markets-sidebar';
|
|
||||||
|
|
||||||
// These must remain dynamically imported as pennant cannot be compiled by nextjs due to ESM
|
// These must remain dynamically imported as pennant cannot be compiled by nextjs due to ESM
|
||||||
// Using dynamic imports is a workaround for this until pennant is published as ESM
|
// Using dynamic imports is a workaround for this until pennant is published as ESM
|
||||||
const MarketPage = lazy(() => import('../client-pages/market'));
|
const MarketPage = lazy(() => import('../client-pages/market'));
|
||||||
|
const ClosedMarketPage = lazy(() => import('../client-pages/closed-market'));
|
||||||
const Portfolio = lazy(() => import('../client-pages/portfolio'));
|
const Portfolio = lazy(() => import('../client-pages/portfolio'));
|
||||||
|
|
||||||
const NotFound = () => (
|
const NotFound = () => (
|
||||||
@@ -40,21 +36,18 @@ const NotFound = () => (
|
|||||||
);
|
);
|
||||||
|
|
||||||
export const routerConfig: RouteObject[] = compact([
|
export const routerConfig: RouteObject[] = compact([
|
||||||
{
|
// Pages that don't use the LayoutWithSidebar must come first
|
||||||
index: true,
|
// to ensure they are matched before the catch all route '/*'
|
||||||
element: <Home />,
|
|
||||||
id: AppRoutes.HOME,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'disclaimer',
|
path: 'disclaimer',
|
||||||
element: <LayoutCentered />,
|
element: <LayoutCentered />,
|
||||||
id: AppRoutes.DISCLAIMER,
|
id: Routes.DISCLAIMER,
|
||||||
children: [{ index: true, element: <Disclaimer /> }],
|
children: [{ index: true, element: <Disclaimer /> }],
|
||||||
},
|
},
|
||||||
// Referrals routing (the pages should be available if the feature flag is on)
|
// Referrals routing (the pages should be available if the feature flag is on)
|
||||||
FLAGS.REFERRALS
|
FLAGS.REFERRALS
|
||||||
? {
|
? {
|
||||||
path: AppRoutes.REFERRALS,
|
path: Routes.REFERRALS,
|
||||||
element: <LayoutWithSky />,
|
element: <LayoutWithSky />,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
@@ -65,11 +58,11 @@ export const routerConfig: RouteObject[] = compact([
|
|||||||
element: <ReferralStatistics />,
|
element: <ReferralStatistics />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: AppRoutes.REFERRALS_CREATE_CODE,
|
path: Routes.REFERRALS_CREATE_CODE,
|
||||||
element: <CreateCodeContainer />,
|
element: <CreateCodeContainer />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: AppRoutes.REFERRALS_APPLY_CODE,
|
path: Routes.REFERRALS_APPLY_CODE,
|
||||||
element: <ApplyCodeForm />,
|
element: <ApplyCodeForm />,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -81,74 +74,75 @@ export const routerConfig: RouteObject[] = compact([
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
|
// All other pages will use the sidebar
|
||||||
{
|
{
|
||||||
path: 'markets/*',
|
path: '/*',
|
||||||
element: (
|
element: <LayoutWithSidebar />,
|
||||||
<LayoutWithSidebar
|
|
||||||
header={<MarketHeader />}
|
|
||||||
sidebar={<MarketsSidebar />}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
index: true,
|
index: true,
|
||||||
element: <MarketsPage />,
|
element: <Home />,
|
||||||
id: AppRoutes.MARKETS,
|
id: Routes.HOME,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'all',
|
path: 'markets',
|
||||||
element: <Navigate to="/markets" />,
|
element: <Outlet />,
|
||||||
},
|
|
||||||
{
|
|
||||||
path: ':marketId',
|
|
||||||
element: <MarketPage />,
|
|
||||||
id: AppRoutes.MARKET,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'portfolio/*',
|
|
||||||
element: <LayoutWithSidebar sidebar={<PortfolioSidebar />} />,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
index: true,
|
|
||||||
element: <Portfolio />,
|
|
||||||
id: AppRoutes.PORTFOLIO,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'assets',
|
|
||||||
element: <Assets />,
|
|
||||||
id: AppRoutes.ASSETS,
|
|
||||||
children: [
|
children: [
|
||||||
{ index: true, element: <Navigate to="deposit" /> },
|
{
|
||||||
{ path: 'deposit', element: <Deposit />, id: AppRoutes.DEPOSIT },
|
index: true,
|
||||||
{ path: 'withdraw', element: <Withdraw />, id: AppRoutes.WITHDRAW },
|
element: <Navigate to="all" />,
|
||||||
{ path: 'transfer', element: <Transfer />, id: AppRoutes.TRANSFER },
|
},
|
||||||
|
{
|
||||||
|
path: 'all',
|
||||||
|
element: <MarketsPage />,
|
||||||
|
id: Routes.MARKETS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ':marketId',
|
||||||
|
element: <MarketPage />,
|
||||||
|
id: Routes.MARKET,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'all/closed/:marketId',
|
||||||
|
element: <ClosedMarketPage />,
|
||||||
|
id: Routes.CLOSED_MARKETS,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
path: 'liquidity/*',
|
|
||||||
element: (
|
|
||||||
<LayoutWithSidebar
|
|
||||||
header={<LiquidityHeader />}
|
|
||||||
sidebar={<LiquiditySidebar />}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
id: AppRoutes.LIQUIDITY,
|
|
||||||
children: [
|
|
||||||
{
|
{
|
||||||
path: ':marketId',
|
path: 'portfolio',
|
||||||
|
element: <Outlet />,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
index: true,
|
||||||
|
element: <Portfolio />,
|
||||||
|
id: Routes.PORTFOLIO,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'assets',
|
||||||
|
element: <Assets />,
|
||||||
|
id: Routes.ASSETS,
|
||||||
|
children: [
|
||||||
|
{ index: true, element: <Navigate to="deposit" /> },
|
||||||
|
{ path: 'deposit', element: <Deposit />, id: Routes.DEPOSIT },
|
||||||
|
{ path: 'withdraw', element: <Withdraw />, id: Routes.WITHDRAW },
|
||||||
|
{ path: 'transfer', element: <Transfer />, id: Routes.TRANSFER },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'liquidity/:marketId',
|
||||||
element: <Liquidity />,
|
element: <Liquidity />,
|
||||||
|
id: Routes.LIQUIDITY,
|
||||||
|
},
|
||||||
|
// NotFound page is here so its caught within parent '/*' route
|
||||||
|
{
|
||||||
|
path: '*',
|
||||||
|
element: <NotFound />,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '*',
|
|
||||||
element: <NotFound />,
|
|
||||||
},
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const ClientRouter = () => {
|
export const ClientRouter = () => {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { pseudoRandom } from '@vegaprotocol/ui-toolkit';
|
|
||||||
|
|
||||||
const generate = pseudoRandom(1);
|
|
||||||
|
|
||||||
export const SSRLoader = () => {
|
export const SSRLoader = () => {
|
||||||
|
const randomDelay = () => {
|
||||||
|
return parseFloat((Math.random() * (4 - 1) + 1).toFixed(2));
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@@ -38,9 +38,10 @@ export const SSRLoader = () => {
|
|||||||
width: 10,
|
width: 10,
|
||||||
height: 10,
|
height: 10,
|
||||||
animation: 'flickering 0.4s linear alternate infinite',
|
animation: 'flickering 0.4s linear alternate infinite',
|
||||||
animationDelay: `-${generate()}s`,
|
animationDelay: `-${randomDelay()}s`,
|
||||||
animationDirection: i % 2 === 0 ? 'reverse' : 'alternate',
|
animationDirection: i % 2 === 0 ? 'reverse' : 'alternate',
|
||||||
background: 'black',
|
background: 'black',
|
||||||
|
opacity: Math.random() > 0.5 ? 1 : 0,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
useEthTransactionUpdater,
|
useEthTransactionUpdater,
|
||||||
useEthWithdrawApprovalsManager,
|
useEthWithdrawApprovalsManager,
|
||||||
} from '@vegaprotocol/web3';
|
} from '@vegaprotocol/web3';
|
||||||
import { useLedgerDownloadManager } from '@vegaprotocol/ledger';
|
|
||||||
|
|
||||||
export const TransactionHandlers = () => {
|
export const TransactionHandlers = () => {
|
||||||
useVegaTransactionManager();
|
useVegaTransactionManager();
|
||||||
@@ -15,6 +14,5 @@ export const TransactionHandlers = () => {
|
|||||||
useEthTransactionManager();
|
useEthTransactionManager();
|
||||||
useEthTransactionUpdater();
|
useEthTransactionUpdater();
|
||||||
useEthWithdrawApprovalsManager();
|
useEthWithdrawApprovalsManager();
|
||||||
useLedgerDownloadManager();
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -45,28 +45,6 @@ const secondRow = {
|
|||||||
} as AccountFields;
|
} as AccountFields;
|
||||||
const multiRowData = [singleRow, secondRow];
|
const multiRowData = [singleRow, secondRow];
|
||||||
|
|
||||||
const zeroBalanceRow = {
|
|
||||||
__typename: 'AccountBalance',
|
|
||||||
type: Types.AccountType.ACCOUNT_TYPE_MARGIN,
|
|
||||||
balance: '0',
|
|
||||||
market: {
|
|
||||||
__typename: 'Market',
|
|
||||||
id: '10cd0a793ad2887b340940337fa6d97a212e0e517fe8e9eab2b5ef3a38633f35',
|
|
||||||
},
|
|
||||||
asset: {
|
|
||||||
__typename: 'Asset',
|
|
||||||
id: '5cfa87844724df6069b94e4c8a6f03af21907d7bc251593d08e4251043ee9f7c',
|
|
||||||
symbol: 'tBTC',
|
|
||||||
decimals: 5,
|
|
||||||
},
|
|
||||||
available: '0',
|
|
||||||
used: '0',
|
|
||||||
total: '0',
|
|
||||||
} as AccountFields;
|
|
||||||
const zeroBalanceRowData = [zeroBalanceRow];
|
|
||||||
|
|
||||||
const onClickDepositMock = jest.fn();
|
|
||||||
|
|
||||||
describe('AccountsTable', () => {
|
describe('AccountsTable', () => {
|
||||||
it('should render correct columns', async () => {
|
it('should render correct columns', async () => {
|
||||||
// 7001-COLL-001
|
// 7001-COLL-001
|
||||||
@@ -93,27 +71,6 @@ describe('AccountsTable', () => {
|
|||||||
).toEqual(expectedHeaders);
|
).toEqual(expectedHeaders);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render deposit button', async () => {
|
|
||||||
render(
|
|
||||||
<AccountTable
|
|
||||||
rowData={zeroBalanceRowData}
|
|
||||||
onClickAsset={() => null}
|
|
||||||
isReadOnly={false}
|
|
||||||
onClickDeposit={onClickDepositMock}
|
|
||||||
pinnedAsset={{
|
|
||||||
decimals: 5,
|
|
||||||
id: '5cfa87844724df6069b94e4c8a6f03af21907d7bc251593d08e4251043ee9f7c',
|
|
||||||
symbol: 'tBTC',
|
|
||||||
name: 'tBTC',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
const depositButton = screen.getByTestId('deposit');
|
|
||||||
expect(depositButton).toBeVisible();
|
|
||||||
await userEvent.click(depositButton);
|
|
||||||
expect(onClickDepositMock).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should apply correct formatting', async () => {
|
it('should apply correct formatting', async () => {
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
<AccountTable
|
<AccountTable
|
||||||
|
|||||||
@@ -88,9 +88,6 @@ describe('TransferForm', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('validates a manually entered address', async () => {
|
it('validates a manually entered address', async () => {
|
||||||
// 1003-TRAN-012
|
|
||||||
// 1003-TRAN-013
|
|
||||||
// 1003-TRAN-004
|
|
||||||
render(<TransferForm {...props} />);
|
render(<TransferForm {...props} />);
|
||||||
submit();
|
submit();
|
||||||
expect(await screen.findAllByText('Required')).toHaveLength(3);
|
expect(await screen.findAllByText('Required')).toHaveLength(3);
|
||||||
@@ -119,11 +116,6 @@ describe('TransferForm', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('validates fields and submits', async () => {
|
it('validates fields and submits', async () => {
|
||||||
// 1003-TRAN-002
|
|
||||||
// 1003-TRAN-003
|
|
||||||
// 1002-WITH-010
|
|
||||||
// 1003-TRAN-011
|
|
||||||
// 1003-TRAN-014
|
|
||||||
render(<TransferForm {...props} />);
|
render(<TransferForm {...props} />);
|
||||||
|
|
||||||
// check current pubkey not shown
|
// check current pubkey not shown
|
||||||
|
|||||||
@@ -4,10 +4,9 @@ import {
|
|||||||
} from '@vegaprotocol/data-provider';
|
} from '@vegaprotocol/data-provider';
|
||||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||||
import { AssetsDocument } from './__generated__/Assets';
|
import { AssetsDocument } from './__generated__/Assets';
|
||||||
import { AssetStatus } from '@vegaprotocol/types';
|
import * as Schema from '@vegaprotocol/types';
|
||||||
import type { AssetsQuery } from './__generated__/Assets';
|
import type { AssetsQuery } from './__generated__/Assets';
|
||||||
import type { Asset } from './asset-data-provider';
|
import type { Asset } from './asset-data-provider';
|
||||||
import { DENY_LIST } from './constants';
|
|
||||||
|
|
||||||
export interface BuiltinAssetSource {
|
export interface BuiltinAssetSource {
|
||||||
__typename: 'BuiltinAsset';
|
__typename: 'BuiltinAsset';
|
||||||
@@ -49,7 +48,6 @@ export const assetsMapProvider = makeDerivedDataProvider<
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Returns a record of assets by id */
|
|
||||||
export const useAssetsMapProvider = () =>
|
export const useAssetsMapProvider = () =>
|
||||||
useDataProvider({
|
useDataProvider({
|
||||||
dataProvider: assetsMapProvider,
|
dataProvider: assetsMapProvider,
|
||||||
@@ -60,32 +58,13 @@ export const enabledAssetsProvider = makeDerivedDataProvider<
|
|||||||
ReturnType<typeof getData>,
|
ReturnType<typeof getData>,
|
||||||
never
|
never
|
||||||
>([assetsProvider], ([assets]) =>
|
>([assetsProvider], ([assets]) =>
|
||||||
(assets as ReturnType<typeof getData>)
|
(assets as ReturnType<typeof getData>).filter(
|
||||||
.filter((a) => a.status === AssetStatus.STATUS_ENABLED)
|
(a) => a.status === Schema.AssetStatus.STATUS_ENABLED
|
||||||
.filter((a) => {
|
)
|
||||||
const env = process.env['NX_VEGA_ENV'];
|
|
||||||
|
|
||||||
if (!env || !DENY_LIST[env]) return true;
|
|
||||||
|
|
||||||
if (DENY_LIST[env].includes(a.id)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Returns all assets */
|
|
||||||
export const useAssetsDataProvider = () =>
|
export const useAssetsDataProvider = () =>
|
||||||
useDataProvider({
|
useDataProvider({
|
||||||
dataProvider: assetsProvider,
|
dataProvider: assetsProvider,
|
||||||
variables: undefined,
|
variables: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
/** Returns assets that are enabled and are not on the deny list */
|
|
||||||
export const useEnabledAssets = () => {
|
|
||||||
return useDataProvider({
|
|
||||||
dataProvider: enabledAssetsProvider,
|
|
||||||
variables: undefined,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,67 +1,4 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
|
||||||
export const WITHDRAW_THRESHOLD_TOOLTIP_TEXT = t(
|
export const WITHDRAW_THRESHOLD_TOOLTIP_TEXT = t(
|
||||||
"The maximum you can withdraw instantly. There's no limit on the size of a withdrawal, but all withdrawals over the threshold will have a delay time added to them"
|
"The maximum you can withdraw instantly. There's no limit on the size of a withdrawal, but all withdrawals over the threshold will have a delay time added to them"
|
||||||
);
|
);
|
||||||
|
|
||||||
// List of defunct and no longer used assets that were created for various testnets
|
|
||||||
export const DENY_LIST: Record<string, string[]> = {
|
|
||||||
TESTNET: [
|
|
||||||
'07f8ce4e3df6c649f3fd794112f545ede9fbd9cfa76a9e380a7502a8d525e4db',
|
|
||||||
'0b87ac58d4af7fc11c8b417153fcb62631cfd9643835ef28db3f5a1caef0b379',
|
|
||||||
'169fd651dd1303fc6f3e1dee8349439140cf467a190bc9a04bff3e617509cfd4',
|
|
||||||
'16ae5dbb1fd7aa2ddef725703bfe66b3647a4da7b844bfdd04e985756f53d9d6',
|
|
||||||
'177e8f6c25a955bd18475084b99b2b1d37f28f3dec393fab7755a7e69c3d8c3b',
|
|
||||||
'20ffe5b1f70364a6b976007d96d306345bc7b880256ddffbbea2c590096ef375',
|
|
||||||
'2792b6ead78a812bd0169b2adbe9c2ea5b8c21510e40b93bf8d2cba6a0838e90',
|
|
||||||
'27c0229ab9edc6a3ba6a3e9716c1b324d6515a3168004256879e2de7c240f453',
|
|
||||||
'285923fed8c66ffb416b163e8ec72d3a87b9b8e2570e7ee7fe97d7092a918bc8',
|
|
||||||
'2af02165ef5ea1d1284b4251117eaae45faa961ec918e3f69b66903a18822387',
|
|
||||||
'2bf406160d06280d92412605eb397d08142736f4e17967e404dc892da278e9da',
|
|
||||||
'373d90d97bd3f97f0c13cf449926cfd7f1ed5f3746c3091e8dd894d60bf5eac8',
|
|
||||||
'3c7d6bd83dac3174923ba65f3af01f42858e267ca5b722fe2b0b3fec14e98ec2',
|
|
||||||
'434b502cabb7a9a36d3036a599d589537da293d8bbab224692629351a7926249',
|
|
||||||
'43bb50d6ba0a871088014cb204079ace4811970ce3d52b15896f6e7a88f900b7',
|
|
||||||
'46e9d23142762db57751b1b507e15747fe9478de504cbc4a0bffd828c39e3776',
|
|
||||||
'4b6957c6a184b002db80c7a4dd811d0e70866be4a4c8fffa4894e78aa132b407',
|
|
||||||
'4cbe1ad4cf232fb2232176e3f440f6e6fdc3ba7b1d95a99cb69116b9e379eae8',
|
|
||||||
'4e4e80abff30cab933b8c4ac6befc618372eb76b2cbddc337eff0b4a3a4d25b8',
|
|
||||||
'51eed0c17e3287996ba6006ce56de2591550ebfad46631f3a0129886967420e1',
|
|
||||||
'522e09779d984ae77b60036f4ecd80f0491f5f0c8cc386a0712b78a020ab62e6',
|
|
||||||
'5c950d4cfe1aaf9174c4358ee5df03f7e6bc556eac06a012cf6c07db154e20c4',
|
|
||||||
'5d8eb4e4e385b85c5c11f47111f315d5d201e04f4ef5e294763bf51036974b91',
|
|
||||||
'5dd5c226e944b4c3df3e8dbbbc473e72fb134f4c5b82a1968cc3cf7c1b40fe9e',
|
|
||||||
'638b4dbe4f3ce5b81546bef29108e8374a7293012c6189ca00f287f8c62a98a5',
|
|
||||||
'644c3a6e258382542a108c4dca16fae93657b6ff40238b2bb55854a125316052',
|
|
||||||
'71ea74ef1aa7582bcdd4ef843b3e79584ded4d16e692f3946cac4c9d61aed4f2',
|
|
||||||
'72f051ea66686ae004086f1ad086866f720f25896319abf3427cae101a58d985',
|
|
||||||
'827bbacde0dd5bfec857933dbbf642147f2afe6a9db0a543839b1b5239ca6786',
|
|
||||||
'83f4970aa1aeab85d01d8a976cbfd0fb3afedf391e18197e8c94c4021b3dffa1',
|
|
||||||
'84a20b554854c9af10ef84d308795dfae455834d1a207798292d66e19fd72c92',
|
|
||||||
'84fff099818dc4f5319477f0812b4341565cfb32ccf735beede734e386b8108f',
|
|
||||||
'85771250f0aa84727a1c01ba8926e59217ca13a7706ba03cf2ad95abbbc2e3bc',
|
|
||||||
'8a246b15bf185065d3f41a710f3eea0fbbe8754a8464501cf4a6d253d69c6423',
|
|
||||||
'8e5553ee4e95db51523640d1d03b00c8ab31dd69d787e169b0d958ef52e19a0a',
|
|
||||||
'92747340d922e9cda5bde17c46af8b409e47d6342f3b9c8fb058a34c6206d373',
|
|
||||||
'9b43c6791aebed5f7b38d91907539173aa278de88294b6422f3dd8282cd2c6e2',
|
|
||||||
'9e0bb9bd7ea2ec51efcdc98b432b6f0b055b2ed7973cfac9b44899d6e6c5deab',
|
|
||||||
'a0cf0835cb5af7bd03c56d12d846eed81c182eab36ea61c074961c115da9c8c4',
|
|
||||||
'a26dc4f6dea91e80d42e4736da7fe3c42f6275df9c4dca48be3ef96850192242',
|
|
||||||
'a4c945aea50ef321eabf8a3bcf0b8bb74288452c4638876ee4df5fb59be4a0ec',
|
|
||||||
'ae28c1043e79bdb95cae0e9919d4436157d14b183e24ab4572793812f3d89222',
|
|
||||||
'b0cf8be4e4d1d2ac5156c2298aa42edc7a74d25b7838d06e2279e94018a47f3f',
|
|
||||||
'b6d11705cb456768ff8271074dd8adcd78bd042a27fc205e9b52f9f945f42ea1',
|
|
||||||
'be478570584211521890fb65a41bded4d2bf29beb0b61ac3cc3b97f828a27b22',
|
|
||||||
'befde6d78ea1c996ae1d959342730220e274992097aeeae00c908a971314e6ae',
|
|
||||||
'bf5b9464d154ffeff57f05f90b223c1045f0754befd01a9a42c99382115e7833',
|
|
||||||
'd2239a7c92bc5c72a959a73ccee7727ba0966f4d88a33d71dbbc26e3f171555d',
|
|
||||||
'd3f9cf6e1711595b4588366a96b725fc039f72a8a28b2e4c1b630016dd2eed39',
|
|
||||||
'd9a9ab00bb79ce7dca7f6329ab627dcea2e53e5c16b85bf4aa8c3980b76fad57',
|
|
||||||
'dd73f7d1e721ef5cba95f454a3019521456c1b43b97084ad29e3e6509148d45d',
|
|
||||||
'eb30d55e90e1f9e5c4727d6fa2a5a8cd36ab9ae9738eb8f3faf53e2bee4861ee',
|
|
||||||
'ede4076aef07fd79502d14326c54ab3911558371baaf697a19d077f4f89de399',
|
|
||||||
'f481abe5700c6940ca996adfa9d07c9d9f07a486a7ce9cdb2e6ee0fa6450fe31',
|
|
||||||
'f7355ec52730c2e6ba02216da8501b4b6934cd363511e9d47ae5b97056d02703',
|
|
||||||
'fdf0ec118d98393a7702cf72e46fc87ad680b152f64b2aac59e093ac2d688fbb',
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ import {
|
|||||||
AccordionChevron,
|
AccordionChevron,
|
||||||
AccordionPanel,
|
AccordionPanel,
|
||||||
Intent,
|
Intent,
|
||||||
ExternalLink,
|
|
||||||
Pill,
|
Pill,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
@@ -386,21 +385,7 @@ export const DealTicketMarginDetails = ({
|
|||||||
value={liquidationPriceEstimateRange}
|
value={liquidationPriceEstimateRange}
|
||||||
formattedValue={liquidationPriceEstimate}
|
formattedValue={liquidationPriceEstimate}
|
||||||
symbol={quoteName}
|
symbol={quoteName}
|
||||||
labelDescription={
|
labelDescription={LIQUIDATION_PRICE_ESTIMATE_TOOLTIP_TEXT}
|
||||||
<>
|
|
||||||
<span>{LIQUIDATION_PRICE_ESTIMATE_TOOLTIP_TEXT}</span>{' '}
|
|
||||||
<span>
|
|
||||||
{t('For full details please see ')}
|
|
||||||
<ExternalLink
|
|
||||||
href={
|
|
||||||
'https://github.com/vegaprotocol/specs/blob/master/non-protocol-specs/0012-NP-LIPE-liquidation-price-estimate.md'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{t('liquidation price estimate documentation.')}
|
|
||||||
</ExternalLink>
|
|
||||||
</span>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
{partyId && (
|
{partyId && (
|
||||||
<AccountBreakdownDialog
|
<AccountBreakdownDialog
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ export const EST_FEES_TOOLTIP_TEXT = t(
|
|||||||
);
|
);
|
||||||
|
|
||||||
export const LIQUIDATION_PRICE_ESTIMATE_TOOLTIP_TEXT = t(
|
export const LIQUIDATION_PRICE_ESTIMATE_TOOLTIP_TEXT = t(
|
||||||
'This is an approximation for the liquidation price for that particular contract position, assuming nothing else changes, which may affect your margin and collateral balances.'
|
'This is an approximation (or a range) for the liquidation price for that particular contract position, assuming nothing else changes, which may affect your margin and collateral balances.'
|
||||||
);
|
);
|
||||||
|
|
||||||
export const EST_SLIPPAGE = t(
|
export const EST_SLIPPAGE = t(
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
||||||
import { AsyncRendererInline } from '@vegaprotocol/ui-toolkit';
|
import { AsyncRendererInline } from '@vegaprotocol/ui-toolkit';
|
||||||
import { DepositManager } from './deposit-manager';
|
import { DepositManager } from './deposit-manager';
|
||||||
import { useEnabledAssets } from '@vegaprotocol/assets';
|
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||||
|
import { enabledAssetsProvider } from '@vegaprotocol/assets';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches data required for the Deposit page
|
* Fetches data required for the Deposit page
|
||||||
*/
|
*/
|
||||||
export const DepositContainer = ({ assetId }: { assetId?: string }) => {
|
export const DepositContainer = ({ assetId }: { assetId?: string }) => {
|
||||||
const { VEGA_ENV } = useEnvironment();
|
const { VEGA_ENV } = useEnvironment();
|
||||||
const { data, error, loading } = useEnabledAssets();
|
const { data, loading, error } = useDataProvider({
|
||||||
|
dataProvider: enabledAssetsProvider,
|
||||||
|
variables: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AsyncRendererInline data={data} loading={loading} error={error}>
|
<AsyncRendererInline data={data} loading={loading} error={error}>
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ describe('Deposit form', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
fireEvent.submit(screen.getByTestId('deposit-form'));
|
fireEvent.submit(screen.getByTestId('deposit-form'));
|
||||||
// 1001-DEPO-004
|
|
||||||
expect(
|
expect(
|
||||||
await screen.findByText(
|
await screen.findByText(
|
||||||
"You can't deposit more than you have in your Ethereum wallet, 5"
|
"You can't deposit more than you have in your Ethereum wallet, 5"
|
||||||
|
|||||||
@@ -6,12 +6,8 @@ fragment FundingPaymentFields on FundingPayment {
|
|||||||
timestamp
|
timestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
query FundingPayments($partyId: ID!, $pagination: Pagination, $marketId: ID) {
|
query FundingPayments($partyId: ID!, $pagination: Pagination) {
|
||||||
fundingPayments(
|
fundingPayments(partyId: $partyId, pagination: $pagination) {
|
||||||
partyId: $partyId
|
|
||||||
pagination: $pagination
|
|
||||||
marketId: $marketId
|
|
||||||
) {
|
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
...FundingPaymentFields
|
...FundingPaymentFields
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ export type FundingPaymentFieldsFragment = { __typename?: 'FundingPayment', mark
|
|||||||
export type FundingPaymentsQueryVariables = Types.Exact<{
|
export type FundingPaymentsQueryVariables = Types.Exact<{
|
||||||
partyId: Types.Scalars['ID'];
|
partyId: Types.Scalars['ID'];
|
||||||
pagination?: Types.InputMaybe<Types.Pagination>;
|
pagination?: Types.InputMaybe<Types.Pagination>;
|
||||||
marketId?: Types.InputMaybe<Types.Scalars['ID']>;
|
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
@@ -24,8 +23,8 @@ export const FundingPaymentFieldsFragmentDoc = gql`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export const FundingPaymentsDocument = gql`
|
export const FundingPaymentsDocument = gql`
|
||||||
query FundingPayments($partyId: ID!, $pagination: Pagination, $marketId: ID) {
|
query FundingPayments($partyId: ID!, $pagination: Pagination) {
|
||||||
fundingPayments(partyId: $partyId, pagination: $pagination, marketId: $marketId) {
|
fundingPayments(partyId: $partyId, pagination: $pagination) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
...FundingPaymentFields
|
...FundingPaymentFields
|
||||||
@@ -56,7 +55,6 @@ export const FundingPaymentsDocument = gql`
|
|||||||
* variables: {
|
* variables: {
|
||||||
* partyId: // value for 'partyId'
|
* partyId: // value for 'partyId'
|
||||||
* pagination: // value for 'pagination'
|
* pagination: // value for 'pagination'
|
||||||
* marketId: // value for 'marketId'
|
|
||||||
* },
|
* },
|
||||||
* });
|
* });
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -8,14 +8,12 @@ import { fundingPaymentsWithMarketProvider } from './funding-payments-data-provi
|
|||||||
|
|
||||||
interface FundingPaymentsManagerProps {
|
interface FundingPaymentsManagerProps {
|
||||||
partyId: string;
|
partyId: string;
|
||||||
marketId?: string;
|
|
||||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||||
gridProps: ReturnType<typeof useDataGridEvents>;
|
gridProps: ReturnType<typeof useDataGridEvents>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FundingPaymentsManager = ({
|
export const FundingPaymentsManager = ({
|
||||||
partyId,
|
partyId,
|
||||||
marketId,
|
|
||||||
onMarketClick,
|
onMarketClick,
|
||||||
gridProps,
|
gridProps,
|
||||||
}: FundingPaymentsManagerProps) => {
|
}: FundingPaymentsManagerProps) => {
|
||||||
@@ -29,7 +27,7 @@ export const FundingPaymentsManager = ({
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
variables: { partyId, marketId },
|
variables: { partyId },
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,2 +1 @@
|
|||||||
export * from './lib/ledger-export-form';
|
export * from './lib/ledger-export-form';
|
||||||
export * from './lib/ledger-download-store';
|
|
||||||
|
|||||||
@@ -1,142 +0,0 @@
|
|||||||
import { create } from 'zustand';
|
|
||||||
import type { ReactNode } from 'react';
|
|
||||||
import { useCallback, useEffect } from 'react';
|
|
||||||
import type { Toast } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import { useToasts, Intent } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
import { subscribeWithSelector } from 'zustand/middleware';
|
|
||||||
|
|
||||||
type DownloadSettings = {
|
|
||||||
title: string;
|
|
||||||
link: string;
|
|
||||||
filename?: string;
|
|
||||||
isDownloaded?: boolean;
|
|
||||||
isChanged?: boolean;
|
|
||||||
isError?: boolean;
|
|
||||||
errorMessage?: string;
|
|
||||||
isDelayed?: boolean;
|
|
||||||
intent?: Intent;
|
|
||||||
blob?: Blob;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type LedgerDownloadFileStore = {
|
|
||||||
queue: DownloadSettings[];
|
|
||||||
hasItem: (link: string) => boolean;
|
|
||||||
removeItem: (link: string) => void;
|
|
||||||
updateQueue: (item: DownloadSettings) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useLedgerDownloadFile = create<LedgerDownloadFileStore>()(
|
|
||||||
subscribeWithSelector((set, get) => ({
|
|
||||||
queue: [],
|
|
||||||
hasItem: (link: string) =>
|
|
||||||
get().queue.findIndex((item) => item.link === link) > -1,
|
|
||||||
removeItem: (link: string) => {
|
|
||||||
const queue = get().queue;
|
|
||||||
const index = queue.findIndex((item) => item.link === link);
|
|
||||||
if (index > -1) {
|
|
||||||
queue.splice(index, 1);
|
|
||||||
set({ queue: [...queue] });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
updateQueue: (newitem: DownloadSettings) => {
|
|
||||||
const queue = get().queue;
|
|
||||||
const index = queue.findIndex((item) => item.link === newitem.link);
|
|
||||||
if (index > -1) {
|
|
||||||
queue[index] = { ...queue[index], ...newitem };
|
|
||||||
set({ queue: [...queue] });
|
|
||||||
} else {
|
|
||||||
set({ queue: [newitem, ...queue] });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
|
|
||||||
const ErrorContent = ({ message }: { message?: string }) => (
|
|
||||||
<>
|
|
||||||
<h4 className="mb-1 text-sm">{t('Something went wrong')}</h4>
|
|
||||||
<p>{message || t('Try again later')}</p>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
const InfoContent = ({ progress = false }) => (
|
|
||||||
<>
|
|
||||||
<p>{t('Please note this can take several minutes.')}</p>
|
|
||||||
<p>{t('You will be notified here when your file is ready.')}</p>
|
|
||||||
<h4 className="my-2">
|
|
||||||
{progress ? t('Still in progress') : t('Download has been started')}
|
|
||||||
</h4>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
export const useLedgerDownloadManager = () => {
|
|
||||||
const queue = useLedgerDownloadFile((store) => store.queue);
|
|
||||||
const updateQueue = useLedgerDownloadFile((store) => store.updateQueue);
|
|
||||||
const removeItem = useLedgerDownloadFile((store) => store.removeItem);
|
|
||||||
const [setToast, updateToast, hasToast, removeToast] = useToasts((store) => [
|
|
||||||
store.setToast,
|
|
||||||
store.update,
|
|
||||||
store.hasToast,
|
|
||||||
store.remove,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const onDownloadClose = useCallback(
|
|
||||||
(id: string) => {
|
|
||||||
removeToast(id);
|
|
||||||
removeItem(id);
|
|
||||||
},
|
|
||||||
[removeToast, removeItem]
|
|
||||||
);
|
|
||||||
|
|
||||||
const createToast = (item: DownloadSettings) => {
|
|
||||||
let content: ReactNode;
|
|
||||||
switch (true) {
|
|
||||||
case item.isError:
|
|
||||||
content = <ErrorContent message={item.errorMessage} />;
|
|
||||||
break;
|
|
||||||
case Boolean(item.blob):
|
|
||||||
content = (
|
|
||||||
<>
|
|
||||||
<h4 className="mb-1 text-sm">{t('Your file is ready')}</h4>
|
|
||||||
<a
|
|
||||||
onClick={() => onDownloadClose(item.link)}
|
|
||||||
href={URL.createObjectURL(item.blob as Blob)}
|
|
||||||
download={item.filename}
|
|
||||||
className="underline"
|
|
||||||
>
|
|
||||||
{t('Get file here')}
|
|
||||||
</a>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
content = <InfoContent progress={item.isDelayed} />;
|
|
||||||
}
|
|
||||||
const toast: Toast = {
|
|
||||||
id: item.link,
|
|
||||||
intent: item.intent || Intent.Primary,
|
|
||||||
content: (
|
|
||||||
<>
|
|
||||||
<h3 className="mb-1 text-md uppercase">{item.title}</h3>
|
|
||||||
{content}
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
onClose: () => onDownloadClose(item.link),
|
|
||||||
loader: !item.isDownloaded && !item.isError,
|
|
||||||
};
|
|
||||||
if (hasToast(toast.id)) {
|
|
||||||
updateToast(toast.id, toast);
|
|
||||||
} else {
|
|
||||||
setToast(toast);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
queue.forEach((item) => {
|
|
||||||
if (item.isChanged) {
|
|
||||||
createToast(item);
|
|
||||||
updateQueue({ ...item, isChanged: false });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, [queue, createToast, updateQueue]);
|
|
||||||
};
|
|
||||||
@@ -1,21 +1,10 @@
|
|||||||
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
|
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
|
||||||
import { createDownloadUrl, LedgerExportForm } from './ledger-export-form';
|
import { createDownloadUrl, LedgerExportForm } from './ledger-export-form';
|
||||||
import { formatForInput, toNanoSeconds } from '@vegaprotocol/utils';
|
import { formatForInput, toNanoSeconds } from '@vegaprotocol/utils';
|
||||||
import {
|
|
||||||
useLedgerDownloadManager,
|
|
||||||
useLedgerDownloadFile,
|
|
||||||
} from './ledger-download-store';
|
|
||||||
import { Intent } from '@vegaprotocol/ui-toolkit';
|
|
||||||
|
|
||||||
const mockSetToast = jest.fn();
|
|
||||||
jest.mock('@vegaprotocol/ui-toolkit', () => ({
|
|
||||||
...jest.requireActual('@vegaprotocol/ui-toolkit'),
|
|
||||||
useToasts: jest.fn(() => [mockSetToast, jest.fn(), jest.fn(() => false)]),
|
|
||||||
}));
|
|
||||||
const vegaUrl = 'https://vega-url.co.uk/querystuff';
|
const vegaUrl = 'https://vega-url.co.uk/querystuff';
|
||||||
|
|
||||||
const mockResponse = {
|
const mockResponse = {
|
||||||
ok: true,
|
|
||||||
headers: { get: jest.fn() },
|
headers: { get: jest.fn() },
|
||||||
blob: () => '',
|
blob: () => '',
|
||||||
};
|
};
|
||||||
@@ -39,7 +28,6 @@ describe('LedgerExportForm', () => {
|
|||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
jest.useRealTimers();
|
jest.useRealTimers();
|
||||||
jest.clearAllMocks();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be properly rendered', async () => {
|
it('should be properly rendered', async () => {
|
||||||
@@ -55,6 +43,8 @@ describe('LedgerExportForm', () => {
|
|||||||
// userEvent does not work with faked timers
|
// userEvent does not work with faked timers
|
||||||
fireEvent.click(screen.getByTestId('ledger-download-button'));
|
fireEvent.click(screen.getByTestId('ledger-download-button'));
|
||||||
|
|
||||||
|
expect(screen.getByTestId('download-spinner')).toBeInTheDocument();
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(global.fetch).toHaveBeenCalledWith(
|
expect(global.fetch).toHaveBeenCalledWith(
|
||||||
`https://vega-url.co.uk/api/v2/ledgerentry/export?partyId=${partyId}&assetId=${
|
`https://vega-url.co.uk/api/v2/ledgerentry/export?partyId=${partyId}&assetId=${
|
||||||
@@ -62,6 +52,9 @@ describe('LedgerExportForm', () => {
|
|||||||
}&dateRange.startTimestamp=1691057410000000000`
|
}&dateRange.startTimestamp=1691057410000000000`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.queryByTestId('download-spinner')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('assetID should be properly change request url', async () => {
|
it('assetID should be properly change request url', async () => {
|
||||||
@@ -82,6 +75,8 @@ describe('LedgerExportForm', () => {
|
|||||||
|
|
||||||
fireEvent.click(screen.getByTestId('ledger-download-button'));
|
fireEvent.click(screen.getByTestId('ledger-download-button'));
|
||||||
|
|
||||||
|
expect(screen.getByTestId('download-spinner')).toBeInTheDocument();
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(global.fetch).toHaveBeenCalledWith(
|
expect(global.fetch).toHaveBeenCalledWith(
|
||||||
`https://vega-url.co.uk/api/v2/ledgerentry/export?partyId=${partyId}&assetId=${
|
`https://vega-url.co.uk/api/v2/ledgerentry/export?partyId=${partyId}&assetId=${
|
||||||
@@ -89,6 +84,9 @@ describe('LedgerExportForm', () => {
|
|||||||
}&dateRange.startTimestamp=1691057410000000000`
|
}&dateRange.startTimestamp=1691057410000000000`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.queryByTestId('download-spinner')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('date-from should properly change request url', async () => {
|
it('date-from should properly change request url', async () => {
|
||||||
@@ -112,6 +110,8 @@ describe('LedgerExportForm', () => {
|
|||||||
|
|
||||||
fireEvent.click(screen.getByTestId('ledger-download-button'));
|
fireEvent.click(screen.getByTestId('ledger-download-button'));
|
||||||
|
|
||||||
|
expect(screen.getByTestId('download-spinner')).toBeInTheDocument();
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(global.fetch).toHaveBeenCalledWith(
|
expect(global.fetch).toHaveBeenCalledWith(
|
||||||
`https://vega-url.co.uk/api/v2/ledgerentry/export?partyId=${partyId}&assetId=${
|
`https://vega-url.co.uk/api/v2/ledgerentry/export?partyId=${partyId}&assetId=${
|
||||||
@@ -119,6 +119,10 @@ describe('LedgerExportForm', () => {
|
|||||||
}&dateRange.startTimestamp=${toNanoSeconds(newDate)}`
|
}&dateRange.startTimestamp=${toNanoSeconds(newDate)}`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.queryByTestId('download-spinner')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('date-to should properly change request url', async () => {
|
it('date-to should properly change request url', async () => {
|
||||||
@@ -152,6 +156,10 @@ describe('LedgerExportForm', () => {
|
|||||||
)}`
|
)}`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.queryByTestId('download-spinner')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Time zone sentence should be properly displayed', () => {
|
it('Time zone sentence should be properly displayed', () => {
|
||||||
@@ -197,50 +205,6 @@ describe('LedgerExportForm', () => {
|
|||||||
screen.queryByText(/^The downloaded file uses the UTC/)
|
screen.queryByText(/^The downloaded file uses the UTC/)
|
||||||
).not.toBeInTheDocument();
|
).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('A toast notification should be displayed', async () => {
|
|
||||||
useLedgerDownloadFile.setState({ queue: [] });
|
|
||||||
const TestWrapper = () => {
|
|
||||||
useLedgerDownloadManager();
|
|
||||||
return (
|
|
||||||
<LedgerExportForm
|
|
||||||
partyId={partyId}
|
|
||||||
vegaUrl={vegaUrl}
|
|
||||||
assets={assetsMock}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
render(<TestWrapper />);
|
|
||||||
expect(screen.getByText('symbol asset-id')).toBeInTheDocument();
|
|
||||||
|
|
||||||
fireEvent.click(screen.getByTestId('ledger-download-button'));
|
|
||||||
|
|
||||||
const link = `https://vega-url.co.uk/api/v2/ledgerentry/export?partyId=${partyId}&assetId=${
|
|
||||||
Object.keys(assetsMock)[0]
|
|
||||||
}&dateRange.startTimestamp=1691057410000000000`;
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(mockSetToast).toHaveBeenCalledWith({
|
|
||||||
id: link,
|
|
||||||
content: expect.any(Object),
|
|
||||||
onClose: expect.any(Function),
|
|
||||||
intent: Intent.Primary,
|
|
||||||
loader: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(global.fetch).toHaveBeenCalledWith(link);
|
|
||||||
});
|
|
||||||
|
|
||||||
mockSetToast.mockClear();
|
|
||||||
(global.fetch as jest.Mock).mockClear();
|
|
||||||
fireEvent.click(screen.getByTestId('ledger-download-button')); // click again
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(mockSetToast).toHaveBeenCalled();
|
|
||||||
expect(global.fetch).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('createDownloadUrl', () => {
|
describe('createDownloadUrl', () => {
|
||||||
|
|||||||
@@ -1,22 +1,17 @@
|
|||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
import { format, subDays } from 'date-fns';
|
import { z } from 'zod';
|
||||||
import {
|
import {
|
||||||
Intent,
|
|
||||||
Loader,
|
|
||||||
TradingButton,
|
TradingButton,
|
||||||
|
Loader,
|
||||||
TradingFormGroup,
|
TradingFormGroup,
|
||||||
TradingInput,
|
TradingInput,
|
||||||
TradingSelect,
|
TradingSelect,
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import { z } from 'zod';
|
import { toNanoSeconds, VEGA_ID_REGEX } from '@vegaprotocol/utils';
|
||||||
import {
|
|
||||||
formatForInput,
|
|
||||||
toNanoSeconds,
|
|
||||||
VEGA_ID_REGEX,
|
|
||||||
} from '@vegaprotocol/utils';
|
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { localLoggerFactory } from '@vegaprotocol/logger';
|
import { localLoggerFactory } from '@vegaprotocol/logger';
|
||||||
import { useLedgerDownloadFile } from './ledger-download-store';
|
import { formatForInput } from '@vegaprotocol/utils';
|
||||||
|
import { subDays } from 'date-fns';
|
||||||
|
|
||||||
const DEFAULT_EXPORT_FILE_NAME = 'ledger_entries.csv';
|
const DEFAULT_EXPORT_FILE_NAME = 'ledger_entries.csv';
|
||||||
|
|
||||||
@@ -78,14 +73,10 @@ export const LedgerExportForm = ({ partyId, vegaUrl, assets }: Props) => {
|
|||||||
const maxFromDate = formatForInput(new Date(dateTo || now.current));
|
const maxFromDate = formatForInput(new Date(dateTo || now.current));
|
||||||
const maxToDate = formatForInput(now.current);
|
const maxToDate = formatForInput(now.current);
|
||||||
|
|
||||||
|
const [isDownloading, setIsDownloading] = useState(false);
|
||||||
const [assetId, setAssetId] = useState(Object.keys(assets)[0]);
|
const [assetId, setAssetId] = useState(Object.keys(assets)[0]);
|
||||||
const protohost = getProtoHost(vegaUrl);
|
const protohost = getProtoHost(vegaUrl);
|
||||||
const disabled = Boolean(!assetId);
|
const disabled = Boolean(!assetId || isDownloading);
|
||||||
|
|
||||||
const hasItem = useLedgerDownloadFile((store) => store.hasItem);
|
|
||||||
const updateDownloadQueue = useLedgerDownloadFile(
|
|
||||||
(store) => store.updateQueue
|
|
||||||
);
|
|
||||||
|
|
||||||
const assetDropDown = (
|
const assetDropDown = (
|
||||||
<TradingSelect
|
<TradingSelect
|
||||||
@@ -96,6 +87,7 @@ export const LedgerExportForm = ({ partyId, vegaUrl, assets }: Props) => {
|
|||||||
}}
|
}}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
data-testid="select-ledger-asset"
|
data-testid="select-ledger-asset"
|
||||||
|
disabled={isDownloading}
|
||||||
>
|
>
|
||||||
{Object.keys(assets).map((assetKey) => (
|
{Object.keys(assets).map((assetKey) => (
|
||||||
<option key={assetKey} value={assetKey}>
|
<option key={assetKey} value={assetKey}>
|
||||||
@@ -105,78 +97,32 @@ export const LedgerExportForm = ({ partyId, vegaUrl, assets }: Props) => {
|
|||||||
</TradingSelect>
|
</TradingSelect>
|
||||||
);
|
);
|
||||||
|
|
||||||
const link = createDownloadUrl({
|
|
||||||
protohost,
|
|
||||||
partyId,
|
|
||||||
assetId,
|
|
||||||
dateFrom,
|
|
||||||
dateTo,
|
|
||||||
});
|
|
||||||
|
|
||||||
const startDownload = async (event: React.FormEvent<HTMLFormElement>) => {
|
const startDownload = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const title = t('Downloading for %s from %s till %s', [
|
|
||||||
assets[assetId],
|
|
||||||
format(new Date(dateFrom), 'dd MMMM yyyy HH:mm'),
|
|
||||||
format(new Date(dateTo || Date.now()), 'dd MMMM yyyy HH:mm'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
const downloadStoreItem = {
|
|
||||||
title,
|
|
||||||
link,
|
|
||||||
isChanged: true,
|
|
||||||
};
|
|
||||||
if (hasItem(link)) {
|
|
||||||
updateDownloadQueue(downloadStoreItem);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const ts = setTimeout(() => {
|
|
||||||
updateDownloadQueue({
|
|
||||||
...downloadStoreItem,
|
|
||||||
intent: Intent.Warning,
|
|
||||||
isDelayed: true,
|
|
||||||
isChanged: true,
|
|
||||||
});
|
|
||||||
}, 1000 * 30);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
updateDownloadQueue(downloadStoreItem);
|
const link = createDownloadUrl({
|
||||||
|
protohost,
|
||||||
|
partyId,
|
||||||
|
assetId,
|
||||||
|
dateFrom,
|
||||||
|
dateTo,
|
||||||
|
});
|
||||||
|
setIsDownloading(true);
|
||||||
const resp = await fetch(link);
|
const resp = await fetch(link);
|
||||||
if (!resp?.ok) {
|
|
||||||
if (resp?.status === 429) {
|
|
||||||
throw new Error('Too many requests. Try again later.');
|
|
||||||
}
|
|
||||||
throw new Error('Download of ledger entries failed');
|
|
||||||
}
|
|
||||||
const { headers } = resp;
|
const { headers } = resp;
|
||||||
const nameHeader = headers.get('content-disposition');
|
const nameHeader = headers.get('content-disposition');
|
||||||
const filename = nameHeader?.split('=').pop() ?? DEFAULT_EXPORT_FILE_NAME;
|
const filename = nameHeader?.split('=').pop() ?? DEFAULT_EXPORT_FILE_NAME;
|
||||||
updateDownloadQueue({
|
|
||||||
...downloadStoreItem,
|
|
||||||
filename,
|
|
||||||
});
|
|
||||||
const blob = await resp.blob();
|
const blob = await resp.blob();
|
||||||
if (blob) {
|
if (blob) {
|
||||||
updateDownloadQueue({
|
const link = document.createElement('a');
|
||||||
...downloadStoreItem,
|
link.href = URL.createObjectURL(blob);
|
||||||
blob,
|
link.download = filename;
|
||||||
isDownloaded: true,
|
link.click();
|
||||||
isChanged: true,
|
|
||||||
intent: Intent.Success,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
localLoggerFactory({ application: 'ledger' }).error('Download file', err);
|
localLoggerFactory({ application: 'ledger' }).error('Download file', err);
|
||||||
updateDownloadQueue({
|
|
||||||
...downloadStoreItem,
|
|
||||||
intent: Intent.Danger,
|
|
||||||
isError: true,
|
|
||||||
isChanged: true,
|
|
||||||
errorMessage: (err as Error).message || undefined,
|
|
||||||
});
|
|
||||||
} finally {
|
} finally {
|
||||||
clearTimeout(ts);
|
setIsDownloading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -199,6 +145,7 @@ export const LedgerExportForm = ({ partyId, vegaUrl, assets }: Props) => {
|
|||||||
id="date-from"
|
id="date-from"
|
||||||
value={dateFrom}
|
value={dateFrom}
|
||||||
onChange={(e) => setDateFrom(e.target.value)}
|
onChange={(e) => setDateFrom(e.target.value)}
|
||||||
|
disabled={disabled}
|
||||||
max={maxFromDate}
|
max={maxFromDate}
|
||||||
/>
|
/>
|
||||||
</TradingFormGroup>
|
</TradingFormGroup>
|
||||||
@@ -209,10 +156,19 @@ export const LedgerExportForm = ({ partyId, vegaUrl, assets }: Props) => {
|
|||||||
id="date-to"
|
id="date-to"
|
||||||
value={dateTo}
|
value={dateTo}
|
||||||
onChange={(e) => setDateTo(e.target.value)}
|
onChange={(e) => setDateTo(e.target.value)}
|
||||||
|
disabled={disabled}
|
||||||
max={maxToDate}
|
max={maxToDate}
|
||||||
/>
|
/>
|
||||||
</TradingFormGroup>
|
</TradingFormGroup>
|
||||||
<div className="relative text-sm" title={t('Download all to .csv file')}>
|
<div className="relative text-sm" title={t('Download all to .csv file')}>
|
||||||
|
{isDownloading && (
|
||||||
|
<div
|
||||||
|
className="absolute flex items-center justify-center w-full h-full"
|
||||||
|
data-testid="download-spinner"
|
||||||
|
>
|
||||||
|
<Loader size="small" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<TradingButton
|
<TradingButton
|
||||||
fill
|
fill
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
|||||||
@@ -128,26 +128,6 @@ export const NetworkParams = {
|
|||||||
'governance_proposal_VolumeDiscountProgram_requiredMajority',
|
'governance_proposal_VolumeDiscountProgram_requiredMajority',
|
||||||
governance_proposal_VolumeDiscountProgram_requiredParticipation:
|
governance_proposal_VolumeDiscountProgram_requiredParticipation:
|
||||||
'governance_proposal_VolumeDiscountProgram_requiredParticipation',
|
'governance_proposal_VolumeDiscountProgram_requiredParticipation',
|
||||||
governance_proposal_transfer_maxAmount:
|
|
||||||
'governance_proposal_transfer_maxAmount',
|
|
||||||
governance_proposal_transfer_maxClose:
|
|
||||||
'governance_proposal_transfer_maxClose',
|
|
||||||
governance_proposal_transfer_maxEnact:
|
|
||||||
'governance_proposal_transfer_maxEnact',
|
|
||||||
governance_proposal_transfer_maxFraction:
|
|
||||||
'governance_proposal_transfer_maxFraction',
|
|
||||||
governance_proposal_transfer_minClose:
|
|
||||||
'governance_proposal_transfer_minClose',
|
|
||||||
governance_proposal_transfer_minEnact:
|
|
||||||
'governance_proposal_transfer_minEnact',
|
|
||||||
governance_proposal_transfer_minProposerBalance:
|
|
||||||
'governance_proposal_transfer_minProposerBalance',
|
|
||||||
governance_proposal_transfer_minVoterBalance:
|
|
||||||
'governance_proposal_transfer_minVoterBalance',
|
|
||||||
governance_proposal_transfer_requiredMajority:
|
|
||||||
'governance_proposal_transfer_requiredMajority',
|
|
||||||
governance_proposal_transfer_requiredParticipation:
|
|
||||||
'governance_proposal_transfer_requiredParticipation',
|
|
||||||
validators_delegation_minAmount: 'validators_delegation_minAmount',
|
validators_delegation_minAmount: 'validators_delegation_minAmount',
|
||||||
spam_protection_minimumWithdrawalQuantumMultiple:
|
spam_protection_minimumWithdrawalQuantumMultiple:
|
||||||
'spam_protection_minimumWithdrawalQuantumMultiple',
|
'spam_protection_minimumWithdrawalQuantumMultiple',
|
||||||
|
|||||||
@@ -104,13 +104,7 @@ describe('TradesTable', () => {
|
|||||||
const [firstDateCell, secondDateCell] = createdAtCells;
|
const [firstDateCell, secondDateCell] = createdAtCells;
|
||||||
|
|
||||||
// Compare the times as strings
|
// Compare the times as strings
|
||||||
// Note: I've changed it form literal strings to this because of the
|
expect(firstDateCell.textContent).toBe('6:59:00 PM');
|
||||||
// locale mismatch. Mocking the format also resulted with strange "space" diff.
|
expect(secondDateCell.textContent).toBe('7:00:00 PM');
|
||||||
expect(firstDateCell.textContent).toBe(
|
|
||||||
getTimeFormat().format(new Date(trade2.createdAt))
|
|
||||||
);
|
|
||||||
expect(secondDateCell.textContent).toBe(
|
|
||||||
getTimeFormat().format(new Date(trade.createdAt))
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Generated
+7
-20
@@ -4605,8 +4605,6 @@ export type QueryreferralFeeStatsArgs = {
|
|||||||
assetId?: InputMaybe<Scalars['ID']>;
|
assetId?: InputMaybe<Scalars['ID']>;
|
||||||
epoch?: InputMaybe<Scalars['Int']>;
|
epoch?: InputMaybe<Scalars['Int']>;
|
||||||
marketId?: InputMaybe<Scalars['ID']>;
|
marketId?: InputMaybe<Scalars['ID']>;
|
||||||
referee?: InputMaybe<Scalars['ID']>;
|
|
||||||
referrer?: InputMaybe<Scalars['ID']>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -4622,9 +4620,9 @@ export type QueryreferralSetRefereesArgs = {
|
|||||||
/** Queries allow a caller to read data and filter data via GraphQL. */
|
/** Queries allow a caller to read data and filter data via GraphQL. */
|
||||||
export type QueryreferralSetStatsArgs = {
|
export type QueryreferralSetStatsArgs = {
|
||||||
epoch?: InputMaybe<Scalars['Int']>;
|
epoch?: InputMaybe<Scalars['Int']>;
|
||||||
|
id: Scalars['ID'];
|
||||||
pagination?: InputMaybe<Pagination>;
|
pagination?: InputMaybe<Pagination>;
|
||||||
partyId?: InputMaybe<Scalars['ID']>;
|
partyId?: InputMaybe<Scalars['ID']>;
|
||||||
setId?: InputMaybe<Scalars['ID']>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -4884,10 +4882,6 @@ export type ReferralSetStats = {
|
|||||||
referralSetRunningNotionalTakerVolume: Scalars['String'];
|
referralSetRunningNotionalTakerVolume: Scalars['String'];
|
||||||
/** Reward factor applied to the party. */
|
/** Reward factor applied to the party. */
|
||||||
rewardFactor: Scalars['String'];
|
rewardFactor: Scalars['String'];
|
||||||
/** The proportion of the referees taker fees to be rewarded to the referrer. */
|
|
||||||
rewardsFactorMultiplier: Scalars['String'];
|
|
||||||
/** The multiplier applied to the referral reward factor when calculating referral rewards due to the referrer. */
|
|
||||||
rewardsMultiplier: Scalars['String'];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Connection type for retrieving cursor-based paginated referral set statistics information */
|
/** Connection type for retrieving cursor-based paginated referral set statistics information */
|
||||||
@@ -6149,18 +6143,7 @@ export type UpdateProductConfiguration = UpdateFutureProduct | UpdatePerpetualPr
|
|||||||
|
|
||||||
export type UpdateReferralProgram = {
|
export type UpdateReferralProgram = {
|
||||||
__typename?: 'UpdateReferralProgram';
|
__typename?: 'UpdateReferralProgram';
|
||||||
/** Defined tiers in increasing order. First element will give Tier 1, second element will give Tier 2, etc. */
|
changes: ReferralProgram;
|
||||||
benefitTiers: Array<BenefitTier>;
|
|
||||||
/** Timestamp as RFC3339, after which when the current epoch ends, the programs will end and benefits will be disabled. */
|
|
||||||
endOfProgramTimestamp: Scalars['String'];
|
|
||||||
/**
|
|
||||||
* Defined staking tiers in increasing order. First element will give Tier 1,
|
|
||||||
* second element will give Tier 2, and so on. Determines the level of
|
|
||||||
* benefit a party can expect based on their staking.
|
|
||||||
*/
|
|
||||||
stakingTiers: Array<StakingTier>;
|
|
||||||
/** Number of epochs over which to evaluate a referral set's running volume. */
|
|
||||||
windowLength: Scalars['Int'];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Update an existing spot market on Vega */
|
/** Update an existing spot market on Vega */
|
||||||
@@ -6192,7 +6175,11 @@ export type UpdateVolumeDiscountProgram = {
|
|||||||
benefitTiers: Array<VolumeBenefitTier>;
|
benefitTiers: Array<VolumeBenefitTier>;
|
||||||
/** The end time of the program */
|
/** The end time of the program */
|
||||||
endOfProgramTimestamp: Scalars['Timestamp'];
|
endOfProgramTimestamp: Scalars['Timestamp'];
|
||||||
/** The window length to consider for the volume discount program */
|
/** ID of the proposal that created the discount program */
|
||||||
|
id: Scalars['ID'];
|
||||||
|
/** The current version of the volume discount program */
|
||||||
|
version: Scalars['Int'];
|
||||||
|
/** The window legnth to consider for the volume discount program */
|
||||||
windowLength: Scalars['Int'];
|
windowLength: Scalars['Int'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import { useMemo } from 'react';
|
||||||
import styles from './loader.module.scss';
|
import styles from './loader.module.scss';
|
||||||
|
|
||||||
export const pseudoRandom = (seed: number) => {
|
const pseudoRandom = (seed: number) => {
|
||||||
let value = seed;
|
let value = seed;
|
||||||
return () => {
|
return () => {
|
||||||
value = (value * 16807) % 2147483647;
|
value = (value * 16807) % 2147483647;
|
||||||
@@ -9,8 +10,6 @@ export const pseudoRandom = (seed: number) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const generate = pseudoRandom(1);
|
|
||||||
|
|
||||||
export interface LoaderProps {
|
export interface LoaderProps {
|
||||||
size?: 'small' | 'large';
|
size?: 'small' | 'large';
|
||||||
forceTheme?: 'dark' | 'light';
|
forceTheme?: 'dark' | 'light';
|
||||||
@@ -28,6 +27,8 @@ export const Loader = ({ size = 'large', forceTheme }: LoaderProps) => {
|
|||||||
size === 'small' ? 'w-[15px] h-[15px]' : 'w-[50px] h-[50px]';
|
size === 'small' ? 'w-[15px] h-[15px]' : 'w-[50px] h-[50px]';
|
||||||
const items = size === 'small' ? 9 : 25;
|
const items = size === 'small' ? 9 : 25;
|
||||||
|
|
||||||
|
const generate = useMemo(() => pseudoRandom(1), []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center" data-testid="loader">
|
<div className="flex flex-col items-center" data-testid="loader">
|
||||||
<div className={`${wrapperClasses} flex flex-wrap`}>
|
<div className={`${wrapperClasses} flex flex-wrap`}>
|
||||||
|
|||||||
@@ -8,13 +8,5 @@ When I enter on ledger entries tab in portfolio page
|
|||||||
- in the form **Must** see a dropdown for select an asset, in which reports will be downloaded (<a name="7007-LEEN-002" href="#7007-LEEN-002">7007-LEEN-002</a>)
|
- in the form **Must** see a dropdown for select an asset, in which reports will be downloaded (<a name="7007-LEEN-002" href="#7007-LEEN-002">7007-LEEN-002</a>)
|
||||||
- in the form **Must** see inputs for select time period, in which reports will be downloaded (<a name="7007-LEEN-003" href="#7007-LEEN-003">7007-LEEN-003</a>)
|
- in the form **Must** see inputs for select time period, in which reports will be downloaded (<a name="7007-LEEN-003" href="#7007-LEEN-003">7007-LEEN-003</a>)
|
||||||
- default preselected period **Must** be the last 7 days (<a name="7007-LEEN-004" href="#7007-LEEN-004">7007-LEEN-004</a>)
|
- default preselected period **Must** be the last 7 days (<a name="7007-LEEN-004" href="#7007-LEEN-004">7007-LEEN-004</a>)
|
||||||
|
- during download a loader component **Must** be visible and all interactive elements in the form **Must** be disabled (<a name="7007-LEEN-005" href="#7007-LEEN-005">7007-LEEN-005</a>)
|
||||||
- **Must** see a note about time in file are in UTC and timezone of the user relative to UTC (<a name="7007-LEEN-006" href="#7007-LEEN-006">7007-LEEN-006</a>)
|
- **Must** see a note about time in file are in UTC and timezone of the user relative to UTC (<a name="7007-LEEN-006" href="#7007-LEEN-006">7007-LEEN-006</a>)
|
||||||
- As a user, I **must** see a message saying that this can take several minutes (<a name="7007-LEEN-007" href="#7007-LEEN-007">7007-LEEN-007</a>)
|
|
||||||
- After half a minute, the message is updated to say something like 'Still in progress' (<a name="7007-LEEN-008" href="#7007-LEEN-008">7007-LEEN-008</a>)
|
|
||||||
- A toast is shown when the download is complete (<a name="7007-LEEN-009" href="#7007-LEEN-009">7007-LEEN-009</a>)
|
|
||||||
- The download button should never be disabled
|
|
||||||
- If user tries to download file which is already in download: (<a name="7007-LEEN-010" href="#7007-LEEN-010">7007-LEEN-010</a>)
|
|
||||||
- if notification stayed open, nothing happens
|
|
||||||
- If notification was closed, will be open, no any new request will be fired
|
|
||||||
- If something has changed in the form (asset, dates, `Date.now`) new download will start.
|
|
||||||
- The state of the download form should be in sync with the download itself if you navigate away from the page or reload (<a name="7007-LEEN-011" href="#7007-LEEN-011">7007-LEEN-011</a>)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user