Skip to content

Commit

Permalink
Staking form UI refinement 2 (#444)
Browse files Browse the repository at this point in the history
* feat: staking form ui refinement

---------

Co-authored-by: Tong Li <[email protected]>
Co-authored-by: David Totraev <[email protected]>
  • Loading branch information
3 people authored Dec 4, 2024
1 parent ff97500 commit 8f0c314
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 107 deletions.
2 changes: 1 addition & 1 deletion src/app/components/PersonalBalance/PersonalBalance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export function PersonalBalance() {

return (
<div className="flex flex-col gap-4 p-1 xl:justify-between mb-12">
<Heading variant="h5" className="text-primary-dark md:text-4xl">
<Heading variant="h4" className="text-primary-dark md:text-4xl">
Wallet Balance
</Heading>
<div className="flex flex-col justify-between bg-secondary-contrast rounded p-6 text-base md:flex-row">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Heading, Text } from "@babylonlabs-io/bbn-core-ui";
import { useEffect } from "react";
import InfiniteScroll from "react-infinite-scroll-component";

Expand Down Expand Up @@ -43,10 +44,13 @@ export const FinalityProviders: React.FC<FinalityProvidersProps> = ({
}

return (
<>
<p>
<strong>Step-1:</strong> Select a finality provider
</p>
<div className="flex flex-col">
<Heading variant="h5" className="text-primary-dark">
Step 1
</Heading>
<Text variant="body1" className="text-primary-light">
Select a Finality Provider
</Text>
<div className="flex gap-3">
<FinalityProviderSearch
searchValue={searchValue}
Expand Down Expand Up @@ -95,6 +99,6 @@ export const FinalityProviders: React.FC<FinalityProvidersProps> = ({
))}
</InfiniteScroll>
</div>
</>
</div>
);
};
15 changes: 9 additions & 6 deletions src/app/components/Staking/Form/StakingAmount.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ChangeEvent, FocusEvent, useEffect, useState } from "react";
import { twJoin } from "tailwind-merge";

