make all actionable buttons to show loading state with appropriate text

Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
This commit is contained in:
Uncle Fatso 2026-04-28 15:06:12 +03:00
parent d9aff4dc6a
commit b022a3c64c
Signed by: f4ts0
GPG Key ID: 565F4F2860226EBB
8 changed files with 58 additions and 21 deletions

View File

@ -1,7 +1,7 @@
{ {
"name": "ghost-dao-interface", "name": "ghost-dao-interface",
"private": true, "private": true,
"version": "0.7.29", "version": "0.7.30",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View File

@ -27,6 +27,7 @@ import { useBreakoutModal } from "../../../hooks/breakoutModal";
export const ClaimBonds = ({ chainId, address, secondsTo }) => { export const ClaimBonds = ({ chainId, address, secondsTo }) => {
const isSmallScreen = useScreenSize("md"); const isSmallScreen = useScreenSize("md");
const [isPending, setIsPending] = useState(false); const [isPending, setIsPending] = useState(false);
const [pendingIndexes, setPendingIndexes] = useState([]);
const [isWarmup, setIsWapmup] = useState(false); const [isWarmup, setIsWapmup] = useState(false);
const [isPreClaimConfirmed, setPreClaimConfirmed] = useState(false); const [isPreClaimConfirmed, setPreClaimConfirmed] = useState(false);
const [isPayoutGhst, _] = useState(true); const [isPayoutGhst, _] = useState(true);
@ -55,6 +56,7 @@ export const ClaimBonds = ({ chainId, address, secondsTo }) => {
const onSubmit = async (indexes) => { const onSubmit = async (indexes) => {
setIsPending(true); setIsPending(true);
setPendingIndexes(indexes);
const defaultFunction = async () => { const defaultFunction = async () => {
await redeem({ chainId, user: address, isGhst: isPayoutGhst, indexes }); await redeem({ chainId, user: address, isGhst: isPayoutGhst, indexes });
@ -82,6 +84,7 @@ export const ClaimBonds = ({ chainId, address, secondsTo }) => {
breakoutFromBonding({ defaultFunction, toExecute, amount, warmupLeft }) breakoutFromBonding({ defaultFunction, toExecute, amount, warmupLeft })
} }
setPendingIndexes([]);
setIsPending(false); setIsPending(false);
} }
@ -177,9 +180,10 @@ export const ClaimBonds = ({ chainId, address, secondsTo }) => {
<TertiaryButton <TertiaryButton
fullWidth fullWidth
disabled={isPending || secondsTo < note.matured} disabled={isPending || secondsTo < note.matured}
loading={isPending && pendingIndexes.includes(note.id)}
onClick={() => onSubmit([note.id])} onClick={() => onSubmit([note.id])}
> >
Claim {isPending && pendingIndexes.includes(note.id) ? "Claiming" : "Claim"}
</TertiaryButton> </TertiaryButton>
</Box> </Box>
</Box> </Box>
@ -236,9 +240,10 @@ export const ClaimBonds = ({ chainId, address, secondsTo }) => {
<TertiaryButton <TertiaryButton
fullWidth fullWidth
disabled={isPending || secondsTo < note.matured} disabled={isPending || secondsTo < note.matured}
loading={isPending && pendingIndexes.includes(note.id)}
onClick={() => onSubmit([note.id])} onClick={() => onSubmit([note.id])}
> >
Claim {isPending && pendingIndexes.includes(note.id) ? "Claiming" : "Claim"}
</TertiaryButton> </TertiaryButton>
</TableCell> </TableCell>
</TableRow> </TableRow>

View File

@ -278,9 +278,10 @@ const WelcomeView = ({
{warmupPeriod <= 0 && <SecondaryButton {warmupPeriod <= 0 && <SecondaryButton
onClick={() => callDefaultFunction()} onClick={() => callDefaultFunction()}
disabled={isPending || warmupPeriod > 0} disabled={isPending || warmupPeriod > 0}
loading={isPending}
fullWidth fullWidth
> >
Claim {isStakingOpened ? "(3, 3) Stake" : "(1, 1) Bond"} {isPending ? "Claiming..." : `Claim ${isStakingOpened ? "(3, 3) Stake" : "(1, 1) Bond"}`}
</SecondaryButton>} </SecondaryButton>}
<Box display="flex" justifyContent="center" flexDirection="column" alignItems="center"> <Box display="flex" justifyContent="center" flexDirection="column" alignItems="center">
@ -463,10 +464,11 @@ const ConfirmStep = ({
<PrimaryButton <PrimaryButton
onClick={() => execute()} onClick={() => execute()}
loading={isPending}
disabled={isPending || !acknowledgeWalletCustody || !acknowledgeBridgingRisk} disabled={isPending || !acknowledgeWalletCustody || !acknowledgeBridgingRisk}
fullWidth fullWidth
> >
I Confirm {isPending ? "Confirming..." : "I Confirm"}
</PrimaryButton> </PrimaryButton>
</> </>
) )

View File

@ -246,7 +246,7 @@ export const BridgeCardAction = ({
loading={isPending} loading={isPending}
onClick={() => ghostOrConnect()} onClick={() => ghostOrConnect()}
> >
{address === "" ? "Connect" : "Bridge" } {address === "" ? "Connect" : isPending ? "Bridging..." : "Bridge" }
</PrimaryButton> </PrimaryButton>
</Box> </Box>
) )

View File

@ -317,9 +317,9 @@ const PoolContainer = ({
"Connect" "Connect"
: :
pairAddress === "0x0000000000000000000000000000000000000000" ? pairAddress === "0x0000000000000000000000000000000000000000" ?
"Create Pool" isPending ? "Creating Pool..." : "Create Pool"
: :
"Add Liquidity" isPending ? "Adding Liquidity..." : "Add Liquidity"
} }
</SecondaryButton> </SecondaryButton>
</TokenAllowanceGuard> </TokenAllowanceGuard>

View File

@ -147,8 +147,12 @@ const SwapContainer = ({
if (isWrapping) text = "Wrap"; if (isWrapping) text = "Wrap";
else if (isUnwrapping) text = "Unwrap"; else if (isUnwrapping) text = "Unwrap";
else if (pairAddress === EMPTY_ADDRESS) text = "Create Pool"; else if (pairAddress === EMPTY_ADDRESS) text = "Create Pool";
if (isPending) text = `${text}ping...`
if (pairAddress === EMPTY_ADDRESS && isPending) text = "Creating Pool..."
return text; return text;
}, [isWrapping, isUnwrapping, pairAddress]); }, [isPending, isWrapping, isUnwrapping, pairAddress]);
const swapTokens = async () => { const swapTokens = async () => {
setIsPending(true); setIsPending(true);

View File

@ -178,6 +178,7 @@ const NewProposal = ({ config, address, connect, chainId }) => {
isPending isPending
} }
fullWidth fullWidth
loading={isPending}
onClick={() => submitProposal()} onClick={() => submitProposal()}
> >
{isPending ? "Submitting..." : "Submit Proposal"} {isPending ? "Submitting..." : "Submit Proposal"}

View File

@ -80,7 +80,9 @@ const ProposalDetails = ({ chainId, address, connect, config }) => {
const { id } = useParams(); const { id } = useParams();
const proposalId = BigInt(id); const proposalId = BigInt(id);
const [isPending, setIsPending] = useState(false); const [isPendingVote, setIsPendingVote] = useState(-1);
const [isPendingExecute, setIsPendingExecute] = useState(false);
const [isPendingRelease, setIsPendingRelease] = useState(false);
const [selectedDiscussionUrl, setSelectedDiscussionUrl] = useState(undefined); const [selectedDiscussionUrl, setSelectedDiscussionUrl] = useState(undefined);
const isSemiSmallScreen = useMediaQuery("(max-width: 745px)"); const isSemiSmallScreen = useMediaQuery("(max-width: 745px)");
@ -150,8 +152,12 @@ const ProposalDetails = ({ chainId, address, connect, config }) => {
return url; return url;
}, [proposalProposer, config]); }, [proposalProposer, config]);
const isPending = useMemo(() => {
return isPendingExecute || isPendingRelease || isPendingVote > -1;
}, [isPendingExecute, isPendingRelease, isPendingVote]);
const handleVote = useCallback(async (support) => { const handleVote = useCallback(async (support) => {
setIsPending(true); setIsPendingVote(support);
const result = await castVote(chainId, address, proposalId, support); const result = await castVote(chainId, address, proposalId, support);
if (result) { if (result) {
@ -161,21 +167,21 @@ const ProposalDetails = ({ chainId, address, connect, config }) => {
setStorageValue(chainId, address, VOTED_PROPOSALS_PREFIX, proposals.map(id => id.toString())); setStorageValue(chainId, address, VOTED_PROPOSALS_PREFIX, proposals.map(id => id.toString()));
await queryClient.invalidateQueries(); await queryClient.invalidateQueries();
} }
setIsPending(false); setIsPendingVote(-1);
}, [chainId, address, proposalId]); }, [chainId, address, proposalId]);
const handleExecute = async () => { const handleExecute = async () => {
setIsPending(true); setIsPendingExecute(true);
await executeProposal(chainId, address, proposalId); await executeProposal(chainId, address, proposalId);
await queryClient.invalidateQueries(); await queryClient.invalidateQueries();
setIsPending(false); setIsPendingExecute(false);
} }
const handleRelease = async () => { const handleRelease = async () => {
setIsPending(true); setIsPendingRelease(true);
await releaseLocked(chainId, address, proposalId); await releaseLocked(chainId, address, proposalId);
await queryClient.invalidateQueries(); await queryClient.invalidateQueries();
setIsPending(false); setIsPendingRelease(false);
} }
return ( return (
@ -292,16 +298,18 @@ const ProposalDetails = ({ chainId, address, connect, config }) => {
<SecondaryButton <SecondaryButton
fullWidth fullWidth
disabled={proposalState !== 1 || pastVotes?._value === 0n || isPending} disabled={proposalState !== 1 || pastVotes?._value === 0n || isPending}
loading={isPendingVote === 1}
onClick={() => handleVote(1)} onClick={() => handleVote(1)}
> >
{isPending ? "Voting..." : "For"} {isPendingVote === 1 ? "Voting For..." : "For"}
</SecondaryButton> </SecondaryButton>
<SecondaryButton <SecondaryButton
fullWidth fullWidth
disabled={proposalState !== 1 || pastVotes?._value === 0n || isPending} disabled={proposalState !== 1 || pastVotes?._value === 0n || isPending}
loading={isPendingVote === 0}
onClick={() => handleVote(0)} onClick={() => handleVote(0)}
> >
{isPending ? "Voting..." : "Against"} {isPendingVote === 0 ? "Voting Against..." : "Against"}
</SecondaryButton> </SecondaryButton>
</> </>
: <SecondaryButton : <SecondaryButton
@ -337,6 +345,8 @@ const ProposalDetails = ({ chainId, address, connect, config }) => {
chainId={chainId} chainId={chainId}
proposalId={id} proposalId={id}
isPending={isPending} isPending={isPending}
isPendingExecute={isPendingExecute}
isPendingRelease={isPendingRelease}
/> />
</Paper> </Paper>
</Box> </Box>
@ -392,7 +402,20 @@ const ProposalDetails = ({ chainId, address, connect, config }) => {
) )
} }
const VotingTimeline = ({ connect, handleExecute, handleRelease, proposalLocked, proposalId, chainId, state, address, isProposer, isPending }) => { const VotingTimeline = ({
connect,
handleExecute,
handleRelease,
proposalLocked,
proposalId,
chainId,
state,
address,
isProposer,
isPending,
isPendingExecute,
isPendingRelease,
}) => {
const { delay: propsalVotingDelay } = useProposalVotingDelay(chainId, proposalId); const { delay: propsalVotingDelay } = useProposalVotingDelay(chainId, proposalId);
const { snapshot: proposalSnapshot } = useProposalSnapshot(chainId, proposalId); const { snapshot: proposalSnapshot } = useProposalSnapshot(chainId, proposalId);
const { deadline: proposalDeadline } = useProposalDeadline(chainId, proposalId); const { deadline: proposalDeadline } = useProposalDeadline(chainId, proposalId);
@ -414,13 +437,15 @@ const VotingTimeline = ({ connect, handleExecute, handleRelease, proposalLocked,
<Box width="100%" display="flex" gap="10px"> <Box width="100%" display="flex" gap="10px">
{(isProposer && (proposalLocked?._value ?? 0n) > 0n) && <SecondaryButton {(isProposer && (proposalLocked?._value ?? 0n) > 0n) && <SecondaryButton
fullWidth fullWidth
loading={isPendingRelease}
disabled={isPending || state < 2} disabled={isPending || state < 2}
onClick={() => address === "" ? connect() : handleRelease()} onClick={() => address === "" ? connect() : handleRelease()}
> >
{address === "" ? "Connect" : "Release"} {address === "" ? "Connect" : isPendingRelease ? "Releasing..." : "Release"}
</SecondaryButton>} </SecondaryButton>}
<SecondaryButton <SecondaryButton
fullWidth fullWidth
loading={isPending}
disabled={isPending || state !== 4} disabled={isPending || state !== 4}
onClick={() => address === "" ? connect() : handleExecute()} onClick={() => address === "" ? connect() : handleExecute()}
> >
@ -428,7 +453,7 @@ const VotingTimeline = ({ connect, handleExecute, handleRelease, proposalLocked,
? "Connect" ? "Connect"
: state !== 4 : state !== 4
? convertStatusToLabel(state) ? convertStatusToLabel(state)
: isPending ? "Executing..." : "Execute" : isPendingExecute ? "Executing..." : "Execute"
} }
</SecondaryButton> </SecondaryButton>
</Box> </Box>