Skip to content

Commit

Permalink
feat: add unstaking completed modal
Browse files Browse the repository at this point in the history
  • Loading branch information
icfor committed Apr 5, 2024
1 parent 19028e0 commit c17e7dd
Show file tree
Hide file tree
Showing 10 changed files with 451 additions and 27 deletions.
211 changes: 211 additions & 0 deletions public/icons/unstaking_coins.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions public/locales/en/staking.json
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,13 @@
},
"available": "Staked",
"button": "Unstake",
"complete": {
"desc1Solana": "Your tokens are currently in the unstaking period and will be successfully unstaked on <0>{{unstakeDate}}</0>.",
"desc2Solana": "Remember to withdraw your tokens after unstaking period is complete and status changes to \"Unstaked\".",
"intro": "Your tokens are currently in the unstaking period",
"introSub": "Hope to see you again soon.",
"titleComplete": "Unstake successful"
},
"info2T": "During this time, you <1>won't receive</1> staking rewards.",
"info3": "However, if you change your mind, you can cancel the process at any time.",
"info4": "To continue staking after this period, you'll need to stake again.",
Expand Down
7 changes: 7 additions & 0 deletions public/locales/zh-CN/staking.json
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,13 @@
},
"available": "质押",
"button": "取消质押",
"complete": {
"desc1Solana": "Your tokens are currently in the unstaking period and will be successfully unstaked on <0>{{unstakeDate}}</0>.",
"desc2Solana": "Remember to withdraw your tokens after unstaking period is complete and status changes to \"Unstaked\".",
"intro": "Your tokens are currently in the unstaking period",
"introSub": "Hope to see you again soon.",
"titleComplete": "Unstake successful"
},
"info2T": "在此期间,您<1>不会收到</1>质押奖励。",
"info3": "但是,如果您改变主意,可以随时取消该过程。",
"info4": "要在此期限后继续质押,您需要再次质押。",
Expand Down
7 changes: 7 additions & 0 deletions public/locales/zh-HK/staking.json
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,13 @@
},
"available": "質押",
"button": "取消質押",
"complete": {
"desc1Solana": "Your tokens are currently in the unstaking period and will be successfully unstaked on <0>{{unstakeDate}}</0>.",
"desc2Solana": "Remember to withdraw your tokens after unstaking period is complete and status changes to \"Unstaked\".",
"intro": "Your tokens are currently in the unstaking period",
"introSub": "Hope to see you again soon.",
"titleComplete": "Unstake successful"
},
"info2T": "在此期間,您<1>不會收到</1>質押獎勵。",
"info3": "但是,如果您改變主意,可以隨時取消該過程。",
"info4": "要在此期限後繼續質押,您需要再次質押。",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,6 @@ const PopOver = ({
fetchCoinPriceForNetwork(stakingRef.current, stakingNetworkId);
}, [stakingRef, stakingNetworkId]);

// @TODO: Remove
const [rerenderKey, rerender] = useState(0);

const accountsWithDelegations = accounts?.filter(accountHasDelegations);
const accountsWithRewards = accounts?.filter(accountHasRewards);