import { getNetworkConfig } from "@/config/network.config";
import { btcToSatoshi, satoshiToBtc } from "@/utils/btc";
Expand Down Expand Up @@ -125,17 +126,19 @@ export const StakingAmount: React.FC<StakingAmountProps> = ({
</div>
<input
type="string"
className={`no-focus input input-bordered w-full ${error ? "input-error" : ""}`}
className={twJoin(
"no-focus input input-bordered w-full",
error ? "input-error" : "",
)}
value={value}
onChange={handleChange}
onBlur={handleBlur}
placeholder={coinName}
/>
{error && (
<div className="my-2 min-h-[20px]">
<p className="text-center text-sm text-error">{error}</p>
</div>
)}

<div className="text-left my-2 min-h-5">
{error && <p className="text-sm text-error-main">{error}</p>}
</div>
</label>
);
};
10 changes: 5 additions & 5 deletions src/app/components/Staking/Form/StakingFee.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export const StakingFee: React.FC<StakingFeeProps> = ({

const defaultModeRender = () => {
return (
<div className="flex flex-col justify-center gap-1 items-center">
<div className="flex flex-col gap-1 items-start">
<div className="min-h-8 flex justify-center flex-col items-center">
{mempoolFeeRates ? (
<div>
Expand All @@ -67,7 +67,7 @@ export const StakingFee: React.FC<StakingFeeProps> = ({
)}
</div>
<button
className="btn btn-sm btn-link no-underline"
className="btn btn-sm btn-link w-full no-underline"
onClick={() => setCustomMode(true)}
disabled={!mempoolFeeRates || !stakingFeeSat}
>
Expand All @@ -83,8 +83,8 @@ export const StakingFee: React.FC<StakingFeeProps> = ({
selectedFeeRate && mempoolFeeRates && selectedFeeRate < defaultFeeRate;

return (
<div className="flex flex-col gap-2">
<div className="flex flex-col items-center">
<div className="flex flex-col gap-1">
<div className="flex flex-col items-start">
<p>
Selected fee rate:{" "}
<strong>{selectedFeeRate || defaultFeeRate} sat/vB</strong>
Expand Down Expand Up @@ -123,7 +123,7 @@ export const StakingFee: React.FC<StakingFeeProps> = ({
const customModeReady = customMode && mempoolFeeRates && stakingFeeSat;

return (
<div className="my-2 text-sm">
<div className="text-sm">
{customModeReady ? selectedModeRender() : defaultModeRender()}
</div>
);
Expand Down
40 changes: 11 additions & 29 deletions src/app/components/Staking/Form/StakingTime.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { ChangeEvent, FocusEvent, useEffect, useState } from "react";

import { getNetworkConfig } from "@/config/network.config";
import { blocksToDisplayTime } from "@/utils/time";
import { twJoin } from "tailwind-merge";

import { validateNoDecimalPoints } from "./validation/validation";

Expand All @@ -28,8 +26,6 @@ export const StakingTime: React.FC<StakingTimeProps> = ({
const errorLabel = "Staking term";
const generalErrorMessage = "You should input staking term";

const { coinName } = getNetworkConfig();

// Use effect to reset the state when reset prop changes
useEffect(() => {
setValue("");
Expand Down Expand Up @@ -99,30 +95,13 @@ export const StakingTime: React.FC<StakingTimeProps> = ({

const isFixed = minStakingTimeBlocks === maxStakingTimeBlocks;
if (isFixed) {
return (
<div className="card mb-2 bg-base-200 p-4">
<p>
You can unbond and withdraw your stake anytime with an unbonding time
of {blocksToDisplayTime(unbondingTimeBlocks)}.
</p>
<p>
There is also a build-in maximum staking period of{" "}
{blocksToDisplayTime(minStakingTimeBlocks)}.
</p>
<p>
If the stake is not unbonded before the end of this period, it will
automatically become withdrawable by you anytime afterwards.
</p>
<p>
The above times are approximates based on average {coinName} block
time.
</p>
</div>
);
// If the staking time is fixed, don't show the input field, but make sure value is set
onStakingTimeBlocksChange(minStakingTimeBlocks);
return null;
}

return (
<label className="form-control w-full flex-1">
<label className="form-control w-full">
<div className="label">
<span className="label-text-alt text-base">Term</span>
<span className="label-text-alt">
Expand All @@ -131,14 +110,17 @@ export const StakingTime: React.FC<StakingTimeProps> = ({
</div>
<input
type="string"
className={`no-focus input input-bordered w-full ${error && "input-error"}`}
className={twJoin(
`no-focus input input-bordered w-full`,
error && "input-error",
)}
value={value}
onChange={handleChange}
onBlur={handleBlur}
placeholder="Blocks"
/>
<div className="mb-2 mt-4 min-h-[20px]">
<p className="text-center text-sm text-error">{error}</p>
<div className="text-left my-2 min-h-5">
<p className="text-sm text-error-main">{error}</p>
</div>
</label>
);
Expand Down
160 changes: 100 additions & 60 deletions src/app/components/Staking/Staking.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Button, Heading, Text } from "@babylonlabs-io/bbn-core-ui";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useEffect, useMemo, useState } from "react";
import { MdErrorOutline } from "react-icons/md";
import { Tooltip } from "react-tooltip";
import { useLocalStorage } from "usehooks-ts";

Expand Down Expand Up @@ -462,85 +464,123 @@ export const Staking = () => {
signReady && feeRate && availableUTXOs && stakingAmountSat;

return (
<>
<p>
<strong>Step-2:</strong> Set up staking terms
</p>
<div className="flex flex-1 flex-col">
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-4">
<Heading variant="h5" className="text-primary-dark">
Step 2
</Heading>
<Text variant="body1" className="text-primary-light">
Set Staking Amount
</Text>
<div className="rounded bg-[#F9F9F9] flex flex-row items-start justify-between py-2 px-4 text-primary-light">
<div className="py-2 pr-3">
<MdErrorOutline size={22} />
</div>
<div className="flex flex-col gap-1 grow">
<Text
variant="subtitle1"
className="font-medium text-primary-dark"
>
Info
</Text>
<Text variant="body1">
You can unbond and withdraw your stake anytime with an
unbonding time of 7 days.
</Text>
<a
href="#"
target="_blank"
rel="noopener noreferrer"
className="text-secondary-main hover:text-primary-main"
>
Learn More
</a>
</div>
</div>
<div className="flex flex-1 flex-col">
<StakingTime
minStakingTimeBlocks={minStakingTimeBlocks}
maxStakingTimeBlocks={maxStakingTimeBlocks}
unbondingTimeBlocks={unbondingTime}
onStakingTimeBlocksChange={handleStakingTimeBlocksChange}
reset={resetFormInputs}
/>
<StakingAmount
minStakingAmountSat={minStakingAmountSat}
maxStakingAmountSat={maxStakingAmountSat}
btcWalletBalanceSat={btcWalletBalanceSat}
onStakingAmountSatChange={handleStakingAmountSatChange}
reset={resetFormInputs}
/>
{signReady && (
<StakingFee
mempoolFeeRates={mempoolFeeRates}
stakingFeeSat={stakingFeeSat}
selectedFeeRate={selectedFeeRate}
onSelectedFeeRateChange={setSelectedFeeRate}
<div className="flex flex-1 flex-col">
<StakingTime
minStakingTimeBlocks={minStakingTimeBlocks}
maxStakingTimeBlocks={maxStakingTimeBlocks}
unbondingTimeBlocks={unbondingTime}
onStakingTimeBlocksChange={handleStakingTimeBlocksChange}
reset={resetFormInputs}
/>
<StakingAmount
minStakingAmountSat={minStakingAmountSat}
maxStakingAmountSat={maxStakingAmountSat}
btcWalletBalanceSat={btcWalletBalanceSat}
onStakingAmountSatChange={handleStakingAmountSatChange}
reset={resetFormInputs}
/>
{signReady && (
<StakingFee
mempoolFeeRates={mempoolFeeRates}
stakingFeeSat={stakingFeeSat}
selectedFeeRate={selectedFeeRate}
onSelectedFeeRateChange={setSelectedFeeRate}
reset={resetFormInputs}
/>
)}
</div>
<span
className="cursor-pointer text-xs mt-4"
data-tooltip-id="tooltip-staking-preview"
data-tooltip-content={signNotReadyReason}
data-tooltip-place="top"
>
<Button
size="large"
fluid
disabled={!previewReady}
onClick={() => setPreviewModalOpen(true)}
>
Preview
</Button>
<Tooltip
id="tooltip-staking-preview"
className="tooltip-wrap"
/>
</span>
{previewReady && (
<PreviewModal
isOpen={previewModalOpen}
onClose={handlePreviewModalClose}
onSign={handleDelegationEoiCreation}
finalityProvider={finalityProvider?.description.moniker}
finalityProviderAvatar={
finalityProvider?.description.identity
}
stakingAmountSat={stakingAmountSat}
stakingTimeBlocks={stakingTimeBlocksWithFixed}
stakingFeeSat={stakingFeeSat}
feeRate={feeRate}
unbondingFeeSat={unbondingFeeSat}
awaitingWalletResponse={awaitingWalletResponse}
/>
)}
</div>
<span
className="cursor-pointer text-xs"
data-tooltip-id="tooltip-staking-preview"
data-tooltip-content={signNotReadyReason}
data-tooltip-place="top"
>
<button
className="btn-primary btn mt-2 w-full"
disabled={!previewReady}
onClick={() => setPreviewModalOpen(true)}
>
Preview
</button>
<Tooltip id="tooltip-staking-preview" className="tooltip-wrap" />
</span>
{previewReady && (
<PreviewModal
isOpen={previewModalOpen}
onClose={handlePreviewModalClose}
onSign={handleDelegationEoiCreation}
finalityProvider={finalityProvider?.description.moniker}
finalityProviderAvatar={finalityProvider?.description.identity}
stakingAmountSat={stakingAmountSat}
stakingTimeBlocks={stakingTimeBlocksWithFixed}
stakingFeeSat={stakingFeeSat}
feeRate={feeRate}
unbondingFeeSat={unbondingFeeSat}
awaitingWalletResponse={awaitingWalletResponse}
/>
)}
</div>
</>
</div>
);
}
};

return (
<div className="card flex flex-col gap-2 bg-base-300 p-4 shadow-sm lg:flex-1">
<h3 className="mb-4 font-bold">Staking</h3>
<div className="card flex flex-col gap-2 p-4 lg:flex-1">
<Heading variant="h4" className="text-primary-dark md:text-4xl">
Bitcoin Staking
</Heading>
<div className="flex flex-col gap-4 lg:flex-row">
<div className="flex flex-1 flex-col gap-4 lg:basis-3/5 xl:basis-2/3">
<div className="flex flex-1 flex-col gap-4 lg:basis-3/5 xl:basis-2/3 p-6 rounded border bg-secondary-contrast border-primary-light/20">
<FinalityProviders
onFinalityProvidersLoad={setFinalityProviders}
selectedFinalityProvider={finalityProvider}
onFinalityProviderChange={handleChooseFinalityProvider}
/>
</div>
<div className="divider m-0 lg:divider-horizontal lg:m-0" />
<div className="flex flex-1 flex-col gap-4 lg:basis-2/5 xl:basis-1/3">
<div className="flex flex-1 flex-col gap-4 lg:basis-2/5 xl:basis-1/3 p-6 rounded border bg-secondary-contrast border-primary-light/20">
{renderStakingForm()}
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/Stats/Stats.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const Stats = memo(() => {

return (
<div className="flex flex-col gap-4 p-1 xl:justify-between mb-12">
<Heading variant="h5" className="text-primary-contrast md:text-4xl">
<Heading variant="h4" className="text-primary-contrast md:text-4xl">
Babylon Stats
</Heading>
<div className="flex flex-col justify-between bg-secondary-contrast rounded p-6 text-base md:flex-row">
Expand Down

0 comments on commit 8f0c314

Please sign in to comment.