import { CloseRounded } from "@mui/icons-material"; import { Snackbar, Alert, IconButton, Box, type SnackbarOrigin, } from "@mui/material"; import React, { useMemo, useSyncExternalStore } from "react"; import { useTranslation } from "react-i18next"; import { subscribeNotices, hideNotice, getSnapshotNotices, } from "@/services/notice-service"; import type { TranslationKey } from "@/types/generated/i18n-keys"; type NoticePosition = NonNullable; type NoticeItem = ReturnType[number]; type TranslationFn = ReturnType["t"]; const VALID_POSITIONS: NoticePosition[] = [ "top-left", "top-right", "bottom-left", "bottom-right", ]; const resolvePosition = (position?: NoticePosition | null): NoticePosition => { if (position && VALID_POSITIONS.includes(position)) { return position; } return "top-right"; }; const getAnchorOrigin = (position: NoticePosition): SnackbarOrigin => { const [vertical, horizontal] = position.split("-") as [ SnackbarOrigin["vertical"], SnackbarOrigin["horizontal"], ]; return { vertical, horizontal }; }; const resolveNoticeMessage = ( notice: NoticeItem, t: TranslationFn, ): React.ReactNode => { const i18n = notice.i18n; if (!i18n) return notice.message; const params = (i18n.params ?? {}) as Record; const { prefixKey, prefixParams, prefix, message, ...restParams } = params; const prefixKeyParams = prefixParams && typeof prefixParams === "object" ? (prefixParams as Record) : undefined; const resolvedPrefix = typeof prefixKey === "string" ? t(prefixKey as TranslationKey, { defaultValue: prefixKey, ...(prefixKeyParams ?? {}), ...restParams, }) : typeof prefix === "string" ? prefix : undefined; const messageStr = typeof message === "string" ? message : undefined; const defaultValue = messageStr === undefined ? undefined : resolvedPrefix ? `${resolvedPrefix} ${messageStr}` : messageStr; return t(i18n.key as TranslationKey, { defaultValue, ...restParams, ...(resolvedPrefix !== undefined ? { prefix: resolvedPrefix } : {}), ...(messageStr !== undefined ? { message: messageStr } : {}), }); }; interface NoticeManagerProps { position?: NoticePosition | null; } export const NoticeManager: React.FC = ({ position }) => { const { t } = useTranslation(); const resolvedPosition = useMemo(() => resolvePosition(position), [position]); const anchorOrigin = useMemo( () => getAnchorOrigin(resolvedPosition), [resolvedPosition], ); const currentNotices = useSyncExternalStore( subscribeNotices, getSnapshotNotices, ); const handleClose = (id: number) => { hideNotice(id); }; return ( {currentNotices.map((notice) => ( handleClose(notice.id)} > } > {resolveNoticeMessage(notice, t)} ))} ); };