buster/apps/web-tss/src/context/BusterNotifications/BusterNotifications.tsx

151 lines
4.0 KiB
TypeScript
Raw Normal View History

2025-08-13 01:22:17 +08:00
'use client';
import type React from 'react';
import type { PropsWithChildren } from 'react';
import { type ExternalToast, toast } from 'sonner';
import { createContext, useContextSelector } from 'use-context-selector';
import { ConfirmModal } from '@/components/ui/modal/ConfirmModal';
import { Toaster } from '@/components/ui/toaster/Toaster';
import { useOpenConfirmModal } from './useConfirmModal';
export type NotificationType = 'success' | 'info' | 'warning' | 'error';
export interface NotificationProps {
type?: NotificationType;
title?: string;
message?: string;
duration?: number;
action?: {
label: string;
onClick: () => void | (() => Promise<void>);
};
}
const openNotification = (props: NotificationProps) => {
const { title, message, type } = props;
const hasTitle = !!title;
const toastOptions: ExternalToast = {
...props,
2025-08-13 11:54:24 +08:00
description: !hasTitle && message ? message : message,
2025-08-13 01:22:17 +08:00
};
switch (type) {
case 'success':
return toast.success(title, toastOptions);
case 'info':
return toast.info(title, toastOptions);
case 'warning':
return toast.warning(title, toastOptions);
case 'error':
return toast.error(title, toastOptions);
default:
return toast(title, toastOptions);
}
};
const openErrorNotification = (data: NotificationProps | unknown) => {
const values = typeof data === 'object' && data !== null ? (data as NotificationProps) : {};
const type = values.type || 'error';
const title = values.title || 'Error';
const message = values.message || 'Something went wrong. Please try again.';
return openNotification({ ...values, message, title, type });
};
const openInfoNotification = ({
type = 'info',
message = 'Info',
title = 'Info',
...props
}: NotificationProps) => {
return openNotification({ ...props, title, message, type });
};
const openSuccessNotification = ({
type = 'success',
title = 'Success',
message = 'success',
...props
}: NotificationProps) => {
return openNotification({ ...props, message, title, type });
};
const openWarningNotification = ({
type = 'warning',
title = 'Warning',
message = 'Warning',
...props
}: NotificationProps) => {
return openNotification({ ...props, message, title, type });
};
const openMessage = (props: {
type: NotificationType;
message: string;
onClose?: () => void;
duration?: number;
}) => {
return openNotification({
...props,
title: props.message,
2025-08-13 11:54:24 +08:00
message: '',
2025-08-13 01:22:17 +08:00
});
};
const openErrorMessage = (message: string) => {
return openMessage({ type: 'error', message });
};
const openInfoMessage = (message: string, duration?: number) => {
return openMessage({ type: 'info', message, duration });
};
const openSuccessMessage = (message: string) => {
return openMessage({ type: 'success', message });
};
const useBusterNotificationsInternal = () => {
return {
openErrorNotification,
openInfoNotification,
openSuccessNotification,
openWarningNotification,
openErrorMessage,
openInfoMessage,
openSuccessMessage,
2025-08-13 11:54:24 +08:00
openNotification,
2025-08-13 01:22:17 +08:00
};
};
type BusterNotificationsContext = ReturnType<typeof useBusterNotificationsInternal> & {
openConfirmModal: ReturnType<typeof useOpenConfirmModal>['openConfirmModal'];
};
const BusterNotifications = createContext<BusterNotificationsContext>(
{} as BusterNotificationsContext
);
export const BusterNotificationsProvider: React.FC<PropsWithChildren> = ({ children }) => {
const { openConfirmModal, confirmModalProps } = useOpenConfirmModal();
const value = useBusterNotificationsInternal();
return (
<BusterNotifications.Provider value={{ ...value, openConfirmModal }}>
{children}
<Toaster />
<ConfirmModal {...confirmModalProps} />
</BusterNotifications.Provider>
);
};
const useBusterNotificationsSelector = <T,>(selector: (state: BusterNotificationsContext) => T) => {
return useContextSelector(BusterNotifications, selector);
};
export const useBusterNotifications = () => {
return useBusterNotificationsSelector((state) => state);
};
export { openErrorNotification };