Skip to content

Commit

Permalink
add loading state everywhere is wallet triggering
Browse files Browse the repository at this point in the history
  • Loading branch information
Sworzen1 committed Feb 5, 2024
1 parent c0aa804 commit 1567f63
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 61 deletions.
45 changes: 29 additions & 16 deletions govtool/frontend/src/components/atoms/LoadingButton.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,37 @@
import { Button, CircularProgress } from '@mui/material';
import type { ButtonProps } from '@mui/material';
import { Button, CircularProgress } from "@mui/material";
import type { ButtonProps, SxProps } from "@mui/material";

export type ButtonIntent = 'primary' | 'secondary';
export interface ExtendedButtonProps extends Omit<ButtonProps, 'size'> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- conflicting event types for form and button event handlers
onClick?: (e?: any) => void;
interface Props extends Omit<ButtonProps, "size" | "sx"> {
isLoading?: boolean;
size?: "small" | "medium" | "large" | "extraLarge";
sx?: SxProps;
}

interface Props extends ExtendedButtonProps {
isLoading: boolean;
}
export const LoadingButton = ({
isLoading,
disabled,
children,
size = "large",
sx,
...rest
}: Props) => {
const buttonHeight = {
extraLarge: 48,
large: 40,
medium: 36,
small: 32,
}[size];

export const LoadingButton = ({ isLoading, disabled, children, ...rest }: Props) => {
return (
<Button disabled={disabled || isLoading} {...rest}>
{isLoading ? (
<CircularProgress />
) : (
children
<Button
disabled={disabled || isLoading}
sx={{ height: buttonHeight, ...sx }}
{...rest}
>
{isLoading && (
<CircularProgress size={26} sx={{ position: "absolute" }} />
)}
{children}
</Button>
);
}
};
48 changes: 27 additions & 21 deletions govtool/frontend/src/components/molecules/DashboardActionCard.tsx
Original file line number Diff line number Diff line change
@@ -1,54 +1,58 @@
import { Box, ButtonProps, Skeleton } from "@mui/material";
import { FC, ReactNode } from "react";

import { Button, CopyButton, Typography } from "@atoms";
import { CopyButton, LoadingButton, Typography } from "@atoms";
import { useScreenDimension } from "@hooks";
import { theme } from "@/theme";

type DashboardActionCardProps = {
cardId?: string;
cardTitle?: string;
dataTestidDelegationStatus?: string;
dataTestidDrepIdBox?: string;
dataTestidFirstButton?: string;
dataTestidSecondButton?: string;
description?: ReactNode;
firstButtonAction?: () => void;
firstButtonDisabled?: boolean;
firstButtonIsLoading?: boolean;
firstButtonLabel?: string;
firstButtonVariant?: ButtonProps["variant"];
imageHeight?: number;
imageURL?: string;
imageWidth?: number;
inProgress?: boolean;
isLoading?: boolean;
secondButtonAction?: () => void;
secondButtonIsLoading?: boolean;
secondButtonLabel?: string;
secondButtonVariant?: ButtonProps["variant"];
title?: ReactNode;
cardTitle?: string;
cardId?: string;
inProgress?: boolean;
isLoading?: boolean;
dataTestidFirstButton?: string;
dataTestidSecondButton?: string;
dataTestidDrepIdBox?: string;
dataTestidDelegationStatus?: string;
};

export const DashboardActionCard: FC<DashboardActionCardProps> = ({
...props
}) => {
const {
cardId,
cardTitle,
dataTestidDrepIdBox,
dataTestidFirstButton,
dataTestidSecondButton,
dataTestidDrepIdBox,
description,
firstButtonAction,
firstButtonDisabled = false,
firstButtonIsLoading = false,
firstButtonLabel,
firstButtonVariant = "contained",
imageURL,
inProgress,
isLoading = false,
secondButtonAction,
secondButtonIsLoading = false,
secondButtonLabel,
secondButtonVariant = "outlined",
title,
cardId,
cardTitle,
inProgress,
isLoading = false,
} = props;

const {
Expand Down Expand Up @@ -202,12 +206,13 @@ export const DashboardActionCard: FC<DashboardActionCardProps> = ({
}
>
{firstButtonLabel ? (
<Button
<LoadingButton
data-testid={dataTestidFirstButton}
onClick={firstButtonAction}
variant={firstButtonVariant}
disabled={firstButtonDisabled}
isLoading={firstButtonIsLoading}
onClick={firstButtonAction}
size="large"
variant={firstButtonVariant}
sx={{
mr:
screenWidth < 768
Expand All @@ -221,14 +226,15 @@ export const DashboardActionCard: FC<DashboardActionCardProps> = ({
}}
>
{firstButtonLabel}
</Button>
</LoadingButton>
) : null}
{secondButtonLabel ? (
<Button
<LoadingButton
data-testid={dataTestidSecondButton}
isLoading={secondButtonIsLoading}
onClick={secondButtonAction}
variant={secondButtonVariant}
size="large"
variant={secondButtonVariant}
sx={{
width: isMobile ? "100%" : "auto",
marginTop:
Expand All @@ -242,7 +248,7 @@ export const DashboardActionCard: FC<DashboardActionCardProps> = ({
}}
>
{secondButtonLabel}
</Button>
</LoadingButton>
) : null}
</Box>
)}
Expand Down
14 changes: 6 additions & 8 deletions govtool/frontend/src/components/molecules/VoteActionForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const VoteActionForm = ({
isDirty,
clearErrors,
areFormErrors,
isLoading,
isVoteLoading,
} = useVoteActionForm();

useEffect(() => {
Expand Down Expand Up @@ -80,7 +80,7 @@ export const VoteActionForm = ({
areFormErrors ||
(!isContext && voteFromEP === vote)
}
isLoading={isLoading}
isLoading={isVoteLoading}
variant="contained"
sx={{
borderRadius: 50,
Expand All @@ -92,7 +92,7 @@ export const VoteActionForm = ({
Change vote
</LoadingButton>
);
}, [confirmVote, areFormErrors, vote]);
}, [confirmVote, areFormErrors, vote, isVoteLoading]);

return (
<Box flex={1} display="flex" flexDirection="column" width={"full"}>
Expand Down Expand Up @@ -253,7 +253,7 @@ export const VoteActionForm = ({
{isMobile ? renderCancelButton : renderChangeVoteButton}
</Box>
) : (
<Button
<LoadingButton
data-testid={"vote-button"}
variant="contained"
disabled={
Expand All @@ -262,14 +262,12 @@ export const VoteActionForm = ({
(isContext && areFormErrors && isDirty) ||
voteFromEP === vote
}
isLoading={isVoteLoading}
onClick={confirmVote}
size="extraLarge"
sx={{
width: "100%",
}}
>
Vote
</Button>
</LoadingButton>
)}
</Box>
);
Expand Down
10 changes: 5 additions & 5 deletions govtool/frontend/src/components/organisms/DashboardCards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,14 @@ export const DashboardCards = () => {
useGetAdaHolderCurrentDelegationQuery(stakeKey);
const { screenWidth, isMobile } = useScreenDimension();
const { openModal } = useModal();
const [isLoading, setIsLoading] = useState<boolean>(false);
const [isRetirementLoading, setIsRetirementLoading] =
useState<boolean>(false);
const { votingPower, powerIsLoading } =
useGetAdaHolderVotingPowerQuery(stakeKey);

const retireAsDrep = useCallback(async () => {
try {
setIsLoading(true);
setIsRetirementLoading(true);
const isPendingTx = isPendingTransaction();
if (isPendingTx) return;
const certBuilder = await buildDRepRetirementCert();
Expand All @@ -59,7 +60,6 @@ export const DashboardCards = () => {
} catch (error: any) {
const errorMessage = error.info ? error.info : error;

setIsLoading(false);
openModal({
type: "statusModal",
state: {
Expand All @@ -71,7 +71,7 @@ export const DashboardCards = () => {
},
});
} finally {
setIsLoading(false);
setIsRetirementLoading(false);
}
}, [buildDRepRetirementCert, buildSignSubmitConwayCertTx]);

Expand Down Expand Up @@ -328,7 +328,6 @@ export const DashboardCards = () => {
}
dataTestidDrepIdBox="my-drep-id"
firstButtonVariant={dRep?.isRegistered ? "outlined" : "contained"}
firstButtonDisabled={isLoading}
secondButtonVariant={
registerTransaction?.transactionHash
? "outlined"
Expand Down Expand Up @@ -359,6 +358,7 @@ export const DashboardCards = () => {
? retireAsDrep
: () => navigateTo(PATHS.registerAsdRep)
}
firstButtonIsLoading={isRetirementLoading}
firstButtonLabel={
registerTransaction?.transactionHash
? ""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useEffect, useState, useCallback, useMemo } from "react";
import { useNavigate } from "react-router-dom";
import { Box, Grid } from "@mui/material";

import { ActionRadio, Button, Typography } from "@atoms";
import { ActionRadio, Button, LoadingButton, Typography } from "@atoms";
import { ICONS, PATHS } from "@consts";
import { useCardano, useModal } from "@context";
import {
Expand Down Expand Up @@ -31,6 +31,8 @@ export const DelegateTodRepStepOne = ({ setStep }: DelegateProps) => {
const { openModal, closeModal } = useModal();
const [areOptions, setAreOptions] = useState<boolean>(false);
const [chosenOption, setChosenOption] = useState<string>("");
const [isDelegationLoading, setIsDelegationLoading] =
useState<boolean>(false);
const {
palette: { boxShadow2 },
} = theme;
Expand Down Expand Up @@ -86,6 +88,7 @@ export const DelegateTodRepStepOne = ({ setStep }: DelegateProps) => {
}, [chosenOption, areOptions]);

const delegate = useCallback(async () => {
setIsDelegationLoading(true);
try {
const certBuilder = await buildVoteDelegationCert(chosenOption);
const result = await buildSignSubmitConwayCertTx({
Expand All @@ -97,16 +100,19 @@ export const DelegateTodRepStepOne = ({ setStep }: DelegateProps) => {
const errorMessage = error.info ? error.info : error;

openErrorDelegationModal(errorMessage);
} finally {
setIsDelegationLoading(false);
}
}, [chosenOption, buildSignSubmitConwayCertTx, buildVoteDelegationCert]);

const renderDelegateButton = useMemo(() => {
return (
<Button
<LoadingButton
data-testid={
chosenOption !== dRepID ? "next-step-button" : "delegate-button"
}
disabled={!chosenOption}
isLoading={isDelegationLoading}
onClick={() => {
if (chosenOption === "Delegate to DRep") {
setStep(2);
Expand All @@ -122,9 +128,16 @@ export const DelegateTodRepStepOne = ({ setStep }: DelegateProps) => {
variant="contained"
>
{chosenOption !== dRepID ? "Next step" : "Delegate"}
</Button>
</LoadingButton>
);
}, [chosenOption, dRep?.isRegistered, isMobile, delegate, dRepID]);
}, [
chosenOption,
delegate,
dRep?.isRegistered,
dRepID,
isDelegationLoading,
isMobile,
]);

const renderCancelButton = useMemo(() => {
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useMemo } from "react";
import { Box, Link } from "@mui/material";

import { Button, Input, Typography } from "../atoms";
import { Button, Input, LoadingButton, Typography } from "../atoms";
import { useScreenDimension, useDelegateTodRepForm } from "@hooks";
import { theme } from "@/theme";
import { openInNewTab } from "@utils";
Expand All @@ -17,14 +17,15 @@ export const DelegateTodRepStepTwo = ({ setStep }: DelegateProps) => {
palette: { boxShadow2 },
} = theme;

const { control, isDelegateButtonDisabled, delegate } =
const { control, delegate, isDelegateButtonDisabled, isDelegationLoading } =
useDelegateTodRepForm();

const renderDelegateButton = useMemo(() => {
return (
<Button
<LoadingButton
data-testid={"delegate-button"}
disabled={isDelegateButtonDisabled}
isLoading={isDelegationLoading}
onClick={delegate}
size="extraLarge"
sx={{
Expand All @@ -34,9 +35,9 @@ export const DelegateTodRepStepTwo = ({ setStep }: DelegateProps) => {
variant="contained"
>
Delegate
</Button>
</LoadingButton>
);
}, [isDelegateButtonDisabled, delegate, isMobile]);
}, [isDelegateButtonDisabled, delegate, isMobile, isDelegationLoading]);

const renderBackButton = useMemo(() => {
return (
Expand Down
2 changes: 1 addition & 1 deletion govtool/frontend/src/hooks/forms/useDelegateTodRepForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,6 @@ export const useDelegateTodRepForm = () => {
isDelegateButtonDisabled,
delegate: handleSubmit(delegate),
modal,
isLoading,
isDelegationLoading: isLoading,
};
};
2 changes: 1 addition & 1 deletion govtool/frontend/src/hooks/forms/useVoteActionForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,6 @@ export const useVoteActionForm = () => {
isDirty,
clearErrors,
areFormErrors,
isLoading,
isVoteLoading: isLoading,
};
};

0 comments on commit 1567f63

Please sign in to comment.