import {computed, defineAsyncComponent, h, shallowRef, type VNode, watchEffect} from 'vue' import {useRoute, useRouter, type RouteLocationNormalizedGeneric} from 'vue-router' import {useBaseStore} from '@/stores/base' import {useProjectStore} from '@/stores/projects' export function useRouteWithModal() { const router = useRouter() const route = useRoute() const backdropView = computed(() => route.fullPath ? window.history.state?.backdropView : undefined) const baseStore = useBaseStore() const projectStore = useProjectStore() const routeWithModal = computed(() => { return backdropView.value ? router.resolve(backdropView.value) as RouteLocationNormalizedGeneric : route }) const currentModal = shallowRef() watchEffect(() => { if (!backdropView.value) { currentModal.value = undefined return } // this is adapted from vue-router // https://github.com/vuejs/vue-router-next/blob/798cab0d1e21f9b4d45a2bd12b840d2c7415f38a/src/RouterView.ts#L125 const routePropsOption = route.matched[0]?.props.default let routeProps = undefined if (routePropsOption) { if (routePropsOption === true) { routeProps = route.params } else { if (typeof routePropsOption === 'function') { routeProps = routePropsOption(route) } else { routeProps = routePropsOption } } } if (typeof routeProps === 'undefined') { currentModal.value = undefined return } routeProps.backdropView = backdropView.value let component = route.matched[0]?.components?.default if (typeof component === 'function') { component = defineAsyncComponent(component) } if (!component) { currentModal.value = undefined return } currentModal.value = h(component, routeProps) }) const historyState = computed(() => route.fullPath ? window.history.state : undefined) function closeModal() { // If the current project was changed because the user moved the currently opened task while coming from kanban, // we need to reflect that change in the route when they close the task modal. // The last route is only available as resolved string, therefore we need to use a regex for matching here const routeMatch = new RegExp('\\/projects\\/\\d+\\/(\\d+)', 'g') const match = historyState.value?.back ? routeMatch.exec(historyState.value.back) : null if (match !== null && baseStore.currentProject && baseStore.currentProject.id !== 0) { let viewId: string | number = match[1] if (!viewId) { const project = projectStore.projects[baseStore.currentProject.id] viewId = project?.views?.[0]?.id } // Only navigate if we have a valid project and view if (baseStore.currentProject.id && viewId) { // Preserve query parameters (e.g., date range) from the backdrop view const backdropRoute = historyState.value?.backdropView && router.resolve(historyState.value.backdropView) const newRoute = { name: 'project.view', params: { projectId: baseStore.currentProject.id, viewId, }, query: backdropRoute?.query || {}, } router.push(newRoute) return } } // Try browser history first if (historyState.value?.back) { router.back() return } // Try backdrop view const backdropRoute = historyState.value?.backdropView && router.resolve(historyState.value.backdropView) if (backdropRoute && backdropRoute.params?.projectId !== '0') { router.push(backdropRoute) return } // Fallback to current project or home if (baseStore.currentProject && baseStore.currentProject.id !== 0) { router.push({ name: 'project.index', params: { projectId: baseStore.currentProject.id }, }) } else { router.push({ name: 'home' }) } } return {routeWithModal, currentModal, closeModal} }