From a6ffc86cb1a4536d3addcb86e173d942bc4c06e7 Mon Sep 17 00:00:00 2001 From: Govard Barkhatov Date: Sat, 14 Sep 2024 14:51:46 +0300 Subject: [PATCH 1/7] modal loading while staking --- src/app/components/Loading/Loading.tsx | 14 +++++-- src/app/components/Modals/GeneralModal.tsx | 6 +++ src/app/components/Modals/PreviewModal.tsx | 43 ++++++++++++++-------- src/app/components/Staking/Staking.tsx | 9 ++++- 4 files changed, 53 insertions(+), 19 deletions(-) diff --git a/src/app/components/Loading/Loading.tsx b/src/app/components/Loading/Loading.tsx index 0c4c5f85..1653397f 100644 --- a/src/app/components/Loading/Loading.tsx +++ b/src/app/components/Loading/Loading.tsx @@ -1,12 +1,20 @@ +import { twJoin } from "tailwind-merge"; + interface LoadingProps { text?: string; + noBorder?: boolean; } -export const LoadingView: React.FC = () => { +export const LoadingView: React.FC = ({ text, noBorder }) => { return ( -
+
-

Please wait...

+

{text || "Please wait..."}

); }; diff --git a/src/app/components/Modals/GeneralModal.tsx b/src/app/components/Modals/GeneralModal.tsx index 0159b139..d7a85c5e 100644 --- a/src/app/components/Modals/GeneralModal.tsx +++ b/src/app/components/Modals/GeneralModal.tsx @@ -8,6 +8,8 @@ interface GeneralModalProps { small?: boolean; children: ReactNode; className?: string; + disableModalClose?: boolean; + closeOnEsc?: boolean; } export const GeneralModal: React.FC = ({ @@ -16,6 +18,8 @@ export const GeneralModal: React.FC = ({ children, small, className = "", + disableModalClose, + closeOnEsc = true, }) => { const modalRef = useRef(null); @@ -46,6 +50,8 @@ export const GeneralModal: React.FC = ({ }} showCloseIcon={false} blockScroll={false} + closeOnEsc={closeOnEsc} + closeOnOverlayClick={!disableModalClose} > {children} diff --git a/src/app/components/Modals/PreviewModal.tsx b/src/app/components/Modals/PreviewModal.tsx index 33a653f8..94f8d289 100644 --- a/src/app/components/Modals/PreviewModal.tsx +++ b/src/app/components/Modals/PreviewModal.tsx @@ -5,6 +5,8 @@ import { blocksToDisplayTime } from "@/utils/blocksToDisplayTime"; import { satoshiToBtc } from "@/utils/btcConversions"; import { maxDecimals } from "@/utils/maxDecimals"; +import { LoadingView } from "../Loading/Loading"; + import { GeneralModal } from "./GeneralModal"; interface PreviewModalProps { @@ -19,6 +21,7 @@ interface PreviewModalProps { unbondingTimeBlocks: number; confirmationDepth: number; unbondingFeeSat: number; + disableModalClose: boolean; } export const PreviewModal: React.FC = ({ @@ -33,6 +36,7 @@ export const PreviewModal: React.FC = ({ feeRate, confirmationDepth, unbondingFeeSat, + disableModalClose, }) => { const cardStyles = "card border bg-base-300 p-4 text-sm dark:border-0 dark:bg-base-200"; @@ -40,12 +44,17 @@ export const PreviewModal: React.FC = ({ const { coinName } = getNetworkConfig(); return ( - +

Preview

@@ -112,19 +121,23 @@ export const PreviewModal: React.FC = ({ "Pending" stake is only accessible through the device it was created.

-
- - -
+ {disableModalClose ? ( + + ) : ( +
+ + +
+ )}
); diff --git a/src/app/components/Staking/Staking.tsx b/src/app/components/Staking/Staking.tsx index b32de4f0..bbfb7ded 100644 --- a/src/app/components/Staking/Staking.tsx +++ b/src/app/components/Staking/Staking.tsx @@ -91,6 +91,7 @@ export const Staking: React.FC = ({ useState(); // Selected fee rate, comes from the user input const [selectedFeeRate, setSelectedFeeRate] = useState(0); + const [disableModalClose, setDisableModalClose] = useState(false); const [previewModalOpen, setPreviewModalOpen] = useState(false); const [resetFormInputs, setResetFormInputs] = useState(false); const [feedbackModal, setFeedbackModal] = useState<{ @@ -226,6 +227,7 @@ export const Staking: React.FC = ({ ]); const handleResetState = () => { + setDisableModalClose(false); setFinalityProvider(undefined); setStakingAmountSat(0); setStakingTimeBlocks(0); @@ -243,6 +245,8 @@ export const Staking: React.FC = ({ const handleSign = async () => { try { + // Prevent the modal from closing + setDisableModalClose(true); // Initial validation if (!btcWallet) throw new Error("Wallet is not connected"); if (!address) throw new Error("Address is not set"); @@ -292,6 +296,8 @@ export const Staking: React.FC = ({ queryClient.invalidateQueries({ queryKey: [UTXO_KEY, address] }); }, }); + } finally { + setDisableModalClose(false); } }; @@ -649,7 +655,7 @@ export const Staking: React.FC = ({ {previewReady && ( {} : handlePreviewModalClose} onSign={handleSign} finalityProvider={finalityProvider?.description.moniker} stakingAmountSat={stakingAmountSat} @@ -659,6 +665,7 @@ export const Staking: React.FC = ({ feeRate={feeRate} unbondingTimeBlocks={unbondingTime} unbondingFeeSat={unbondingFeeSat} + disableModalClose={disableModalClose} /> )}
From 1e88ce1a9024284f690377e3f45dcac605d3683c Mon Sep 17 00:00:00 2001 From: Govard Barkhatov Date: Sat, 14 Sep 2024 21:10:29 +0300 Subject: [PATCH 2/7] onClose={handlePreviewModalClose} --- src/app/components/Staking/Staking.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/components/Staking/Staking.tsx b/src/app/components/Staking/Staking.tsx index bbfb7ded..702aa94c 100644 --- a/src/app/components/Staking/Staking.tsx +++ b/src/app/components/Staking/Staking.tsx @@ -655,7 +655,7 @@ export const Staking: React.FC = ({ {previewReady && ( {} : handlePreviewModalClose} + onClose={handlePreviewModalClose} onSign={handleSign} finalityProvider={finalityProvider?.description.moniker} stakingAmountSat={stakingAmountSat} From 576f251fa10f1ca37c66cfe603b25086032e0638 Mon Sep 17 00:00:00 2001 From: Govard Barkhatov Date: Sat, 14 Sep 2024 21:28:06 +0300 Subject: [PATCH 3/7] loading state unbonding withdrawing --- .../components/Delegations/Delegations.tsx | 8 +++ .../components/Modals/UnbondWithdrawModal.tsx | 50 +++++++++++-------- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/src/app/components/Delegations/Delegations.tsx b/src/app/components/Delegations/Delegations.tsx index 6a839edc..80af42c9 100644 --- a/src/app/components/Delegations/Delegations.tsx +++ b/src/app/components/Delegations/Delegations.tsx @@ -98,6 +98,7 @@ const DelegationsContent: React.FC = ({ const [modalMode, setModalMode] = useState(); const { showError } = useError(); const { isApiNormal, isGeoBlocked } = useHealthCheck(); + const [disableModalClose, setDisableModalClose] = useState(false); // Local storage state for intermediate delegations (withdrawing, unbonding) const intermediateDelegationsLocalStorageKey = @@ -149,6 +150,8 @@ const DelegationsContent: React.FC = ({ // It constructs an unbonding transaction, creates a signature for it, and submits both to the back-end API const handleUnbond = async (id: string) => { try { + // Prevent the modal from closing + setDisableModalClose(true); // Sign the unbonding transaction const { delegation } = await signUnbondingTx( id, @@ -171,6 +174,7 @@ const DelegationsContent: React.FC = ({ setModalOpen(false); setTxID(""); setModalMode(undefined); + setDisableModalClose(false); } }; @@ -178,6 +182,8 @@ const DelegationsContent: React.FC = ({ // It constructs a withdrawal transaction, creates a signature for it, and submits it to the Bitcoin network const handleWithdraw = async (id: string) => { try { + // Prevent the modal from closing + setDisableModalClose(true); // Sign the withdrawal transaction const { delegation } = await signWithdrawalTx( id, @@ -203,6 +209,7 @@ const DelegationsContent: React.FC = ({ setModalOpen(false); setTxID(""); setModalMode(undefined); + setDisableModalClose(false); } }; @@ -343,6 +350,7 @@ const DelegationsContent: React.FC = ({ : handleWithdraw(txID); }} mode={modalMode} + disableModalClose={disableModalClose} /> )} diff --git a/src/app/components/Modals/UnbondWithdrawModal.tsx b/src/app/components/Modals/UnbondWithdrawModal.tsx index 2dd70909..af93ee76 100644 --- a/src/app/components/Modals/UnbondWithdrawModal.tsx +++ b/src/app/components/Modals/UnbondWithdrawModal.tsx @@ -5,6 +5,8 @@ import { blocksToDisplayTime } from "@/utils/blocksToDisplayTime"; import { satoshiToBtc } from "@/utils/btcConversions"; import { maxDecimals } from "@/utils/maxDecimals"; +import { LoadingView } from "../Loading/Loading"; + import { GeneralModal } from "./GeneralModal"; export const MODE_UNBOND = "unbond"; @@ -18,6 +20,7 @@ interface PreviewModalProps { onClose: (value: boolean) => void; onProceed: () => void; mode: MODE; + disableModalClose: boolean; } export const UnbondWithdrawModal: React.FC = ({ @@ -27,6 +30,7 @@ export const UnbondWithdrawModal: React.FC = ({ onClose, onProceed, mode, + disableModalClose, }) => { const { coinName, networkName } = getNetworkConfig(); @@ -59,37 +63,41 @@ export const UnbondWithdrawModal: React.FC = ({ const content = mode === MODE_UNBOND ? unbondContent : withdrawContent; return ( - +

{title}

{content}

-
- - -
+ {disableModalClose ? ( + + ) : ( +
+ + +
+ )}
); From 0ef462e8371973c13cfbee1dd3d5c1d55debd602 Mon Sep 17 00:00:00 2001 From: Govard Barkhatov Date: Sun, 15 Sep 2024 14:35:05 +0300 Subject: [PATCH 4/7] isAwaitingWalletResponse --- src/app/components/Delegations/Delegations.tsx | 13 +++++++------ src/app/components/Modals/GeneralModal.tsx | 6 +++--- src/app/components/Modals/PreviewModal.tsx | 10 +++++----- src/app/components/Modals/UnbondWithdrawModal.tsx | 10 +++++----- src/app/components/Staking/Staking.tsx | 11 ++++++----- 5 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/app/components/Delegations/Delegations.tsx b/src/app/components/Delegations/Delegations.tsx index 80af42c9..6c9e2937 100644 --- a/src/app/components/Delegations/Delegations.tsx +++ b/src/app/components/Delegations/Delegations.tsx @@ -98,7 +98,8 @@ const DelegationsContent: React.FC = ({ const [modalMode, setModalMode] = useState(); const { showError } = useError(); const { isApiNormal, isGeoBlocked } = useHealthCheck(); - const [disableModalClose, setDisableModalClose] = useState(false); + const [isAwaitingWalletResponse, setisAwaitingWalletResponse] = + useState(false); // Local storage state for intermediate delegations (withdrawing, unbonding) const intermediateDelegationsLocalStorageKey = @@ -151,7 +152,7 @@ const DelegationsContent: React.FC = ({ const handleUnbond = async (id: string) => { try { // Prevent the modal from closing - setDisableModalClose(true); + setisAwaitingWalletResponse(true); // Sign the unbonding transaction const { delegation } = await signUnbondingTx( id, @@ -174,7 +175,7 @@ const DelegationsContent: React.FC = ({ setModalOpen(false); setTxID(""); setModalMode(undefined); - setDisableModalClose(false); + setisAwaitingWalletResponse(false); } }; @@ -183,7 +184,7 @@ const DelegationsContent: React.FC = ({ const handleWithdraw = async (id: string) => { try { // Prevent the modal from closing - setDisableModalClose(true); + setisAwaitingWalletResponse(true); // Sign the withdrawal transaction const { delegation } = await signWithdrawalTx( id, @@ -209,7 +210,7 @@ const DelegationsContent: React.FC = ({ setModalOpen(false); setTxID(""); setModalMode(undefined); - setDisableModalClose(false); + setisAwaitingWalletResponse(false); } }; @@ -350,7 +351,7 @@ const DelegationsContent: React.FC = ({ : handleWithdraw(txID); }} mode={modalMode} - disableModalClose={disableModalClose} + isAwaitingWalletResponse={isAwaitingWalletResponse} /> )} diff --git a/src/app/components/Modals/GeneralModal.tsx b/src/app/components/Modals/GeneralModal.tsx index d7a85c5e..40071cf1 100644 --- a/src/app/components/Modals/GeneralModal.tsx +++ b/src/app/components/Modals/GeneralModal.tsx @@ -8,7 +8,7 @@ interface GeneralModalProps { small?: boolean; children: ReactNode; className?: string; - disableModalClose?: boolean; + isAwaitingWalletResponse?: boolean; closeOnEsc?: boolean; } @@ -18,7 +18,7 @@ export const GeneralModal: React.FC = ({ children, small, className = "", - disableModalClose, + isAwaitingWalletResponse, closeOnEsc = true, }) => { const modalRef = useRef(null); @@ -51,7 +51,7 @@ export const GeneralModal: React.FC = ({ showCloseIcon={false} blockScroll={false} closeOnEsc={closeOnEsc} - closeOnOverlayClick={!disableModalClose} + closeOnOverlayClick={!isAwaitingWalletResponse} > {children} diff --git a/src/app/components/Modals/PreviewModal.tsx b/src/app/components/Modals/PreviewModal.tsx index 94f8d289..c6b2df74 100644 --- a/src/app/components/Modals/PreviewModal.tsx +++ b/src/app/components/Modals/PreviewModal.tsx @@ -21,7 +21,7 @@ interface PreviewModalProps { unbondingTimeBlocks: number; confirmationDepth: number; unbondingFeeSat: number; - disableModalClose: boolean; + isAwaitingWalletResponse: boolean; } export const PreviewModal: React.FC = ({ @@ -36,7 +36,7 @@ export const PreviewModal: React.FC = ({ feeRate, confirmationDepth, unbondingFeeSat, - disableModalClose, + isAwaitingWalletResponse, }) => { const cardStyles = "card border bg-base-300 p-4 text-sm dark:border-0 dark:bg-base-200"; @@ -47,14 +47,14 @@ export const PreviewModal: React.FC = ({

Preview

@@ -121,7 +121,7 @@ export const PreviewModal: React.FC = ({ "Pending" stake is only accessible through the device it was created.

- {disableModalClose ? ( + {isAwaitingWalletResponse ? ( ) : (
diff --git a/src/app/components/Modals/UnbondWithdrawModal.tsx b/src/app/components/Modals/UnbondWithdrawModal.tsx index af93ee76..dfde06b9 100644 --- a/src/app/components/Modals/UnbondWithdrawModal.tsx +++ b/src/app/components/Modals/UnbondWithdrawModal.tsx @@ -20,7 +20,7 @@ interface PreviewModalProps { onClose: (value: boolean) => void; onProceed: () => void; mode: MODE; - disableModalClose: boolean; + isAwaitingWalletResponse: boolean; } export const UnbondWithdrawModal: React.FC = ({ @@ -30,7 +30,7 @@ export const UnbondWithdrawModal: React.FC = ({ onClose, onProceed, mode, - disableModalClose, + isAwaitingWalletResponse, }) => { const { coinName, networkName } = getNetworkConfig(); @@ -67,21 +67,21 @@ export const UnbondWithdrawModal: React.FC = ({ open={open} onClose={onClose} closeOnEsc={false} - disableModalClose={disableModalClose} + isAwaitingWalletResponse={isAwaitingWalletResponse} small >

{title}

{content}

- {disableModalClose ? ( + {isAwaitingWalletResponse ? ( ) : (
diff --git a/src/app/components/Staking/Staking.tsx b/src/app/components/Staking/Staking.tsx index 702aa94c..5521541b 100644 --- a/src/app/components/Staking/Staking.tsx +++ b/src/app/components/Staking/Staking.tsx @@ -91,7 +91,8 @@ export const Staking: React.FC = ({ useState(); // Selected fee rate, comes from the user input const [selectedFeeRate, setSelectedFeeRate] = useState(0); - const [disableModalClose, setDisableModalClose] = useState(false); + const [isAwaitingWalletResponse, setIsAwaitingWalletResponse] = + useState(false); const [previewModalOpen, setPreviewModalOpen] = useState(false); const [resetFormInputs, setResetFormInputs] = useState(false); const [feedbackModal, setFeedbackModal] = useState<{ @@ -227,7 +228,7 @@ export const Staking: React.FC = ({ ]); const handleResetState = () => { - setDisableModalClose(false); + setIsAwaitingWalletResponse(false); setFinalityProvider(undefined); setStakingAmountSat(0); setStakingTimeBlocks(0); @@ -246,7 +247,7 @@ export const Staking: React.FC = ({ const handleSign = async () => { try { // Prevent the modal from closing - setDisableModalClose(true); + setIsAwaitingWalletResponse(true); // Initial validation if (!btcWallet) throw new Error("Wallet is not connected"); if (!address) throw new Error("Address is not set"); @@ -297,7 +298,7 @@ export const Staking: React.FC = ({ }, }); } finally { - setDisableModalClose(false); + setIsAwaitingWalletResponse(false); } }; @@ -665,7 +666,7 @@ export const Staking: React.FC = ({ feeRate={feeRate} unbondingTimeBlocks={unbondingTime} unbondingFeeSat={unbondingFeeSat} - disableModalClose={disableModalClose} + isAwaitingWalletResponse={isAwaitingWalletResponse} /> )}
From a06a6db78311e902f0142f6d10faacec55cd8390 Mon Sep 17 00:00:00 2001 From: Govard Barkhatov Date: Mon, 16 Sep 2024 11:05:56 +0300 Subject: [PATCH 5/7] remove x, change text to Awaiting wallet signature and broadcast --- src/app/components/Modals/PreviewModal.tsx | 19 ++++++++++++------- .../components/Modals/UnbondWithdrawModal.tsx | 19 ++++++++++++------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/app/components/Modals/PreviewModal.tsx b/src/app/components/Modals/PreviewModal.tsx index c6b2df74..291f88db 100644 --- a/src/app/components/Modals/PreviewModal.tsx +++ b/src/app/components/Modals/PreviewModal.tsx @@ -52,12 +52,14 @@ export const PreviewModal: React.FC = ({ >

Preview

- + {!isAwaitingWalletResponse && ( + + )}
@@ -122,7 +124,10 @@ export const PreviewModal: React.FC = ({ created.

{isAwaitingWalletResponse ? ( - + ) : (
+ {!isAwaitingWalletResponse && ( + + )}

{content}

{isAwaitingWalletResponse ? ( - + ) : (

{content}

- {isAwaitingWalletResponse ? ( + {awaitingWalletResponse ? ( = ({ useState(); // Selected fee rate, comes from the user input const [selectedFeeRate, setSelectedFeeRate] = useState(0); - const [isAwaitingWalletResponse, setIsAwaitingWalletResponse] = - useState(false); + const [awaitingWalletResponse, setAwaitingWalletResponse] = useState(false); const [previewModalOpen, setPreviewModalOpen] = useState(false); const [resetFormInputs, setResetFormInputs] = useState(false); const [feedbackModal, setFeedbackModal] = useState<{ @@ -228,7 +227,7 @@ export const Staking: React.FC = ({ ]); const handleResetState = () => { - setIsAwaitingWalletResponse(false); + setAwaitingWalletResponse(false); setFinalityProvider(undefined); setStakingAmountSat(0); setStakingTimeBlocks(0); @@ -247,7 +246,7 @@ export const Staking: React.FC = ({ const handleSign = async () => { try { // Prevent the modal from closing - setIsAwaitingWalletResponse(true); + setAwaitingWalletResponse(true); // Initial validation if (!btcWallet) throw new Error("Wallet is not connected"); if (!address) throw new Error("Address is not set"); @@ -298,7 +297,7 @@ export const Staking: React.FC = ({ }, }); } finally { - setIsAwaitingWalletResponse(false); + setAwaitingWalletResponse(false); } }; @@ -666,7 +665,7 @@ export const Staking: React.FC = ({ feeRate={feeRate} unbondingTimeBlocks={unbondingTime} unbondingFeeSat={unbondingFeeSat} - isAwaitingWalletResponse={isAwaitingWalletResponse} + awaitingWalletResponse={awaitingWalletResponse} /> )}