UNPKG

@magicbell/react-headless

Version:

Hooks to build a notification inbox

104 lines (88 loc) 3.53 kB
import { useCallback, useEffect } from 'react'; import useConfig from '../stores/config/index.js'; import { useNotificationStoresCollection } from '../stores/notifications/index.js'; import { QueryParams } from '../types/INotificationsStoresCollection.js'; import INotificationStore from '../types/INotificationStore.js'; type FetchOptions = Partial<{ reset: boolean; }>; export interface NotificationStore extends INotificationStore { isEmpty: boolean; hasNextPage: boolean; /** * Fetch notifications from the API server. The pagination data is also * updated. By default the array of notifications is not reset. * * @param queryParams Parameters to send to the API. */ fetch: (queryParams?: QueryParams, options?: FetchOptions) => Promise<void>; /** * Fetch the next page of notifications from the API server. The notifications * will be appended to the current list of notifications. * * @param queryParams Parameters to send to the API. */ fetchNextPage: (queryParams?: Omit<QueryParams, 'page' | 'per_page'>, options?: FetchOptions) => Promise<void>; /** * Mark all notifications as seen. Resets the `unseenCount` attribute. * * @param options.persist Mark all notifications as seen in the server * @param options.updateModels Mark all fetched notifications as seen */ markAllAsSeen: (options?: Partial<{ persist: boolean; updateModels: boolean }>) => Promise<boolean>; /** * Mark all notifications as read. Resets the `unreadCount` attribute. * * @param options.persist Mark all notifications as read in the server * @param options.updateModels Mark all fetched notifications as read */ markAllAsRead: (options?: Partial<{ persist: boolean; updateModels: boolean }>) => Promise<boolean>; } /** * Hook to get a notifications store from the stores collection. It fetches the * first page of the store if it is not fetched already. * * @param storeId ID of the notifications store (optional) * * @example * const store = useNotifications('mentions'); */ export default function useNotifications(storeId = 'default'): NotificationStore | null { const { stores, fetchStore, markAllAsSeen, markAllAsRead } = useNotificationStoresCollection(); const config = useConfig(); const store = stores[storeId]; const fetch = useCallback<NotificationStore['fetch']>( (queryParams, options) => fetchStore(storeId, queryParams, options), [fetchStore, storeId], ); const fetchNextPage = useCallback<NotificationStore['fetchNextPage']>( (queryParams = {}, options) => { const page = store.currentPage + 1; return fetchStore(storeId, { ...queryParams, page }, options); }, [fetchStore, storeId, store?.currentPage], ); useEffect(() => { if (!store) return; if (!config.lastFetchedAt || store.lastFetchedAt) return; void fetch({ page: 1 }); }, [config.lastFetchedAt, store, fetch]); const markAllAsReadFn = useCallback<NotificationStore['markAllAsRead']>( (options) => markAllAsRead({ ...options, storeId }), [markAllAsRead, storeId], ); const markAllAsSeenFn = useCallback<NotificationStore['markAllAsSeen']>( (options) => markAllAsSeen({ ...options, storeId }), [markAllAsSeen, storeId], ); if (!store) return null; return { ...store, isEmpty: store.notifications.length === 0, hasNextPage: store.currentPage < store.totalPages, fetch, fetchNextPage, markAllAsSeen: markAllAsSeenFn, markAllAsRead: markAllAsReadFn, }; }