import { createContext, Suspense, useCallback, useContext, useEffect, useRef, useState } from 'react' import PropTypes from 'prop-types' import { Modal, Spin, Flex } from 'antd' import { LoadingOutlined } from '@ant-design/icons' import { useLocation, useNavigate } from 'react-router-dom' import { getModelByName } from '../../../database/ObjectModels' import { buildActionUrl, findAction, getObjectIdFromSearch, resolveAction, stripModalActionParams } from '../../../utils/modelActions' import { AuthContext } from './AuthContext' const ActionsContext = createContext() const ActionsProvider = ({ children }) => { const navigate = useNavigate() const location = useLocation() const { userProfile } = useContext(AuthContext) const [currentObject, setCurrentObject] = useState(null) const [currentObjectType, setCurrentObjectType] = useState(null) const [modalAction, setModalAction] = useState(null) const [onModalOk, setOnModalOk] = useState(null) const lastHandledAction = useRef(null) const searchParams = new URLSearchParams(location.search) const actionName = searchParams.get('action') const actionObjectType = searchParams.get('actionObjectType') const clearAction = useCallback(() => { const nextUrl = stripModalActionParams(location.pathname, location.search) if (nextUrl !== location.pathname + location.search) { navigate(nextUrl, { replace: true }) } setModalAction(null) lastHandledAction.current = null }, [location.pathname, location.search, navigate]) const handleModalOk = useCallback(() => { onModalOk?.() clearAction() }, [clearAction, onModalOk]) const callAction = useCallback( (action, objectData, objectType) => { setCurrentObject(objectData) setCurrentObjectType(objectType) const model = getModelByName(objectType) const url = buildActionUrl( model, action, objectData._id, location.pathname, location.search ) navigate(url) }, [location.pathname, location.search, navigate] ) useEffect(() => { if (!actionObjectType) return const model = getModelByName(actionObjectType) if (!model.pages?.length) return const onPage = model.pages.some( (page) => location.pathname === `${model.url}/${page.name}` ) if (!onPage) return if (actionName) { const action = findAction(model, actionName) if (action?.type === 'modal' || action?.type === 'alias') return } const params = new URLSearchParams(location.search) params.delete('actionObjectType') const query = params.toString() const nextUrl = location.pathname + (query ? `?${query}` : '') if (nextUrl !== location.pathname + location.search) { navigate(nextUrl, { replace: true }) } }, [ actionName, actionObjectType, location.pathname, location.search, navigate ]) useEffect(() => { if (!actionName || !actionObjectType) { setModalAction(null) lastHandledAction.current = null return } const actionKey = `${actionObjectType}:${actionName}:${location.pathname}${location.search}` if (lastHandledAction.current === actionKey) return const model = getModelByName(actionObjectType) const action = findAction(model, actionName) if (!action) return if (action.type === 'modal' || action.type === 'alias') { lastHandledAction.current = actionKey setModalAction(action) return } if (action.type === 'page') { const pageName = action.pageName || action.name const objectId = getObjectIdFromSearch(actionObjectType, location.search) || currentObject?._id if (!objectId) return const expectedPath = `${model.url}/${pageName}` if (location.pathname === expectedPath) { lastHandledAction.current = actionKey return } lastHandledAction.current = actionKey const url = buildActionUrl( model, action, objectId, location.pathname, location.search ) navigate(url, { replace: true }) } }, [ actionName, actionObjectType, currentObject, location.pathname, location.search, navigate ]) const modalObjectData = currentObject ? { ...currentObject, _user: userProfile } : { _user: userProfile } const resolvedModalAction = modalAction?.type === 'alias' ? resolveAction(modalAction) : modalAction const [modelWidth, setModelWidth] = useState(520) useEffect(() => { if (resolvedModalAction?.modalWidth) { setModelWidth(resolvedModalAction.modalWidth) } }, [resolvedModalAction?.modalWidth]) const [modalCentered, setModalCentered] = useState(false) useEffect(() => { if (resolvedModalAction?.modalCentered !== undefined) { setModalCentered(resolvedModalAction.modalCentered) } }, [resolvedModalAction?.modalCentered]) const modalContentData = modalAction?.type === 'alias' ? typeof modalAction.objectData === 'function' ? modalAction.objectData(modalObjectData) : (modalAction.objectData ?? {}) : resolvedModalAction?.name === 'new' ? {} : modalObjectData return ( } /> } > {resolvedModalAction?.content ? resolvedModalAction.content(modalContentData, { onOk: handleModalOk }) : null} {children} ) } ActionsProvider.propTypes = { children: PropTypes.node.isRequired } const useActions = () => useContext(ActionsContext) // eslint-disable-next-line react-refresh/only-export-components export { ActionsProvider, ActionsContext, useActions }