From b795db4c6302d0f4550be8d893ff901ad0f953c5 Mon Sep 17 00:00:00 2001 From: kenny-io Date: Wed, 18 Sep 2024 17:46:09 +0400 Subject: [PATCH 1/2] Disable the redeem rewards button for the same duration the remove stake button is disabled --- components/molecules/RewardsCard.tsx | 42 +++++++++++++++++---------- components/molecules/StakeDisplay.tsx | 7 ++--- utils/formatTime.ts | 5 ++++ 3 files changed, 33 insertions(+), 21 deletions(-) create mode 100644 utils/formatTime.ts diff --git a/components/molecules/RewardsCard.tsx b/components/molecules/RewardsCard.tsx index 88cf870..15db711 100644 --- a/components/molecules/RewardsCard.tsx +++ b/components/molecules/RewardsCard.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from "react"; +import { useEffect, useState, useMemo } from "react"; import rewardsCardBg from "../../assets/rewards-card.png"; import { useNodeStatus } from "../../hooks/useNodeStatus"; import { Card } from "../layouts/Card"; @@ -8,9 +8,10 @@ import useModalStore from "../../hooks/useModalStore"; import { ConfirmRedemptionModal } from "./ConfirmRedemptionModal"; import { MobileModalWrapper } from "../layouts/MobileModalWrapper"; import { BgImage } from "../atoms/BgImage"; +import { useSettings } from "../../hooks/useSettings"; +import { formatRemainingTime } from "../../utils/formatTime"; function formatDate(date: Date) { - // Format date and time separately const datePart = date.toLocaleDateString("en-US", { weekday: "short", day: "2-digit", @@ -23,7 +24,6 @@ function formatDate(date: Date) { hour12: true, }); - // Combine date and time parts return `${datePart} ${timePart}`; } @@ -38,20 +38,26 @@ export const RewardsCard = () => { const { nodeStatus } = useNodeStatus(); const { address, isConnected } = useAccount(); const { chain } = useNetwork(); - const [canRedeem, setCanRedeem] = useState( - isConnected && - chain?.id === CHAIN_ID && - nodeStatus?.state === "stopped" && - parseFloat(nodeStatus?.lockedStake || "0") > 0 - ); + const { settings } = useSettings(); + const [canRedeem, setCanRedeem] = useState(false); + + const { isStoppedForLongerThan15Minutes, remainingWaitTime } = useMemo(() => { + if (!settings?.lastStopped) return { isStoppedForLongerThan15Minutes: true, remainingWaitTime: 0 }; + const timeDifference = Date.now() - settings.lastStopped; + const isStoppedForLongerThan15Minutes = timeDifference > 15 * 60 * 1000; + const remainingWaitTime = Math.max(15 * 60 * 1000 - timeDifference, 0); + return { isStoppedForLongerThan15Minutes, remainingWaitTime }; + }, [settings?.lastStopped]); + useEffect(() => { setCanRedeem( isConnected && chain?.id === CHAIN_ID && nodeStatus?.state === "stopped" && - parseFloat(nodeStatus?.lockedStake || "0") > 0 + parseFloat(nodeStatus?.lockedStake || "0") > 0 && + isStoppedForLongerThan15Minutes ); - }, [nodeStatus?.state, nodeStatus?.lockedStake, isConnected, chain?.id]); + }, [nodeStatus?.state, nodeStatus?.lockedStake, isConnected, chain?.id, isStoppedForLongerThan15Minutes]); return ( @@ -65,7 +71,6 @@ export const RewardsCard = () => { {parseFloat(nodeStatus?.currentRewards || "0").toFixed(2)}{" "} SHM - {/* (~0.00$) */}
Earned since last validating cycle @@ -92,11 +97,16 @@ export const RewardsCard = () => { (canRedeem ? "text-primary" : `text-gray-400 ${ - nodeStatus?.state === "active" ? "tooltip" : "" + nodeStatus?.state === "active" || !isStoppedForLongerThan15Minutes ? "tooltip" : "" }`) } - data-tip="It is not possible to redeem rewards while you are validating. - If absolutely necessary, use the force stop option in settings (Not Recommended)." + data-tip={ + nodeStatus?.state === "active" + ? "It is not possible to redeem rewards while you are validating. If absolutely necessary, use the force stop option in settings (Not Recommended)." + : !isStoppedForLongerThan15Minutes + ? `Node has been stopped recently. Please wait for another ${formatRemainingTime(remainingWaitTime)} before redeeming rewards.` + : "" + } disabled={!canRedeem} onClick={() => { resetModal(); @@ -128,4 +138,4 @@ export const RewardsCard = () => {
); -}; +}; \ No newline at end of file diff --git a/components/molecules/StakeDisplay.tsx b/components/molecules/StakeDisplay.tsx index 1d318ff..74b346f 100644 --- a/components/molecules/StakeDisplay.tsx +++ b/components/molecules/StakeDisplay.tsx @@ -11,6 +11,7 @@ import { ClipboardIcon } from "../atoms/ClipboardIcon"; import { MobileModalWrapper } from "../layouts/MobileModalWrapper"; import { useAccountStakeInfo } from "../../hooks/useAccountStakeInfo"; import { useSettings } from "../../hooks/useSettings"; +import { formatRemainingTime } from "../../utils/formatTime"; export const StakeDisplay = () => { const addressRef = useRef(null); @@ -52,11 +53,7 @@ export const StakeDisplay = () => { return { isStoppedForLongerThan15Minutes, remainingWaitTime }; }, [settings?.lastStopped]); - const formatRemainingTime = (ms: number) => { - const minutes = Math.floor(ms / 60000); - const seconds = Math.floor((ms % 60000) / 1000); - return `${minutes}m ${seconds}s`; - }; + return ( diff --git a/utils/formatTime.ts b/utils/formatTime.ts new file mode 100644 index 0000000..33c25f3 --- /dev/null +++ b/utils/formatTime.ts @@ -0,0 +1,5 @@ +export const formatRemainingTime = (ms: number) => { + const minutes = Math.floor(ms / 60000); + const seconds = Math.floor((ms % 60000) / 1000); + return `${minutes}m ${seconds}s`; + }; \ No newline at end of file From 9bbc5624126cfcdfee8c522f5df5ae19da44b6d3 Mon Sep 17 00:00:00 2001 From: kenny-io Date: Wed, 18 Sep 2024 19:10:23 +0400 Subject: [PATCH 2/2] Fix the notification when a user rejects the tx while onboarding --- components/molecules/AddStakeModal.tsx | 4 ++++ pages/onboarding/index.tsx | 30 +++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/components/molecules/AddStakeModal.tsx b/components/molecules/AddStakeModal.tsx index 66128a9..cb57e21 100644 --- a/components/molecules/AddStakeModal.tsx +++ b/components/molecules/AddStakeModal.tsx @@ -60,9 +60,11 @@ export const AddStakeModal = () => { }, }); } else { + // This will handle both rejection and other failure cases setCurrentToast({ severity: ToastSeverity.DANGER, title: "Staking Unsuccessful", + description: "Transaction failed or was rejected by the user", followupNotification: { title: "Staking Unsuccessful", type: NotificationType.REWARD, @@ -87,6 +89,8 @@ export const AddStakeModal = () => { title: "Processing Adding Stake", description: "Your add stake transaction is in process.", }); + } else { + resetToast(); } }, [isLoading]); diff --git a/pages/onboarding/index.tsx b/pages/onboarding/index.tsx index 9c67cd6..39aed3c 100644 --- a/pages/onboarding/index.tsx +++ b/pages/onboarding/index.tsx @@ -43,6 +43,9 @@ const Onboarding = () => { const [accountBalance, setAccountBalance] = useState(""); const [chainId, setChainId] = useState(0); const [tokenClaimPhase, setTokenClaimPhase] = useState(0); // 0: hasn't claimed yet, 1: initiated request, 2: has claimed + const { resetToast } = useToastStore((state: any) => ({ + resetToast: state.resetToast, + })); const { isConnected, address } = useAccount({ onConnect: async (args) => { if (args?.address) { @@ -102,7 +105,30 @@ const Onboarding = () => { stakeAmount: minimumStakeRequirement.toString(), totalStaked: nodeStatus?.lockedStake ? Number(nodeStatus?.lockedStake) : 0, onStake: (wasTxnSuccessful: boolean) => { - setIsStakingComplete(wasTxnSuccessful); + if (wasTxnSuccessful) { + setIsStakingComplete(true); + setCurrentToast({ + severity: ToastSeverity.SUCCESS, + title: "Stake Added", + description: `${stakedAmount.toFixed(2)} SHM staked Successfully`, + followupNotification: { + title: "Stake Added", + type: NotificationType.REWARD, + severity: NotificationSeverity.SUCCESS, + }, + }); + } else { + setCurrentToast({ + severity: ToastSeverity.DANGER, + title: "Staking Unsuccessful", + description: "Transaction failed or was rejected by the user", + followupNotification: { + title: "Staking Unsuccessful", + type: NotificationType.REWARD, + severity: NotificationSeverity.DANGER, + }, + }); + } }, }); @@ -158,6 +184,8 @@ const Onboarding = () => { description: "Your add stake transaction is in process.", duration: 300000, // 300 seconds }); + } else { + resetToast(); } }, [isStaking]);