Skip to content

Commit

Permalink
feat: add staking confirmation screen
Browse files Browse the repository at this point in the history
  • Loading branch information
VmMad committed Nov 13, 2024
1 parent 33e3089 commit fe7776a
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 55 deletions.
4 changes: 2 additions & 2 deletions apps/ui-kit/src/lib/components/organisms/dialog/Dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const DialogOverlay = React.forwardRef<
>(({ showCloseIcon, ...props }, ref) => (
<RadixDialog.Overlay
ref={ref}
className="absolute inset-0 z-[99998] bg-shader-neutral-light-48 backdrop-blur-md"
className="fixed inset-0 z-[99998] bg-shader-neutral-light-48 backdrop-blur-md"
{...props}
>
<DialogClose className={cx('fixed right-3 top-3', { hidden: !showCloseIcon })}>
Expand Down Expand Up @@ -70,7 +70,7 @@ const DialogContent = React.forwardRef<
<RadixDialog.Content
ref={ref}
className={cx(
'absolute z-[99999] flex flex-col justify-center overflow-hidden bg-primary-100 dark:bg-neutral-6 md:w-96',
'fixed z-[99999] flex flex-col justify-center overflow-hidden bg-primary-100 dark:bg-neutral-6 md:w-96',
positionClass,
)}
{...props}
Expand Down
45 changes: 30 additions & 15 deletions apps/wallet-dashboard/components/Dialogs/Staking/StakeDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,21 @@ import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils';
import { NotificationType } from '@/stores/notificationStore';
import { prepareObjectsForTimelockedStakingTransaction } from '@/lib/utils';
import { Dialog, DialogBody, DialogContent, DialogPosition, Header } from '@iota/apps-ui-kit';
import { SuccessScreenView } from './views/ConfirmAndExit';

interface StakeDialogProps {
isTimelockedStaking?: boolean;
onSuccess?: (digest: string) => void;
isOpen: boolean;
setOpen: (bool: boolean) => void;
}

enum Step {
SelectValidator,
EnterAmount,
TransactionDetails,
}

function StakeDialog({
onSuccess,
isTimelockedStaking,
isOpen,
setOpen,
}: StakeDialogProps): JSX.Element {
function StakeDialog({ isTimelockedStaking, isOpen, setOpen }: StakeDialogProps): JSX.Element {
const [step, setStep] = useState<Step>(Step.SelectValidator);
const [selectedValidator, setSelectedValidator] = useState<string>('');
const [amount, setAmount] = useState<string>('');
Expand Down Expand Up @@ -91,6 +87,12 @@ function StakeDialog({
}
}

function resetDefaultValues(): void {
setStep(Step.SelectValidator);
setSelectedValidator('');
setAmount('');
}

function handleStake(): void {
if (isTimelockedStaking && groupedTimelockObjects.length === 0) {
addNotification('Invalid stake amount. Please try again.', NotificationType.Error);
Expand All @@ -105,10 +107,8 @@ function StakeDialog({
transaction: newStakeData?.transaction,
},
{
onSuccess: (tx) => {
if (onSuccess) {
onSuccess(tx.digest);
}
onSuccess: () => {
setStep(Step.TransactionDetails);
},
},
)
Expand All @@ -120,20 +120,26 @@ function StakeDialog({
});
}

const title = {
const title: Record<Step, string> = {
[Step.SelectValidator]: 'Select Validator',
[Step.EnterAmount]: 'Enter Amount',
[Step.TransactionDetails]: 'Transaction',
};

function handleClose(): void {
setOpen(false);
resetDefaultValues();
}

return (
<Dialog open={isOpen} onOpenChange={setOpen}>
<DialogContent containerId="overlay-portal-container" position={DialogPosition.Right}>
<div className="flex min-h-full flex-col">
<Header
title={title[step]}
onClose={() => setOpen(false)}
titleCentered
onBack={handleBack}
onClose={handleClose}
titleCentered={step !== Step.TransactionDetails}
onBack={step === Step.EnterAmount ? handleBack : undefined}
/>
<div className="flex w-full flex-1 [&_>div]:flex [&_>div]:w-full [&_>div]:flex-1">
<DialogBody>
Expand All @@ -155,6 +161,15 @@ function StakeDialog({
onStake={handleStake}
/>
)}
{step === Step.TransactionDetails && (
<SuccessScreenView
validatorAddress={selectedValidator}
gasBudget={newStakeData?.gasBudget}
onConfirm={handleClose}
amount={amount}
symbol={metadata?.symbol}
/>
)}
</DialogBody>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import {
Button,
ButtonType,
Card,
CardBody,
CardImage,
CardType,
ImageShape,
ImageType,
} from '@iota/apps-ui-kit';
import { useValidatorInfo } from '@iota/core';
import { StakingTransactionDetails } from './StakingTransactionDetails';
import { Validator } from './Validator';
import { IotaLogoMark } from '@iota/ui-icons';

interface SuccessScreenViewProps {
validatorAddress: string;
gasBudget: string | number | null | undefined;
onConfirm: () => void;
amount: string;
symbol: string | undefined;
}

export function SuccessScreenView({
validatorAddress,
gasBudget,
onConfirm,
amount,
symbol,
}: SuccessScreenViewProps): React.JSX.Element {
const { apy } = useValidatorInfo({
validatorAddress,
});

return (
<div className="flex flex-1 flex-col">
<div className="flex w-full flex-1 flex-col justify-between">
<div className="flex flex-col gap-y-md">
<Validator address={validatorAddress} isSelected showAction={false} />

<Card type={CardType.Outlined}>
<CardImage type={ImageType.BgSolid} shape={ImageShape.Rounded}>
<IotaLogoMark className="h-5 w-5 text-neutral-10" />
</CardImage>
<CardBody title={`${amount} ${symbol}`} subtitle="Stake" />
</Card>

<StakingTransactionDetails gasBudget={gasBudget} apy={apy} />
</div>
</div>

<div className="flex w-full">
<Button
type={ButtonType.Primary}
fullWidth
onClick={onConfirm}
text="Confirm & Exit"
/>
</div>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,11 @@
import React from 'react';
import { useFormatCoin, useBalance, CoinFormat } from '@iota/core';
import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils';
import {
Button,
ButtonType,
KeyValueInfo,
Panel,
Divider,
Input,
InputType,
} from '@iota/apps-ui-kit';
import { useStakeTxnInfo } from '../hooks';
import { useCurrentAccount, useIotaClientQuery } from '@iota/dapp-kit';
import { Button, ButtonType, Input, InputType } from '@iota/apps-ui-kit';
import { useCurrentAccount } from '@iota/dapp-kit';
import { Validator } from './Validator';
import { StakedInfo } from './StakedInfo';
import { StakingTransactionDetails } from './StakingTransactionDetails';

interface EnterAmountViewProps {
selectedValidator: string;
Expand All @@ -40,7 +32,6 @@ function EnterAmountView({
const account = useCurrentAccount();
const accountAddress = account?.address;

const { data: system } = useIotaClientQuery('getLatestIotaSystemState');
const { data: iotaBalance } = useBalance(accountAddress!);

const coinBalance = BigInt(iotaBalance?.totalBalance || 0);
Expand All @@ -50,10 +41,6 @@ function EnterAmountView({
IOTA_TYPE_ARG,
CoinFormat.FULL,
);
const [gas, symbol] = useFormatCoin(gasBudget, IOTA_TYPE_ARG);
const { stakedRewardsStartEpoch, timeBeforeStakeRewardsRedeemableAgoDisplay } = useStakeTxnInfo(
system?.epoch,
);

return (
<div className="flex w-full flex-col justify-between">
Expand All @@ -73,28 +60,7 @@ function EnterAmountView({
caption={`${maxTokenFormatted} ${maxTokenFormattedSymbol} Available`}
/>
</div>

<Panel hasBorder>
<div className="flex flex-col gap-y-sm p-md">
<KeyValueInfo
keyText="Staking Rewards Start"
value={stakedRewardsStartEpoch}
fullwidth
/>
<KeyValueInfo
keyText="Redeem Rewards"
value={timeBeforeStakeRewardsRedeemableAgoDisplay}
fullwidth
/>
<Divider />
<KeyValueInfo
keyText="Gas fee"
value={gas || '--'}
supportingLabel={symbol}
fullwidth
/>
</div>
</Panel>
<StakingTransactionDetails gasBudget={gasBudget} />
</div>

<div className="flex w-full justify-between gap-sm">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { Divider, KeyValueInfo, Panel } from '@iota/apps-ui-kit';
import { useFormatCoin } from '@iota/core';
import { useIotaClientQuery } from '@iota/dapp-kit';
import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils';
import { useStakeTxnInfo } from '../hooks';

interface StakingTransactionDetailsProps {
gasBudget: string | number | null | undefined;
apy?: number | null;
}

export function StakingTransactionDetails({
gasBudget,
apy,
}: StakingTransactionDetailsProps): React.JSX.Element {
const [gas, gasSymbol] = useFormatCoin(gasBudget, IOTA_TYPE_ARG);
const { data: system } = useIotaClientQuery('getLatestIotaSystemState');

const { stakedRewardsStartEpoch, timeBeforeStakeRewardsRedeemableAgoDisplay } = useStakeTxnInfo(
system?.epoch,
);

return (
<Panel hasBorder>
<div className="flex flex-col gap-y-sm p-md">
{apy !== null && apy !== undefined ? (
<KeyValueInfo keyText="APY" value={`${apy}%`} fullwidth />
) : null}

<KeyValueInfo
keyText="Staking Rewards Start"
value={stakedRewardsStartEpoch}
fullwidth
/>
<KeyValueInfo
keyText="Redeem Rewards"
value={timeBeforeStakeRewardsRedeemableAgoDisplay}
fullwidth
/>
<Divider />
<KeyValueInfo
keyText="Gas fee"
value={gas || '--'}
supportingLabel={gasSymbol}
fullwidth
/>
</div>
</Panel>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@

export { default as EnterAmountView } from './EnterAmountView';
export { default as SelectValidatorView } from './SelectValidatorView';
export * from './StakingTransactionDetails';

0 comments on commit fe7776a

Please sign in to comment.