Skip to content

Commit

Permalink
feat: improve ui and data collection
Browse files Browse the repository at this point in the history
  • Loading branch information
icfor committed Mar 6, 2024
1 parent 2282850 commit 530d13f
Show file tree
Hide file tree
Showing 16 changed files with 204 additions and 74 deletions.
6 changes: 3 additions & 3 deletions src/features/staking/components/delegation-details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ const DelegationRowBase = ({
staking.dispatch(
setModalOpened({
content: {
validator,
delegations: [delegation],
},
type: "rewards",
}),
Expand Down Expand Up @@ -204,7 +204,7 @@ const UnbondingRow = ({
unbonding,
validator,
}: UnbondingRowProps) => {
const { client, staking } = stakingRef;
const { account, client, staking } = stakingRef;

const logo = useValidatorLogo(validator?.description.identity);
const validatorAddress = unbonding.validator;
Expand Down Expand Up @@ -247,7 +247,7 @@ const UnbondingRow = ({
if (!client) return;

const addresses = {
delegator: staking.state.tokens?.denom || "",
delegator: account.bech32Address || "",
validator: unbonding.validator,
};

Expand Down
39 changes: 27 additions & 12 deletions src/features/staking/components/modals/rewards.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { memo, useEffect, useRef, useState } from "react";
import { toast } from "react-toastify";

import { Button, HeroText } from "@/features/core/components/base";
import CommonModal from "@/features/core/components/common-modal";
Expand All @@ -18,18 +19,32 @@ const claimRewards = async (
const { client, staking } = stakingRef;
const { modal } = staking.state;

const validatorAddress = modal?.content.validator.operatorAddress;

if (!client || !validatorAddress) return;

const addresses = {
delegator: stakingRef.account.bech32Address,
validator: validatorAddress,
};

claimRewardsAction(addresses, client, stakingRef.staking).finally(() => {
setStep("completed");
});
const { delegations } = modal?.content || {};

if (!client || !delegations?.length) return;

delegations
.reduce(async (promise, delegation) => {
await promise;

const addresses = {
delegator: stakingRef.account.bech32Address,
validator: delegation.validatorAddress,
};

return claimRewardsAction(addresses, client, stakingRef.staking);
}, Promise.resolve())
.then(() => {
setStep("completed");
})
.catch(() => {
toast(
"There was an unexpected error claiming your rewards. Please try again later.",
{
type: "error",
},
);
});
};

const RewardsModal = () => {
Expand Down
41 changes: 22 additions & 19 deletions src/features/staking/components/modals/staking.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ const StakingModal = () => {

const { validator } = modal?.content;

if (!validator) return null;

const amountXIONParsed = new BigNumber(amountXION);

const amountUSD = (() => {
Expand Down Expand Up @@ -177,23 +179,29 @@ const StakingModal = () => {
);
}

const getHasAmountError = () =>
!amountUSD ||
!availableTokens ||
amountXIONParsed.isNaN() ||
amountXIONParsed.gt(availableTokens);
const validateAmount = () => {
if (
!amountUSD ||
!availableTokens ||
amountXIONParsed.isNaN() ||
amountXIONParsed.gt(availableTokens)
) {
setFormError({
...formError,
amount: "Invalid amount",
});

return true;
}
};

const onSubmit: FormEventHandler<HTMLFormElement> = (e) => {
e?.stopPropagation();
e?.preventDefault();

if (
!client ||
hasErrors ||
getHasAmountError() ||
amountXIONParsed.lt(0)
)
return;
if (validateAmount()) return;

if (!client || hasErrors || amountXIONParsed.lt(0)) return;

setStep("review");
};
Expand Down Expand Up @@ -229,12 +237,7 @@ const StakingModal = () => {
disabled={isLoading}
error={!!formError.amount}
onBlur={() => {
if (getHasAmountError()) {
setFormError({
...formError,
amount: "Invalid amount",
});
}
validateAmount();
}}
onChange={(e) => {
if (formError.amount) {
Expand Down Expand Up @@ -263,7 +266,7 @@ const StakingModal = () => {
</div>
<div className="mt-[48px] w-full">
<Button disabled={isLoading || hasErrors} type="submit">
Delegate Now
DELEGATE NOW
</Button>
</div>
</form>
Expand Down
38 changes: 38 additions & 0 deletions src/features/staking/components/modals/unstaking.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { toast } from "react-toastify";

import {
Button,
FormError,
Heading2,
Heading8,
HeroText,
Expand Down Expand Up @@ -32,6 +33,10 @@ const UnstakingModal = () => {
const [step, setStep] = useState<Step>(initialStep);
const [isLoading, setIsLoading] = useState(false);

const [formError, setFormError] = useState<
Record<string, string | undefined>
>({ amount: undefined, memo: undefined });

const [amountXION, setAmount] = useState("");
const [memo, setMemo] = useState("");

Expand All @@ -43,6 +48,7 @@ const UnstakingModal = () => {
() => () => {
setStep(initialStep);
setAmount("");
setFormError({});
setMemo("");
},
[isOpen],
Expand All @@ -52,6 +58,8 @@ const UnstakingModal = () => {

const { validator } = modal?.content;

if (!validator) return null;

const amountXIONParsed = new BigNumber(amountXION);

const amountUSD = (() => {
Expand All @@ -65,10 +73,28 @@ const UnstakingModal = () => {
validator.operatorAddress,
);

const validateAmount = () => {
if (
!amountUSD ||
!delegatedTokens ||
amountXIONParsed.lte(0) ||
amountXIONParsed.gt(new BigNumber(delegatedTokens.amount))
) {
setFormError({
...formError,
amount: "Invalid amount",
});

return true;
}
};

const onSubmit: FormEventHandler<HTMLFormElement> = (e) => {
e?.stopPropagation();
e?.preventDefault();

if (validateAmount()) return;

if (!client || !amountXIONParsed.gt(0)) return;

setStep("review");
Expand Down Expand Up @@ -217,11 +243,23 @@ const UnstakingModal = () => {
<div className="mt-[8px]">
<InputBox
disabled={isLoading}
onBlur={() => {
validateAmount();
}}
onChange={(e) => {
if (formError.amount) {
setFormError({ ...formError, amount: undefined });
}

setAmount(e.target.value);
}}
value={amountXION}
/>
{formError.amount && (
<div>
<FormError>{formError.amount}</FormError>
</div>
)}
</div>
<div className="mt-[40px] w-full">
<OpenInput
Expand Down
30 changes: 25 additions & 5 deletions src/features/staking/components/staking-overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@ import {
} from "@/features/core/components/base";

import { useStaking } from "../context/hooks";
import { getTotalDelegation, getTotalRewards } from "../context/selectors";
import { setModalOpened } from "../context/reducer";
import {
getAPR,
getTotalDelegation,
getTotalRewards,
} from "../context/selectors";
import { getEmptyXionCoin } from "../lib/core/coins";
import { basePath } from "../lib/core/constants";
import { getIsMinimumClaimable } from "../lib/core/tx";
import { formatCoin, formatXionToUSD } from "../lib/formatters";
import { formatAPR, formatCoin, formatXionToUSD } from "../lib/formatters";
import { DivisorVertical } from "./divisor";

const divisorStyle = "absolute bottom-[24px] right-[10px] top-[24px]";
Expand Down Expand Up @@ -56,6 +61,8 @@ const StakingOverview = () => {
const totalRewards =
getTotalRewards(null, staking.state) || getEmptyXionCoin();

const apr = getAPR(staking.state);

const availableDelegation = staking.state.tokens || getEmptyXionCoin();

return (
Expand All @@ -71,8 +78,21 @@ const StakingOverview = () => {
<Heading8>Claimable Rewards</Heading8>
<div className="flex flex-row items-center gap-4">
<Heading2>{formatXionToUSD(totalRewards)}</Heading2>
{getIsMinimumClaimable(totalRewards) /* @TODO */ && (
<ButtonPill>Claim</ButtonPill>
{getIsMinimumClaimable(totalRewards) && (
<ButtonPill
onClick={() => {
staking.dispatch(
setModalOpened({
content: {
delegations: staking.state.delegations?.items || [],
},
type: "rewards",
}),
);
}}
>
Claim
</ButtonPill>
)}
</div>
<BodyMedium>{formatCoin(totalRewards)}</BodyMedium>
Expand All @@ -82,7 +102,7 @@ const StakingOverview = () => {
</div>
<div className={columnStyle}>
<Heading8>APR</Heading8>
<Heading2>15.57%</Heading2>
<Heading2>{formatAPR(apr)}</Heading2>
<div className={divisorStyle}>
<DivisorVertical />
</div>
Expand Down
17 changes: 7 additions & 10 deletions src/features/staking/components/validator-delegation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,21 +65,18 @@ export default function ValidatorDelegation() {
return <div>Loading ...</div>;
}

const availableToStakeBN = getTokensAvailableBG(stakingRef.staking.state);
const availableToStakeBN = getTokensAvailableBG(staking.state);

const userTotalDelegation = getTotalDelegation(
stakingRef.staking.state,
null,
);
const userTotalDelegation = getTotalDelegation(staking.state, null);

const userTotalUnbondings = getTotalUnbonding(stakingRef.staking.state, null);
const userTotalUnbondings = getTotalUnbonding(staking.state, null);

const totalRewards = getTotalRewards(
validatorDetails.operatorAddress,
stakingRef.staking.state,
staking.state,
);

const canShowDetail = getCanShowDetails(stakingRef.staking.state);
const canShowDetail = getCanShowDetails(staking.state);

const content = !isConnected ? (
<div className="flex h-[220px] flex-col items-center justify-center gap-[32px] rounded-[24px] bg-bg-600 uppercase">
Expand All @@ -106,10 +103,10 @@ export default function ValidatorDelegation() {
<ButtonPill
disabled={isLoadingBlocking}
onClick={() => {
stakingRef.staking.dispatch(
staking.dispatch(
setModalOpened({
content: {
validator: validatorDetails,
delegations: staking.state.delegations?.items || [],
},
type: "rewards",
}),
Expand Down
23 changes: 16 additions & 7 deletions src/features/staking/context/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { Coin } from "@cosmjs/stargate";
import {
getBalance,
getDelegations,
getInflation,
getPool,
getRewards,
getUnbondingDelegations,
Expand All @@ -17,6 +18,7 @@ import {
addDelegations,
addUnbondings,
setExtraValidators,
setInflation,
setIsInfoLoading,
setPool,
setTokens,
Expand All @@ -30,13 +32,19 @@ export const fetchStakingDataAction = async (staking: StakingContextType) => {
try {
staking.dispatch(setIsInfoLoading(true));

const [validatorsBonded, validatorsUnbonded, validatorsUnbonding, pool] =
await Promise.all([
getValidatorsList("BOND_STATUS_BONDED"),
getValidatorsList("BOND_STATUS_UNBONDED"),
getValidatorsList("BOND_STATUS_UNBONDING"),
getPool(),
]);
const [
validatorsBonded,
validatorsUnbonded,
validatorsUnbonding,
inflation,
pool,
] = await Promise.all([
getValidatorsList("BOND_STATUS_BONDED"),
getValidatorsList("BOND_STATUS_UNBONDED"),
getValidatorsList("BOND_STATUS_UNBONDING"),
getInflation(),
getPool(),
]);

(
[
Expand All @@ -59,6 +67,7 @@ export const fetchStakingDataAction = async (staking: StakingContextType) => {
});

staking.dispatch(setPool(pool));
staking.dispatch(setInflation(inflation.toString()));

staking.dispatch(setIsInfoLoading(false));
} catch (error) {
Expand Down
Loading

0 comments on commit 530d13f

Please sign in to comment.