import { useContext } from 'react'; import {split, treeFind} from '@ywfe/utils'; import { match } from 'path-to-regexp'; import * as R from 'ramda'; import { HistoryState } from './router.interface'; import * as CONTANTS from './contants'; import { transformToHistoryState, hasMenu, matchParams } from './utils'; import { RouterContextProps, YRouteProps, PushConfigProps } from './router.interface'; import { RouterContext } from './context'; export const useHistory = () => { const { store, useGetModuleDataByName } = useContext(RouterContext); const [state, dispatch] = store(); const { fetchAPI22504Data } = useGetModuleDataByName(); const push = async (url: string, pushConfig?: PushConfigProps) => { if(process.env.STAGE === 'application') { goto(url, undefined, '', pushConfig); return false; } if(!state.current || !state.current.path){ return false; } const { moduleId, moduleName } = state.current; const menu = url.replace(/\//g, '_'); const path = split('?', url)[0]; const isCurrentMenu = hasMenu(moduleName, path); if(moduleId && moduleName && isCurrentMenu){ goto(`/${moduleName}/${moduleId}/${menu}`, undefined, '', pushConfig); } else { let name = split('/', split('?', url)[0]).join('/'); name = name.indexOf('/') === 0 ? name : '/' + name; const moduleData = await getModuleDataByName(name); if (moduleData && moduleData.moduleNameEn && moduleData.moduleId) { goto( `/${moduleData.moduleNameEn}/${moduleData.moduleId}/${menu}`, moduleData, '', pushConfig ); } } }; const goto = (data: string | HistoryState, moduleData?: any, deleteModuleName?: string, pushConfig?: PushConfigProps) => { const dataState = transformToHistoryState(data); if(dataState && !R.isEmpty(dataState)) { const params = { ...dataState.params, } if (pushConfig && pushConfig.target === '_blank') { window.open(dataState.url) } else { window.history.pushState(params, dataState.title || CONTANTS.DEFAULT_TITLE, dataState.url); updateCurrent(dataState.path, dataState.search, moduleData, deleteModuleName); } } }; const replace = async (data: string | HistoryState) => { const dataState = transformToHistoryState(data); if(dataState && !R.isEmpty(dataState)) { window.history.replaceState(dataState.params || {}, dataState.title || CONTANTS.DEFAULT_TITLE, dataState.url); updateCurrent(dataState.path, dataState.search); } }; const pop = (data: any, pathname: string) => { const {path, moduleId, moduleName, menu} = data; const params = getParams(pathname); const {routes, historyRoute} = state; const parentPath = `/${moduleName}/${moduleId}`; const currentPathname = params.menu ? `${parentPath}/${menu}` : parentPath; const currentHistoryRoute = historyRoute[moduleName]; const currentHistory = (Object.keys(currentHistoryRoute)).filter(((item: any)=> item.path !== path)); const newHistory = currentHistory.length ? currentHistory.filter((data: string) => data !== path) : []; let index = currentHistory.indexOf(path); index = index - 1 >= 0 ? index - 1 : index + 1; const nextPath = currentHistory[index]; const isCurrent = pathname === currentPathname; let next; if(isCurrent) { let current; if(routes[parentPath]){ const res = treeFind({key: 'path', value: nextPath}, 'children', routes[parentPath].children); current = res[0]; } if(current){ dispatch({ type: 'pop', payload: { data: { history: newHistory, current, prev: path } } }); next = current; } } else { dispatch({ type: 'pop', payload: { data: { history: newHistory, prev: path } } }); } return next; } const goBack = () => { go(-1); }; const forward = () => { go(1); }; const go = (delta: number) => { window.history.go(delta); }; const registerRoute = (data: YRouteProps) => { dispatch({ type: 'register', payload: { data, }, }); }; const updateCurrent = (pathname: string, search = '', moduleData?: any, deleteModuleName?: string) => { dispatch({ type: 'update_current', payload: { data: { pathname, search, moduleData, deleteModuleName }, }, }); }; const getRoutes = async () => { const res = await dispatch({ type: 'get_routes', payload: { data: {} } }); return res.routes; } const updateHistory = (newHistory: string[], newHistoryRoute: any) => { dispatch({ type: 'update_changed', payload: { data: { newHistory, newHistoryRoute } } }) } const getModuleDataByName = async (resourceNameEn: string) => { const res = await fetchAPI22504Data({ resourceNameEn }); return res; }; const getCurrentPath = (pathname: string, path: string) => { const matchFn = match(path); const matchRes = matchFn(pathname); if(matchRes && !R.isEmpty(matchRes)){ return path; } else { return ''; } } const getParams = (pathname: string) => { return matchParams(pathname, '/:moduleName/:moduleId/:menu?') } return { state: {...state}, goto, goBack, forward, go, pop, registerRoute, push, replace, updateCurrent, getRoutes, updateHistory, getCurrentPath, getParams }; };