Expand All @@ -159,19 +156,8 @@ const PopOver = ({
onClickCapture={() => setShowPopover("")}
/>
<div>{networkImage}</div>
<button
onClick={() => {
rerender((prev) => prev + 1);
}}
>
Rerender
</button>
{network.name && <div className={styles.name}>{network.name}</div>}
<StakingDataBox
key={rerenderKey}
network={network}
onFocusContent={setIsContentFocused}
/>
<StakingDataBox network={network} onFocusContent={setIsContentFocused} />
{!!networkSummary && !isContentFocused && (
<div className={styles.dataBox}>
{(() => {
Expand Down
12 changes: 10 additions & 2 deletions src/screens/staking/components/staking_section/staking_modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,25 @@ import {
import { getSelectedAccount } from "@src/screens/staking/lib/staking_sdk/context/selectors";
import type { StakingNetworkInfo } from "@src/screens/staking/lib/staking_sdk/core";
import { mainNetworkDenom } from "@src/screens/staking/lib/staking_sdk/core/base";
import { solanaNetworks } from "@src/screens/staking/lib/staking_sdk/core/solana";
import { formatCoin } from "@src/screens/staking/lib/staking_sdk/formatters";
import { getAccountNormalisedBalance } from "@src/screens/staking/lib/staking_sdk/utils/accounts";
import {
getEmptyCoin,
getIsCoin,
} from "@src/screens/staking/lib/staking_sdk/utils/coins";
import { getUnbondingTimeForNetwork } from "@src/screens/staking/lib/staking_sdk/utils/networks";
import {
getHasStaked,
setHasStaked,
} from "@src/screens/staking/lib/staking_sdk/utils/storage";
import {
MAX_MEMO,
stakeAmount,
} from "@src/screens/staking/lib/staking_sdk/wallet_operations";
import { StakeError } from "@src/screens/staking/lib/staking_sdk/wallet_operations/base";
import { PostHogCustomEvent } from "@src/utils/posthog";

import { solanaNetworks } from "../../lib/staking_sdk/core/solana";
import Label from "./label";
import ModalBase, { ModalError } from "./modal_base";
import NetworksSelect from "./networks_select";
Expand Down Expand Up @@ -163,7 +167,11 @@ const StakingModal = () => {
walletAddress: selectedAccount.address,
});

if (solanaNetworks.has(selectedAccount.networkId)) {
if (
solanaNetworks.has(selectedAccount.networkId) &&
!getHasStaked()
) {
setHasStaked();
setHasCompleted(true);

return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,96 @@
flex-direction: column;
gap: 12px;
}

.coinsImg {
width: 250px;
}

.completedWrapper {
align-items: flex-start;
align-self: stretch;
border-radius: 24px;
box-shadow: 4px 8px 24px 0 rgba(90, 117, 255, 0.24);
display: flex;
flex-direction: column;
justify-content: center;
margin-bottom: 36px;
max-width: 550px;
padding: 24px;
}

.intro {
color: #616161;
font-size: 18px;
font-style: normal;
font-weight: 600;
line-height: 21px;
text-shadow: 0 4px 24px rgba(2, 38, 225, 0.24);
}

.bulb {
width: 24px;
}

.blueCard {
align-items: flex-start;
background: #dbedff;
border-left: 4px solid #007fff;
border-radius: 8px;
display: flex;
flex-direction: row;
gap: 12px;
padding: 16px;
}

.tipsTitle {
color: #25282d;
font-size: 16px;
font-weight: 600;
letter-spacing: 0.032px;
line-height: 20px;
margin-bottom: 12px;
}

.rewards {
color: #25282d;
font-size: 14px;
font-weight: 600;
letter-spacing: -0.056px;
line-height: 20px;
margin-bottom: 12px;
}

.tipsList {
color: #616161;

b {
color: #007fff;
font-size: 14px;
font-style: normal;
font-weight: 600;
letter-spacing: -0.056px;
line-height: 20px;
}
}

.tipsTitleWrapper {
display: flex;
flex-direction: row;
gap: 8px;
}

.introSub {
color: #9d9d9d;
font-size: 14px;
font-weight: 400;
letter-spacing: 0.308px;
line-height: 20px;
}

.introWrapper {
display: flex;
flex-direction: column;
gap: 8px;
margin: 24px 0;
}
93 changes: 90 additions & 3 deletions src/screens/staking/components/staking_section/unstaking_modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import { useEffect, useState } from "react";
import FormInput from "@src/components/form_input";
import HighlightButton from "@src/components/highlight-button";
import IconWarning from "@src/components/icons/icon_warning.svg";
import IconInfoCircle from "@src/components/icons/info-circle.svg";
import LoadingSpinner from "@src/components/loading_spinner";
import { toastSuccess } from "@src/components/notification";
import { tooltipId } from "@src/components/tooltip";
import {
displayGenericError,
notEnoughGasError,
Expand All @@ -33,6 +35,10 @@ import {
sumAllCoins,
} from "@src/screens/staking/lib/staking_sdk/utils/coins";
import { getUnbondingTimeForNetwork } from "@src/screens/staking/lib/staking_sdk/utils/networks";
import {
getHasUnstaked,
setHasUnstaked,
} from "@src/screens/staking/lib/staking_sdk/utils/storage";
import {
MAX_MEMO,
unstake,
Expand Down Expand Up @@ -83,6 +89,7 @@ const UnstakingModal = () => {
const [amount, setAmount] = useState("");
const [amountError, setAmountError] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [hasCompleted, setHasCompleted] = useState(true);

const { state: stakingState } = stakingRef.current;

Expand All @@ -95,6 +102,7 @@ const UnstakingModal = () => {
setAmountError("");
setMemo("");
setMemoError("");
setHasCompleted(true);
};
}
}, [isOpen]);
Expand Down Expand Up @@ -134,6 +142,8 @@ const UnstakingModal = () => {
: []
).filter((acc) => ["activating", "active"].includes(acc.status));

const onClose = () => setSelectedAccount(stakingRef.current, null, null);

const onSubmit = (e: any) => {
e?.preventDefault();

Expand Down Expand Up @@ -171,8 +181,6 @@ const UnstakingModal = () => {
if (unstaked.success) {
await syncAccountData(stakingRef.current, selectedAccount);

setSelectedAccount(stakingRef.current, null, null);

stakingRef.current.postHog?.capture(
PostHogCustomEvent.UnstakedTokens,
{
Expand All @@ -182,6 +190,18 @@ const UnstakingModal = () => {
},
);

if (
solanaNetworks.has(selectedAccount.networkId) &&
!getHasUnstaked()
) {
setHasUnstaked();
setHasCompleted(true);

return;
}

setSelectedAccount(stakingRef.current, null, null);

toastSuccess({
subtitle: t("unstakingModal.success.subtitle"),
title: t("unstakingModal.success.title", {
Expand Down Expand Up @@ -220,9 +240,76 @@ const UnstakingModal = () => {
? sumAllCoins(delegationProp)
: delegationProp;

if (hasCompleted) {
return (
<ModalBase
onClose={onClose}
open={isOpen}
title={t("unstakingModal.complete.titleComplete")}
>
<div className={styles.completedWrapper}>
<img
alt=""
className={styles.coinsImg}
src="/icons/unstaking_coins.svg"
/>
<div className={styles.introWrapper}>
<div className={styles.intro}>
{t("unstakingModal.complete.intro")}
</div>
<div className={styles.introSub}>
{t("unstakingModal.complete.introSub")}
</div>
</div>
<div className={styles.blueCard}>
<img alt="" className={styles.bulb} src="/icons/bulb_stars.svg" />
<div>
<div className={styles.tipsTitleWrapper}>
<span className={styles.tipsTitle}>
{t("stakingModal.complete.tips")}
</span>
<IconInfoCircle
data-tooltip-content={t("stakingModal.complete.tipsTooltip")}
data-tooltip-id={tooltipId}
/>
</div>
<div className={styles.rewards}>
{t("stakingModal.complete.release")}
</div>
<ul className={styles.tipsList}>
{unlockedDate && (
<li>
<Trans
components={[<b key="0" />]}
i18nKey="unstakingModal.complete.desc1Solana"
ns="staking"
values={{
unstakeDate: unlockedDate.date,
}}
/>
</li>
)}
<li>
<Trans
components={[<b key="0" />]}
i18nKey="unstakingModal.complete.desc2Solana"
ns="staking"
/>
</li>
</ul>
</div>
</div>
</div>
<HighlightButton onClick={onClose} pinkShadow size="big">
{t("common:close")}
</HighlightButton>
</ModalBase>
);
}

return (
<ModalBase
onClose={() => setSelectedAccount(stakingRef.current, null, null)}
onClose={onClose}
open={isOpen}
title={t("unstakingModal.title")}
>
Expand Down
Loading

0 comments on commit c17e7dd

Please sign in to comment.