{"version":3,"file":"index.esm.mjs","sources":["../../../src/addons/use-notification-center/useNotificationCenter.ts"],"sourcesContent":["import { useState, useEffect, useRef } from 'react';\nimport { toast, ToastItem, Id } from 'react-toastify';\n\ntype Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;\n\nexport interface NotificationCenterItem<Data = {}>\n  extends Optional<ToastItem<Data>, 'content' | 'data'> {\n  read: boolean;\n  createdAt: number;\n}\n\nexport type SortFn<Data> = (\n  l: NotificationCenterItem<Data>,\n  r: NotificationCenterItem<Data>\n) => number;\n\nexport type FilterFn<Data = {}> = (\n  item: NotificationCenterItem<Data>\n) => boolean;\n\nexport interface UseNotificationCenterParams<Data = {}> {\n  /**\n   * initial data to rehydrate the notification center\n   */\n  data?: NotificationCenterItem<Data>[];\n\n  /**\n   * By default, the notifications are sorted from the newest to the oldest using\n   * the `createdAt` field. Use this to provide your own sort function\n   *\n   * Usage:\n   * ```\n   * // old notifications first\n   * useNotificationCenter({\n   *   sort: ((l, r) => l.createdAt - r.createdAt)\n   * })\n   * ```\n   */\n  sort?: SortFn<Data>;\n\n  /**\n   * Keep the toast that meets the condition specified in the callback function.\n   *\n   * Usage:\n   * ```\n   * // keep only the toasts when hidden is set to false\n   * useNotificationCenter({\n   *   filter: item => item.data.hidden === false\n   * })\n   * ```\n   */\n  filter?: FilterFn<Data>;\n}\n\nexport interface UseNotificationCenter<Data> {\n  /**\n   * Contains all the notifications\n   */\n  notifications: NotificationCenterItem[];\n\n  /**\n   * Clear all notifications\n   */\n  clear(): void;\n\n  /**\n   * Mark all notification as read\n   */\n  markAllAsRead(): void;\n\n  /**\n   * Mark all notification as read or not.\n   *\n   * Usage:\n   * ```\n   * markAllAsRead(false) // mark all notification as not read\n   *\n   * markAllAsRead(true) // same as calling markAllAsRead()\n   * ```\n   */\n  markAllAsRead(read?: boolean): void;\n\n  /**\n   * Mark one or more notifications as read.\n   *\n   * Usage:\n   * ```\n   * markAsRead(\"anId\")\n   * markAsRead([\"a\",\"list\", \"of\", \"id\"])\n   * ```\n   */\n  markAsRead(id: Id | Id[]): void;\n\n  /**\n   * Mark one or more notifications as read.The second parameter let you mark the notificaiton as read or not.\n   *\n   * Usage:\n   * ```\n   * markAsRead(\"anId\", false)\n   * markAsRead([\"a\",\"list\", \"of\", \"id\"], false)\n   *\n   * markAsRead(\"anId\", true) // same as markAsRead(\"anId\")\n   * ```\n   */\n  markAsRead(id: Id | Id[], read?: boolean): void;\n\n  /**\n   * Remove one or more notifications\n   *\n   * Usage:\n   * ```\n   * remove(\"anId\")\n   * remove([\"a\",\"list\", \"of\", \"id\"])\n   * ```\n   */\n  remove(id: Id | Id[]): void;\n\n  /**\n   * Push a notification to the notification center.\n   * Returns null when an item with the given id already exists\n   *\n   * Usage:\n   * ```\n   * const id = add({id: \"id\", content: \"test\", data: { foo: \"hello\" } })\n   *\n   * // Return the id of the notificaiton, generate one if none provided\n   * const id = add({ data: {title: \"a title\", text: \"some text\"} })\n   * ```\n   */\n  add(item: Partial<NotificationCenterItem<Data>>): Id | null;\n\n  /**\n   * Update the notification that match the id\n   * Returns null when no matching notification found\n   *\n   * Usage:\n   * ```\n   * const id = update(\"anId\",  {content: \"test\", data: { foo: \"hello\" } })\n   *\n   * // It's also possible to update the id\n   * const id = update(\"anId\"m { id:\"anotherOne\", data: {title: \"a title\", text: \"some text\"} })\n   * ```\n   */\n  update(id: Id, item: Partial<NotificationCenterItem<Data>>): Id | null;\n\n  /**\n   * Retrive one or more notifications\n   *\n   * Usage:\n   * ```\n   * find(\"anId\")\n   * find([\"a\",\"list\", \"of\", \"id\"])\n   * ```\n   */\n  find(id: Id): NotificationCenterItem<Data> | undefined;\n\n  /**\n   * Retrive one or more notifications\n   *\n   * Usage:\n   * ```\n   * find(\"anId\")\n   * find([\"a\",\"list\", \"of\", \"id\"])\n   * ```\n   */\n  find(id: Id[]): NotificationCenterItem<Data>[] | undefined;\n\n  /**\n   * Retrieve the count for unread notifications\n   */\n  unreadCount: number;\n\n  /**\n   * Sort notifications using the newly provided function\n   *\n   * Usage:\n   * ```\n   * // old notifications first\n   * sort((l, r) => l.createdAt - r.createdAt)\n   * ```\n   */\n  sort(sort: SortFn<Data>): void;\n}\n\nexport function useNotificationCenter<Data = {}>(\n  params: UseNotificationCenterParams<Data> = {}\n): UseNotificationCenter<Data> {\n  const sortFn = useRef(params.sort || defaultSort);\n  const filterFn = useRef(params.filter || null);\n  const [notifications, setNotifications] = useState<\n    NotificationCenterItem<Data>[]\n  >(() => {\n    if (params.data) {\n      return filterFn.current\n        ? params.data.filter(filterFn.current).sort(sortFn.current)\n        : [...params.data].sort(sortFn.current);\n    }\n    return [];\n  });\n  // used to method to be used inside effect without having stale `notifications`\n  const notificationsRef = useRef(notifications);\n\n  useEffect(() => {\n    notificationsRef.current = notifications;\n  }, [notifications]);\n\n  useEffect(() => {\n    return toast.onChange(toast => {\n      if (toast.status === 'added' || toast.status === 'updated') {\n        const newItem = decorate(toast as NotificationCenterItem<Data>);\n        if (filterFn.current && !filterFn.current(newItem)) return;\n\n        setNotifications(prev => {\n          let nextState: NotificationCenterItem<Data>[] = [];\n          const updateIdx = prev.findIndex(v => v.id === newItem.id);\n\n          if (updateIdx !== -1) {\n            nextState = prev.slice();\n            Object.assign(nextState[updateIdx], newItem, {\n              createdAt: Date.now()\n            });\n          } else if (prev.length === 0) {\n            nextState = [newItem];\n          } else {\n            nextState = [newItem, ...prev];\n          }\n          return nextState.sort(sortFn.current);\n        });\n      }\n    });\n  }, []);\n\n  const remove = (id: Id | Id[]) => {\n    setNotifications(prev =>\n      prev.filter(\n        Array.isArray(id) ? v => !id.includes(v.id) : v => v.id !== id\n      )\n    );\n  };\n\n  const clear = () => {\n    setNotifications([]);\n  };\n\n  const markAllAsRead = (read = true) => {\n    setNotifications(prev =>\n      prev.map(v => {\n        v.read = read;\n        return v;\n      })\n    );\n  };\n\n  const markAsRead = (id: Id | Id[], read = true) => {\n    let map = (v: NotificationCenterItem<Data>) => {\n      if (v.id === id) v.read = read;\n      return v;\n    };\n\n    if (Array.isArray(id)) {\n      map = v => {\n        if (id.includes(v.id)) v.read = read;\n        return v;\n      };\n    }\n\n    setNotifications(prev => prev.map(map));\n  };\n\n  const find = (id: Id | Id[]) => {\n    return Array.isArray(id)\n      ? notificationsRef.current.filter(v => id.includes(v.id))\n      : notificationsRef.current.find(v => v.id === id);\n  };\n\n  const add = (item: Partial<NotificationCenterItem<Data>>) => {\n    if (notificationsRef.current.find(v => v.id === item.id)) return null;\n\n    const newItem = decorate(item);\n\n    setNotifications(prev => [...prev, newItem].sort(sortFn.current));\n\n    return newItem.id;\n  };\n\n  const update = (id: Id, item: Partial<NotificationCenterItem<Data>>) => {\n    const index = notificationsRef.current.findIndex(v => v.id === id);\n\n    if (index !== -1) {\n      setNotifications(prev => {\n        const nextState = [...prev];\n        Object.assign(nextState[index], item, {\n          createdAt: item.createdAt || Date.now()\n        });\n\n        return nextState.sort(sortFn.current);\n      });\n\n      return item.id as Id;\n    }\n\n    return null;\n  };\n\n  const sort = (compareFn: SortFn<Data>) => {\n    sortFn.current = compareFn;\n    setNotifications(prev => prev.slice().sort(compareFn));\n  };\n\n  return {\n    notifications,\n    clear,\n    markAllAsRead,\n    markAsRead,\n    add,\n    update,\n    remove,\n    // @ts-ignore fixme: overloading issue\n    find,\n    sort,\n    get unreadCount() {\n      return notifications.reduce(\n        (prev, cur) => (!cur.read ? prev + 1 : prev),\n        0\n      );\n    }\n  };\n}\n\nfunction decorate<Data>(\n  item: NotificationCenterItem<Data> | Partial<NotificationCenterItem<Data>>\n) {\n  if (item.id == null) item.id = Date.now().toString(36).substring(2, 9);\n  if (!item.createdAt) item.createdAt = Date.now();\n  if (item.read == null) item.read = false;\n  return item as NotificationCenterItem<Data>;\n}\n\n// newest to oldest\nfunction defaultSort(l: NotificationCenterItem, r: NotificationCenterItem) {\n  return r.createdAt - l.createdAt;\n}\n"],"names":["useNotificationCenter","params","sortFn","useRef","sort","defaultSort","filterFn","filter","notifications","setNotifications","useState","data","current","notificationsRef","useEffect","toast","onChange","status","newItem","decorate","prev","nextState","updateIdx","findIndex","v","id","slice","Object","assign","createdAt","Date","now","length","clear","markAllAsRead","read","map","markAsRead","Array","isArray","includes","add","item","find","update","index","remove","compareFn","unreadCount","reduce","cur","toString","substring","l","r"],"mappings":"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"}