Keep a navigation out of a modal from popping history it does not own
This commit is contained in:
parent
da6962d995
commit
31b1f7f80d
1 changed files with 25 additions and 10 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
import type {Component} from "svelte"
|
import type {Component} from "svelte"
|
||||||
import {randomId, last, Emitter} from "@welshman/lib"
|
import {call, ms, randomId, last, Emitter} from "@welshman/lib"
|
||||||
import {goto, pushState, replaceState} from "$app/navigation"
|
import {goto, pushState, replaceState} from "$app/navigation"
|
||||||
import {page} from "$app/state"
|
import {page} from "$app/state"
|
||||||
import type {DialogSize} from "@lib/components/Dialog.svelte"
|
import type {DialogSize} from "@lib/components/Dialog.svelte"
|
||||||
|
|
@ -26,6 +26,8 @@ export const emitter = new Emitter()
|
||||||
|
|
||||||
const modals: Record<string, Modal> = {}
|
const modals: Record<string, Modal> = {}
|
||||||
|
|
||||||
|
let dropping: Promise<boolean> | undefined
|
||||||
|
|
||||||
// Modal ids live in SvelteKit page state, so a `goto` that drops `state` closes them.
|
// Modal ids live in SvelteKit page state, so a `goto` that drops `state` closes them.
|
||||||
export const getModalStack = () => (page.state.modals ?? []).map(id => modals[id]).filter(Boolean)
|
export const getModalStack = () => (page.state.modals ?? []).map(id => modals[id]).filter(Boolean)
|
||||||
|
|
||||||
|
|
@ -33,24 +35,37 @@ export const getModal = () => last(getModalStack())
|
||||||
|
|
||||||
export type NavigateOptions = Parameters<typeof goto>[1] & {keepModal?: boolean}
|
export type NavigateOptions = Parameters<typeof goto>[1] & {keepModal?: boolean}
|
||||||
|
|
||||||
|
// A back with nothing left to pop fires no popstate, so the wait for one is bounded.
|
||||||
const popHistory = () =>
|
const popHistory = () =>
|
||||||
new Promise(resolve => {
|
new Promise<boolean>(resolve => {
|
||||||
addEventListener("popstate", resolve, {once: true})
|
const finish = (popped: boolean) => {
|
||||||
|
clearTimeout(timeout)
|
||||||
|
removeEventListener("popstate", onPopState)
|
||||||
|
resolve(popped)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onPopState = () => finish(true)
|
||||||
|
const timeout = setTimeout(() => finish(false), ms(1))
|
||||||
|
|
||||||
|
addEventListener("popstate", onPopState)
|
||||||
history.back()
|
history.back()
|
||||||
})
|
})
|
||||||
|
|
||||||
// SvelteKit reuses its navigation index for a replacing `goto`, so entries are given back instead.
|
// SvelteKit reuses its navigation index for a replacing `goto`, so entries are given back instead.
|
||||||
const dropModalEntries = async () => {
|
const dropModalEntries = () => {
|
||||||
let dropped = false
|
dropping ??= call(async () => {
|
||||||
|
let dropped = false
|
||||||
|
|
||||||
while (getModalStack().length > 0) {
|
while (getModalStack().length > 0 && (await popHistory())) {
|
||||||
await popHistory()
|
dropped = true
|
||||||
|
}
|
||||||
|
|
||||||
dropped = true
|
return dropped
|
||||||
}
|
}).finally(() => {
|
||||||
|
dropping = undefined
|
||||||
|
})
|
||||||
|
|
||||||
return dropped
|
return dropping
|
||||||
}
|
}
|
||||||
|
|
||||||
export const navigate = async (path: string, {keepModal, ...options}: NavigateOptions = {}) => {
|
export const navigate = async (path: string, {keepModal, ...options}: NavigateOptions = {}) => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue