import { Button, ButtonProps, IconButton, IconButtonProps, Tooltip, } from "@material-ui/core"; import CircularProgress from "@material-ui/core/CircularProgress"; import { useSnackbar } from "notistack"; import { ReactNode, useState } from "react"; import { useIsContextAvailable } from "store/hooks"; interface PromiseInvokeButtonProps { onSuccess?: (data: T) => void | null; onInvoke: () => Promise; onPendingChange?: (isPending: boolean) => void | null; isLoadingOverride?: boolean; isIconButton?: boolean; loadIcon?: ReactNode; disabled?: boolean; displayErrorSnackbar?: boolean; tooltipTitle?: string | null; requiresContext?: boolean; } export default function PromiseInvokeButton({ disabled = false, onSuccess = null, onInvoke, endIcon, loadIcon = null, isLoadingOverride = false, isIconButton = false, displayErrorSnackbar = false, onPendingChange = null, requiresContext = true, tooltipTitle = null, ...rest }: PromiseInvokeButtonProps & ButtonProps) { const { enqueueSnackbar } = useSnackbar(); const isContextAvailable = useIsContextAvailable(); const [isPending, setIsPending] = useState(false); const isLoading = isPending || isLoadingOverride; const actualEndIcon = isLoading ? loadIcon || : endIcon; async function handleClick() { if (!isPending) { try { onPendingChange?.(true); setIsPending(true); const result = await onInvoke(); onSuccess?.(result); } catch (e: unknown) { if (displayErrorSnackbar) { enqueueSnackbar(e as string, { autoHideDuration: 60 * 1000, variant: "error", }); } } finally { setIsPending(false); onPendingChange?.(false); } } } const requiresContextButNotAvailable = requiresContext && !isContextAvailable; const isDisabled = disabled || isLoading || requiresContextButNotAvailable; const actualTooltipTitle = (requiresContextButNotAvailable ? "Wait for the application to load all required components" : tooltipTitle) ?? ""; return ( {isIconButton ? ( {actualEndIcon} ) : (