Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(bridge-ui-v2): NFT bridge stepper #14811

Merged
merged 4 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 66 additions & 19 deletions packages/bridge-ui-v2/src/components/Bridge/Bridge.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
import { chainConfig } from '$chainConfig';
import { FlatAlert } from '$components/Alert';
import ChainSelectorWrapper from '$components/Bridge/ChainSelectorWrapper.svelte';
import { Button } from '$components/Button';
import { Card } from '$components/Card';
import { successToast, warningToast } from '$components/NotificationToast';
import { errorToast, infoToast } from '$components/NotificationToast/NotificationToast.svelte';
import { OnAccount } from '$components/OnAccount';
import { OnNetwork } from '$components/OnNetwork';
import { Step, Stepper } from '$components/Stepper';
import { TokenDropdown } from '$components/TokenDropdown';
import {
type BridgeArgs,
Expand Down Expand Up @@ -41,8 +43,18 @@
import Amount from './Amount.svelte';
import { ProcessingFee } from './ProcessingFee';
import Recipient from './Recipient.svelte';
import { bridgeService, destNetwork, enteredAmount, processingFee, recipientAddress, selectedToken } from './state';

import {
activeBridge,
bridgeService,
destNetwork,
enteredAmount,
processingFee,
recipientAddress,
selectedToken,
} from './state';
import { BridgeTypes, NFTSteps } from './types';

let activeStep: NFTSteps = NFTSteps.IMPORT;
let amountComponent: Amount;
let recipientComponent: Recipient;
let processingFeeComponent: ProcessingFee;
Expand Down Expand Up @@ -296,33 +308,68 @@
}
}
}

const nextStep = () => (activeStep = Math.min(activeStep + 1, NFTSteps.CONFIRM));
const back = () => (activeStep = Math.max(activeStep - 1, NFTSteps.IMPORT));

let nftStepTitle: string;
let nftStepDescription: string;

$: {
const stepKey = NFTSteps[activeStep].toLowerCase(); // Convert enum to string and to lowercase
nftStepTitle = $t(`bridge.title.nft.${stepKey}`);
nftStepDescription = $t(`bridge.description.nft.${stepKey}`);
}

$: if ($selectedToken && amountComponent) {
amountComponent.validateAmount();
}
</script>

