Merge pull request #3620 from thornbill/update-dialog-routing

Update dialog history handling
This commit is contained in:
Bill Thornton 2022-06-09 14:16:50 -04:00 committed by GitHub
commit 1dbbb4c65d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 57 additions and 46 deletions

View File

@ -11,7 +11,7 @@ import ServerConnections from './ServerConnections';
import alert from './alert'; import alert from './alert';
import reactControllerFactory from './reactControllerFactory'; 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). * 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 { class AppRouter {
allRoutes = new Map(); allRoutes = new Map();
currentRouteInfo; currentRouteInfo = { route: {} };
currentViewLoadRequest; currentViewLoadRequest;
firstConnectionResult; firstConnectionResult;
forcedLogoutMsg; forcedLogoutMsg;
msgTimeout; msgTimeout;
promiseShow; promiseShow;
resolveOnNextShow; resolveOnNextShow;
previousRoute = {};
constructor() { constructor() {
document.addEventListener('viewshow', () => this.onViewShow()); document.addEventListener('viewshow', () => this.onViewShow());
@ -482,9 +481,9 @@ class AppRouter {
#getHandler(route) { #getHandler(route) {
return (ctx, next) => { return (ctx, next) => {
const ignore = route.dummyRoute === true || this.previousRoute.dummyRoute === true; const ignore = ctx.path === this.currentRouteInfo.path;
this.previousRoute = route;
if (ignore) { if (ignore) {
console.debug('[appRouter] path did not change, ignoring route change');
// Resolve 'show' promise // Resolve 'show' promise
this.onViewShow(); this.onViewShow();
return; return;

View File

@ -1,4 +1,4 @@
import { appRouter } from '../appRouter'; import { history } from '../appRouter';
import focusManager from '../focusManager'; import focusManager from '../focusManager';
import browser from '../../scripts/browser'; import browser from '../../scripts/browser';
import layoutManager from '../layoutManager'; import layoutManager from '../layoutManager';
@ -39,7 +39,7 @@ import '../../assets/css/scrollstyles.scss';
try { try {
parentNode.removeChild(elem); parentNode.removeChild(elem);
} catch (err) { } 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; self.originalUrl = window.location.href;
const activeElement = document.activeElement; const activeElement = document.activeElement;
let removeScrollLockOnClose = false; let removeScrollLockOnClose = false;
let unlisten;
function onHashChange() { function onHashChange({ location }) {
const isBack = self.originalUrl === window.location.href; const dialogs = location.state?.dialogs || [];
const shouldClose = !dialogs.includes(hash);
if (isBack || !isOpened(dlg)) { if ((shouldClose || !isOpened(dlg)) && unlisten) {
window.removeEventListener('popstate', onHashChange); unlisten();
} }
if (isBack) { if (shouldClose) {
self.closedByBack = true; close(dlg);
closeDialog(dlg);
} }
} }
function onBackCommand(e) { function onBackCommand(e) {
if (e.detail.command === 'back') { if (e.detail.command === 'back') {
self.closedByBack = true;
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
closeDialog(dlg); close(dlg);
} }
} }
@ -77,7 +77,9 @@ import '../../assets/css/scrollstyles.scss';
inputManager.off(dlg, onBackCommand); inputManager.off(dlg, onBackCommand);
} }
window.removeEventListener('popstate', onHashChange); if (unlisten) {
unlisten();
}
removeBackdrop(dlg); removeBackdrop(dlg);
dlg.classList.remove('opened'); dlg.classList.remove('opened');
@ -86,10 +88,22 @@ import '../../assets/css/scrollstyles.scss';
document.body.classList.remove('noScroll'); document.body.classList.remove('noScroll');
} }
if (!self.closedByBack && isHistoryEnabled(dlg)) { if (isHistoryEnabled(dlg)) {
const state = window.history.state || {}; const state = history.location.state || {};
if (state.dialogId === hash) { if (state.dialogs?.length > 0) {
appRouter.back(); 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(); //resolve();
// if we just called history.back(), then use a timeout to allow the history events to fire first // if we just called history.back(), then use a timeout to allow the history events to fire first
setTimeout(() => { setTimeout(() => {
dlg.dispatchEvent(new CustomEvent('close', {
bubbles: false,
cancelable: false
}));
resolve({ resolve({
element: dlg, element: dlg
closedByBack: self.closedByBack
}); });
}, 1); }, 1);
} }
dlg.addEventListener('close', onDialogClosed); dlg.addEventListener('_close', onDialogClosed);
const center = !dlg.classList.contains('dialog-fixedSize'); const center = !dlg.classList.contains('dialog-fixedSize');
if (center) { if (center) {
@ -144,9 +162,20 @@ import '../../assets/css/scrollstyles.scss';
animateDialogOpen(dlg); animateDialogOpen(dlg);
if (isHistoryEnabled(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 { } else {
inputManager.on(dlg, onBackCommand); inputManager.on(dlg, onBackCommand);
} }
@ -213,16 +242,6 @@ import '../../assets/css/scrollstyles.scss';
} }
export function close(dlg) { export function close(dlg) {
if (isOpened(dlg)) {
if (isHistoryEnabled(dlg)) {
appRouter.back();
} else {
closeDialog(dlg);
}
}
}
function closeDialog(dlg) {
if (!dlg.classList.contains('hide')) { if (!dlg.classList.contains('hide')) {
dlg.dispatchEvent(new CustomEvent('closing', { dlg.dispatchEvent(new CustomEvent('closing', {
bubbles: false, bubbles: false,
@ -233,7 +252,7 @@ import '../../assets/css/scrollstyles.scss';
focusManager.popScope(dlg); focusManager.popScope(dlg);
dlg.classList.add('hide'); dlg.classList.add('hide');
dlg.dispatchEvent(new CustomEvent('close', { dlg.dispatchEvent(new CustomEvent('_close', {
bubbles: false, bubbles: false,
cancelable: false cancelable: false
})); }));
@ -348,7 +367,7 @@ import '../../assets/css/scrollstyles.scss';
if (enableAnimation()) { if (enableAnimation()) {
backdrop.classList.remove('dialogBackdropOpened'); backdrop.classList.remove('dialogBackdropOpened');
// this is not firing animatonend // this is not firing animationend
setTimeout(onAnimationFinish, 300); setTimeout(onAnimationFinish, 300);
return; return;
} }

View File

@ -1088,9 +1088,7 @@ import template from './metadataEditor.template.html';
export default { export default {
show: function (itemId, serverId) { show: function (itemId, serverId) {
return new Promise(function (resolve) { return new Promise(resolve => show(itemId, serverId, resolve));
return show(itemId, serverId, resolve);
});
}, },
embed: function (elem, itemId, serverId) { embed: function (elem, itemId, serverId) {

View File

@ -560,11 +560,6 @@ import { appRouter } from '../components/appRouter';
serverRequest: true serverRequest: true
}); });
defineRoute({
path: '/dialog',
dummyRoute: true
});
defineRoute({ defineRoute({
path: '/', path: '/',
autoFocus: false, autoFocus: false,