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 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;

View File

@ -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;
}

View File

@ -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) {

View File

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