<Card class="w-full md:w-[524px]" title={$t('bridge.title.default')} text={$t('bridge.description')}>
<div class="space-y-[30px]">
<div class="f-between-center gap-4">
<ChainSelectorWrapper />
</div>
{#if $activeBridge === BridgeTypes.FUNGIBLE}
<Card class="w-full md:w-[524px]" title={$t('bridge.title.default')} text={$t('bridge.description')}>
<div class="space-y-[30px]">
<div class="f-between-center gap-4">
<ChainSelectorWrapper />
</div>

<TokenDropdown {tokens} bind:value={$selectedToken} />
{#if $selectedToken?.symbol === 'BLL' && !$selectedToken?.imported}
<FlatAlert class="!mt-2" message={$t('bridge.errors.bll_token')} type="warning" />
{/if}
<Amount bind:this={amountComponent} />
<TokenDropdown {tokens} bind:value={$selectedToken} />
{#if $selectedToken?.symbol === 'BLL' && !$selectedToken?.imported}
<FlatAlert class="!mt-2" message={$t('bridge.errors.bll_token')} type="warning" />
{/if}
<Amount bind:this={amountComponent} />

<div class="space-y-[16px]">
<Recipient bind:this={recipientComponent} />
<ProcessingFee bind:this={processingFeeComponent} />
</div>
<div class="space-y-[16px]">
<Recipient bind:this={recipientComponent} />
<ProcessingFee bind:this={processingFeeComponent} />
</div>

<div class="h-sep" />
<div class="h-sep" />

<Actions {approve} {bridge} />
<Actions {approve} {bridge} />
</div>
</Card>
{:else if $activeBridge === BridgeTypes.NFT}
<div class="f-col">
<Stepper {activeStep}>
<Step stepIndex={NFTSteps.IMPORT} currentStepIndex={activeStep} isActive={activeStep === NFTSteps.IMPORT}
>{$t('bridge.title.nft.import')}</Step>
<Step stepIndex={NFTSteps.REVIEW} currentStepIndex={activeStep} isActive={activeStep === NFTSteps.REVIEW}
>{$t('bridge.title.nft.review')}</Step>
<Step stepIndex={NFTSteps.CONFIRM} currentStepIndex={activeStep} isActive={activeStep === NFTSteps.CONFIRM}
>{$t('bridge.title.nft.confirm')}</Step>
</Stepper>

<Card class="mt-[32px] w-full md:w-[524px]" title={nftStepTitle} text={nftStepDescription}>
<div class="f-between-center w-full gap-4">
<Button type="primary" class="px-[28px] py-[14px] rounded-full flex-1 text-white" on:click={back}
>Previous Step</Button>
<Button type="primary" class="px-[28px] py-[14px] rounded-full flex-1 text-white" on:click={nextStep}
>Next Step</Button>
</div>
</Card>
</div>
</Card>
{/if}

<OnNetwork change={onNetworkChange} />

Expand Down
25 changes: 17 additions & 8 deletions packages/bridge-ui-v2/src/components/Bridge/BridgeTabs.svelte
Original file line number Diff line number Diff line change
@@ -1,25 +1,34 @@
<script lang="ts">
import { t } from 'svelte-i18n';

import { page } from '$app/stores';
import { LinkButton } from '$components/LinkButton';
import { PUBLIC_NFT_BRIDGE_ENABLED } from '$env/static/public';
import { classNames } from '$libs/util/classNames';

import { activeBridge } from './state';
import { BridgeTypes } from './types';

let classes = classNames('space-x-2', $$props.class);

$: isERC20Bridge = $page.route.id === '/';
$: isNFTBridge = $page.route.id === '/nft';
$: isERC20Bridge = $activeBridge === BridgeTypes.FUNGIBLE;
$: isNFTBridge = $activeBridge === BridgeTypes.NFT;

const onBridgeClick = (type: BridgeTypes) => {
activeBridge.set(type);
};
</script>

{#if PUBLIC_NFT_BRIDGE_ENABLED === 'true'}
<div class={classes}>
<LinkButton class="py-2 px-[20px]" href="/" active={isERC20Bridge}>
<button
class="{isERC20Bridge ? 'btn-primary' : 'btn-ghost'} px-[28px] py-[14px] rounded-full text-white"
on:click={() => onBridgeClick(BridgeTypes.FUNGIBLE)}>
<span> {$t('nav.token')}</span>
</LinkButton>
</button>

<LinkButton class="py-2 px-[20px]" href="/nft" active={isNFTBridge}>
<button
class="{isNFTBridge ? 'btn-primary' : 'btn-ghost'} h-[44px] px-[28px] py-[14px] rounded-full text-white"
on:click={() => onBridgeClick(BridgeTypes.NFT)}>
<span> {$t('nav.nft')}</span>
</LinkButton>
</button>
</div>
{/if}
3 changes: 3 additions & 0 deletions packages/bridge-ui-v2/src/components/Bridge/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { bridges } from '$libs/bridge';
import { chains } from '$libs/chain';
import type { Token } from '$libs/token';

import { type BridgeType, BridgeTypes } from './types';

// Note: we could combine this with Context API, but since we'll only
// have one Bridge component, it would be an overkill. If we wanted to
// instantiate multiple Bridge components, then we'd need to use
Expand All @@ -14,6 +16,7 @@ import type { Token } from '$libs/token';
// but once again, we don't need such level of security that we have to
// prevent other components outside the Bridge from accessing this store.

export const activeBridge = writable<BridgeType>(BridgeTypes.FUNGIBLE);
export const selectedToken = writable<Maybe<Token>>(null);
export const tokenBalance = writable<Maybe<FetchBalanceResult>>(null);
export const enteredAmount = writable<bigint>(BigInt(0));
Expand Down
12 changes: 12 additions & 0 deletions packages/bridge-ui-v2/src/components/Bridge/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export enum BridgeTypes {
FUNGIBLE = 'FUNGIBLE',
NFT = 'NFT',
}

export enum NFTSteps {
IMPORT,
REVIEW,
CONFIRM,
}

export type BridgeType = BridgeTypes;
4 changes: 3 additions & 1 deletion packages/bridge-ui-v2/src/components/Card/Card.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@

<div class={classes}>
<div class="card-body body-regular p-4 md:p-[50px] gap-0">
<h2 class="card-title title-screen-bold">{title}</h2>
{#if title}
<h2 class="card-title title-screen-bold">{title}</h2>
{/if}
{#if text}
<p>{text}</p>
{/if}
Expand Down
13 changes: 13 additions & 0 deletions packages/bridge-ui-v2/src/components/Stepper/Step.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script lang="ts">
export let isActive = false;
export let stepIndex: number;
export let currentStepIndex: number;
</script>

<li
data-content=""
class="step
{isActive ? 'step-primary' : ''}
{stepIndex < currentStepIndex && !isActive ? 'step-previous' : ''}">
<slot />
</li>
23 changes: 23 additions & 0 deletions packages/bridge-ui-v2/src/components/Stepper/Stepper.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<script lang="ts">
import { classNames } from '$libs/util/classNames';
export let activeStep: number = 0;

const styles = `
w-full
md:card
md:rounded-[20px]
md:border
md:border-divider-border
dark:md:glassy-gradient-card
dark:md:glass-background-gradient`;

$: classes = classNames(styles, $$props.class);
</script>

<div class={classes}>
<div class="card-body body-regular gap-0 p-0">
<ul class="steps my-[30px]">
<slot {activeStep} />
</ul>
</div>
</div>
2 changes: 2 additions & 0 deletions packages/bridge-ui-v2/src/components/Stepper/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as Step } from './Step.svelte';
export { default as Stepper } from './Stepper.svelte';
18 changes: 15 additions & 3 deletions packages/bridge-ui-v2/src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,20 @@
"bridge": {
"title": {
"default": "Bridge Token",
"nft": "Bridge NFT"
"nft": {
"import": "Import NFT",
"review": "Review",
"confirm": "Confirm"
}
},
"description": {
"default": "Send your assets across chains.",
"nft": {
"import": "Transfer your NFTs across chains",
"review": "Verify your NFT details.",
"confirm": "Confirm your NFT details."
}
},
"description": "Send your assets across chains.",
"actions": {
"approve": {
"tx": "Transaction sent to approve {token} tokens. Click <a href=\"{url}\" target=\"_blank\"><b>here</b></a> to see it in the explorer.",
Expand Down Expand Up @@ -48,7 +59,8 @@
"approved": "Approved",
"bridge": "Bridge",
"bridging": "Bridging",
"import": "Fetch NFT data"
"fetch": "Fetch NFT data",
"import": "Import"
}
},
"recipient": {
Expand Down
31 changes: 31 additions & 0 deletions packages/bridge-ui-v2/src/styles/override.css
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,34 @@ input[type='number'] {
flex-direction: column;
align-items: center;
}

.step:after {
height: 0px !important;
width: 0px !important;
}

.step-primary:after {
height: 8px !important;
width: 8px !important;
}

.step:before {
background-color: rgba(255, 198, 233, 0.2) !important;
height: 2px !important;
}

.step-primary:before {
background-color: #e81899 !important;
height: 2px !important;
}

.step-previous:before {
background-color: #e81899 !important;
height: 2px !important;
}

.step-previous:after {
@apply step-primary;
height: 0px !important;
width: 0px !important;
}
Loading