Skip to content

Commit

Permalink
feat: voting power delegation confirmation dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
Szymon Masłowski committed Nov 15, 2024
1 parent b205fce commit c47c9b1
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 85 deletions.
1 change: 0 additions & 1 deletion source/renderer/app/Routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ import { IS_STAKING_INFO_PAGE_AVAILABLE } from './config/stakingConfig';
import AnalyticsConsentPage from './containers/profile/AnalyticsConsentPage';
import TrackedRoute from './analytics/TrackedRoute';
import { Voting } from './containers/voting/Voting';
import VotingPowerDelegation from './components/voting/voting-governance/VotingPowerDelegation';
import VotingGovernancePage from './containers/voting/VotingGovernancePage';

export const Routes = withRouter(() => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { injectIntl } from 'react-intl';
import { Input } from 'react-polymorph/lib/components/Input';
import { Button } from 'react-polymorph/lib/components/Button';

import BigNumber from 'bignumber.js';
import BorderedBox from '../../widgets/BorderedBox';
import { messages } from './VotingPowerDelegation.messages';
import styles from './VotingPowerDelegation.scss';
Expand All @@ -14,11 +15,14 @@ import Wallet from '../../../domains/Wallet';
import StakePool from '../../../domains/StakePool';
import ItemsDropdown from '../../widgets/forms/ItemsDropdown';
import { assertIsBech32WithPrefix } from '../../../../../common/utils/assertIsBech32WithPrefix';
import { VotingPowerDelegationConfirmationDialog } from './VotingPowerDelegationConfirmationDialog';
import type { DelegateVotesParams } from '../../../api/voting/types';

type Props = {
getStakePoolById: (...args: Array<any>) => any;
intl: Intl;
onExternalLinkClick: (...args: Array<any>) => any;
onSubmit: (params: DelegateVotesParams) => void;
stakePools: Array<StakePool>;
wallets: Array<Wallet>;
};
Expand All @@ -40,9 +44,13 @@ function VotingPowerDelegation({
getStakePoolById,
intl,
onExternalLinkClick,
onSubmit,
wallets,
stakePools,
}: Props) {
const [confirmationDialogVisible, setConfirmationDialogVisible] = useState(
false
);
const [selectedWalletId, setSelectedWalletId] = useState<string | null>(null);
const [selectedVoteType, setSelectedVoteType] = useState<VoteType>('drep');
const [drepInputState, setDrepInputState] = useState({
Expand Down Expand Up @@ -73,97 +81,107 @@ function VotingPowerDelegation({
},
];
return (
<div className={styles.component}>
<BorderedBox>
<h1 className={styles.heading}>
{intl.formatMessage(messages.heading)}
</h1>
<div className={styles.info}>
<p>
<FormattedHTMLMessageWithLink
message={{
...messages.paragraph1,
values: {
linkLabel: messages.learnMoreLinkLabel,
linkURL: messages.paragraph1LinkUrl,
},
}}
onExternalLinkClick={onExternalLinkClick}
/>
</p>
</div>

<WalletsDropdown
className={styles.walletSelect}
// @ts-ignore ts-migrate(2322) FIXME: Type '{ className: any; label: any; numberOfStakeP... Remove this comment to see the full error message
label={'Select a wallet to delegate from'}
numberOfStakePools={stakePools.length}
wallets={wallets}
onChange={(walletId: string) => setSelectedWalletId(walletId)}
placeholder={'Select a wallet …'}
value={selectedWalletId}
getStakePoolById={getStakePoolById}
/>
<>
<div className={styles.component}>
<BorderedBox>
<h1 className={styles.heading}>
{intl.formatMessage(messages.heading)}
</h1>
<div className={styles.info}>
<p>
<FormattedHTMLMessageWithLink
message={{
...messages.paragraph1,
values: {
linkLabel: messages.learnMoreLinkLabel,
linkURL: messages.paragraph1LinkUrl,
},
}}
onExternalLinkClick={onExternalLinkClick}
/>
</p>
</div>

{selectedWalletId && (
<ItemsDropdown
className={styles.voteTypeSelect}
label={'Select voting registration type'}
placeholder={'Select delegation option'}
options={voteTypes}
handleChange={(option) => setSelectedVoteType(option.value)}
value={selectedVoteType}
<WalletsDropdown
className={styles.walletSelect}
// @ts-ignore ts-migrate(2322) FIXME: Type '{ className: any; label: any; numberOfStakeP... Remove this comment to see the full error message
label={'Select a wallet to delegate from'}
numberOfStakePools={stakePools.length}
wallets={wallets}
onChange={(walletId: string) => setSelectedWalletId(walletId)}
placeholder={'Select a wallet …'}
value={selectedWalletId}
getStakePoolById={getStakePoolById}
/>
)}

{selectedWalletId && selectedVoteType === 'drep' && (
<Input
className={styles.drepInput}
onChange={(value) => {
setDrepInputState({
blurred: false,
value,
});
}}
onBlur={() => {
setDrepInputState((prevState) => ({
...prevState,
blurred: true,
}));
}}
spellCheck={false}
value={drepInputState.value}
label={
<div>
Please type or paste a valid DRep ID here. Look up{' '}
<a
onClick={() =>
onExternalLinkClick('https://www.1694.io/en/dreps/list')
}
>
DRep directory
</a>
</div>
}
placeholder={'Paste DRep ID here.'}
error={drepInputIsValid ? undefined : 'Invalid DRep ID'}
{selectedWalletId && (
<ItemsDropdown
className={styles.voteTypeSelect}
label={'Select voting registration type'}
placeholder={'Select delegation option'}
options={voteTypes}
handleChange={(option) => setSelectedVoteType(option.value)}
value={selectedVoteType}
/>
)}

{selectedWalletId && selectedVoteType === 'drep' && (
<Input
className={styles.drepInput}
onChange={(value) => {
setDrepInputState({
blurred: false,
value,
});
}}
onBlur={() => {
setDrepInputState((prevState) => ({
...prevState,
blurred: true,
}));
}}
spellCheck={false}
value={drepInputState.value}
label={
<div>
Please type or paste a valid DRep ID here. Look up{' '}
<a
onClick={() =>
onExternalLinkClick('https://www.1694.io/en/dreps/list')
}
>
DRep directory
</a>
</div>
}
placeholder={'Paste DRep ID here.'}
error={drepInputIsValid ? undefined : 'Invalid DRep ID'}
/>
)}
<Button
label={'Submit'}
className={styles.voteSubmit}
disabled={!formIsValid}
onClick={() => setConfirmationDialogVisible(true)}
/>
)}
<Button
label={'Submit'}
className={styles.voteSubmit}
disabled={!formIsValid}
onClick={() =>
console.log('Submitting: ', {
voteType: selectedVoteType,
...(selectedVoteType === 'drep'
? { drepId: drepInputState.value }
: {}),
</BorderedBox>
</div>
{confirmationDialogVisible && (
<VotingPowerDelegationConfirmationDialog
fees={new BigNumber('1')}
onClose={() => setConfirmationDialogVisible(false)}
onConfirm={(passphrase) =>
onSubmit({
walletId: selectedWalletId,
passphrase,
dRepId: drepInputState.value || selectedVoteType,
})
}
selectedWallet={wallets.find((w) => w.id === selectedWalletId)}
vote={drepInputState.value || selectedVoteType}
/>
</BorderedBox>
</div>
)}
</>
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import React, { useState } from 'react';
import BigNumber from 'bignumber.js';
import { Input } from 'react-polymorph/lib/components/Input';
import { InputSkin } from 'react-polymorph/lib/skins/simple/InputSkin';
import { noop } from 'lodash';
import Dialog from '../../widgets/Dialog';
import DialogCloseButton from '../../widgets/DialogCloseButton';
import { formattedWalletAmount } from '../../../utils/formatters';
import Wallet from '../../../domains/Wallet';

type VotingPowerDelegationConfirmationDialogProps = {
fees: BigNumber;
onClose: () => void;
onConfirm: (passphrase: string) => void;
selectedWallet: Wallet;
vote: string;
};

type State = 'Awaiting' | 'Submitting';

export function VotingPowerDelegationConfirmationDialog({
fees,
onClose,
onConfirm,
selectedWallet,
vote,
}: VotingPowerDelegationConfirmationDialogProps) {
const [state, setState] = useState<State>();
const [passphrase, setPassphrase] = useState('');

const handleClose = state === 'Submitting' ? noop : onClose;

const handleConfirm =
state === 'Submitting'
? noop
: () => {
setState('Submitting');
onConfirm(passphrase);
};

return (
<Dialog
title={'Confirm transaction'}
actions={[
{
label: 'Cancel',
onClick: handleClose,
disabled: state === 'Submitting',
},
{
label: 'Confirm',
onClick: handleConfirm,
primary: true,
disabled: state === 'Submitting',
},
]}
onClose={handleClose}
closeButton={<DialogCloseButton onClose={handleClose} />}
>
<h4>Vote</h4>
<br />
{vote}
<br />
<br />

<h4>Average fees</h4>
<br />
{formattedWalletAmount(fees, false)}
<br />
<br />

{selectedWallet.isHardwareWallet ? (
<div>{'TODO <HardwareWalletStatus />'}</div>
) : (
<Input
value={passphrase}
onChange={(value) => setPassphrase(value)}
type={'password'}
label={'Type your password'}
skin={InputSkin}
/>
)}
</Dialog>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ class VotingGovernancePage extends Component<Props> {

render() {
const { wallets, staking, app } = this.props.stores;
const { voting } = this.props.actions;
const { openExternalLink } = app;
return (
<VotingPowerDelegation
onExternalLinkClick={openExternalLink}
onSubmit={(params) => voting.delegateVotes.trigger(params)}
wallets={wallets.all}
stakePools={staking.stakePools}
getStakePoolById={staking.getStakePoolById}
Expand Down

0 comments on commit c47c9b1

Please sign in to comment.