import { useCallback, useEffect, useRef, type ReactNode } from 'react'; import { useLocation, useSearchParams } from 'react-router'; import { toast } from 'react-toastify/unstyled'; import { SearchParamKeys } from '../lib/constants'; import { CODE_TO_MESSAGE_MAP, QueryMessageCode, QueryMessageType } from '../lib/QueryMessages'; type QueryMessageString = `${QueryMessageCode}__${QueryMessageType}`; export type QueryMessage = { type: QueryMessageType; code: QueryMessageCode; message: ReactNode; }; export type UseQueryMessageOptions = { displayMessages?: boolean; }; export type UseQueryMessageReturn = { setQueryMessage: (messageCode: QueryMessageCode, messageType: QueryMessageType) => void; clearQueryMessage: () => void; toSearchParamQueryMessage: (message: QueryMessageCode, type: QueryMessageType) => QueryMessageString; }; export function useQueryMessage( { displayMessages }: UseQueryMessageOptions = { displayMessages: true, } ): UseQueryMessageReturn { const location = useLocation(); const [searchParams, setSearchParams] = useSearchParams(); const messageStr = useRef(null); useEffect(() => { // Reset messageStr when location changes to allow re-displaying the same message on navigation messageStr.current = null; }, [location.pathname]); useEffect(() => { const queryMessageStr = searchParams.get(SearchParamKeys.Message); if (!(queryMessageStr && queryMessageStr !== messageStr.current)) return; const [queryMessage, queryMessageString] = toQueryMessage(queryMessageStr) ?? [null, null]; if (!queryMessage) return; messageStr.current = queryMessageString; if (displayMessages) { toast[queryMessage.type](queryMessage.code, { position: 'top-center', autoClose: 5000, hideProgressBar: false, closeOnClick: true, pauseOnHover: true, draggable: false, progress: undefined, theme: 'colored', toastId: 'login-route-info-message', }); } }, [displayMessages, searchParams]); const setQueryMessage = useCallback( (messageCode: QueryMessageCode, messageType: QueryMessageType) => { const queryMessageString: QueryMessageString = `${messageCode}__${messageType}`; messageStr.current = queryMessageString; setSearchParams((prev) => { prev.set(SearchParamKeys.Message, queryMessageString); return prev; }); }, [setSearchParams] ); const clearQueryMessage = useCallback(() => { messageStr.current = null; setSearchParams((prev) => { prev.delete(SearchParamKeys.Message); return prev; }); }, [setSearchParams]); const toSearchParamQueryMessage = useCallback((message: QueryMessageCode, type: QueryMessageType): QueryMessageString => { return `${message}__${type}`; }, []); return { setQueryMessage, clearQueryMessage, toSearchParamQueryMessage, }; } function isValidQueryMessageCode(code: string): code is QueryMessageCode { return Object.values(QueryMessageCode).includes(code as QueryMessageCode); } function isValidQueryMessageType(type: string): type is QueryMessageType { return Object.values(QueryMessageType).includes(type as QueryMessageType); } function toQueryMessage(value: string): [QueryMessage, QueryMessageString] | null { const [code, type] = value.split('__'); if (!isValidQueryMessageCode(code) || !isValidQueryMessageType(type)) return null; return [ { code: code, type: type, message: CODE_TO_MESSAGE_MAP[code], }, `${code}__${type}`, ]; }