mirror of
https://github.com/fleetdm/fleet
synced 2026-05-22 00:18:27 +00:00
## #22661  - Update `FlashMessage` behavior to, by default, hide itself when the user performs any URL-changing navigation - Add `persistOnPageChange` option to `renderFlash` API and associated notification context and reducer logic, allowing override of this behavior on a per-call basis - Ensure proper order of evaluation of URL changes and render flash action dispatches on the event loop - Clean up legacy unused "undo"-related arguments and logic - Allow the user to click in the same horizontal dimension as a flash message - Other misc. cleanup and refactoring [Demo - messages hidden on page (any URL) change](https://www.loom.com/share/1e884b6ba11c4b59bc74f51df3690131?sid=9b53e78b-6535-4541-b676-377760366cf4) - [x] Changes file added for user-visible changes in `changes/`, - [x] Manual QA for all new/changed functionality --------- Co-authored-by: Jacob Shandling <jacob@fleetdm.com>
106 lines
2.4 KiB
TypeScript
106 lines
2.4 KiB
TypeScript
import React, {
|
|
createContext,
|
|
useReducer,
|
|
ReactNode,
|
|
useCallback,
|
|
useMemo,
|
|
} from "react";
|
|
import { INotification } from "interfaces/notification";
|
|
import { noop } from "lodash";
|
|
|
|
type Props = {
|
|
children: ReactNode;
|
|
};
|
|
|
|
type InitialStateType = {
|
|
notification: INotification | null;
|
|
renderFlash: (
|
|
alertType: "success" | "error" | "warning-filled" | null,
|
|
message: JSX.Element | string | null,
|
|
options?: { persistOnPageChange?: boolean }
|
|
) => void;
|
|
hideFlash: () => void;
|
|
};
|
|
|
|
export type INotificationContext = InitialStateType;
|
|
|
|
const initialState = {
|
|
notification: null,
|
|
renderFlash: noop,
|
|
hideFlash: noop,
|
|
};
|
|
|
|
const actionTypes = {
|
|
RENDER_FLASH: "RENDER_FLASH",
|
|
HIDE_FLASH: "HIDE_FLASH",
|
|
} as const;
|
|
|
|
const reducer = (state: any, action: any) => {
|
|
switch (action.type) {
|
|
case actionTypes.RENDER_FLASH:
|
|
return {
|
|
...state,
|
|
notification: {
|
|
alertType: action.alertType,
|
|
isVisible: true,
|
|
message: action.message,
|
|
persistOnPageChange: action.options?.persistOnPageChange ?? false,
|
|
},
|
|
};
|
|
case actionTypes.HIDE_FLASH:
|
|
return initialState;
|
|
default:
|
|
return state;
|
|
}
|
|
};
|
|
|
|
export const NotificationContext = createContext<InitialStateType>(
|
|
initialState
|
|
);
|
|
|
|
const NotificationProvider = ({ children }: Props) => {
|
|
const [state, dispatch] = useReducer(reducer, initialState);
|
|
const renderFlash = useCallback(
|
|
(
|
|
alertType: "success" | "error" | "warning-filled" | null,
|
|
message: JSX.Element | string | null,
|
|
options?: {
|
|
persistOnPageChange?: boolean;
|
|
}
|
|
) => {
|
|
// wrapping the dispatch in a timeout ensures it is evaluated on the next event loop,
|
|
// preventing bugs related to the FlashMessage's self-hiding behavior on URL changes.
|
|
// react router v3 router.push is asynchronous
|
|
setTimeout(() => {
|
|
dispatch({
|
|
type: actionTypes.RENDER_FLASH,
|
|
alertType,
|
|
message,
|
|
options,
|
|
});
|
|
});
|
|
},
|
|
[]
|
|
);
|
|
|
|
const hideFlash = useCallback(() => {
|
|
dispatch({ type: actionTypes.HIDE_FLASH });
|
|
}, []);
|
|
|
|
const value = useMemo(
|
|
() => ({
|
|
notification: state.notification,
|
|
renderFlash,
|
|
hideFlash,
|
|
}),
|
|
[state.notification, renderFlash, hideFlash]
|
|
);
|
|
|
|
return (
|
|
<NotificationContext.Provider value={value}>
|
|
{children}
|
|
</NotificationContext.Provider>
|
|
);
|
|
};
|
|
|
|
export default NotificationProvider;
|