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';
|
2025-08-14 04:19:50 +08:00
|
|
|
// import { ConfirmModal } from '@/components/ui/modal/ConfirmModal';
|
2025-08-13 01:22:17 +08:00
|
|
|
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 />
|
2025-08-14 04:19:50 +08:00
|
|
|
{/* <ConfirmModal {...confirmModalProps} /> */}
|
2025-08-13 01:22:17 +08:00
|
|
|
</BusterNotifications.Provider>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
const useBusterNotificationsSelector = <T,>(selector: (state: BusterNotificationsContext) => T) => {
|
|
|
|
return useContextSelector(BusterNotifications, selector);
|
|
|
|
};
|
|
|
|
|
|
|
|
export const useBusterNotifications = () => {
|
|
|
|
return useBusterNotificationsSelector((state) => state);
|
|
|
|
};
|
|
|
|
|
|
|
|
export { openErrorNotification };
|