fix(ui): replace native confirm() dialogs with the global in-app modal
window.confirm blocks the JS event loop, which froze companion remote input while open — the remote user could raise the mesh "Clear" prompt (or reboot / backup-delete / uninstall confirms) and then never dismiss it, because the synthetic events that would dismiss it queue behind the dialog itself. New promise-based appConfirm() (useAppConfirm.ts) + one AppConfirmModal mounted globally in App.vue, built on BaseModal (Teleport-to-body, full-viewport backdrop, glass card — the canonical modal contract). All six native confirm() call sites migrated: mesh clear-all, mesh message delete, dashboard reboot, backup delete, backup USB copy, app uninstall. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
458444d700
commit
876ecc4bdf
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Promise-based in-app replacement for window.confirm(). Native browser
|
||||
* modals block the JS event loop while open, which freezes the companion
|
||||
* remote-input relay (and every other async path) — a remote user could
|
||||
* raise a confirm they can never dismiss, since the synthetic events that
|
||||
* would dismiss it are queued behind the very dialog they need to close.
|
||||
*
|
||||
* State is module-level so the single <AppConfirmModal /> mounted in
|
||||
* App.vue serves every caller:
|
||||
*
|
||||
* const ok = await appConfirm('Clear everything? This cannot be undone.')
|
||||
* const ok = await appConfirm({ message: '…', confirmLabel: 'Delete', danger: true })
|
||||
*/
|
||||
import { ref } from 'vue'
|
||||
|
||||
export interface AppConfirmOptions {
|
||||
title?: string
|
||||
message: string
|
||||
confirmLabel?: string
|
||||
cancelLabel?: string
|
||||
/** Styles the confirm button as destructive (orange/warning). */
|
||||
danger?: boolean
|
||||
}
|
||||
|
||||
interface ActiveConfirm extends AppConfirmOptions {
|
||||
_resolve: (value: boolean) => void
|
||||
}
|
||||
|
||||
export const confirmState = ref<ActiveConfirm | null>(null)
|
||||
|
||||
export function appConfirm(options: AppConfirmOptions | string): Promise<boolean> {
|
||||
const opts = typeof options === 'string' ? { message: options } : options
|
||||
// A second confirm while one is open cancels the first — the browser
|
||||
// primitive this replaces could only ever show one at a time.
|
||||
confirmState.value?._resolve(false)
|
||||
return new Promise<boolean>((resolve) => {
|
||||
confirmState.value = { ...opts, _resolve: resolve }
|
||||
})
|
||||
}
|
||||
|
||||
export function resolveConfirm(result: boolean) {
|
||||
confirmState.value?._resolve(result)
|
||||
confirmState.value = null
|
||||
}
|
||||
Reference in New Issue
Block a user