Use our own timers to dismiss alerts (#3641)
This commit is contained in:
parent
66a448720a
commit
2f60e78c4f
2 changed files with 42 additions and 36 deletions
|
@ -4,10 +4,10 @@ import {
|
||||||
AlertActionCloseButton,
|
AlertActionCloseButton,
|
||||||
AlertVariant,
|
AlertVariant,
|
||||||
} from "@patternfly/react-core";
|
} from "@patternfly/react-core";
|
||||||
import type { AlertType } from "./Alerts";
|
import type { AlertEntry } from "./Alerts";
|
||||||
|
|
||||||
type AlertPanelProps = {
|
type AlertPanelProps = {
|
||||||
alerts: AlertType[];
|
alerts: AlertEntry[];
|
||||||
onCloseAlert: (id: number) => void;
|
onCloseAlert: (id: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -27,8 +27,6 @@ export function AlertPanel({ alerts, onCloseAlert }: AlertPanelProps) {
|
||||||
onClose={() => onCloseAlert(id)}
|
onClose={() => onCloseAlert(id)}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
timeout
|
|
||||||
onTimeout={() => onCloseAlert(id)}
|
|
||||||
>
|
>
|
||||||
{description && <p>{description}</p>}
|
{description && <p>{description}</p>}
|
||||||
</Alert>
|
</Alert>
|
||||||
|
|
|
@ -1,22 +1,25 @@
|
||||||
import { FunctionComponent, useState } from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { AlertVariant } from "@patternfly/react-core";
|
import { AlertVariant } from "@patternfly/react-core";
|
||||||
import axios from "axios";
|
|
||||||
import type { AxiosError } from "axios";
|
import type { AxiosError } from "axios";
|
||||||
|
import axios from "axios";
|
||||||
|
import { FunctionComponent, useCallback, useMemo, useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { createNamedContext } from "../../utils/createNamedContext";
|
import { createNamedContext } from "../../utils/createNamedContext";
|
||||||
import useRequiredContext from "../../utils/useRequiredContext";
|
import useRequiredContext from "../../utils/useRequiredContext";
|
||||||
|
import useSetTimeout from "../../utils/useSetTimeout";
|
||||||
import { AlertPanel } from "./AlertPanel";
|
import { AlertPanel } from "./AlertPanel";
|
||||||
|
|
||||||
|
const ALERT_TIMEOUT = 8000;
|
||||||
|
|
||||||
export type AddAlertFunction = (
|
export type AddAlertFunction = (
|
||||||
message: string,
|
message: string,
|
||||||
variant?: AlertVariant,
|
variant?: AlertVariant,
|
||||||
description?: string
|
description?: string
|
||||||
) => void;
|
) => void;
|
||||||
|
|
||||||
export type AddErrorFunction = (message: string, error: any) => void;
|
export type AddErrorFunction = (message: string, error: unknown) => void;
|
||||||
|
|
||||||
type AlertProps = {
|
export type AlertProps = {
|
||||||
addAlert: AddAlertFunction;
|
addAlert: AddAlertFunction;
|
||||||
addError: AddErrorFunction;
|
addError: AddErrorFunction;
|
||||||
};
|
};
|
||||||
|
@ -28,7 +31,7 @@ export const AlertContext = createNamedContext<AlertProps | undefined>(
|
||||||
|
|
||||||
export const useAlerts = () => useRequiredContext(AlertContext);
|
export const useAlerts = () => useRequiredContext(AlertContext);
|
||||||
|
|
||||||
export type AlertType = {
|
export type AlertEntry = {
|
||||||
id: number;
|
id: number;
|
||||||
message: string;
|
message: string;
|
||||||
variant: AlertVariant;
|
variant: AlertVariant;
|
||||||
|
@ -37,65 +40,70 @@ export type AlertType = {
|
||||||
|
|
||||||
export const AlertProvider: FunctionComponent = ({ children }) => {
|
export const AlertProvider: FunctionComponent = ({ children }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [alerts, setAlerts] = useState<AlertType[]>([]);
|
const setTimeout = useSetTimeout();
|
||||||
|
const [alerts, setAlerts] = useState<AlertEntry[]>([]);
|
||||||
|
|
||||||
const hideAlert = (id: number) => {
|
const removeAlert = (id: number) =>
|
||||||
setAlerts((alerts) => alerts.filter((alert) => alert.id !== id));
|
setAlerts((alerts) => alerts.filter((alert) => alert.id !== id));
|
||||||
};
|
|
||||||
|
|
||||||
const addAlert = (
|
const addAlert = useCallback<AddAlertFunction>(
|
||||||
message: string,
|
(message, variant = AlertVariant.success, description) => {
|
||||||
variant: AlertVariant = AlertVariant.success,
|
const alert: AlertEntry = {
|
||||||
description?: string
|
|
||||||
) => {
|
|
||||||
setAlerts([
|
|
||||||
{
|
|
||||||
id: Math.random(),
|
id: Math.random(),
|
||||||
message,
|
message,
|
||||||
variant,
|
variant,
|
||||||
description,
|
description,
|
||||||
},
|
};
|
||||||
...alerts,
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const addError = (message: string, error: Error | AxiosError | string) => {
|
setAlerts((alerts) => [alert, ...alerts]);
|
||||||
|
setTimeout(() => removeAlert(alert.id), ALERT_TIMEOUT);
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const addError = useCallback<AddErrorFunction>((message, error) => {
|
||||||
addAlert(
|
addAlert(
|
||||||
t(message, {
|
t(message, {
|
||||||
error: getErrorMessage(error),
|
error: getErrorMessage(error),
|
||||||
}),
|
}),
|
||||||
AlertVariant.danger
|
AlertVariant.danger
|
||||||
);
|
);
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
|
const value = useMemo(() => ({ addAlert, addError }), []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AlertContext.Provider value={{ addAlert, addError }}>
|
<AlertContext.Provider value={value}>
|
||||||
<AlertPanel alerts={alerts} onCloseAlert={hideAlert} />
|
<AlertPanel alerts={alerts} onCloseAlert={removeAlert} />
|
||||||
{children}
|
{children}
|
||||||
</AlertContext.Provider>
|
</AlertContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function getErrorMessage(
|
function getErrorMessage(error: unknown) {
|
||||||
error: Error | AxiosError<Record<string, unknown>> | string
|
|
||||||
) {
|
|
||||||
if (typeof error === "string") {
|
if (typeof error === "string") {
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!axios.isAxiosError(error)) {
|
if (axios.isAxiosError(error)) {
|
||||||
|
return getErrorMessageAxios(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error instanceof Error) {
|
||||||
return error.message;
|
return error.message;
|
||||||
}
|
}
|
||||||
|
|
||||||
const responseData = (error.response?.data ?? {}) as Record<string, unknown>;
|
throw new Error("Unable to determine error message.");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getErrorMessageAxios(error: AxiosError) {
|
||||||
|
const data = (error.response?.data ?? {}) as Record<string, unknown>;
|
||||||
|
|
||||||
for (const key of ["error_description", "errorMessage", "error"]) {
|
for (const key of ["error_description", "errorMessage", "error"]) {
|
||||||
const value = responseData[key];
|
const value = data[key];
|
||||||
|
|
||||||
if (typeof value === "string") {
|
if (typeof value === "string") {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return error.message;
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue