From 92ae36e5ea98b716cfc51174824ba5afc00902f1 Mon Sep 17 00:00:00 2001 From: BoHsuu Date: Fri, 13 Mar 2026 10:29:45 +0700 Subject: [PATCH 1/5] Improve UX of Transfer page --- .../components/Transfer/TransferContainer.tsx | 33 ++++++---- packages/nextjs/components/ui/Spinner.tsx | 12 ++++ .../app/transaction/useTransferTransaction.ts | 28 ++++++--- packages/nextjs/hooks/app/useGenerateProof.ts | 4 +- packages/nextjs/hooks/app/useStepLoading.ts | 61 +++++++++++++++++++ packages/nextjs/styles/globals.css | 12 +++- 6 files changed, 126 insertions(+), 24 deletions(-) create mode 100644 packages/nextjs/components/ui/Spinner.tsx create mode 100644 packages/nextjs/hooks/app/useStepLoading.ts diff --git a/packages/nextjs/components/Transfer/TransferContainer.tsx b/packages/nextjs/components/Transfer/TransferContainer.tsx index be46f515..794c6d17 100644 --- a/packages/nextjs/components/Transfer/TransferContainer.tsx +++ b/packages/nextjs/components/Transfer/TransferContainer.tsx @@ -6,6 +6,7 @@ import { ResolvedToken, parseTokenAmount } from "@polypay/shared"; import { parseEther } from "viem"; import { ContactPicker } from "~~/components/contact-book/ContactPicker"; import { TokenPillPopover } from "~~/components/popovers/TokenPillPopover"; +import { Spinner } from "~~/components/ui/Spinner"; import { useMetaMultiSigWallet, useTransferTransaction } from "~~/hooks"; import { useCreateBatchItem } from "~~/hooks/api"; import { useNetworkTokens } from "~~/hooks/app/useNetworkTokens"; @@ -69,7 +70,7 @@ export default function TransferContainer() { } }, [form]); - const { transfer, isLoading, loadingState } = useTransferTransaction({ + const { transfer, isLoading, loadingState, loadingStep, totalSteps } = useTransferTransaction({ onSuccess: () => { form.reset(); setSelectedContactId(null); @@ -145,15 +146,15 @@ export default function TransferContainer() { return (
{/* Background images */} -
+
Top globe
-
+
Bottom globe
{/* Main content */} -
+
{/* Title section */}
transfering
@@ -164,11 +165,6 @@ export default function TransferContainer() {
- {/* Loading state */} - {isLoading && loadingState && ( -
{loadingState}
- )} -
{/* Action buttons */} + {isLoading && loadingState && ( +
+
+ Step {loadingStep} of {totalSteps} — {loadingState} +
+
+
+
+
+ )}
diff --git a/packages/nextjs/components/Batch/TransactionSummary.tsx b/packages/nextjs/components/Batch/TransactionSummary.tsx index 97121066..a27b84b0 100644 --- a/packages/nextjs/components/Batch/TransactionSummary.tsx +++ b/packages/nextjs/components/Batch/TransactionSummary.tsx @@ -19,6 +19,8 @@ interface TransactionSummaryProps { isLoading?: boolean; loadingState?: string; accountId: string | null; + loadingStep?: number; + totalSteps?: number; } const TransactionSummary: React.FC = ({ @@ -28,6 +30,8 @@ const TransactionSummary: React.FC = ({ isLoading = false, loadingState = "", accountId, + loadingStep = 0, + totalSteps = 4, }) => { const { data: contacts = [] } = useContacts(accountId); return ( @@ -116,13 +120,26 @@ const TransactionSummary: React.FC = ({ {/* Confirm Button Section */}
+ {isLoading && loadingState && loadingStep > 0 && ( +
+
+ Step {loadingStep} of {totalSteps} — {loadingState} +
+
+
+
+
+ )}
diff --git a/packages/nextjs/components/Batch/TransactionSummaryDrawer.tsx b/packages/nextjs/components/Batch/TransactionSummaryDrawer.tsx index 7b17e2f1..8f24dbfe 100644 --- a/packages/nextjs/components/Batch/TransactionSummaryDrawer.tsx +++ b/packages/nextjs/components/Batch/TransactionSummaryDrawer.tsx @@ -19,6 +19,8 @@ interface TransactionSummaryDrawerProps { onConfirm?: () => void; isLoading?: boolean; loadingState?: string; + loadingStep?: number; + totalSteps?: number; } export const TransactionSummaryDrawer = memo(function TransactionSummaryDrawer({ @@ -29,6 +31,8 @@ export const TransactionSummaryDrawer = memo(function TransactionSummaryDrawer({ onConfirm, isLoading = false, loadingState = "", + loadingStep = 0, + totalSteps = 4, }: TransactionSummaryDrawerProps) { const [isAnimating, setIsAnimating] = useState(false); @@ -68,6 +72,8 @@ export const TransactionSummaryDrawer = memo(function TransactionSummaryDrawer({ accountId={accountId} isLoading={isLoading} loadingState={loadingState} + loadingStep={loadingStep} + totalSteps={totalSteps} className="h-full" />
diff --git a/packages/nextjs/hooks/app/transaction/useBatchTransaction.ts b/packages/nextjs/hooks/app/transaction/useBatchTransaction.ts index 1d9700c4..e31faa58 100644 --- a/packages/nextjs/hooks/app/transaction/useBatchTransaction.ts +++ b/packages/nextjs/hooks/app/transaction/useBatchTransaction.ts @@ -4,6 +4,7 @@ import { useWalletClient } from "wagmi"; import { useMetaMultiSigWallet } from "~~/hooks"; import { useCreateTransaction, useReserveNonce } from "~~/hooks/api"; import { useGenerateProof } from "~~/hooks/app/useGenerateProof"; +import { useStepLoading } from "~~/hooks/app/useStepLoading"; import { useIdentityStore } from "~~/services/store"; import { formatErrorMessage } from "~~/utils/formatError"; import { notification } from "~~/utils/scaffold-eth"; @@ -12,9 +13,16 @@ interface UseBatchTransactionOptions { onSuccess?: () => void; } +const BATCH_STEPS = [ + { id: 1, label: "Preparing your batch..." }, + { id: 2, label: "Waiting for wallet approval..." }, + { id: 3, label: "Securing your transaction..." }, + { id: 4, label: "Almost done, submitting..." }, +]; + export const useBatchTransaction = (options?: UseBatchTransactionOptions) => { - const [isLoading, setIsLoading] = useState(false); - const [loadingState, setLoadingState] = useState(""); + const { isLoading, loadingState, loadingStep, totalSteps, startStep, setStepByLabel, reset } = + useStepLoading(BATCH_STEPS); const { data: walletClient } = useWalletClient(); const { secret, commitment: myCommitment } = useIdentityStore(); @@ -22,7 +30,7 @@ export const useBatchTransaction = (options?: UseBatchTransactionOptions) => { const { mutateAsync: createTransaction } = useCreateTransaction(); const { mutateAsync: reserveNonce } = useReserveNonce(); const { generateProof } = useGenerateProof({ - onLoadingStateChange: setLoadingState, + onLoadingStateChange: setStepByLabel, }); const proposeBatch = async (selectedBatchItems: BatchItem[]) => { @@ -42,16 +50,15 @@ export const useBatchTransaction = (options?: UseBatchTransactionOptions) => { return; } - setIsLoading(true); - try { const selectedIds = selectedBatchItems.map(item => item.id); // 1. Reserve nonce from backend + startStep(1); const { nonce } = await reserveNonce(metaMultiSigWallet.address); // 2. Get current threshold and commitments - setLoadingState("Preparing batch transaction..."); + startStep(1); const currentThreshold = await metaMultiSigWallet.read.signaturesRequired(); // 3. Prepare batch data @@ -79,7 +86,7 @@ export const useBatchTransaction = (options?: UseBatchTransactionOptions) => { const { proof, publicInputs, nullifier, vk } = await generateProof(txHash); // 7. Submit to backend - setLoadingState("Submitting to backend..."); + startStep(4); const result = await createTransaction({ nonce, type: TxType.BATCH, @@ -104,8 +111,7 @@ export const useBatchTransaction = (options?: UseBatchTransactionOptions) => { console.error("Propose batch error:", error); notification.error(formatErrorMessage(error, "Failed to propose batch")); } finally { - setIsLoading(false); - setLoadingState(""); + reset(); } }; @@ -113,5 +119,7 @@ export const useBatchTransaction = (options?: UseBatchTransactionOptions) => { proposeBatch, isLoading, loadingState, + loadingStep, + totalSteps, }; }; From eab8ec6838da74a3ef26fe175c2a3b34a6b1f99f Mon Sep 17 00:00:00 2001 From: BoHsuu Date: Fri, 13 Mar 2026 11:17:25 +0700 Subject: [PATCH 3/5] Improve UX of EditAccountModal --- .../modals/EditAccountModal/EditStep.tsx | 5 --- .../EditAccountModal/SubmittingStep.tsx | 26 ++++++++++++++- .../modals/EditAccountModal/index.tsx | 6 +++- packages/nextjs/components/ui/Spinner.tsx | 7 +--- .../app/transaction/useSignerTransaction.ts | 32 +++++++++++-------- packages/nextjs/styles/globals.css | 2 +- 6 files changed, 51 insertions(+), 27 deletions(-) diff --git a/packages/nextjs/components/modals/EditAccountModal/EditStep.tsx b/packages/nextjs/components/modals/EditAccountModal/EditStep.tsx index 8d39a2a0..5a921a44 100644 --- a/packages/nextjs/components/modals/EditAccountModal/EditStep.tsx +++ b/packages/nextjs/components/modals/EditAccountModal/EditStep.tsx @@ -174,11 +174,6 @@ const EditStep: React.FC = ({
- {/* Loading state */} - {loading && loadingState && ( -
{loadingState}
- )} - {/* Content */}
{/* Account Signers Section */} diff --git a/packages/nextjs/components/modals/EditAccountModal/SubmittingStep.tsx b/packages/nextjs/components/modals/EditAccountModal/SubmittingStep.tsx index 21058d66..6a2dadb9 100644 --- a/packages/nextjs/components/modals/EditAccountModal/SubmittingStep.tsx +++ b/packages/nextjs/components/modals/EditAccountModal/SubmittingStep.tsx @@ -2,7 +2,17 @@ import React from "react"; -const SubmittingStep = () => { +interface SubmittingStepProps { + loadingState?: string; + loadingStep?: number; + totalSteps?: number; +} + +const SubmittingStep: React.FC = ({ + loadingState = "", + loadingStep = 0, + totalSteps = 4, +}) => { return (
{/* Rocket animation video */} @@ -30,6 +40,20 @@ const SubmittingStep = () => { This may take a few moments. Please don't close this window.

+ + {loadingStep > 0 && totalSteps > 0 && ( +
+
+ Step {loadingStep} of {totalSteps} — {loadingState} +
+
+
+
+
+ )}
); }; diff --git a/packages/nextjs/components/modals/EditAccountModal/index.tsx b/packages/nextjs/components/modals/EditAccountModal/index.tsx index 4de1fe8d..f5fe4c52 100644 --- a/packages/nextjs/components/modals/EditAccountModal/index.tsx +++ b/packages/nextjs/components/modals/EditAccountModal/index.tsx @@ -25,6 +25,8 @@ const EditAccountModal: React.FC = ({ isOpen, onClose }) => { updateThreshold, isLoading: loading, loadingState, + loadingStep, + totalSteps, signers, threshold: originalThreshold, refetchCommitments, @@ -190,7 +192,9 @@ const EditAccountModal: React.FC = ({ isOpen, onClose }) => { /> )} - {step === "submitting" && } + {step === "submitting" && ( + + )} ); }; diff --git a/packages/nextjs/components/ui/Spinner.tsx b/packages/nextjs/components/ui/Spinner.tsx index 55a24517..b1293298 100644 --- a/packages/nextjs/components/ui/Spinner.tsx +++ b/packages/nextjs/components/ui/Spinner.tsx @@ -3,10 +3,5 @@ interface SpinnerProps { } export function Spinner({ className = "h-4 w-4" }: SpinnerProps) { - return ( -
- ); + return
; } - diff --git a/packages/nextjs/hooks/app/transaction/useSignerTransaction.ts b/packages/nextjs/hooks/app/transaction/useSignerTransaction.ts index 5128c5ea..7d917c47 100644 --- a/packages/nextjs/hooks/app/transaction/useSignerTransaction.ts +++ b/packages/nextjs/hooks/app/transaction/useSignerTransaction.ts @@ -3,6 +3,7 @@ import { SignerData, TxType, encodeAddSigners, encodeRemoveSigners, encodeUpdate import { useWalletClient } from "wagmi"; import { useGenerateProof, useMetaMultiSigWallet, useWalletCommitments, useWalletThreshold } from "~~/hooks"; import { useCreateTransaction, useReserveNonce } from "~~/hooks/api/useTransaction"; +import { useStepLoading } from "~~/hooks/app/useStepLoading"; import { formatErrorMessage } from "~~/utils/formatError"; import { notification } from "~~/utils/scaffold-eth"; @@ -10,14 +11,21 @@ interface UseSignerTransactionOptions { onSuccess?: () => void; } +const SIGNER_STEPS = [ + { id: 1, label: "Preparing your proposal..." }, + { id: 2, label: "Waiting for wallet approval..." }, + { id: 3, label: "Securing your transaction..." }, + { id: 4, label: "Almost done, submitting..." }, +]; + export const useSignerTransaction = (options?: UseSignerTransactionOptions) => { - const [isLoading, setIsLoading] = useState(false); - const [loadingState, setLoadingState] = useState(""); + const { isLoading, loadingState, loadingStep, totalSteps, startStep, setStepByLabel, reset } = + useStepLoading(SIGNER_STEPS); const { data: walletClient } = useWalletClient(); const metaMultiSigWallet = useMetaMultiSigWallet(); const { generateProof } = useGenerateProof({ - onLoadingStateChange: setLoadingState, + onLoadingStateChange: setStepByLabel, }); const { mutateAsync: createTransaction } = useCreateTransaction(); const { mutateAsync: reserveNonce } = useReserveNonce(); @@ -44,6 +52,8 @@ export const useSignerTransaction = (options?: UseSignerTransactionOptions) => { throw new Error("Wallet not connected"); } + startStep(1); + const { nonce } = await reserveNonce(metaMultiSigWallet.address); const currentThreshold = await metaMultiSigWallet.read.signaturesRequired(); @@ -56,7 +66,7 @@ export const useSignerTransaction = (options?: UseSignerTransactionOptions) => { const { proof, publicInputs, nullifier, vk } = await generateProof(txHash); - setLoadingState("Submitting to backend..."); + startStep(4); await createTransaction({ nonce, @@ -91,7 +101,6 @@ export const useSignerTransaction = (options?: UseSignerTransactionOptions) => { return; } - setIsLoading(true); try { const commitments = newSigners.map(s => s.commitment.trim()); const callData = encodeAddSigners(commitments, newThreshold); @@ -110,8 +119,7 @@ export const useSignerTransaction = (options?: UseSignerTransactionOptions) => { console.error("Failed to add signer:", error); notification.error(formatErrorMessage(error, "Failed to add signer")); } finally { - setIsLoading(false); - setLoadingState(""); + reset(); } }; @@ -143,7 +151,6 @@ export const useSignerTransaction = (options?: UseSignerTransactionOptions) => { return; } - setIsLoading(true); try { const commitments = signersToRemove.map(s => s.commitment.trim()); const callData = encodeRemoveSigners(commitments, adjustedThreshold); @@ -162,8 +169,7 @@ export const useSignerTransaction = (options?: UseSignerTransactionOptions) => { console.error("Failed to remove signer:", error); notification.error(formatErrorMessage(error, "Failed to remove signer")); } finally { - setIsLoading(false); - setLoadingState(""); + reset(); } }; @@ -178,7 +184,6 @@ export const useSignerTransaction = (options?: UseSignerTransactionOptions) => { return; } - setIsLoading(true); try { const callData = encodeUpdateThreshold(newThreshold); await executeSignerTransaction(TxType.SET_THRESHOLD, callData, { @@ -191,8 +196,7 @@ export const useSignerTransaction = (options?: UseSignerTransactionOptions) => { console.error("Failed to update threshold:", error); notification.error(formatErrorMessage(error, "Failed to update threshold")); } finally { - setIsLoading(false); - setLoadingState(""); + reset(); } }; @@ -202,6 +206,8 @@ export const useSignerTransaction = (options?: UseSignerTransactionOptions) => { updateThreshold, isLoading, loadingState, + loadingStep, + totalSteps, signers, threshold, refetchCommitments, diff --git a/packages/nextjs/styles/globals.css b/packages/nextjs/styles/globals.css index bf668410..d4de78b5 100644 --- a/packages/nextjs/styles/globals.css +++ b/packages/nextjs/styles/globals.css @@ -287,4 +287,4 @@ button { stroke-width: 3px; stroke: #ff6ee9; backdrop-filter: blur(45.77786636352539px); -} \ No newline at end of file +} From 4ec4dfe45a4fc90caefe7199b97dcc64fa63233d Mon Sep 17 00:00:00 2001 From: BoHsuu Date: Fri, 13 Mar 2026 16:28:08 +0700 Subject: [PATCH 4/5] Enhance transaction loading feedback in Dashboard and modals - Updated TransactionRow component to display detailed loading steps during transaction approval. - Integrated step loading functionality in useTransactionVote hook for improved user experience during transaction processes. --- .../components/Dashboard/TransactionRow.tsx | 23 ++++++++++- .../modals/EditAccountModal/EditStep.tsx | 1 - .../EditAccountModal/SubmittingStep.tsx | 6 +-- .../app/transaction/useTransactionVote.ts | 39 ++++++++++++------- packages/nextjs/hooks/app/useStepLoading.ts | 8 +++- packages/nextjs/utils/formatError.ts | 2 +- 6 files changed, 54 insertions(+), 25 deletions(-) diff --git a/packages/nextjs/components/Dashboard/TransactionRow.tsx b/packages/nextjs/components/Dashboard/TransactionRow.tsx index ba7bfef3..1b19adf0 100644 --- a/packages/nextjs/components/Dashboard/TransactionRow.tsx +++ b/packages/nextjs/components/Dashboard/TransactionRow.tsx @@ -632,7 +632,17 @@ export function TransactionRow({ tx, onSuccess }: TransactionRowProps) { // Get totalSigners realtime from wallet commitments const totalSigners = commitmentsData?.length || 0; - const { approve, deny, execute, isLoading: loading, loadingState } = useTransactionVote({ onSuccess }); + const { + approve, + deny, + execute, + isLoading: loading, + loadingState, + loadingStep, + totalSteps, + } = useTransactionVote({ + onSuccess, + }); const handleApprove = async () => { await approve(tx); @@ -718,7 +728,16 @@ export function TransactionRow({ tx, onSuccess }: TransactionRowProps) {
{/* Loading State */} {loading && loadingState && ( -
{loadingState}
+
+
+ {loadingStep > 0 && totalSteps > 1 && ( + + Step {loadingStep}/{totalSteps} + + )} + {loadingState} +
+
)} {/* Main Container */} diff --git a/packages/nextjs/components/modals/EditAccountModal/EditStep.tsx b/packages/nextjs/components/modals/EditAccountModal/EditStep.tsx index 5a921a44..3546dcdf 100644 --- a/packages/nextjs/components/modals/EditAccountModal/EditStep.tsx +++ b/packages/nextjs/components/modals/EditAccountModal/EditStep.tsx @@ -24,7 +24,6 @@ const EditStep: React.FC = ({ existingSigners, originalThreshold, loading, - loadingState, onNext, onClose, }) => { diff --git a/packages/nextjs/components/modals/EditAccountModal/SubmittingStep.tsx b/packages/nextjs/components/modals/EditAccountModal/SubmittingStep.tsx index 6a2dadb9..2044a136 100644 --- a/packages/nextjs/components/modals/EditAccountModal/SubmittingStep.tsx +++ b/packages/nextjs/components/modals/EditAccountModal/SubmittingStep.tsx @@ -8,11 +8,7 @@ interface SubmittingStepProps { totalSteps?: number; } -const SubmittingStep: React.FC = ({ - loadingState = "", - loadingStep = 0, - totalSteps = 4, -}) => { +const SubmittingStep: React.FC = ({ loadingState = "", loadingStep = 0, totalSteps = 4 }) => { return (
{/* Rocket animation video */} diff --git a/packages/nextjs/hooks/app/transaction/useTransactionVote.ts b/packages/nextjs/hooks/app/transaction/useTransactionVote.ts index cda599e7..404688a6 100644 --- a/packages/nextjs/hooks/app/transaction/useTransactionVote.ts +++ b/packages/nextjs/hooks/app/transaction/useTransactionVote.ts @@ -16,6 +16,7 @@ import { useWalletClient } from "wagmi"; import { accountKeys, useMetaMultiSigWallet, userKeys } from "~~/hooks"; import { useApproveTransaction, useDenyTransaction, useExecuteTransaction } from "~~/hooks/api/useTransaction"; import { useGenerateProof } from "~~/hooks/app/useGenerateProof"; +import { useStepLoading } from "~~/hooks/app/useStepLoading"; import { useIdentityStore } from "~~/services/store/useIdentityStore"; import { formatErrorMessage } from "~~/utils/formatError"; import { notification } from "~~/utils/scaffold-eth"; @@ -117,9 +118,16 @@ function buildTransactionParams(tx: TransactionRowData): { return { to, value, callData }; } +const APPROVE_STEPS = [ + { id: 1, label: "Preparing approval..." }, + { id: 2, label: "Waiting for wallet approval..." }, + { id: 3, label: "Securing your transaction..." }, + { id: 4, label: "Almost done, submitting..." }, +]; + export const useTransactionVote = (options?: UseTransactionVoteOptions) => { - const [isLoading, setIsLoading] = useState(false); - const [loadingState, setLoadingState] = useState(""); + const { isLoading, loadingState, loadingStep, totalSteps, startStep, setStepByLabel, reset, startLoading } = + useStepLoading(APPROVE_STEPS); const { commitment } = useIdentityStore(); const { data: walletClient } = useWalletClient(); @@ -129,7 +137,7 @@ export const useTransactionVote = (options?: UseTransactionVoteOptions) => { const { mutateAsync: denyApi } = useDenyTransaction(); const { mutateAsync: executeApi } = useExecuteTransaction(); const { generateProof } = useGenerateProof({ - onLoadingStateChange: setLoadingState, + onLoadingStateChange: setStepByLabel, }); const queryClient = useQueryClient(); @@ -144,7 +152,8 @@ export const useTransactionVote = (options?: UseTransactionVoteOptions) => { return; } - setIsLoading(true); + // Full 4-step flow + startStep(1); try { // 1. Build callData based on tx type const { to, value, callData } = buildTransactionParams(tx); @@ -161,7 +170,7 @@ export const useTransactionVote = (options?: UseTransactionVoteOptions) => { const proofData = await generateProof(txHash); // 4. Submit to backend - setLoadingState("Submitting to backend..."); + startStep(4); await approveApi({ txId: tx.txId, dto: { @@ -179,8 +188,7 @@ export const useTransactionVote = (options?: UseTransactionVoteOptions) => { console.error("Approve error:", error); notification.error(formatErrorMessage(error, "Failed to approve")); } finally { - setIsLoading(false); - setLoadingState(""); + reset(); } }; @@ -195,9 +203,10 @@ export const useTransactionVote = (options?: UseTransactionVoteOptions) => { return; } - setIsLoading(true); + // Single-step fast action + startLoading("Submitting deny vote..."); try { - setLoadingState("Submitting deny vote..."); + // Fast single-step action; keep step 1 await denyApi({ txId: tx.txId, dto: { @@ -211,8 +220,7 @@ export const useTransactionVote = (options?: UseTransactionVoteOptions) => { console.error("Deny error:", error); notification.error(formatErrorMessage(error, "Failed to deny")); } finally { - setIsLoading(false); - setLoadingState(""); + reset(); } }; @@ -222,9 +230,9 @@ export const useTransactionVote = (options?: UseTransactionVoteOptions) => { return; } - setIsLoading(true); + // Single-step fast action + startLoading("Executing on-chain..."); try { - setLoadingState("Executing on-chain..."); const result = await executeApi({ txId, dto: { @@ -244,8 +252,7 @@ export const useTransactionVote = (options?: UseTransactionVoteOptions) => { console.error("Execute error:", error); notification.error(formatErrorMessage(error, "Failed to execute")); } finally { - setIsLoading(false); - setLoadingState(""); + reset(); } }; @@ -255,5 +262,7 @@ export const useTransactionVote = (options?: UseTransactionVoteOptions) => { execute, isLoading, loadingState, + loadingStep, + totalSteps, }; }; diff --git a/packages/nextjs/hooks/app/useStepLoading.ts b/packages/nextjs/hooks/app/useStepLoading.ts index 7f6b0b07..9285661d 100644 --- a/packages/nextjs/hooks/app/useStepLoading.ts +++ b/packages/nextjs/hooks/app/useStepLoading.ts @@ -37,6 +37,12 @@ export function useStepLoading(steps: StepDefinition[]) { setLoadingState(""); }; + const startLoading = (label: string) => { + setIsLoading(true); + setLoadingState(label); + setLoadingStep(0); + }; + // Prevent accidental refresh while loading useEffect(() => { const handleBeforeUnload = (e: BeforeUnloadEvent) => { @@ -56,6 +62,6 @@ export function useStepLoading(steps: StepDefinition[]) { startStep, setStepByLabel, reset, + startLoading, }; } - diff --git a/packages/nextjs/utils/formatError.ts b/packages/nextjs/utils/formatError.ts index 0965a90b..983f18ba 100644 --- a/packages/nextjs/utils/formatError.ts +++ b/packages/nextjs/utils/formatError.ts @@ -7,7 +7,7 @@ type ErrorLike = { const ERROR_PATTERNS: [RegExp, string | null][] = [ [/user rejected|user denied|user refused|ACTION_REJECTED/i, "Transaction was cancelled."], - [/insufficient funds/i, "Insufficient funds for this transaction."], + [/insufficient (funds|balance|account)/i, "Insufficient funds for this transaction."], [/nonce too (high|low)/i, "Transaction conflict. Please try again."], [/gas required exceeds|cannot estimate gas/i, "Transaction failed. Please try again."], [/rate.?limit|429/i, "Too many requests. Please wait a moment."], From 7dc58dbb8e4ff393e3475a41c92bea21b0a48579 Mon Sep 17 00:00:00 2001 From: BoHsuu Date: Fri, 13 Mar 2026 16:53:13 +0700 Subject: [PATCH 5/5] Changed button label from Transfer now to Submit Transfer to enhance user understanding during the transfer process. --- packages/nextjs/components/Transfer/TransferContainer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nextjs/components/Transfer/TransferContainer.tsx b/packages/nextjs/components/Transfer/TransferContainer.tsx index 794c6d17..e8e603b9 100644 --- a/packages/nextjs/components/Transfer/TransferContainer.tsx +++ b/packages/nextjs/components/Transfer/TransferContainer.tsx @@ -279,7 +279,7 @@ export default function TransferContainer() { > {isLoading && } - {isLoading ? "Processing..." : "Transfer now"} + {isLoading ? "Processing..." : "Submit Transfer"}