1
0
mirror of https://github.com/Sonarr/Sonarr.git synced 2026-04-20 21:54:58 -04:00
Files
Sonarr/frontend/src/Components/Link/SpinnerErrorButton.tsx
T
2025-01-25 19:37:58 -08:00

144 lines
3.3 KiB
TypeScript

import React, { useEffect, useMemo, useRef, useState } from 'react';
import { Error } from 'App/State/AppSectionState';
import Icon, { IconKind, IconName } from 'Components/Icon';
import SpinnerButton, {
SpinnerButtonProps,
} from 'Components/Link/SpinnerButton';
import usePrevious from 'Helpers/Hooks/usePrevious';
import { icons } from 'Helpers/Props';
import { ValidationFailure } from 'typings/pending';
import styles from './SpinnerErrorButton.css';
function getTestResult(error: Error | string | undefined) {
if (!error) {
return {
wasSuccessful: true,
hasWarning: false,
hasError: false,
};
}
if (typeof error === 'string' || error.status !== 400) {
return {
wasSuccessful: false,
hasWarning: false,
hasError: true,
};
}
const failures = error.responseJSON as ValidationFailure[];
const { hasError, hasWarning } = failures.reduce(
(acc, failure) => {
if (failure.isWarning) {
acc.hasWarning = true;
} else {
acc.hasError = true;
}
return acc;
},
{ hasWarning: false, hasError: false }
);
return {
wasSuccessful: false,
hasWarning,
hasError,
};
}
interface SpinnerErrorButtonProps extends SpinnerButtonProps {
isSpinning: boolean;
error?: Error | string;
children: React.ReactNode;
}
function SpinnerErrorButton({
kind,
isSpinning,
error,
children,
...otherProps
}: SpinnerErrorButtonProps) {
const wasSpinning = usePrevious(isSpinning);
const updateTimeout = useRef<ReturnType<typeof setTimeout>>();
const [result, setResult] = useState({
wasSuccessful: false,
hasWarning: false,
hasError: false,
});
const { wasSuccessful, hasWarning, hasError } = result;
const showIcon = wasSuccessful || hasWarning || hasError;
const { iconName, iconKind } = useMemo<{
iconName: IconName;
iconKind: IconKind;
}>(() => {
if (hasWarning) {
return {
iconName: icons.WARNING,
iconKind: 'warning',
};
}
if (hasError) {
return {
iconName: icons.DANGER,
iconKind: 'danger',
};
}
return {
iconName: icons.CHECK,
iconKind: kind === 'primary' ? 'default' : 'success',
};
}, [kind, hasError, hasWarning]);
useEffect(() => {
if (wasSpinning && !isSpinning) {
const testResult = getTestResult(error);
setResult(testResult);
const { wasSuccessful, hasWarning, hasError } = testResult;
if (wasSuccessful || hasWarning || hasError) {
updateTimeout.current = setTimeout(() => {
setResult({
wasSuccessful: false,
hasWarning: false,
hasError: false,
});
}, 3000);
}
}
}, [isSpinning, wasSpinning, error]);
useEffect(() => {
return () => {
if (updateTimeout.current) {
clearTimeout(updateTimeout.current);
}
};
}, []);
return (
<SpinnerButton kind={kind} isSpinning={isSpinning} {...otherProps}>
<span className={showIcon ? styles.showIcon : undefined}>
{showIcon && (
<span className={styles.iconContainer}>
<Icon name={iconName} kind={iconKind} />
</span>
)}
<span className={styles.label}>{children}</span>
</span>
</SpinnerButton>
);
}
export default SpinnerErrorButton;