import { faCheck, faCircleNotch, faTimes, } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FunctionComponent, PropsWithChildren, ReactElement, useCallback, useEffect, useState, } from "react"; import { Button, ButtonProps } from "react-bootstrap"; import { UseQueryResult } from "react-query"; import { useTimeoutWhen } from "rooks"; import { LoadingIndicator } from "."; interface QueryOverlayProps { result: UseQueryResult; children: ReactElement; } export const QueryOverlay: FunctionComponent = ({ children, result: { isLoading, isError, error }, }) => { if (isLoading) { return ; } else if (isError) { return

{error as string}

; } return children; }; interface PromiseProps { promise: () => Promise; children: FunctionComponent; } export function PromiseOverlay({ promise, children }: PromiseProps) { const [item, setItem] = useState(null); useEffect(() => { promise().then(setItem); }, [promise]); if (item === null) { return ; } else { return children(item); } } interface AsyncButtonProps { as?: ButtonProps["as"]; variant?: ButtonProps["variant"]; size?: ButtonProps["size"]; className?: string; disabled?: boolean; onChange?: (v: boolean) => void; noReset?: boolean; animation?: boolean; promise: () => Promise | null; onSuccess?: (result: T) => void; error?: () => void; } enum RequestState { Success, Error, Invalid, } export function AsyncButton( props: PropsWithChildren> ): JSX.Element { const { children: propChildren, className, promise, onSuccess, noReset, animation, error, onChange, disabled, ...button } = props; const [loading, setLoading] = useState(false); const [state, setState] = useState(RequestState.Invalid); const needFire = state !== RequestState.Invalid && !noReset; useTimeoutWhen( () => { setState(RequestState.Invalid); }, 2 * 1000, needFire ); const click = useCallback(() => { if (state !== RequestState.Invalid) { return; } const result = promise(); if (result) { setLoading(true); onChange && onChange(true); result .then((res) => { setState(RequestState.Success); onSuccess && onSuccess(res); }) .catch(() => { setState(RequestState.Error); error && error(); }) .finally(() => { setLoading(false); onChange && onChange(false); }); } }, [error, onChange, promise, onSuccess, state]); const showAnimation = animation ?? true; let children = propChildren; if (showAnimation) { if (loading) { children = ; } if (state === RequestState.Success) { children = ; } else if (state === RequestState.Error) { children = ; } } return ( ); }