Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

To Anyone Needing Notification Component #615

Open
BarishSarac opened this issue Dec 10, 2024 · 1 comment
Open

To Anyone Needing Notification Component #615

BarishSarac opened this issue Dec 10, 2024 · 1 comment

Comments

@BarishSarac
Copy link

BarishSarac commented Dec 10, 2024

It seems like our beloved Notistack is not maintained for a long time, bug correction-wise it's a problematic but worse yet when I upgraded to react v19 and this package still on the v18. I worked with AI to generate a perfect notification system that works for me, so sharing that with anyone who needs one. Modify to the way you like.

layout.tsx

  const notificationOptions = {
    maxNotifications: 5, // Show up to 5 notifications
    autoHideDuration: 5000, // Auto-hide after 5 seconds
    preventDuplicate: true, // Allow duplicate notifications
  };
  
  return(
    <NotificationProvider settings={notificationOptions}>
        <App />
    </NotificationProvider>
  )

notification-provider.tsx


import React, { createContext, useContext, useState, ReactNode } from "react";
import Notification from "./notification";

// Default configuration for the notification system
const defaultNotificationOptions = {
  maxNotifications: 3, // Maximum number of notifications to display at once
  autoHideDuration: 5000, // Auto-hide duration in milliseconds
  preventDuplicate: true, // Prevent duplicate notifications
};

interface NotificationProviderProps {
  children: ReactNode;
  settings?: Partial<typeof defaultNotificationOptions>;
}

interface Notification {
  id: string;
  message: string;
  type: "success" | "error" | "info" | "warning";
  timer?: NodeJS.Timeout; // Timer reference for auto-hide
}

interface NotificationContextType {
  addNotification: (
    message: string,
    type: "success" | "error" | "info" | "warning"
  ) => void;
  removeNotification: (id: string) => void;
  clearAllNotifications: () => void; // Clear all notifications
}

const NotificationContext = createContext<NotificationContextType | null>(null);

export const useNotifications = () => {
  const context = useContext(NotificationContext);
  if (!context) {
    throw new Error(
      "useNotifications must be used within a NotificationProvider"
    );
  }
  return context;
};

export const NotificationProvider: React.FC<NotificationProviderProps> = ({
  children,
  settings = {},
}) => {
  const notificationSettings = { ...defaultNotificationOptions, ...settings };

  const [notifications, setNotifications] = useState<Notification[]>([]);

  const addNotification = (
    message: string,
    type: "success" | "error" | "info" | "warning"
  ) => {
    setNotifications((prev) => {
      // Prevent duplicates if enabled
      if (notificationSettings.preventDuplicate) {
        const isDuplicate = prev.some(
          (n) => n.message === message && n.type === type
        );
        if (isDuplicate) return prev;
      }

      const id = Date.now().toString();

      // Create a new notification
      const newNotification: Notification = { id, message, type };

      // Add the notification and limit to maxNotifications
      const updated = [...prev, newNotification];
      if (updated.length > notificationSettings.maxNotifications) {
        const oldest = updated.shift(); // Remove the oldest notification
        if (oldest?.timer) clearTimeout(oldest.timer); // Clear its timer
      }

      // Set a timer to auto-remove the notification after the specified duration
      newNotification.timer = setTimeout(() => {
        removeNotification(id);
      }, notificationSettings.autoHideDuration);

      return updated;
    });
  };

  const removeNotification = (id: string) => {
    setNotifications((prev) => {
      const notificationToRemove = prev.find((n) => n.id === id);
      if (notificationToRemove?.timer) clearTimeout(notificationToRemove.timer); // Clear its timer
      return prev.filter((n) => n.id !== id);
    });
  };

  const clearAllNotifications = () => {
    setNotifications((prev) => {
      // Clear timers for all notifications
      prev.forEach((n) => {
        if (n.timer) clearTimeout(n.timer);
      });
      return []; // Reset the notifications array
    });
  };

  return (
    <NotificationContext.Provider
      value={{ addNotification, removeNotification, clearAllNotifications }}
    >
      {children}
      <div className="fixed bottom-5 right-5 flex flex-col items-end space-y-4 z-50">
        {notifications.map((notif) => (
          <Notification
            key={notif.id}
            message={notif.message}
            type={notif.type}
            onClose={() => removeNotification(notif.id)}
          />
        ))}
      </div>
    </NotificationContext.Provider>
  );
};

notification.tsx


interface NotificationProps {
  message: string;
  type?: "success" | "error" | "info" | "warning";
  onClose: () => void;
}

const Notification: React.FC<NotificationProps> = ({
  message,
  type = "info",
  onClose,
}) => {
  useEffect(() => {
    // No additional timer logic needed here; it's managed by the provider
    return () => {};
  }, []);

  const typeStyles = {
    success: "bg-green-500 text-white",
    error: "bg-red-500 text-white",
    info: "bg-blue-500 text-white",
    warning: "bg-yellow-500 text-black",
  };

  const icons = {
    success: "✔️", // Replace with a checkmark SVG or icon
    error: "❌", // Replace with an error SVG or icon
    info: "ℹ️", // Replace with an info SVG or icon
    warning: "⚠️", // Replace with a warning SVG or icon
  };

  return (
    <div
      className={`w-80 px-4 py-2 rounded shadow-lg flex items-center space-x-4 ${typeStyles[type]} z-50`}
    >
      <span className="text-xl">{icons[type]}</span>
      <span>{message}</span>
      <button
        className="ml-auto text-white focus:outline-none"
        onClick={onClose}
      >
        ✕
      </button>
    </div>
  );
};

export default Notification;
@Ben-CA
Copy link

Ben-CA commented Dec 27, 2024

Thanks for sharing; have you considered setting this up with a repo and adding to npm?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants