Compare commits
6 Commits
fix/5941-p
...
develop
Author | SHA1 | Date | |
---|---|---|---|
88264d890d | |||
|
26b78f878b | ||
|
054c0377b4 | ||
|
29bcbd06fb | ||
|
1d721dc748 | ||
|
1d71a839b3 |
@ -4,6 +4,5 @@ tmp/*
|
|||||||
.dockerignore
|
.dockerignore
|
||||||
dockerfiles
|
dockerfiles
|
||||||
node_modules
|
node_modules
|
||||||
.git
|
|
||||||
.github
|
.github
|
||||||
.vscode
|
.vscode
|
||||||
|
@ -38,6 +38,7 @@ import { differenceInHours, format, formatDistanceToNowStrict } from 'date-fns';
|
|||||||
import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats';
|
import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats';
|
||||||
import { MarketName } from '../proposal/market-name';
|
import { MarketName } from '../proposal/market-name';
|
||||||
import { Indicator } from '../proposal/indicator';
|
import { Indicator } from '../proposal/indicator';
|
||||||
|
import { type ProposalNode } from '../proposal/proposal-utils';
|
||||||
|
|
||||||
const ProposalTypeTags = ({
|
const ProposalTypeTags = ({
|
||||||
proposal,
|
proposal,
|
||||||
@ -540,10 +541,12 @@ const BatchProposalStateText = ({
|
|||||||
|
|
||||||
export const ProposalHeader = ({
|
export const ProposalHeader = ({
|
||||||
proposal,
|
proposal,
|
||||||
|
restData,
|
||||||
isListItem = true,
|
isListItem = true,
|
||||||
voteState,
|
voteState,
|
||||||
}: {
|
}: {
|
||||||
proposal: Proposal | BatchProposal;
|
proposal: Proposal | BatchProposal;
|
||||||
|
restData?: ProposalNode | null;
|
||||||
isListItem?: boolean;
|
isListItem?: boolean;
|
||||||
voteState?: VoteState | null;
|
voteState?: VoteState | null;
|
||||||
}) => {
|
}) => {
|
||||||
@ -595,7 +598,7 @@ export const ProposalHeader = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<ProposalDetails proposal={proposal} />
|
<ProposalDetails proposal={proposal} />
|
||||||
<VoteBreakdown proposal={proposal} />
|
<VoteBreakdown proposal={proposal} restData={restData} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -91,6 +91,28 @@ export type ProposalNode = {
|
|||||||
proposal: ProposalData;
|
proposal: ProposalData;
|
||||||
proposalType: ProposalNodeType;
|
proposalType: ProposalNodeType;
|
||||||
proposals: SubProposalData[];
|
proposals: SubProposalData[];
|
||||||
|
yes?: [
|
||||||
|
{
|
||||||
|
partyId: string;
|
||||||
|
elsPerMarket?: [
|
||||||
|
{
|
||||||
|
marketId: string;
|
||||||
|
els: string;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
no?: [
|
||||||
|
{
|
||||||
|
partyId: string;
|
||||||
|
elsPerMarket?: [
|
||||||
|
{
|
||||||
|
marketId: string;
|
||||||
|
els: string;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
type SingleProposalNode = ProposalNode & {
|
type SingleProposalNode = ProposalNode & {
|
||||||
|
@ -48,6 +48,7 @@ export const Proposal = ({ proposal, restData }: ProposalProps) => {
|
|||||||
|
|
||||||
<ProposalHeader
|
<ProposalHeader
|
||||||
proposal={proposal}
|
proposal={proposal}
|
||||||
|
restData={restData}
|
||||||
isListItem={false}
|
isListItem={false}
|
||||||
voteState={voteState}
|
voteState={voteState}
|
||||||
/>
|
/>
|
||||||
|
@ -17,6 +17,7 @@ import {
|
|||||||
import { useBatchVoteInformation } from '../../hooks/use-vote-information';
|
import { useBatchVoteInformation } from '../../hooks/use-vote-information';
|
||||||
import { MarketName } from '../proposal/market-name';
|
import { MarketName } from '../proposal/market-name';
|
||||||
import { Indicator } from '../proposal/indicator';
|
import { Indicator } from '../proposal/indicator';
|
||||||
|
import { type ProposalNode } from '../proposal/proposal-utils';
|
||||||
|
|
||||||
export const CompactVotes = ({ number }: { number: BigNumber }) => (
|
export const CompactVotes = ({ number }: { number: BigNumber }) => (
|
||||||
<CompactNumber
|
<CompactNumber
|
||||||
@ -110,24 +111,64 @@ const Status = ({ reached, threshold, text, testId }: StatusProps) => {
|
|||||||
|
|
||||||
export const VoteBreakdown = ({
|
export const VoteBreakdown = ({
|
||||||
proposal,
|
proposal,
|
||||||
|
restData,
|
||||||
}: {
|
}: {
|
||||||
proposal: Proposal | BatchProposal;
|
proposal: Proposal | BatchProposal;
|
||||||
|
restData?: ProposalNode | null;
|
||||||
}) => {
|
}) => {
|
||||||
if (proposal.__typename === 'Proposal') {
|
if (proposal.__typename === 'Proposal') {
|
||||||
return <VoteBreakdownNormal proposal={proposal} />;
|
return <VoteBreakdownNormal proposal={proposal} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (proposal.__typename === 'BatchProposal') {
|
if (proposal.__typename === 'BatchProposal') {
|
||||||
return <VoteBreakdownBatch proposal={proposal} />;
|
return <VoteBreakdownBatch proposal={proposal} restData={restData} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const VoteBreakdownBatch = ({ proposal }: { proposal: BatchProposal }) => {
|
const VoteBreakdownBatch = ({
|
||||||
|
proposal,
|
||||||
|
restData,
|
||||||
|
}: {
|
||||||
|
proposal: BatchProposal;
|
||||||
|
restData?: ProposalNode | null;
|
||||||
|
}) => {
|
||||||
const [fullBreakdown, setFullBreakdown] = useState(false);
|
const [fullBreakdown, setFullBreakdown] = useState(false);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const yesELS =
|
||||||
|
restData?.yes?.reduce((all, y) => {
|
||||||
|
if (y.elsPerMarket) {
|
||||||
|
y.elsPerMarket.forEach((item) => {
|
||||||
|
const share = Number(item.els);
|
||||||
|
if (all[item.marketId]) {
|
||||||
|
all[item.marketId].push(share);
|
||||||
|
} else {
|
||||||
|
all[item.marketId] = [share];
|
||||||
|
}
|
||||||
|
return all;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return all;
|
||||||
|
}, {} as Record<string, number[]>) || {};
|
||||||
|
|
||||||
|
const noELS =
|
||||||
|
restData?.no?.reduce((all, y) => {
|
||||||
|
if (y.elsPerMarket) {
|
||||||
|
y.elsPerMarket.forEach((item) => {
|
||||||
|
const share = Number(item.els);
|
||||||
|
if (all[item.marketId]) {
|
||||||
|
all[item.marketId].push(share);
|
||||||
|
} else {
|
||||||
|
all[item.marketId] = [share];
|
||||||
|
}
|
||||||
|
return all;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return all;
|
||||||
|
}, {} as Record<string, number[]>) || {};
|
||||||
|
|
||||||
const voteInfo = useBatchVoteInformation({
|
const voteInfo = useBatchVoteInformation({
|
||||||
terms: compact(
|
terms: compact(
|
||||||
proposal.subProposals ? proposal.subProposals.map((p) => p?.terms) : []
|
proposal.subProposals ? proposal.subProposals.map((p) => p?.terms) : []
|
||||||
@ -194,6 +235,8 @@ const VoteBreakdownBatch = ({ proposal }: { proposal: BatchProposal }) => {
|
|||||||
proposal={proposal}
|
proposal={proposal}
|
||||||
votes={proposal.votes}
|
votes={proposal.votes}
|
||||||
terms={p.terms}
|
terms={p.terms}
|
||||||
|
yesELS={yesELS}
|
||||||
|
noELS={noELS}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@ -254,6 +297,8 @@ const VoteBreakdownBatch = ({ proposal }: { proposal: BatchProposal }) => {
|
|||||||
proposal={proposal}
|
proposal={proposal}
|
||||||
votes={proposal.votes}
|
votes={proposal.votes}
|
||||||
terms={p.terms}
|
terms={p.terms}
|
||||||
|
yesELS={yesELS}
|
||||||
|
noELS={noELS}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@ -271,17 +316,17 @@ const VoteBreakdownBatchSubProposal = ({
|
|||||||
votes,
|
votes,
|
||||||
terms,
|
terms,
|
||||||
indicator,
|
indicator,
|
||||||
|
yesELS,
|
||||||
|
noELS,
|
||||||
}: {
|
}: {
|
||||||
proposal: BatchProposal;
|
proposal: BatchProposal;
|
||||||
votes: VoteFieldsFragment;
|
votes: VoteFieldsFragment;
|
||||||
terms: ProposalTermsFieldsFragment;
|
terms: ProposalTermsFieldsFragment;
|
||||||
indicator?: number;
|
indicator?: number;
|
||||||
|
yesELS: Record<string, number[]>;
|
||||||
|
noELS: Record<string, number[]>;
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const voteInfo = useVoteInformation({
|
|
||||||
votes,
|
|
||||||
terms,
|
|
||||||
});
|
|
||||||
|
|
||||||
const isProposalOpen = proposal?.state === ProposalState.STATE_OPEN;
|
const isProposalOpen = proposal?.state === ProposalState.STATE_OPEN;
|
||||||
const isUpdateMarket = terms?.change?.__typename === 'UpdateMarket';
|
const isUpdateMarket = terms?.change?.__typename === 'UpdateMarket';
|
||||||
@ -294,6 +339,15 @@ const VoteBreakdownBatchSubProposal = ({
|
|||||||
marketId = terms.change.market.id;
|
marketId = terms.change.market.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const voteInfo = useVoteInformation({
|
||||||
|
votes,
|
||||||
|
terms,
|
||||||
|
// yes votes ELS for this specific proposal (market)
|
||||||
|
yesELS: marketId ? yesELS[marketId] : undefined,
|
||||||
|
// no votes ELS for this specific proposal (market)
|
||||||
|
noELS: marketId ? noELS[marketId] : undefined,
|
||||||
|
});
|
||||||
|
|
||||||
const marketName = marketId ? (
|
const marketName = marketId ? (
|
||||||
<>
|
<>
|
||||||
: <MarketName marketId={marketId} />
|
: <MarketName marketId={marketId} />
|
||||||
|
@ -8,13 +8,18 @@ import {
|
|||||||
type VoteFieldsFragment,
|
type VoteFieldsFragment,
|
||||||
} from '../__generated__/Proposals';
|
} from '../__generated__/Proposals';
|
||||||
import { type ProposalChangeType } from '../types';
|
import { type ProposalChangeType } from '../types';
|
||||||
|
import sum from 'lodash/sum';
|
||||||
|
|
||||||
export const useVoteInformation = ({
|
export const useVoteInformation = ({
|
||||||
votes,
|
votes,
|
||||||
terms,
|
terms,
|
||||||
|
yesELS,
|
||||||
|
noELS,
|
||||||
}: {
|
}: {
|
||||||
votes: VoteFieldsFragment;
|
votes: VoteFieldsFragment;
|
||||||
terms: ProposalTermsFieldsFragment;
|
terms: ProposalTermsFieldsFragment;
|
||||||
|
yesELS?: number[];
|
||||||
|
noELS?: number[];
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
appState: { totalSupply, decimals },
|
appState: { totalSupply, decimals },
|
||||||
@ -31,7 +36,9 @@ export const useVoteInformation = ({
|
|||||||
paramsForChange,
|
paramsForChange,
|
||||||
votes,
|
votes,
|
||||||
totalSupply,
|
totalSupply,
|
||||||
decimals
|
decimals,
|
||||||
|
yesELS,
|
||||||
|
noELS
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -72,7 +79,11 @@ const getVoteData = (
|
|||||||
},
|
},
|
||||||
votes: ProposalFieldsFragment['votes'],
|
votes: ProposalFieldsFragment['votes'],
|
||||||
totalSupply: BigNumber,
|
totalSupply: BigNumber,
|
||||||
decimals: number
|
decimals: number,
|
||||||
|
/** A list of ELS yes votes */
|
||||||
|
yesELS?: number[],
|
||||||
|
/** A list if ELS no votes */
|
||||||
|
noELS?: number[]
|
||||||
) => {
|
) => {
|
||||||
const requiredMajorityPercentage = params.requiredMajority
|
const requiredMajorityPercentage = params.requiredMajority
|
||||||
? new BigNumber(params.requiredMajority).times(100)
|
? new BigNumber(params.requiredMajority).times(100)
|
||||||
@ -86,17 +97,31 @@ const getVoteData = (
|
|||||||
addDecimal(votes.no.totalTokens ?? 0, decimals)
|
addDecimal(votes.no.totalTokens ?? 0, decimals)
|
||||||
);
|
);
|
||||||
|
|
||||||
const noEquityLikeShareWeight = !votes.no.totalEquityLikeShareWeight
|
let noEquityLikeShareWeight = !votes.no.totalEquityLikeShareWeight
|
||||||
? new BigNumber(0)
|
? new BigNumber(0)
|
||||||
: new BigNumber(votes.no.totalEquityLikeShareWeight).times(100);
|
: new BigNumber(votes.no.totalEquityLikeShareWeight).times(100);
|
||||||
|
// there's no meaningful `totalEquityLikeShareWeight` in batch proposals,
|
||||||
|
// it has to be deduced from `elsPerMarket` of `no` votes of given proposal
|
||||||
|
// data. (by REST DATA)
|
||||||
|
if (noELS != null) {
|
||||||
|
const noTotalELS = sum(noELS);
|
||||||
|
noEquityLikeShareWeight = new BigNumber(noTotalELS).times(100);
|
||||||
|
}
|
||||||
|
|
||||||
const yesTokens = new BigNumber(
|
const yesTokens = new BigNumber(
|
||||||
addDecimal(votes.yes.totalTokens ?? 0, decimals)
|
addDecimal(votes.yes.totalTokens ?? 0, decimals)
|
||||||
);
|
);
|
||||||
|
|
||||||
const yesEquityLikeShareWeight = !votes.yes.totalEquityLikeShareWeight
|
let yesEquityLikeShareWeight = !votes.yes.totalEquityLikeShareWeight
|
||||||
? new BigNumber(0)
|
? new BigNumber(0)
|
||||||
: new BigNumber(votes.yes.totalEquityLikeShareWeight).times(100);
|
: new BigNumber(votes.yes.totalEquityLikeShareWeight).times(100);
|
||||||
|
// there's no meaningful `totalEquityLikeShareWeight` in batch proposals,
|
||||||
|
// it has to be deduced from `elsPerMarket` of `yes` votes of given proposal
|
||||||
|
// data. (by REST DATA)
|
||||||
|
if (noELS != null) {
|
||||||
|
const yesTotalELS = sum(yesELS);
|
||||||
|
yesEquityLikeShareWeight = new BigNumber(yesTotalELS).times(100);
|
||||||
|
}
|
||||||
|
|
||||||
const totalTokensVoted = yesTokens.plus(noTokens);
|
const totalTokensVoted = yesTokens.plus(noTokens);
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 547 B |
Before Width: | Height: | Size: 1.5 KiB |
@ -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: {
|
Fees: {
|
||||||
keyFields: false,
|
keyFields: false,
|
||||||
},
|
},
|
||||||
|
PartyProfile: {
|
||||||
|
keyFields: ['partyId'],
|
||||||
|
},
|
||||||
// The folling types are cached by the data provider and not by apollo
|
// The folling types are cached by the data provider and not by apollo
|
||||||
PositionUpdate: {
|
PositionUpdate: {
|
||||||
keyFields: false,
|
keyFields: false,
|
||||||
|
@ -8,6 +8,12 @@ import {
|
|||||||
mockConfig,
|
mockConfig,
|
||||||
MockedWalletProvider,
|
MockedWalletProvider,
|
||||||
} from '@vegaprotocol/wallet-react/testing';
|
} 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', () => ({
|
jest.mock('@vegaprotocol/proposals', () => ({
|
||||||
ProtocolUpgradeCountdown: () => null,
|
ProtocolUpgradeCountdown: () => null,
|
||||||
@ -24,15 +30,45 @@ describe('Navbar', () => {
|
|||||||
publicKey: '2'.repeat(64),
|
publicKey: '2'.repeat(64),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
const key1Alias = 'key 1 alias';
|
||||||
const marketId = 'abc';
|
const marketId = 'abc';
|
||||||
const navbarContent = 'navbar-menu-content';
|
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[]) => {
|
const renderComponent = (initialEntries?: string[]) => {
|
||||||
return render(
|
return render(
|
||||||
<MemoryRouter initialEntries={initialEntries}>
|
<MemoryRouter initialEntries={initialEntries}>
|
||||||
<MockedWalletProvider>
|
<MockedProvider mocks={[partyProfilesMock]}>
|
||||||
<Navbar />
|
<MockedWalletProvider>
|
||||||
</MockedWalletProvider>
|
<Navbar />
|
||||||
|
</MockedWalletProvider>
|
||||||
|
</MockedProvider>
|
||||||
</MemoryRouter>
|
</MemoryRouter>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -140,6 +176,7 @@ describe('Navbar', () => {
|
|||||||
const activeKey = within(menu.getByTestId(/key-1+-mobile/));
|
const activeKey = within(menu.getByTestId(/key-1+-mobile/));
|
||||||
expect(activeKey.getByText(mockKeys[0].name)).toBeInTheDocument();
|
expect(activeKey.getByText(mockKeys[0].name)).toBeInTheDocument();
|
||||||
expect(activeKey.getByTestId('icon-tick')).toBeInTheDocument();
|
expect(activeKey.getByTestId('icon-tick')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText(key1Alias)).toBeInTheDocument();
|
||||||
|
|
||||||
const inactiveKey = within(menu.getByTestId(/key-2+-mobile/));
|
const inactiveKey = within(menu.getByTestId(/key-2+-mobile/));
|
||||||
await userEvent.click(inactiveKey.getByText(mockKeys[1].name));
|
await userEvent.click(inactiveKey.getByText(mockKeys[1].name));
|
||||||
|
@ -31,39 +31,27 @@ export const ProfileDialog = () => {
|
|||||||
const pubKey = useProfileDialogStore((store) => store.pubKey);
|
const pubKey = useProfileDialogStore((store) => store.pubKey);
|
||||||
const setOpen = useProfileDialogStore((store) => store.setOpen);
|
const setOpen = useProfileDialogStore((store) => store.setOpen);
|
||||||
|
|
||||||
const { send, status, error, reset } = useSimpleTransaction({
|
|
||||||
onSuccess: () => {
|
|
||||||
refetch();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const profileEdge = data?.partiesProfilesConnection?.edges.find(
|
const profileEdge = data?.partiesProfilesConnection?.edges.find(
|
||||||
(e) => e.node.partyId === pubKey
|
(e) => e.node.partyId === pubKey
|
||||||
);
|
);
|
||||||
|
|
||||||
const sendTx = (field: FormFields) => {
|
|
||||||
send({
|
|
||||||
updatePartyProfile: {
|
|
||||||
alias: field.alias,
|
|
||||||
metadata: [],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
open={open}
|
open={open}
|
||||||
onChange={() => {
|
onChange={() => {
|
||||||
setOpen(undefined);
|
setOpen(undefined);
|
||||||
reset();
|
|
||||||
}}
|
}}
|
||||||
title={t('Edit profile')}
|
title={t('Edit profile')}
|
||||||
>
|
>
|
||||||
<ProfileForm
|
<ProfileFormContainer
|
||||||
profile={profileEdge?.node}
|
profile={profileEdge?.node}
|
||||||
status={status}
|
onSuccess={() => {
|
||||||
error={error}
|
refetch();
|
||||||
onSubmit={sendTx}
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setOpen(undefined);
|
||||||
|
}, 1000);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
@ -77,6 +65,32 @@ type Profile = NonNullable<
|
|||||||
PartyProfilesQuery['partiesProfilesConnection']
|
PartyProfilesQuery['partiesProfilesConnection']
|
||||||
>['edges'][number]['node'];
|
>['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 = ({
|
const ProfileForm = ({
|
||||||
profile,
|
profile,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
@ -114,6 +128,14 @@ const ProfileForm = ({
|
|||||||
|
|
||||||
const errorMessage = errors.alias?.message || error;
|
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 (
|
return (
|
||||||
<form onSubmit={handleSubmit(onSubmit)} className="mt-3">
|
<form onSubmit={handleSubmit(onSubmit)} className="mt-3">
|
||||||
<FormGroup label="Alias" labelFor="alias">
|
<FormGroup label="Alias" labelFor="alias">
|
||||||
@ -131,12 +153,6 @@ const ProfileForm = ({
|
|||||||
</p>
|
</p>
|
||||||
</InputError>
|
</InputError>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{status === 'confirmed' && (
|
|
||||||
<p className="mt-2 mb-4 text-sm text-success">
|
|
||||||
{t('Profile updated')}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<TradingButton
|
<TradingButton
|
||||||
type="submit"
|
type="submit"
|
||||||
|
@ -94,6 +94,7 @@ export const VegaWalletConnectButton = ({
|
|||||||
<KeypairRadioGroup
|
<KeypairRadioGroup
|
||||||
pubKey={pubKey}
|
pubKey={pubKey}
|
||||||
pubKeys={pubKeys}
|
pubKeys={pubKeys}
|
||||||
|
activeKey={activeKey?.publicKey}
|
||||||
onSelect={selectPubKey}
|
onSelect={selectPubKey}
|
||||||
/>
|
/>
|
||||||
<TradingDropdownSeparator />
|
<TradingDropdownSeparator />
|
||||||
@ -138,15 +139,18 @@ export const VegaWalletConnectButton = ({
|
|||||||
const KeypairRadioGroup = ({
|
const KeypairRadioGroup = ({
|
||||||
pubKey,
|
pubKey,
|
||||||
pubKeys,
|
pubKeys,
|
||||||
|
activeKey,
|
||||||
onSelect,
|
onSelect,
|
||||||
}: {
|
}: {
|
||||||
pubKey: string | undefined;
|
pubKey: string | undefined;
|
||||||
pubKeys: Key[];
|
pubKeys: Key[];
|
||||||
|
activeKey: string | undefined;
|
||||||
onSelect: (pubKey: string) => void;
|
onSelect: (pubKey: string) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { data } = usePartyProfilesQuery({
|
const { data } = usePartyProfilesQuery({
|
||||||
variables: { partyIds: pubKeys.map((pk) => pk.publicKey) },
|
variables: { partyIds: pubKeys.map((pk) => pk.publicKey) },
|
||||||
skip: pubKeys.length <= 0,
|
skip: pubKeys.length <= 0,
|
||||||
|
fetchPolicy: 'cache-and-network',
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -156,14 +160,27 @@ const KeypairRadioGroup = ({
|
|||||||
(e) => e.node.partyId === pk.publicKey
|
(e) => e.node.partyId === pk.publicKey
|
||||||
);
|
);
|
||||||
return (
|
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>
|
</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 t = useT();
|
||||||
const [copied, setCopied] = useCopyTimeout();
|
const [copied, setCopied] = useCopyTimeout();
|
||||||
const setOpen = useProfileDialogStore((store) => store.setOpen);
|
const setOpen = useProfileDialogStore((store) => store.setOpen);
|
||||||
@ -194,8 +211,13 @@ const KeypairItem = ({ pk, alias }: { pk: Key; alias: string | undefined }) => {
|
|||||||
data-testid={`key-${pk.publicKey}`}
|
data-testid={`key-${pk.publicKey}`}
|
||||||
>
|
>
|
||||||
<Tooltip description={t('Public facing key alias. Click to edit')}>
|
<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')}
|
{alias ? alias : t('No alias')}
|
||||||
|
{isActive && <VegaIcon name={VegaIconNames.EDIT} />}
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
@ -12,6 +12,8 @@ import CopyToClipboard from 'react-copy-to-clipboard';
|
|||||||
import { ViewType, useSidebar } from '../sidebar';
|
import { ViewType, useSidebar } from '../sidebar';
|
||||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||||
import { useT } from '../../lib/use-t';
|
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 = ({
|
export const VegaWalletMenu = ({
|
||||||
setMenu,
|
setMenu,
|
||||||
@ -23,6 +25,12 @@ export const VegaWalletMenu = ({
|
|||||||
const currentRouteId = useGetCurrentRouteId();
|
const currentRouteId = useGetCurrentRouteId();
|
||||||
const setViews = useSidebar((store) => store.setViews);
|
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(() => {
|
const activeKey = useMemo(() => {
|
||||||
return pubKeys?.find((pk) => pk.publicKey === pubKey);
|
return pubKeys?.find((pk) => pk.publicKey === pubKey);
|
||||||
}, [pubKey, pubKeys]);
|
}, [pubKey, pubKeys]);
|
||||||
@ -37,14 +45,21 @@ export const VegaWalletMenu = ({
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="grow my-4" role="list">
|
<div className="grow my-4" role="list">
|
||||||
{(pubKeys || []).map((pk) => (
|
{(pubKeys || []).map((pk) => {
|
||||||
<KeypairListItem
|
const profile = data?.partiesProfilesConnection?.edges.find(
|
||||||
key={pk.publicKey}
|
(e) => e.node.partyId === pk.publicKey
|
||||||
pk={pk}
|
);
|
||||||
isActive={activeKey?.publicKey === pk.publicKey}
|
return (
|
||||||
onSelectItem={onSelectItem}
|
<KeypairListItem
|
||||||
/>
|
key={pk.publicKey}
|
||||||
))}
|
pk={pk}
|
||||||
|
isActive={activeKey?.publicKey === pk.publicKey}
|
||||||
|
onSelectItem={onSelectItem}
|
||||||
|
alias={profile?.node.alias}
|
||||||
|
setMenu={setMenu}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col gap-2 m-4">
|
<div className="flex flex-col gap-2 m-4">
|
||||||
@ -72,18 +87,23 @@ export const VegaWalletMenu = ({
|
|||||||
const KeypairListItem = ({
|
const KeypairListItem = ({
|
||||||
pk,
|
pk,
|
||||||
isActive,
|
isActive,
|
||||||
|
alias,
|
||||||
onSelectItem,
|
onSelectItem,
|
||||||
|
setMenu,
|
||||||
}: {
|
}: {
|
||||||
pk: Key;
|
pk: Key;
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
|
alias: string | undefined;
|
||||||
onSelectItem: (pk: string) => void;
|
onSelectItem: (pk: string) => void;
|
||||||
|
setMenu: (open: 'nav' | 'wallet' | null) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const t = useT();
|
const t = useT();
|
||||||
const [copied, setCopied] = useCopyTimeout();
|
const [copied, setCopied] = useCopyTimeout();
|
||||||
|
const setOpen = useProfileDialogStore((store) => store.setOpen);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<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`}
|
data-testid={`key-${pk.publicKey}-mobile`}
|
||||||
>
|
>
|
||||||
<span className="flex gap-2 items-center mr-2">
|
<span className="flex gap-2 items-center mr-2">
|
||||||
@ -106,6 +126,24 @@ const KeypairListItem = ({
|
|||||||
</CopyToClipboard>
|
</CopyToClipboard>
|
||||||
{copied && <span className="text-xs">{t('Copied')}</span>}
|
{copied && <span className="text-xs">{t('Copied')}</span>}
|
||||||
</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>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -186,7 +186,7 @@ def test_reward_history(
|
|||||||
|
|
||||||
def test_staking_reward(
|
def test_staking_reward(
|
||||||
setup_environment: Tuple[Page, str, str],
|
setup_environment: Tuple[Page, str, str],
|
||||||
) -> None:
|
):
|
||||||
page, tDAI_market, tDAI_asset_id = setup_environment
|
page, tDAI_market, tDAI_asset_id = setup_environment
|
||||||
expect(page.get_by_test_id("active-rewards-card")).to_have_count(2)
|
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)
|
staking_reward_card = page.get_by_test_id("active-rewards-card").nth(1)
|
||||||
|
@ -39,6 +39,8 @@ const nextConfig = {
|
|||||||
GIT_COMMIT: commitHash,
|
GIT_COMMIT: commitHash,
|
||||||
GIT_TAG: tag,
|
GIT_TAG: tag,
|
||||||
},
|
},
|
||||||
|
basePath: '/apps/vegas', // Set the base path
|
||||||
|
assetPrefix: '/apps/vegas', // Set the asset prefix
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = SENTRY_AUTH_TOKEN
|
module.exports = SENTRY_AUTH_TOKEN
|
||||||
|
@ -4,11 +4,10 @@ export default function Document() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
{/*
|
<meta
|
||||||
meta tags
|
name="viewport"
|
||||||
- next advised against using _document for this, so they exist in our
|
content="width=device-width, initial-scale=1.0, user-scalable=no"
|
||||||
- single page index.page.tsx
|
/>
|
||||||
*/}
|
|
||||||
|
|
||||||
{/* preload fonts */}
|
{/* preload fonts */}
|
||||||
<link
|
<link
|
||||||
@ -18,15 +17,38 @@ export default function Document() {
|
|||||||
type="font/woff"
|
type="font/woff"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<link
|
||||||
|
rel="preload"
|
||||||
|
href="/AlphaLyrae.woff2"
|
||||||
|
as="font"
|
||||||
|
type="font/woff2"
|
||||||
|
/>
|
||||||
|
|
||||||
{/* icons */}
|
{/* icons */}
|
||||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
<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 */}
|
{/* scripts */}
|
||||||
<script src="/theme-setter.js" type="text/javascript" async />
|
<script src="/theme-setter.js" type="text/javascript" async />
|
||||||
|
|
||||||
{/* manifest */}
|
{/* manifest */}
|
||||||
<link rel="manifest" href="/apps/trading/public/manifest.json" />
|
<link rel="manifest" href="/manifest.json" />
|
||||||
</Head>
|
</Head>
|
||||||
<Html>
|
<Html>
|
||||||
<body
|
<body
|
||||||
|
BIN
apps/trading/public/android-chrome-192x192.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
apps/trading/public/android-chrome-512x512.png
Normal file
After Width: | Height: | Size: 8.7 KiB |
BIN
apps/trading/public/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
apps/trading/public/favicon-16x16.png
Normal file
After Width: | Height: | Size: 265 B |
BIN
apps/trading/public/favicon-32x32.png
Normal file
After Width: | Height: | Size: 281 B |
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 15 KiB |
@ -9,14 +9,14 @@
|
|||||||
"background_color": "#ffffff",
|
"background_color": "#ffffff",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "favicon.ico",
|
"src": "/android-chrome-192x192.png",
|
||||||
"sizes": "64x64 32x32 24x24 16x16",
|
"sizes": "192x192",
|
||||||
"type": "image/x-icon"
|
"type": "image/png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "cover.png",
|
"src": "/android-chrome-512x512.png",
|
||||||
"type": "image/png",
|
"sizes": "512x512",
|
||||||
"sizes": "192x192"
|
"type": "image/png"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,21 @@
|
|||||||
"options": {
|
"options": {
|
||||||
"jestConfig": "libs/types/jest.config.ts"
|
"jestConfig": "libs/types/jest.config.ts"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"generate": {
|
||||||
|
"executor": "nx:run-commands",
|
||||||
|
"options": {
|
||||||
|
"commands": ["npx graphql-codegen --config=libs/types/codegen.yml"],
|
||||||
|
"parallel": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"local-registry": {
|
||||||
|
"executor": "@nx/js:verdaccio",
|
||||||
|
"options": {
|
||||||
|
"port": 4873,
|
||||||
|
"config": ".verdaccio/config.yml",
|
||||||
|
"storage": "tmp/local-registry/storage"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tags": []
|
"tags": []
|
||||||
|
@ -35,6 +35,14 @@
|
|||||||
"options": {
|
"options": {
|
||||||
"jestConfig": "libs/utils/jest.config.ts"
|
"jestConfig": "libs/utils/jest.config.ts"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"local-registry": {
|
||||||
|
"executor": "@nx/js:verdaccio",
|
||||||
|
"options": {
|
||||||
|
"port": 4873,
|
||||||
|
"config": ".verdaccio/config.yml",
|
||||||
|
"storage": "tmp/local-registry/storage"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tags": []
|
"tags": []
|
||||||
|
@ -35,6 +35,14 @@
|
|||||||
"options": {
|
"options": {
|
||||||
"jestConfig": "libs/wallet/jest.config.ts"
|
"jestConfig": "libs/wallet/jest.config.ts"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"local-registry": {
|
||||||
|
"executor": "@nx/js:verdaccio",
|
||||||
|
"options": {
|
||||||
|
"port": 4873,
|
||||||
|
"config": ".verdaccio/config.yml",
|
||||||
|
"storage": "tmp/local-registry/storage"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tags": []
|
"tags": []
|
||||||
|
@ -241,8 +241,5 @@
|
|||||||
"graphql": "15.8.0",
|
"graphql": "15.8.0",
|
||||||
"//": "workaround storybook issue: https://github.com/storybookjs/storybook/issues/21642",
|
"//": "workaround storybook issue: https://github.com/storybookjs/storybook/issues/21642",
|
||||||
"@storybook/react-docgen-typescript-plugin": "1.0.6--canary.9.cd77847.0"
|
"@storybook/react-docgen-typescript-plugin": "1.0.6--canary.9.cd77847.0"
|
||||||
},
|
|
||||||
"nx": {
|
|
||||||
"includedScripts": []
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
14
project.json
@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "nx-monorepo",
|
|
||||||
"$schema": "node_modules/nx/schemas/project-schema.json",
|
|
||||||
"targets": {
|
|
||||||
"local-registry": {
|
|
||||||
"executor": "@nx/js:verdaccio",
|
|
||||||
"options": {
|
|
||||||
"port": 4873,
|
|
||||||
"config": ".verdaccio/config.yml",
|
|
||||||
"storage": "tmp/local-registry/storage"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|