Files
nym/explorer-nextjs/src/components/staking/SubHeaderRowActions.tsx
T
Yana Matrosova 9aed938c79 Yana/pending staking events (#5400)
* Add pending events to stake table

* Add additional tanstack refetch on user stake/unstake/redeem rewards

* refactor repeated fetch functions

* clean up fetching functions

* refactor & clean up

* Add epoch waiting message on epoch change delay

* fine tune epoch change

* clean up

* refactor imports

* Add transaction hash to successful redeem rewards InfoModal

* fix epoch time check and mobile header

* Fix Loading modal width on mobile

* fix epoch logic

* clean up

* clean up logs

* add waiting for epoch to start to landing page

* clean up

* Add refetch of all queries on epoch change

* Finalise state change on epoch change

* clean up

* fix build

* Fix NodesTable mobile view

* Fix stake table mobile view

* fix typo

* Fix blog articles height

* Add loading skeletons to landing page cards

* clean up

* Add skeletons to cards

* Add skeletons, and loading/error refetch on wallet balnce

* clean up

* Add active stakers card

* clean up

* change NGM to mixnet

* Add TVL to Tokenomics card

* Add last total stake to Stake Card

* clean up

* Fix stake sorting function in Stake Table

* Add wrap of identity key and address to Basic Info Card

* Add counter to epoch time on staking page

* clean up

* update epoch labels

* Add circular loading on Toggle Button

* Update Toggle button loading functionality

* Add skeletons to account cards

* Add search functionality on Enter

* clean up

* DOMpurify node name and description

* Add column with id and identity key, wrap names to 2 lines

* Set width of column headers to 110px

* fix pending events for delegations

* Fix Stake button proppagation

* Add full country name to tooltips

* Take out connect wallet from mobile menu toggle

* finetune epoch change intervals

* Add error text to Magic Search

* fix build

* Add react-markdown for Blog articles

* fix graph's width and Table column headings

* fix Magic Search loading

* Fix grid on account page

* fix account card address width

* Fix permanent loading spinner on ToggleButton

* clean up URL's, fix copy address on the Basic Card

* replace mintscan with ping, open tx link on new page

* Take out toggle button if no node bonded by address

* Set fixed column width on tables

* Add not-found page to account, when no node bonded

* Add full country name to tables and node profile card

* clean up

* Table fixes

* Fix sorting in Delegations table Node page

* clean up

* Fix line chart view

* refactor epoch progress bar

* remove unused imports

* remove tanstack delclaration module

* create epoch data provider

* remove logic from togglebutton component

* use epoch provider in components

* invalidateQueries should be awaited

* tidy up QualityIndicatorsCard component formatting

* fix infinite loop in epoch provider

---------

Co-authored-by: Yana <yanok87@users.noreply.github.com>
Co-authored-by: fmtabbara <fmtabbara@hotmail.co.uk>
2025-02-12 13:15:45 +00:00

174 lines
5.0 KiB
TypeScript

"use client";
import { useChain } from "@cosmos-kit/react";
import { Button, Stack } from "@mui/material";
import type { Delegation } from "@nymproject/contract-clients/Mixnet.types";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useCallback, useState } from "react";
import { fetchTotalStakerRewards } from "../../app/api";
import type { NodeRewardDetails } from "../../app/api/types";
import { COSMOS_KIT_USE_CHAIN, NYM_MIXNET_CONTRACT } from "../../config";
import { useNymClient } from "../../hooks/useNymClient";
import Loading from "../loading";
import InfoModal, { type InfoModalProps } from "../modal/InfoModal";
import RedeemRewardsModal from "../redeemRewards/RedeemRewardsModal";
const fee = { gas: "1000000", amount: [{ amount: "1000000", denom: "unym" }] };
// Fetch delegations
const fetchDelegations = async (
address: string,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
nymClient: any,
): Promise<Delegation[]> => {
const data = await nymClient.getDelegatorDelegations({ delegator: address });
return data.delegations;
};
const SubHeaderRowActions = () => {
const [openRedeemRewardsModal, setOpenRedeemRewardsModal] =
useState<boolean>(false);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [infoModalProps, setInfoModalProps] = useState<InfoModalProps>({
open: false,
});
const { address, nymClient } = useNymClient();
const { getSigningCosmWasmClient } = useChain(COSMOS_KIT_USE_CHAIN);
const queryClient = useQueryClient();
// Fetch delegations using React Query
const {
data: delegations = [],
isLoading: isDelegationsLoading,
isError: isDelegationsError,
} = useQuery({
queryKey: ["delegations", address],
queryFn: () => fetchDelegations(address || "", nymClient),
enabled: !!address && !!nymClient, // Only fetch if address and nymClient are available
});
// Fetch total rewards using React Query
const {
data: totalStakerRewards = 0,
isLoading: isRewardsLoading,
isError: isRewardsError,
refetch,
} = useQuery({
queryKey: ["totalStakerRewards", address],
queryFn: () => fetchTotalStakerRewards(address || ""),
enabled: !!address, // Only fetch if address is available
});
const handleRefetch = useCallback(async () => {
refetch();
queryClient.invalidateQueries(); // This will refetch ALL active queries
}, [queryClient, refetch]);
const handleRedeemRewards = useCallback(async () => {
setIsLoading(true);
setOpenRedeemRewardsModal(false);
try {
if (!nymClient || !address || !delegations.length) {
throw new Error("Wallet, client, or delegations not available.");
}
const messages = delegations.map((delegation: NodeRewardDetails) => ({
contractAddress: NYM_MIXNET_CONTRACT,
funds: [],
msg: {
withdraw_delegator_reward: {
node_id: delegation.node_id,
},
},
}));
const cosmWasmSigningClient = await getSigningCosmWasmClient();
const result = await cosmWasmSigningClient.executeMultiple(
address,
messages,
fee,
"Redeeming all rewards",
);
// Success state
setIsLoading(false);
setInfoModalProps({
open: true,
title: "Success",
message: "All rewards have been redeemed successfully!",
tx: result?.transactionHash,
onClose: async () => {
await handleRefetch();
setInfoModalProps({ open: false });
},
});
} catch (error) {
console.error("Error redeeming rewards:", error);
setInfoModalProps({
open: true,
title: "Error",
message:
error instanceof Error ? error.message : "Failed to redeem rewards.",
onClose: () => setInfoModalProps({ open: false }),
});
} finally {
setIsLoading(false);
}
}, [
address,
nymClient,
delegations,
getSigningCosmWasmClient,
handleRefetch,
]);
const handleRedeemRewardsButtonClick = () => {
setOpenRedeemRewardsModal(true);
};
if (!address || !nymClient) {
return null;
}
if (isDelegationsLoading || isRewardsLoading) {
return <Loading />;
}
if (isDelegationsError || isRewardsError) {
return (
<Stack direction="row" spacing={3} justifyContent={"end"}>
<Button variant="contained" disabled>
Error loading data
</Button>
</Stack>
);
}
return (
<Stack direction="row" spacing={3} justifyContent={"end"}>
<Button
variant="contained"
onClick={handleRedeemRewardsButtonClick}
disabled={totalStakerRewards === 0}
>
Redeem NYM
</Button>
{isLoading && <Loading />}
{openRedeemRewardsModal && (
<RedeemRewardsModal
onRedeem={handleRedeemRewards}
onClose={() => setOpenRedeemRewardsModal(false)}
totalRewardsAmount={totalStakerRewards}
/>
)}
<InfoModal {...infoModalProps} />
</Stack>
);
};
export default SubHeaderRowActions;