UNPKG

stream-chat-react

Version:

React components to create chat conversations or livestream style chat

124 lines (123 loc) 5.23 kB
import React, { useContext, useEffect, useMemo, useState, } from 'react'; import { StateStore } from 'stream-chat'; import { DialogManager } from '../components/Dialog/DialogManager'; import { DialogPortalDestination } from '../components/Dialog/DialogPortal'; const dialogManagersRegistry = new StateStore({}); const getDialogManager = (id) => dialogManagersRegistry.getLatestValue()[id]; const getOrCreateDialogManager = (id) => { let manager = getDialogManager(id); if (!manager) { manager = new DialogManager({ id }); dialogManagersRegistry.partialNext({ [id]: manager }); } return manager; }; const removeDialogManager = (id) => { if (!getDialogManager(id)) return; dialogManagersRegistry.partialNext({ [id]: undefined }); }; const DialogManagerProviderContext = React.createContext(undefined); /** * Marks the portal location * @param children * @param id * @constructor */ export const DialogManagerProvider = ({ children, id, }) => { const [dialogManager, setDialogManager] = useState(() => { if (id) return getDialogManager(id) ?? null; return new DialogManager(); // will not be included in the registry }); useEffect(() => { if (!id) return; setDialogManager(getOrCreateDialogManager(id)); return () => { removeDialogManager(id); setDialogManager(null); }; }, [id]); // temporarily do not render until a new dialog manager is created if (!dialogManager) return null; return (React.createElement(DialogManagerProviderContext.Provider, { value: { dialogManager } }, children, React.createElement(DialogPortalDestination, null))); }; const getManagerFromStore = ({ dialogId, dialogManagerId, newState, previousState, }) => { let managerInNewState; let managerInPrevState; if (dialogManagerId) { if (!dialogId) { managerInNewState = newState[dialogManagerId]; managerInPrevState = previousState?.[dialogManagerId]; } else { if (newState[dialogManagerId]?.get(dialogId)) { managerInNewState = newState[dialogManagerId]; } if (previousState?.[dialogManagerId]?.get(dialogId)) { managerInPrevState = previousState[dialogManagerId]; } } } else if (dialogId) { managerInNewState = Object.values(newState).find((dialogMng) => dialogId && dialogMng?.get(dialogId)); managerInPrevState = previousState && Object.values(previousState).find((dialogMng) => dialogId && dialogMng?.get(dialogId)); } return { managerInNewState, managerInPrevState }; }; /** * Retrieves the nearest dialog manager or searches for the dialog manager by dialog manager id or dialog id. * Dialog id will take precedence over dialog manager id if both are provided and dialog manager is found by dialog id. */ export const useDialogManager = ({ dialogId, dialogManagerId, } = {}) => { const nearestDialogManagerContext = useContext(DialogManagerProviderContext); const [dialogManagerContext, setDialogManagerContext] = useState(() => { const { managerInNewState } = getManagerFromStore({ dialogId, dialogManagerId, newState: dialogManagersRegistry.getLatestValue(), previousState: undefined, }); return managerInNewState ? { dialogManager: managerInNewState } : nearestDialogManagerContext; }); useEffect(() => { if (!dialogId && !dialogManagerId) return; const unsubscribe = dialogManagersRegistry.subscribeWithSelector((state) => state, (newState, previousState) => { const { managerInNewState, managerInPrevState } = getManagerFromStore({ dialogId, dialogManagerId, newState, previousState, }); if (!managerInPrevState || managerInNewState?.id !== managerInPrevState.id) { setDialogManagerContext((prevState) => { if (prevState?.dialogManager.id === managerInNewState?.id) return prevState; // fixme: need to handle the possibility that the dialogManager is undefined return { dialogManager: managerInNewState || nearestDialogManagerContext?.dialogManager, }; }); } }); return () => { unsubscribe(); }; }, [dialogId, dialogManagerId, nearestDialogManagerContext?.dialogManager]); if (!dialogManagerContext?.dialogManager) { console.warn(`Dialog manager (manager id: ${dialogManagerId}, dialog id: ${dialogId}) is not available`); } return dialogManagerContext; }; export const modalDialogManagerId = 'modal-dialog-manager'; export const ModalDialogManagerProvider = ({ children }) => (React.createElement(DialogManagerProvider, { id: modalDialogManagerId }, children)); export const useModalDialogManager = () => useMemo(() => getDialogManager(modalDialogManagerId), []);