diff --git a/src/components/appRouter.js b/src/components/appRouter.js index a75d8823df..2119d2095e 100644 --- a/src/components/appRouter.js +++ b/src/components/appRouter.js @@ -11,7 +11,7 @@ import ServerConnections from './ServerConnections'; import alert from './alert'; import reactControllerFactory from './reactControllerFactory'; -const history = createHashHistory(); +export const history = createHashHistory(); /** * Page types of "no return" (when "Go back" should behave differently, probably quitting the application). @@ -20,14 +20,13 @@ const START_PAGE_TYPES = ['home', 'login', 'selectserver']; class AppRouter { allRoutes = new Map(); - currentRouteInfo; + currentRouteInfo = { route: {} }; currentViewLoadRequest; firstConnectionResult; forcedLogoutMsg; msgTimeout; promiseShow; resolveOnNextShow; - previousRoute = {}; constructor() { document.addEventListener('viewshow', () => this.onViewShow()); @@ -482,9 +481,9 @@ class AppRouter { #getHandler(route) { return (ctx, next) => { - const ignore = route.dummyRoute === true || this.previousRoute.dummyRoute === true; - this.previousRoute = route; + const ignore = ctx.path === this.currentRouteInfo.path; if (ignore) { + console.debug('[appRouter] path did not change, ignoring route change'); // Resolve 'show' promise this.onViewShow(); return; diff --git a/src/components/dialogHelper/dialogHelper.js b/src/components/dialogHelper/dialogHelper.js index 59b249b8f5..80b269fed5 100644 --- a/src/components/dialogHelper/dialogHelper.js +++ b/src/components/dialogHelper/dialogHelper.js @@ -1,4 +1,4 @@ -import { appRouter } from '../appRouter'; +import { history } from '../appRouter'; import focusManager from '../focusManager'; import browser from '../../scripts/browser'; import layoutManager from '../layoutManager'; @@ -39,7 +39,7 @@ import '../../assets/css/scrollstyles.scss'; try { parentNode.removeChild(elem); } catch (err) { - console.error('error removing dialog element: ' + err); + console.error('[dialogHelper] error removing dialog element: ' + err); } } } @@ -49,26 +49,26 @@ import '../../assets/css/scrollstyles.scss'; self.originalUrl = window.location.href; const activeElement = document.activeElement; let removeScrollLockOnClose = false; + let unlisten; - function onHashChange() { - const isBack = self.originalUrl === window.location.href; + function onHashChange({ location }) { + const dialogs = location.state?.dialogs || []; + const shouldClose = !dialogs.includes(hash); - if (isBack || !isOpened(dlg)) { - window.removeEventListener('popstate', onHashChange); + if ((shouldClose || !isOpened(dlg)) && unlisten) { + unlisten(); } - if (isBack) { - self.closedByBack = true; - closeDialog(dlg); + if (shouldClose) { + close(dlg); } } function onBackCommand(e) { if (e.detail.command === 'back') { - self.closedByBack = true; e.preventDefault(); e.stopPropagation(); - closeDialog(dlg); + close(dlg); } } @@ -77,7 +77,9 @@ import '../../assets/css/scrollstyles.scss'; inputManager.off(dlg, onBackCommand); } - window.removeEventListener('popstate', onHashChange); + if (unlisten) { + unlisten(); + } removeBackdrop(dlg); dlg.classList.remove('opened'); @@ -86,10 +88,22 @@ import '../../assets/css/scrollstyles.scss'; document.body.classList.remove('noScroll'); } - if (!self.closedByBack && isHistoryEnabled(dlg)) { - const state = window.history.state || {}; - if (state.dialogId === hash) { - appRouter.back(); + if (isHistoryEnabled(dlg)) { + const state = history.location.state || {}; + if (state.dialogs?.length > 0) { + if (state.dialogs[state.dialogs.length - 1] === hash) { + history.back(); + } else if (state.dialogs.includes(hash)) { + console.warn('[dialogHelper] dialog "%s" was closed, but is not the last dialog opened', hash); + // Remove the closed dialog hash from the history state + history.replace( + `${history.location.pathname}${history.location.search}`, + { + ...state, + dialogs: state.dialogs.filter(dialog => dialog !== hash) + } + ); + } } } @@ -112,14 +126,18 @@ import '../../assets/css/scrollstyles.scss'; //resolve(); // if we just called history.back(), then use a timeout to allow the history events to fire first setTimeout(() => { + dlg.dispatchEvent(new CustomEvent('close', { + bubbles: false, + cancelable: false + })); + resolve({ - element: dlg, - closedByBack: self.closedByBack + element: dlg }); }, 1); } - dlg.addEventListener('close', onDialogClosed); + dlg.addEventListener('_close', onDialogClosed); const center = !dlg.classList.contains('dialog-fixedSize'); if (center) { @@ -144,9 +162,20 @@ import '../../assets/css/scrollstyles.scss'; animateDialogOpen(dlg); if (isHistoryEnabled(dlg)) { - appRouter.show(`/dialog?dlg=${hash}`, { dialogId: hash }); + const state = history.location.state || {}; + const dialogs = state.dialogs || []; + // Add new dialog to the list of open dialogs + dialogs.push(hash); - window.addEventListener('popstate', onHashChange); + history.push( + `${history.location.pathname}${history.location.search}`, + { + ...state, + dialogs + } + ); + + unlisten = history.listen(onHashChange); } else { inputManager.on(dlg, onBackCommand); } @@ -213,16 +242,6 @@ import '../../assets/css/scrollstyles.scss'; } export function close(dlg) { - if (isOpened(dlg)) { - if (isHistoryEnabled(dlg)) { - appRouter.back(); - } else { - closeDialog(dlg); - } - } - } - - function closeDialog(dlg) { if (!dlg.classList.contains('hide')) { dlg.dispatchEvent(new CustomEvent('closing', { bubbles: false, @@ -233,7 +252,7 @@ import '../../assets/css/scrollstyles.scss'; focusManager.popScope(dlg); dlg.classList.add('hide'); - dlg.dispatchEvent(new CustomEvent('close', { + dlg.dispatchEvent(new CustomEvent('_close', { bubbles: false, cancelable: false })); @@ -348,7 +367,7 @@ import '../../assets/css/scrollstyles.scss'; if (enableAnimation()) { backdrop.classList.remove('dialogBackdropOpened'); - // this is not firing animatonend + // this is not firing animationend setTimeout(onAnimationFinish, 300); return; } diff --git a/src/components/metadataEditor/metadataEditor.js b/src/components/metadataEditor/metadataEditor.js index 3195a1c64f..10112a8453 100644 --- a/src/components/metadataEditor/metadataEditor.js +++ b/src/components/metadataEditor/metadataEditor.js @@ -1088,9 +1088,7 @@ import template from './metadataEditor.template.html'; export default { show: function (itemId, serverId) { - return new Promise(function (resolve) { - return show(itemId, serverId, resolve); - }); + return new Promise(resolve => show(itemId, serverId, resolve)); }, embed: function (elem, itemId, serverId) { diff --git a/src/scripts/routes.js b/src/scripts/routes.js index 6fd437d1cd..a96ab7a30d 100644 --- a/src/scripts/routes.js +++ b/src/scripts/routes.js @@ -560,11 +560,6 @@ import { appRouter } from '../components/appRouter'; serverRequest: true }); - defineRoute({ - path: '/dialog', - dummyRoute: true - }); - defineRoute({ path: '/', autoFocus: false,