Compare commits

...
24 changed files with 272 additions and 98 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 547 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

-22
View File
@@ -1,22 +0,0 @@
{
"name": "Vega Protocol - Trading",
"short_name": "Console",
"description": "Vega Protocol - Trading dApp",
"start_url": "/",
"display": "standalone",
"orientation": "portrait",
"theme_color": "#000000",
"background_color": "#ffffff",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
}
]
}
@@ -170,6 +170,9 @@ const cacheConfig: InMemoryCacheConfig = {
Fees: {
keyFields: false,
},
PartyProfile: {
keyFields: ['partyId'],
},
// The folling types are cached by the data provider and not by apollo
PositionUpdate: {
keyFields: false,
+40 -3
View File
@@ -8,6 +8,12 @@ import {
mockConfig,
MockedWalletProvider,
} from '@vegaprotocol/wallet-react/testing';
import { MockedProvider, type MockedResponse } from '@apollo/react-testing';
import {
PartyProfilesDocument,
type PartyProfilesQuery,
type PartyProfilesQueryVariables,
} from '../vega-wallet-connect-button/__generated__/PartyProfiles';
jest.mock('@vegaprotocol/proposals', () => ({
ProtocolUpgradeCountdown: () => null,
@@ -24,15 +30,45 @@ describe('Navbar', () => {
publicKey: '2'.repeat(64),
},
];
const key1Alias = 'key 1 alias';
const marketId = 'abc';
const navbarContent = 'navbar-menu-content';
const partyProfilesMock: MockedResponse<
PartyProfilesQuery,
PartyProfilesQueryVariables
> = {
request: {
query: PartyProfilesDocument,
variables: {
partyIds: mockKeys.map((k) => k.publicKey),
},
},
result: {
data: {
partiesProfilesConnection: {
edges: [
{
node: {
partyId: mockKeys[0].publicKey,
alias: key1Alias,
metadata: [],
},
},
],
},
},
},
};
const renderComponent = (initialEntries?: string[]) => {
return render(
<MemoryRouter initialEntries={initialEntries}>
<MockedWalletProvider>
<Navbar />
</MockedWalletProvider>
<MockedProvider mocks={[partyProfilesMock]}>
<MockedWalletProvider>
<Navbar />
</MockedWalletProvider>
</MockedProvider>
</MemoryRouter>
);
};
@@ -140,6 +176,7 @@ describe('Navbar', () => {
const activeKey = within(menu.getByTestId(/key-1+-mobile/));
expect(activeKey.getByText(mockKeys[0].name)).toBeInTheDocument();
expect(activeKey.getByTestId('icon-tick')).toBeInTheDocument();
expect(screen.getByText(key1Alias)).toBeInTheDocument();
const inactiveKey = within(menu.getByTestId(/key-2+-mobile/));
await userEvent.click(inactiveKey.getByText(mockKeys[1].name));
@@ -31,39 +31,27 @@ export const ProfileDialog = () => {
const pubKey = useProfileDialogStore((store) => store.pubKey);
const setOpen = useProfileDialogStore((store) => store.setOpen);
const { send, status, error, reset } = useSimpleTransaction({
onSuccess: () => {
refetch();
},
});
const profileEdge = data?.partiesProfilesConnection?.edges.find(
(e) => e.node.partyId === pubKey
);
const sendTx = (field: FormFields) => {
send({
updatePartyProfile: {
alias: field.alias,
metadata: [],
},
});
};
return (
<Dialog
open={open}
onChange={() => {
setOpen(undefined);
reset();
}}
title={t('Edit profile')}
>
<ProfileForm
<ProfileFormContainer
profile={profileEdge?.node}
status={status}
error={error}
onSubmit={sendTx}
onSuccess={() => {
refetch();
setTimeout(() => {
setOpen(undefined);
}, 1000);
}}
/>
</Dialog>
);
@@ -77,6 +65,32 @@ type Profile = NonNullable<
PartyProfilesQuery['partiesProfilesConnection']
>['edges'][number]['node'];
const ProfileFormContainer = ({
profile,
onSuccess,
}: {
profile: Profile | undefined;
onSuccess: () => void;
}) => {
const { send, status, error } = useSimpleTransaction({ onSuccess });
const sendTx = (field: FormFields) => {
send({
updatePartyProfile: {
alias: field.alias,
metadata: [],
},
});
};
return (
<ProfileForm
profile={profile}
status={status}
error={error}
onSubmit={sendTx}
/>
);
};
const ProfileForm = ({
profile,
onSubmit,
@@ -114,6 +128,14 @@ const ProfileForm = ({
const errorMessage = errors.alias?.message || error;
if (status === 'confirmed') {
return (
<p className="mt-2 mb-4 text-sm text-vega-green-600 dark:text-vega-green">
{t('Profile updated')}
</p>
);
}
return (
<form onSubmit={handleSubmit(onSubmit)} className="mt-3">
<FormGroup label="Alias" labelFor="alias">
@@ -131,12 +153,6 @@ const ProfileForm = ({
</p>
</InputError>
)}
{status === 'confirmed' && (
<p className="mt-2 mb-4 text-sm text-success">
{t('Profile updated')}
</p>
)}
</FormGroup>
<TradingButton
type="submit"
@@ -94,6 +94,7 @@ export const VegaWalletConnectButton = ({
<KeypairRadioGroup
pubKey={pubKey}
pubKeys={pubKeys}
activeKey={activeKey?.publicKey}
onSelect={selectPubKey}
/>
<TradingDropdownSeparator />
@@ -138,15 +139,18 @@ export const VegaWalletConnectButton = ({
const KeypairRadioGroup = ({
pubKey,
pubKeys,
activeKey,
onSelect,
}: {
pubKey: string | undefined;
pubKeys: Key[];
activeKey: string | undefined;
onSelect: (pubKey: string) => void;
}) => {
const { data } = usePartyProfilesQuery({
variables: { partyIds: pubKeys.map((pk) => pk.publicKey) },
skip: pubKeys.length <= 0,
fetchPolicy: 'cache-and-network',
});
return (
@@ -156,14 +160,27 @@ const KeypairRadioGroup = ({
(e) => e.node.partyId === pk.publicKey
);
return (
<KeypairItem key={pk.publicKey} pk={pk} alias={profile?.node.alias} />
<KeypairItem
key={pk.publicKey}
pk={pk}
isActive={activeKey === pk.publicKey}
alias={profile?.node.alias}
/>
);
})}
</TradingDropdownRadioGroup>
);
};
const KeypairItem = ({ pk, alias }: { pk: Key; alias: string | undefined }) => {
const KeypairItem = ({
pk,
isActive,
alias,
}: {
pk: Key;
alias: string | undefined;
isActive: boolean;
}) => {
const t = useT();
const [copied, setCopied] = useCopyTimeout();
const setOpen = useProfileDialogStore((store) => store.setOpen);
@@ -194,8 +211,13 @@ const KeypairItem = ({ pk, alias }: { pk: Key; alias: string | undefined }) => {
data-testid={`key-${pk.publicKey}`}
>
<Tooltip description={t('Public facing key alias. Click to edit')}>
<button data-testid="alias" onClick={() => setOpen(pk.publicKey)}>
<button
data-testid="alias"
onClick={() => setOpen(pk.publicKey)}
className="flex items-center gap-1"
>
{alias ? alias : t('No alias')}
{isActive && <VegaIcon name={VegaIconNames.EDIT} />}
</button>
</Tooltip>
</div>
@@ -12,6 +12,8 @@ import CopyToClipboard from 'react-copy-to-clipboard';
import { ViewType, useSidebar } from '../sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { useT } from '../../lib/use-t';
import { usePartyProfilesQuery } from '../vega-wallet-connect-button/__generated__/PartyProfiles';
import { useProfileDialogStore } from '../../stores/profile-dialog-store';
export const VegaWalletMenu = ({
setMenu,
@@ -23,6 +25,12 @@ export const VegaWalletMenu = ({
const currentRouteId = useGetCurrentRouteId();
const setViews = useSidebar((store) => store.setViews);
const { data } = usePartyProfilesQuery({
variables: { partyIds: pubKeys.map((pk) => pk.publicKey) },
skip: pubKeys.length <= 0,
fetchPolicy: 'cache-and-network',
});
const activeKey = useMemo(() => {
return pubKeys?.find((pk) => pk.publicKey === pubKey);
}, [pubKey, pubKeys]);
@@ -37,14 +45,21 @@ export const VegaWalletMenu = ({
return (
<div>
<div className="grow my-4" role="list">
{(pubKeys || []).map((pk) => (
<KeypairListItem
key={pk.publicKey}
pk={pk}
isActive={activeKey?.publicKey === pk.publicKey}
onSelectItem={onSelectItem}
/>
))}
{(pubKeys || []).map((pk) => {
const profile = data?.partiesProfilesConnection?.edges.find(
(e) => e.node.partyId === pk.publicKey
);
return (
<KeypairListItem
key={pk.publicKey}
pk={pk}
isActive={activeKey?.publicKey === pk.publicKey}
onSelectItem={onSelectItem}
alias={profile?.node.alias}
setMenu={setMenu}
/>
);
})}
</div>
<div className="flex flex-col gap-2 m-4">
@@ -72,18 +87,23 @@ export const VegaWalletMenu = ({
const KeypairListItem = ({
pk,
isActive,
alias,
onSelectItem,
setMenu,
}: {
pk: Key;
isActive: boolean;
alias: string | undefined;
onSelectItem: (pk: string) => void;
setMenu: (open: 'nav' | 'wallet' | null) => void;
}) => {
const t = useT();
const [copied, setCopied] = useCopyTimeout();
const setOpen = useProfileDialogStore((store) => store.setOpen);
return (
<div
className="flex flex-col w-full ml-4 mr-2 mb-4"
className="flex flex-col w-full px-4 mb-4"
data-testid={`key-${pk.publicKey}-mobile`}
>
<span className="flex gap-2 items-center mr-2">
@@ -106,6 +126,24 @@ const KeypairListItem = ({
</CopyToClipboard>
{copied && <span className="text-xs">{t('Copied')}</span>}
</span>
<span
className="flex gap-2 items-center"
data-testid={`key-${pk.publicKey}`}
>
<span className="truncate">{alias ? alias : t('No alias')}</span>
{isActive && (
<button
data-testid="alias"
onClick={() => {
setOpen(pk.publicKey);
setMenu(null);
}}
className="flex items-center gap-1"
>
<VegaIcon name={VegaIconNames.EDIT} />
</button>
)}
</span>
</div>
);
};
@@ -186,7 +186,7 @@ def test_reward_history(
def test_staking_reward(
setup_environment: Tuple[Page, str, str],
) -> None:
):
page, tDAI_market, tDAI_asset_id = setup_environment
expect(page.get_by_test_id("active-rewards-card")).to_have_count(2)
staking_reward_card = page.get_by_test_id("active-rewards-card").nth(1)
+29 -7
View File
@@ -4,11 +4,10 @@ export default function Document() {
return (
<>
<Head>
{/*
meta tags
- next advised against using _document for this, so they exist in our
- single page index.page.tsx
*/}
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=no"
/>
{/* preload fonts */}
<link
@@ -18,15 +17,38 @@ export default function Document() {
type="font/woff"
/>
<link
rel="preload"
href="/AlphaLyrae.woff2"
as="font"
type="font/woff2"
/>
{/* icons */}
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<link rel="apple-touch-icon" content="/favicon.ico" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/favicon-16x16.png"
/>
{/* scripts */}
<script src="/theme-setter.js" type="text/javascript" async />
{/* manifest */}
<link rel="manifest" href="/apps/trading/public/manifest.json" />
<link rel="manifest" href="/manifest.json" />
</Head>
<Html>
<body
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 15 KiB

+6 -6
View File
@@ -9,14 +9,14 @@
"background_color": "#ffffff",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "cover.png",
"type": "image/png",
"sizes": "192x192"
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
@@ -42,6 +42,7 @@ import type {
EthCallSpec,
MarketTradingMode,
SignerKind,
SuccessorConfiguration,
} from '@vegaprotocol/types';
import {
ConditionOperatorMapping,
@@ -69,11 +70,7 @@ import {
useSuccessorMarketIdsQuery,
useSuccessorMarketQuery,
} from '../../__generated__';
import {
useSuccessorMarketProposalDetailsQuery,
type SuccessorMarketProposalDetailsQuery,
type SingleProposal,
} from '@vegaprotocol/proposals';
import { useSuccessorMarketProposalDetailsQuery } from '@vegaprotocol/proposals';
import { getQuoteName, getAsset } from '../../market-utils';
import classNames from 'classnames';
import compact from 'lodash/compact';
@@ -254,15 +251,27 @@ export const KeyDetailsInfoPanel = ({
skip: !featureFlags.SUCCESSOR_MARKETS || !market.proposal?.id,
});
const successorProposal = successorProposalDetails?.proposal as
| SingleProposal<SuccessorMarketProposalDetailsQuery['proposal']>
| undefined;
let successorConfiguration: SuccessorConfiguration | false = false;
const successorConfiguration =
successorProposal?.terms.change.__typename === 'NewMarket' &&
successorProposal.terms.change.successorConfiguration?.__typename ===
'SuccessorConfiguration' &&
successorProposal.terms.change.successorConfiguration;
if (successorProposalDetails?.proposal?.__typename === 'Proposal') {
successorConfiguration =
successorProposalDetails.proposal.terms.change.__typename ===
'NewMarket' &&
successorProposalDetails.proposal.terms.change.successorConfiguration
?.__typename === 'SuccessorConfiguration' &&
successorProposalDetails.proposal.terms.change.successorConfiguration;
} else if (
successorProposalDetails?.proposal?.__typename === 'BatchProposal'
) {
const subTerms = successorProposalDetails.proposal.batchTerms?.changes.find(
(c) => c?.change.__typename === 'NewMarket'
);
successorConfiguration =
subTerms?.change.__typename === 'NewMarket' &&
subTerms?.change.successorConfiguration?.__typename ===
'SuccessorConfiguration' &&
subTerms?.change?.successorConfiguration;
}
// The following queries are needed as the parent market could also have been a successor market.
// Note: the parent market is only passed to this component if the successor markets flag is enabled,
@@ -281,9 +290,27 @@ export const KeyDetailsInfoPanel = ({
},
skip: !parentMarket?.proposal?.id,
});
const parentProposal = parentSuccessorProposalDetails?.proposal as
| SingleProposal<SuccessorMarketProposalDetailsQuery['proposal']>
| undefined;
let parentSuccessorConfig: SuccessorConfiguration | undefined = undefined;
if (parentSuccessorProposalDetails?.proposal?.__typename === 'Proposal') {
const parentProposal = parentSuccessorProposalDetails?.proposal;
parentSuccessorConfig =
parentProposal.terms.change.__typename === 'NewMarket'
? parentProposal.terms.change.successorConfiguration || undefined
: undefined;
} else if (
parentSuccessorProposalDetails?.proposal?.__typename === 'BatchProposal'
) {
const subTerms =
parentSuccessorProposalDetails.proposal.batchTerms?.changes.find(
(c) => c?.change.__typename === 'NewMarket'
);
parentSuccessorConfig =
subTerms?.change.__typename === 'NewMarket'
? subTerms.change.successorConfiguration || undefined
: undefined;
}
const assetDecimals = getAsset(market).decimals;
@@ -339,10 +366,7 @@ export const KeyDetailsInfoPanel = ({
parentMarket && {
name: parentMarket?.tradableInstrument?.instrument?.name,
parentMarketID: grandparentMarketIdData?.market?.parentMarketID,
insurancePoolFraction:
parentProposal?.terms.change.__typename === 'NewMarket' &&
parentProposal?.terms.change.successorConfiguration
?.insurancePoolFraction,
insurancePoolFraction: parentSuccessorConfig?.insurancePoolFraction,
status:
parentMarket?.state && MarketStateMapping[parentMarket.state],
tradingMode:
@@ -68,6 +68,7 @@ query SuccessorMarketProposalDetails($proposalId: ID!) {
terms {
change {
... on NewMarket {
__typename
successorConfiguration {
parentMarketId
insurancePoolFraction
@@ -76,6 +77,22 @@ query SuccessorMarketProposalDetails($proposalId: ID!) {
}
}
}
... on BatchProposal {
id
batchTerms {
changes {
change {
... on NewMarket {
__typename
successorConfiguration {
parentMarketId
insurancePoolFraction
}
}
}
}
}
}
}
}
@@ -32,7 +32,7 @@ export type SuccessorMarketProposalDetailsQueryVariables = Types.Exact<{
}>;
export type SuccessorMarketProposalDetailsQuery = { __typename?: 'Query', proposal?: { __typename?: 'BatchProposal' } | { __typename?: 'Proposal', id?: string | null, terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', successorConfiguration?: { __typename?: 'SuccessorConfiguration', parentMarketId: string, insurancePoolFraction: string } | null } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } } } | null };
export type SuccessorMarketProposalDetailsQuery = { __typename?: 'Query', proposal?: { __typename?: 'BatchProposal', id?: string | null, batchTerms?: { __typename?: 'BatchProposalTerms', changes: Array<{ __typename?: 'BatchProposalTermsChange', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename: 'NewMarket', successorConfiguration?: { __typename?: 'SuccessorConfiguration', parentMarketId: string, insurancePoolFraction: string } | null } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } } | null> } | null } | { __typename?: 'Proposal', id?: string | null, terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename: 'NewMarket', successorConfiguration?: { __typename?: 'SuccessorConfiguration', parentMarketId: string, insurancePoolFraction: string } | null } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } } } | null };
export type InstrumentDetailsQueryVariables = Types.Exact<{
marketId: Types.Scalars['ID'];
@@ -204,6 +204,7 @@ export const SuccessorMarketProposalDetailsDocument = gql`
terms {
change {
... on NewMarket {
__typename
successorConfiguration {
parentMarketId
insurancePoolFraction
@@ -212,6 +213,22 @@ export const SuccessorMarketProposalDetailsDocument = gql`
}
}
}
... on BatchProposal {
id
batchTerms {
changes {
change {
... on NewMarket {
__typename
successorConfiguration {
parentMarketId
insurancePoolFraction
}
}
}
}
}
}
}
}
`;