WIP: feat: route modals everywhere #2735

Closed
dpschen wants to merge 15 commits from dpschen/frontend:feature/route-modals-everywhere into main
2 changed files with 87 additions and 33 deletions
Showing only changes of commit 2a63549be2 - Show all commits

View File

@ -1,5 +1,5 @@
import { computed, shallowRef, watch, h, type VNode, ref } from 'vue'
import { useRoute, useRouter, loadRouteLocation, type RouteLocationNormalizedLoaded, type RouteLocationRaw } from 'vue-router'
import {computed, shallowRef, watch, h, type VNode, ref} from 'vue'
import {useRoute, useRouter, loadRouteLocation, type RouteLocationNormalizedLoaded, type RouteLocationRaw, START_LOCATION, type RouteLocationNormalized} from 'vue-router'
import router from '@/router'
// this is adapted from vue-router
@ -15,8 +15,8 @@ function getRouteProps(route: RouteLocationNormalizedLoaded) {
: {}
}
function resolveAndLoadRoute(route: RouteLocationRaw) {
return loadRouteLocation(router.resolve(route))
function resolveAndLoadRoute(route: RouteLocationRaw, currentLocation?: RouteLocationNormalizedLoaded) {
return loadRouteLocation(router.resolve(route, currentLocation))
}
export function useRouteWithModal() {
@ -27,9 +27,39 @@ export function useRouteWithModal() {
// every time the fullPath changes we check the history state
// this happens also initially
return route.fullPath
? window.history.state?.backdropRoutePath
? history.state?.backdropRoutePath
: undefined
})
router.afterEach((to, from) => {
const resolvedRoute = router.resolve(to)
// if (!resolvedRoute.meta.forceModal) {
if (!resolvedRoute.meta.modal) {
return
}
let routeNormalized: RouteLocationNormalized
if (typeof resolvedRoute.meta?.modal === 'boolean' || resolvedRoute.meta.modal?.force !== true) {
// TODO: also check route history here
routeNormalized = from
} else if (resolvedRoute.meta.modal?.defaultBackdropRoute) {
const {defaultBackdropRoute} = resolvedRoute.meta.modal
const routeRaw = typeof defaultBackdropRoute === 'function'

Something was broken here: since we checked for historyState.value in the if condition it could never have a value in the else cause.

Something was broken here: since we checked for `historyState.value` in the `if` condition it could never have a value in the `else` cause.
? defaultBackdropRoute(resolvedRoute)
: defaultBackdropRoute
routeNormalized = router.resolve(routeRaw)
} else {
// TODO: maybe load parent route here in the future,
// via: route.matched[route.matched.length - 2]
// see: https://router.vuejs.org/guide/migration/#removal-of-parent-from-route-locations
routeNormalized = router.resolve({ name: 'home' })
}
history.replaceState({
...history.state,
foobar: routeNormalized.fullPath,
}, '')
})
const routerIsReady = ref(false)
router.isReady().then(() => {
@ -48,23 +78,34 @@ export function useRouteWithModal() {
}
if (historyStateBackdropRoutePath.value === undefined) {
if (route.meta?.showAsModal !== true) {
if (typeof route.meta?.modal === 'boolean' || route.meta.modal?.force !== true) {
hasModal.value = false
baseRoute.value = route
return
}
// TODO: maybe load parent route here in the future,
// via: route.matched[route.matched.length - 2]
// see: https://router.vuejs.org/guide/migration/#removal-of-parent-from-route-locations
// the route forces to be shown as a modal
hasModal.value = true
baseRoute.value = await resolveAndLoadRoute({ name: 'home' })
let routeRaw: RouteLocationRaw
if (route.meta.modal?.defaultBackdropRoute) {
const {defaultBackdropRoute} = route.meta.modal
routeRaw = typeof defaultBackdropRoute === 'function'
? defaultBackdropRoute(route)
: defaultBackdropRoute
} else {
// TODO: maybe load parent route here in the future,
// via: route.matched[route.matched.length - 2]
// see: https://router.vuejs.org/guide/migration/#removal-of-parent-from-route-locations
routeRaw = { name: 'home' }
}
baseRoute.value = await resolveAndLoadRoute(routeRaw, baseRoute.value)
return
}
// we get the resolved route from the fullpath
// and wait for the route component to be loaded before we assign it
hasModal.value = true
baseRoute.value = await resolveAndLoadRoute(historyStateBackdropRoutePath.value)
baseRoute.value = await resolveAndLoadRoute(historyStateBackdropRoutePath.value, baseRoute.value)
},
{immediate: true},
)
@ -103,7 +144,7 @@ export function useRouteWithModal() {
// TODO: Dialog modals might want to replace the route here via router.replace()
return router.push(backdropRoute.value)
}
if (window.history.state === undefined) {
if (history.state === undefined) {
return router.push({ name: 'home' })
} else {
router.back()
@ -115,6 +156,5 @@ export function useRouteWithModal() {
return {
baseRoute,
modalRoute,
closeModal,
}
}

View File

@ -83,7 +83,12 @@ const NewTeamComponent = () => import('@/views/teams/NewTeam.vue')
declare module 'vue-router' {
interface RouteMeta {
title?: string
showAsModal?: boolean
modal?: boolean | {
/** Forces the view to be always shown as modal */
force?: boolean,
/** Show this route as backdrop if there is no route history */
defaultBackdropRoute?: RouteLocationRaw | ((currentRoute: RouteLocation) => RouteLocationRaw),
}
}
}
@ -220,7 +225,10 @@ const router = createRouter({
name: 'namespace.create',
component: NewNamespaceComponent,
meta: {
showAsModal: true,
modal: {
force: true,
defaultBackdropRoute: {name: 'namespaces.index'},
},
},
},
{
@ -228,7 +236,7 @@ const router = createRouter({
name: 'namespace.settings.edit',
component: NamespaceSettingEdit,
meta: {
showAsModal: true,
modal: true,
},
props: route => ({ namespaceId: Number(route.params.id as string) }),
},
@ -237,7 +245,7 @@ const router = createRouter({
name: 'namespace.settings.share',
component: NamespaceSettingShare,
meta: {
showAsModal: true,
modal: true,
},
},
{
@ -245,7 +253,7 @@ const router = createRouter({
name: 'namespace.settings.archive',
component: NamespaceSettingArchive,
meta: {
showAsModal: true,
modal: true,
},
props: route => ({ namespaceId: Number(route.params.id as string) }),
},
@ -254,7 +262,7 @@ const router = createRouter({
name: 'namespace.settings.delete',
component: NamespaceSettingDelete,
meta: {
showAsModal: true,
modal: true,
},
props: route => ({ namespaceId: Number(route.params.id as string) }),
},
@ -263,6 +271,12 @@ const router = createRouter({
name: 'task.detail',
component: TaskDetailView,
props: route => ({ taskId: Number(route.params.id as string) }),
meta: {
modal: {
force: true,
defaultBackdropRoute: {name: 'home'},
},
},
},
{
path: '/tasks/by/upcoming',
@ -280,7 +294,7 @@ const router = createRouter({
name: 'list.create',
component: NewListComponent,
meta: {
showAsModal: true,
modal: true,
},
},
{
@ -289,7 +303,7 @@ const router = createRouter({
component: ListSettingEdit,
props: route => ({ listId: Number(route.params.listId as string) }),
meta: {
showAsModal: true,
modal: true,
},
},
{
@ -297,7 +311,7 @@ const router = createRouter({
name: 'list.settings.background',
component: ListSettingBackground,
meta: {
showAsModal: true,
modal: true,
},
},
{
@ -305,7 +319,7 @@ const router = createRouter({
name: 'list.settings.duplicate',
component: ListSettingDuplicate,
meta: {
showAsModal: true,
modal: true,
},
},
{
@ -313,7 +327,7 @@ const router = createRouter({
name: 'list.settings.share',
component: ListSettingShare,
meta: {
showAsModal: true,
modal: true,
},
},
{
@ -321,7 +335,7 @@ const router = createRouter({
name: 'list.settings.delete',
component: ListSettingDelete,
meta: {
showAsModal: true,
modal: true,
},
},
{
@ -329,7 +343,7 @@ const router = createRouter({
name: 'list.settings.archive',
component: ListSettingArchive,
meta: {
showAsModal: true,
modal: true,
},
},
{
@ -337,7 +351,7 @@ const router = createRouter({
name: 'filter.settings.edit',
component: FilterEdit,
meta: {
showAsModal: true,
modal: true,
},
props: route => ({ listId: Number(route.params.listId as string) }),
},
@ -346,7 +360,7 @@ const router = createRouter({
name: 'filter.settings.delete',
component: FilterDelete,
meta: {
showAsModal: true,
modal: true,
},
props: route => ({ listId: Number(route.params.listId as string) }),
},
@ -355,7 +369,7 @@ const router = createRouter({
name: 'list.info',
component: ListInfo,
meta: {
showAsModal: true,
modal: true,
},
props: route => ({ listId: Number(route.params.listId as string) }),
},
@ -422,7 +436,7 @@ const router = createRouter({
name: 'teams.create',
component: NewTeamComponent,
meta: {
showAsModal: true,
modal: true,
},
},
{
@ -440,7 +454,7 @@ const router = createRouter({
name: 'labels.create',
component: NewLabelComponent,
meta: {
showAsModal: true,
modal: true,
},
},
{
@ -462,7 +476,7 @@ const router = createRouter({
name: 'filters.create',
component: FilterNew,
meta: {
showAsModal: true,
modal: true,
},
},
{
@ -475,7 +489,7 @@ const router = createRouter({
name: 'about',
component: About,
meta: {
showAsModal: true,
modal: true,
},
},
],