stream-chat-react
Version:
React components to create chat conversations or livestream style chat
124 lines (123 loc) • 5.23 kB
JavaScript
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), []);