Skip to content

Commit

Permalink
Add confirmation layout (#737)
Browse files Browse the repository at this point in the history
* add confirmation layout

* fmt

* jsx in return statement

Co-authored-by: Nathan Seva <thykof@protonmail.ch>

* Revert "jsx in return statement"

This reverts commit d06a80a.

* rename token balance

* move stepsEnum

* clean

---------

Co-authored-by: Nathan Seva <thykof@protonmail.ch>
  • Loading branch information
pivilartisant and Thykof authored Jun 13, 2024
1 parent 91f2d81 commit a86b14a
Show file tree
Hide file tree
Showing 5 changed files with 286 additions and 223 deletions.
3 changes: 2 additions & 1 deletion src/i18n/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"Mainnet": "Mainnet",
"Unknown": "Unknown",
"Invalid-chain": "Invalid chain",
"network": "Network"
"network": "Network",
"next": "next"
},
"navbar": {
"history": "History",
Expand Down
3 changes: 2 additions & 1 deletion src/pages/IndexPage/IndexPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export function IndexPage() {
const { box } = useGlobalStatusesStore();
const { connectedAccount, isFetching } = useAccountStore();

const { isMassaToEvm } = useOperationStore();
const { isMassaToEvm, inputAmount } = useOperationStore();

const massaToEvm = isMassaToEvm();
const isValidEvmNetwork = useEvmChainValidation(ChainContext.BRIDGE);
Expand All @@ -31,6 +31,7 @@ export function IndexPage() {
isFetching ||
!connectedAccount ||
!isValidEvmNetwork ||
!inputAmount ||
!isValidMassaNetwork ||
(BRIDGE_OFF && !massaToEvm) ||
(REDEEM_OFF && massaToEvm);
Expand Down
260 changes: 39 additions & 221 deletions src/pages/IndexPage/Layouts/BridgeRedeemLayout/BridgeRedeemLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,269 +1,87 @@
import { useState } from 'react';
import { Button, Money, formatAmount } from '@massalabs/react-ui-kit';
import Big from 'big.js';
import { FiRepeat } from 'react-icons/fi';
import { parseUnits } from 'viem';
import { useAccount } from 'wagmi';
import { boxLayout } from './BoxLayout';
import { ReactNode, useState } from 'react';
import { Button } from '@massalabs/react-ui-kit';
import { FeesEstimation } from './FeesEstimation';
import { WarningNoEth } from './WarningNoEth';

import { GetTokensPopUpModal } from '@/components';
import { ServiceFeeTooltip } from '@/components/ServiceFeeTooltip/ServiceFeeTooltip';
import { OperationLayout } from './OperationLayout';
import { ConfirmationLayout } from '../ConfirmationLayout/ConfirmationLayout';
import useEvmToken from '@/custom/bridge/useEvmToken';
import { useServiceFee } from '@/custom/bridge/useServiceFee';
import { useSubmitBridge } from '@/custom/bridge/useSubmitBridge';
import { useSubmitRedeem } from '@/custom/bridge/useSubmitRedeem';
import Intl from '@/i18n/i18n';
import { PendingOperationLayout } from '@/pages';
import { Status } from '@/store/globalStatusesStore';
import {
useAccountStore,
useBridgeModeStore,
useGlobalStatusesStore,
useOperationStore,
useTokenStore,
} from '@/store/store';
import { SIDE } from '@/utils/const';
import { getAmountToReceive, serviceFeeToPercent } from '@/utils/utils';
import { useGlobalStatusesStore, useOperationStore } from '@/store/store';

interface BridgeRedeemProps {
isBlurred: string;
isButtonDisabled: boolean;
}

enum StepsEnum {
PENDING = 'pending',
AWAITING_CONFIRMATION = 'confirmation',
}

export function BridgeRedeemLayout(props: BridgeRedeemProps) {
const { isBlurred, isButtonDisabled } = props;

const { setAmountError, amountError } = useGlobalStatusesStore();

const { tokenBalance: _tokenBalanceEVM, isFetched: isBalanceFetched } =
useEvmToken();
const { isConnected: isEvmWalletConnected } = useAccount();
const { tokenBalance: _tokenBalanceEVM } = useEvmToken();
const { box } = useGlobalStatusesStore();
const { isMainnet: getIsMainnet } = useBridgeModeStore();
const isMainnet = getIsMainnet();
const {
isMassaToEvm,
inputAmount,
outputAmount,
setSide,
setInputAmount,
setOutputAmount,
} = useOperationStore();

const [inputField, setInputField] = useState<string>();

const { isFetching } = useAccountStore();

const { selectedToken: token } = useTokenStore();
const { serviceFee } = useServiceFee();

const [openTokensModal, setOpenTokensModal] = useState<boolean>(false);
const { isMassaToEvm, setInputAmount } = useOperationStore();

const massaToEvm = isMassaToEvm();

const { handleSubmitBridge } = useSubmitBridge();
const { handleSubmitRedeem } = useSubmitRedeem();

function handlePercent(percent: number) {
if (!token || !isBalanceFetched) return;
const [step, setStep] = useState<StepsEnum>(StepsEnum.PENDING);
const [cta, setCTA] = useState<string>(Intl.t('general.next'));

if (
(massaToEvm && token.balance <= 0) ||
(!massaToEvm && _tokenBalanceEVM <= 0)
) {
setAmountError(Intl.t('index.approve.error.insufficient-funds'));
return;
}

const amount = massaToEvm
? formatAmount(token?.balance.toString(), token.decimals, '').full
: formatAmount(_tokenBalanceEVM.toString(), token.decimals, '').full;

const x = new Big(amount);
const y = new Big(percent);
const res = x.times(y).round(token.decimals).toFixed();

const _amount = parseUnits(res, token.decimals);

setInputAmount(_amount);
setInputField(res);
const amountToReceive = getAmountToReceive(_amount, serviceFee);
setOutputAmount(amountToReceive);
function prevPage() {
setStep(StepsEnum.PENDING);
setCTA(Intl.t('general.next'));
}

function handleToggleLayout() {
const OperationSteps: Record<StepsEnum, ReactNode> = {
[StepsEnum.PENDING]: <OperationLayout />,
[StepsEnum.AWAITING_CONFIRMATION]: (
<ConfirmationLayout prevPage={prevPage} />
),
};

function handleSubmission() {
if (step === StepsEnum.PENDING) {
setStep(StepsEnum.AWAITING_CONFIRMATION);
setCTA(
massaToEvm
? Intl.t('index.button.redeem')
: Intl.t('index.button.bridge'),
);
return;
}
massaToEvm ? handleSubmitRedeem() : handleSubmitBridge();
// sets inputAmount to undefined for next transfer
setInputAmount(undefined);
setOutputAmount(undefined);
setInputField(undefined);
setSide(massaToEvm ? SIDE.EVM_TO_MASSA : SIDE.MASSA_TO_EVM);
}

function handleGenericSubmit() {
isMassaToEvm() ? handleSubmitRedeem() : handleSubmitBridge();
// sets input field to undefined for next transfer
setInputField(undefined);
}

const isOperationPending = box !== Status.None;

if (isOperationPending) return <PendingOperationLayout />;

function changeAmount(amount: string) {
if (!token) return;
if (!amount) {
setInputAmount(undefined);
setInputField(undefined);
setOutputAmount(undefined);
return;
}

const parsedInputAmount = parseUnits(amount, token.decimals);
setInputAmount(parsedInputAmount);
setInputField(amount);

if (isMassaToEvm()) {
const amountToReceive = getAmountToReceive(parsedInputAmount, serviceFee);
setOutputAmount(amountToReceive);
} else {
setOutputAmount(parsedInputAmount);
}
}

// Money component formats amount without decimals
return (
<>
<div
className={`p-10 max-w-3xl w-full border border-tertiary rounded-2xl
bg-secondary/50 text-f-primary mb-5 ${isBlurred}`}
>
<div className="p-6 bg-primary rounded-2xl mb-5">
<p className="mb-4 mas-body">{Intl.t('index.from')}</p>
{boxLayout().up.header}
{boxLayout().up.wallet}
<div className="mb-4 flex items-center gap-2">
<div className="w-full">
<Money
disable={isFetching}
name="amount"
value={inputField || ''}
onValueChange={(o) => changeAmount(o.value)}
placeholder={Intl.t('index.input.placeholder.amount')}
suffix=""
decimalScale={token?.decimals}
error={amountError}
/>
<div className="flex flex-row-reverse">
<ul className="flex flex-row mas-body2">
<li
onClick={() => handlePercent(0.25)}
className="mr-3.5 hover:cursor-pointer"
>
25%
</li>
<li
onClick={() => handlePercent(0.5)}
className="mr-3.5 hover:cursor-pointer"
>
50%
</li>
<li
onClick={() => handlePercent(0.75)}
className="mr-3.5 hover:cursor-pointer"
>
75%
</li>
<li
onClick={() => handlePercent(1)}
className="mr-3.5 hover:cursor-pointer"
>
Max
</li>
</ul>
</div>
</div>
<div className="w-1/3 mb-4">{boxLayout().up.token}</div>
</div>
<div className="flex justify-between items-center">
<div className="flex items-center gap-2">
{!isMainnet && isEvmWalletConnected && (
<h3
className="mas-h3 text-f-disabled-1 underline cursor-pointer"
onClick={() => setOpenTokensModal(true)}
>
{Intl.t('index.get-tokens')}
</h3>
)}
</div>
{boxLayout().up.balance}
</div>
</div>
<div className="mb-5 flex justify-center items-center">
<Button
disabled={isFetching}
variant="toggle"
onClick={handleToggleLayout}
customClass={`w-12 h-12 inline-block transition ease-in-out delay-10 ${
massaToEvm ? 'rotate-180' : ''
}`}
>
<FiRepeat size={24} />
</Button>
</div>
<div className="mb-5 p-6 bg-primary rounded-2xl">
{isMassaToEvm() ? (
<div className="flex items-center mb-4 gap-2">
<p className="mas-body">
{Intl.t('index.input.placeholder.receive')}
</p>
<ServiceFeeTooltip
inputAmount={
formatAmount(inputAmount || '0', token?.decimals).full
}
serviceFee={serviceFeeToPercent(serviceFee)}
outputAmount={
formatAmount(outputAmount || '0', token?.decimals).full
}
symbol={token?.symbol || ''}
/>
</div>
) : (
<p className="mb-4 mas-body">{Intl.t('index.to')}</p>
)}
{boxLayout().down.header}
{boxLayout().down.wallet}
<div className="mb-4 flex items-center gap-2">
<div className="w-full">
<Money
placeholder={Intl.t('index.input.placeholder.receive')}
name="receive"
value={
formatAmount(outputAmount || '0', token?.decimals).full || ''
}
suffix=""
decimalScale={token?.decimals}
error=""
disable={true}
/>
</div>
<div className="w-1/3">{boxLayout().down.token}</div>
</div>
<WarningNoEth />
</div>
{OperationSteps[step]}
<div className="mb-5">
<Button disabled={isButtonDisabled} onClick={handleGenericSubmit}>
{massaToEvm
? Intl.t('index.button.redeem')
: Intl.t('index.button.bridge')}
<Button disabled={isButtonDisabled} onClick={handleSubmission}>
{cta}
</Button>
</div>
<FeesEstimation />
</div>

{openTokensModal && (
<GetTokensPopUpModal setOpenModal={setOpenTokensModal} />
)}
</>
);
}
Loading

0 comments on commit a86b14a

Please sign in to comment.