Skip to content

Commit

Permalink
feat(tangle-dapp): Implement pool creation flow & onboarding modals (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
yurixander authored Oct 17, 2024
1 parent 82196b8 commit 887a693
Show file tree
Hide file tree
Showing 40 changed files with 856 additions and 103 deletions.
4 changes: 2 additions & 2 deletions apps/tangle-dapp/app/bridge/AmountAndTokenInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import Decimal from 'decimal.js';
import { FC, useMemo } from 'react';
import { twMerge } from 'tailwind-merge';

import AmountInput from '../../components/AmountInput/AmountInput';
import AmountInput from '../../components/AmountInput';
import { BRIDGE_SUPPORTED_TOKENS } from '../../constants/bridge';
import { useBridge } from '../../context/BridgeContext';
import convertDecimalToBn from '../../utils/convertDecimalToBn';
Expand Down Expand Up @@ -68,7 +68,7 @@ const AmountAndTokenInput: FC = () => {
title="Amount"
amount={amount}
setAmount={setAmount}
baseInputOverrides={{
wrapperOverrides={{
isFullWidth: true,
}}
placeholder=""
Expand Down
6 changes: 2 additions & 4 deletions apps/tangle-dapp/app/bridge/BridgeContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import Button from '@webb-tools/webb-ui-components/components/buttons/Button';
import { FC, useMemo, useState } from 'react';
import { twMerge } from 'tailwind-merge';

import AddressInput, {
AddressType,
} from '../../components/AddressInput/AddressInput';
import AddressInput, { AddressType } from '../../components/AddressInput';
import { useBridge } from '../../context/BridgeContext';
import useActiveAccountAddress from '../../hooks/useActiveAccountAddress';
import { isEVMChain } from '../../utils/bridge';
Expand Down Expand Up @@ -84,7 +82,7 @@ const BridgeContainer: FC<BridgeContainerProps> = ({ className }) => {
: AddressType.Substrate
}
title="Receiver Address"
baseInputOverrides={{ isFullWidth: true }}
wrapperOverrides={{ isFullWidth: true }}
value={destinationAddress}
setValue={setDestinationAddress}
setErrorMessage={(error) =>
Expand Down
33 changes: 33 additions & 0 deletions apps/tangle-dapp/app/bridge/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import {
ExchangeFunds,
TokenSwapLineIcon,
WalletLineIcon,
} from '@webb-tools/icons';
import { TANGLE_DOCS_URL } from '@webb-tools/webb-ui-components';
import { Metadata } from 'next';
import { FC } from 'react';

import OnboardingItem from '../../components/OnboardingModal/OnboardingItem';
import OnboardingModal from '../../components/OnboardingModal/OnboardingModal';
import { OnboardingPageKey } from '../../constants';
import createPageMetadata from '../../utils/createPageMetadata';
import BridgeContainer from './BridgeContainer';

Expand All @@ -11,6 +20,30 @@ export const metadata: Metadata = createPageMetadata({
const Bridge: FC = () => {
return (
<div>
<OnboardingModal
title="Get Started with Bridging"
pageKey={OnboardingPageKey.BRIDGE}
learnMoreHref={TANGLE_DOCS_URL}
>
<OnboardingItem
Icon={WalletLineIcon}
title="Connect EVM or Substrate Wallet"
description="Connect your EVM or Substrate wallet depending on the intended source chain. For example, if bridging in assets from Ethereum, connect your MetaMask or EVM-based wallet."
/>

<OnboardingItem
Icon={ExchangeFunds}
title="Bridge EVM Assets"
description="Select source and destination chains, amount, and the recipient account's address to bridge in your EVM-based assets into Tangle to start restaking."
/>

<OnboardingItem
Icon={TokenSwapLineIcon}
title="Restake Your Assets"
description="After bridging, use the restake page to leverage your bridged assets in restaking in order to earn yield and help secure and power Blueprint services on Tangle."
/>
</OnboardingModal>

<BridgeContainer className="mx-auto" />
</div>
);
Expand Down
31 changes: 25 additions & 6 deletions apps/tangle-dapp/app/liquid-staking/page.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
'use client';

import {
AddLineIcon,
CoinIcon,
EditLine,
Search,
SparklingIcon,
WaterDropletIcon,
} from '@webb-tools/icons';
import {
Button,
TabContent,
TabsList as WebbTabsList,
TabsRoot,
TabTrigger,
TANGLE_DOCS_LIQUID_STAKING_URL,
Typography,
} from '@webb-tools/webb-ui-components';
import { FC, useEffect } from 'react';
import { FC, useEffect, useState } from 'react';

import LsStakeCard from '../../components/LiquidStaking/stakeAndUnstake/LsStakeCard';
import LsUnstakeCard from '../../components/LiquidStaking/stakeAndUnstake/LsUnstakeCard';
Expand All @@ -24,6 +26,7 @@ import OnboardingModal from '../../components/OnboardingModal/OnboardingModal';
import StatItem from '../../components/StatItem';
import { OnboardingPageKey } from '../../constants';
import { LsSearchParamKey } from '../../constants/liquidStaking/types';
import LsCreatePoolModal from '../../containers/LsCreatePoolModal';
import LsMyProtocolsTable from '../../containers/LsMyProtocolsTable';
import { LsProtocolsTable } from '../../containers/LsPoolsTable';
import useNetworkStore from '../../context/useNetworkStore';
Expand Down Expand Up @@ -61,6 +64,7 @@ const LiquidStakingPage: FC = () => {

const { network } = useNetworkStore();
const { switchNetwork } = useNetworkSwitcher();
const [isCreatePoolModalOpen, setIsCreatePoolModalOpen] = useState(false);

const lsTangleNetwork = getLsTangleNetwork(lsNetworkId);

Expand All @@ -87,6 +91,11 @@ const LiquidStakingPage: FC = () => {

return (
<div>
<LsCreatePoolModal
isOpen={isCreatePoolModalOpen}
setIsOpen={setIsCreatePoolModalOpen}
/>

<OnboardingModal
title="Get Started with Liquid Staking"
pageKey={OnboardingPageKey.LIQUID_STAKING}
Expand Down Expand Up @@ -141,11 +150,7 @@ const LiquidStakingPage: FC = () => {
</div>

<div className="flex gap-6 h-full">
<StatItem
title="$123.01"
subtitle="My Total Staking"
largeSubtitle
/>
<StatItem title="$123.01" subtitle="My Total Staking" />
</div>
</div>

Expand Down Expand Up @@ -192,6 +197,20 @@ const LiquidStakingPage: FC = () => {
);
})}
</WebbTabsList>

{/**
* TODO: Check what's the min. amount required to create a new pool. If the free balance doesn't meet the min, disable the button and show a tooltip with the reason.
*/}
<Button
onClick={() => setIsCreatePoolModalOpen(true)}
variant="utility"
size="sm"
rightIcon={
<AddLineIcon className="fill-current dark:fill-current" />
}
>
Create Pool
</Button>
</div>

{/* Tabs Content */}
Expand Down
8 changes: 8 additions & 0 deletions apps/tangle-dapp/app/restake/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import { Metadata } from 'next';
import { FC, PropsWithChildren } from 'react';

import createPageMetadata from '../../utils/createPageMetadata';
import Providers from './providers';

export const dynamic = 'force-static';

export const metadata: Metadata = createPageMetadata({
title: 'Restake',
description:
"Explore vaults, deposit assets, and select operators to earn rewards with Tangle's restaking infrastructure.",
});

const RestakeLayout: FC<PropsWithChildren> = ({ children }) => {
return <Providers>{children}</Providers>;
};
Expand Down
13 changes: 9 additions & 4 deletions apps/tangle-dapp/app/restake/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
'use client';

import { AddCircleLineIcon, Search, SparklingIcon } from '@webb-tools/icons';
import {
AddCircleLineIcon,
Search,
SparklingIcon,
UserLineIcon,
} from '@webb-tools/icons';
import { TANGLE_DOCS_RESTAKING_URL } from '@webb-tools/webb-ui-components';
import Button from '@webb-tools/webb-ui-components/components/buttons/Button';
import { Typography } from '@webb-tools/webb-ui-components/typography/Typography';
Expand Down Expand Up @@ -65,7 +70,7 @@ export default function RestakePage() {
/>

<OnboardingItem
Icon={AddCircleLineIcon}
Icon={UserLineIcon}
title="Choose an Operator"
description="Select an operator to delegate your deposited assets. This step is crucial for securing service instances on Tangle."
/>
Expand Down Expand Up @@ -125,9 +130,9 @@ export default function RestakePage() {
<Typography variant="body1">{CONTENT.HOW_IT_WORKS}</Typography>
</div>

{/** TODO: Determine read more link here */}
<Button
href="#"
href={TANGLE_DOCS_RESTAKING_URL}
target="_blank"
variant="link"
size="sm"
className="inline-block ml-auto"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
'use client';

import { isAddress } from '@polkadot/util-crypto';
import { Button, Input } from '@webb-tools/webb-ui-components';
import { Avatar, Button, Input } from '@webb-tools/webb-ui-components';
import { FC, ReactNode, useCallback, useEffect, useRef, useState } from 'react';

import { isEvmAddress } from '../../utils/isEvmAddress';
import BaseInput, { BaseInputProps } from '../AmountInput/BaseInput';
import { isEvmAddress } from '../utils/isEvmAddress';
import InputWrapper, { InputWrapperProps } from './InputWrapper';

export enum AddressType {
EVM,
Expand All @@ -17,25 +17,27 @@ export type AddressInputProps = {
id: string;
title: string;
placeholder?: string;
tooltip?: string;
type: AddressType;
showPasteButton?: boolean;
value: string;
isDisabled?: boolean;
baseInputOverrides?: Partial<BaseInputProps>;
wrapperOverrides?: Partial<InputWrapperProps>;
setValue: (newValue: string) => void;
setErrorMessage?: (error: string | null) => void;
};

const AddressInput: FC<AddressInputProps> = ({
id,
title,
tooltip,
placeholder,
type,
value,
setValue,
showPasteButton = true,
isDisabled = false,
baseInputOverrides,
wrapperOverrides,
setErrorMessage: setErrorMessageOnParent,
}) => {
const [errorMessage, setErrorMessage] = useState<string | null>(null);
Expand All @@ -47,11 +49,10 @@ const AddressInput: FC<AddressInputProps> = ({
});
}, [setValue]);

const actions: ReactNode = (
<>
{showPasteButton && (
const actions: ReactNode =
value === '' && showPasteButton ? (
<>
<Button
isDisabled={value !== ''}
key="paste"
size="sm"
variant="utility"
Expand All @@ -60,17 +61,19 @@ const AddressInput: FC<AddressInputProps> = ({
>
Paste
</Button>
)}

{baseInputOverrides?.actions}
</>
);
{wrapperOverrides?.actions}
</>
) : (
wrapperOverrides?.actions
);

const handleChange = useCallback(
(newValue: string) => {
setValue(newValue);

if (newValue === '') {
setErrorMessage(null);
setValue(newValue);

return;
}
Expand All @@ -87,8 +90,6 @@ const AddressInput: FC<AddressInputProps> = ({
} else {
setErrorMessage(null);
}

setValue(newValue);
},
[setValue, type],
);
Expand All @@ -100,18 +101,25 @@ const AddressInput: FC<AddressInputProps> = ({
}
}, [errorMessage, setErrorMessageOnParent]);

const isEvm = isEvmAddress(value);

return (
<BaseInput
<InputWrapper
id={id}
title={title}
tooltip={tooltip}
errorMessage={errorMessage ?? undefined}
{...baseInputOverrides}
bodyClassName="flex items-center gap-1"
{...wrapperOverrides}
actions={actions}
>
<Avatar theme={isEvm ? 'ethereum' : 'substrate'} value={value} />

<Input
id={id}
inputRef={inputRef}
inputClassName="placeholder:text-md"
className="w-full"
inputClassName="placeholder:text-md text-sm"
type="text"
placeholder={placeholder}
size="sm"
Expand All @@ -123,7 +131,7 @@ const AddressInput: FC<AddressInputProps> = ({
isControlled
spellCheck={false}
/>
</BaseInput>
</InputWrapper>
);
};

Expand Down
Loading

0 comments on commit 887a693

Please sign in to comment.