From dce115d6c33d9d065dfb30f221304dff7d069368 Mon Sep 17 00:00:00 2001 From: Robin Davies Date: Wed, 25 Aug 2021 19:16:13 -0400 Subject: [PATCH] Improvate Dialog handling on browser back button. --- debian/copyright | 2 +- react/CMakeLists.txt | 1 + react/public/var/config.json | 2 +- react/src/AboutDialog.tsx | 6 +-- react/src/BankDialog.tsx | 51 +------------------------ react/src/DialogEx.tsx | 64 ++++++++++++++++++++++++++------ react/src/MidiBindingsDialog.tsx | 51 ++----------------------- react/src/PresetDialog.tsx | 8 ++-- react/src/RenameDialog.tsx | 55 ++------------------------- react/src/UploadDialog.tsx | 53 +++----------------------- react/src/WifiConfigDialog.tsx | 56 ++-------------------------- 11 files changed, 79 insertions(+), 270 deletions(-) diff --git a/debian/copyright b/debian/copyright index 2d33b11..6efca97 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1383,7 +1383,7 @@ Copyright: Open JS Foundation License: BSD-2-clause Files: react/node_modules/espree/node_modules/* -Copyright: contributors +Copyright: Contributors License: Apache-2.0 Files: react/node_modules/esprima/* diff --git a/react/CMakeLists.txt b/react/CMakeLists.txt index 615eb7e..c8f912a 100644 --- a/react/CMakeLists.txt +++ b/react/CMakeLists.txt @@ -24,6 +24,7 @@ add_custom_command( src/WifiChannel.tsx src/JackServerSettingsDialog.tsx src/JackServerSettings.tsx + src/DialogEx.tsx src/AboutDialog.tsx src/App.test.tsx diff --git a/react/public/var/config.json b/react/public/var/config.json index a16e5a4..c1902a0 100644 --- a/react/public/var/config.json +++ b/react/public/var/config.json @@ -1,5 +1,5 @@ { - "socket_server_port": 8080, + "socket_server_port": 80, "socket_server_address": "*", "max_upload_size": 1048576, diff --git a/react/src/AboutDialog.tsx b/react/src/AboutDialog.tsx index 787c070..2c41a38 100644 --- a/react/src/AboutDialog.tsx +++ b/react/src/AboutDialog.tsx @@ -5,7 +5,6 @@ import Typography from '@material-ui/core/Typography'; import { PiPedalModel, PiPedalModelFactory, State } from './PiPedalModel'; import { TransitionProps } from '@material-ui/core/transitions/transition'; import Slide from '@material-ui/core/Slide'; -import Dialog from '@material-ui/core/Dialog'; import AppBar from '@material-ui/core/AppBar'; import Toolbar from '@material-ui/core/Toolbar'; import { Theme, withStyles, WithStyles, createStyles } from '@material-ui/core/styles'; @@ -13,6 +12,7 @@ import ArrowBackIcon from '@material-ui/icons/ArrowBack'; import Divider from '@material-ui/core/Divider'; import JackHostStatus from './JackHostStatus'; import {PiPedalError} from './PiPedalError'; +import DialogEx from './DialogEx'; interface AboutDialogProps extends WithStyles { @@ -187,7 +187,7 @@ const AboutDialog = withStyles(styles, { withTheme: true })( let classes = this.props.classes; return ( - { this.props.onClose() }} TransitionComponent={Transition}>
@@ -238,7 +238,7 @@ const AboutDialog = withStyles(styles, { withTheme: true })(
-
+ ); diff --git a/react/src/BankDialog.tsx b/react/src/BankDialog.tsx index f808453..7ce79f4 100644 --- a/react/src/BankDialog.tsx +++ b/react/src/BankDialog.tsx @@ -26,7 +26,6 @@ import Button from "@material-ui/core/Button"; import ButtonBase from "@material-ui/core/ButtonBase"; import { TransitionProps } from '@material-ui/core/transitions/transition'; import Slide from '@material-ui/core/Slide'; -import Dialog from '@material-ui/core/Dialog'; import AppBar from '@material-ui/core/AppBar'; import Toolbar from '@material-ui/core/Toolbar'; import { Theme, withStyles, WithStyles, createStyles } from '@material-ui/core/styles'; @@ -152,7 +151,6 @@ const BankDialog = withStyles(styles, { withTheme: true })( showDeletePrompt: false }; this.handleBanksChanged = this.handleBanksChanged.bind(this); - this.handlePopState = this.handlePopState.bind(this); } onMoreClick(e: React.SyntheticEvent): void { @@ -229,63 +227,18 @@ const BankDialog = withStyles(styles, { withTheme: true })( hasHooks: boolean = false; - stateWasPopped: boolean = false; - handlePopState(e: any): any { - let state: any = e.state; - if (!state || !state.bankDialog) { - this.stateWasPopped = true; - this.props.onDialogClose(); - } - } - - updateBackButtonHooks(): void { - let wantHooks = this.mounted && this.props.show; - if (wantHooks !== this.hasHooks) { - this.hasHooks = wantHooks; - - if (this.hasHooks) { - this.stateWasPopped = false; - window.addEventListener("popstate", this.handlePopState); - // eslint-disable-next-line no-restricted-globals - let newState: any = history.state; - if (!newState) { - newState = {}; - } - newState.bankDialog = true; - // eslint-disable-next-line no-restricted-globals - history.pushState( - newState, - "PiPedal - Banks", - "#Banks" - ); - } else { - window.removeEventListener("popstate", this.handlePopState); - if (!this.stateWasPopped) { - // eslint-disable-next-line no-restricted-globals - history.back(); - } - // eslint-disable-next-line no-restricted-globals - history.replaceState({}, "PiPedal", "#"); - } - - } - } - componentDidUpdate() { - this.updateBackButtonHooks(); } componentDidMount() { this.model.banks.addOnChangedHandler(this.handleBanksChanged); this.handleBanksChanged(); // scroll selected item into view. this.mounted = true; - this.updateBackButtonHooks(); } componentWillUnmount() { this.model.banks.removeOnChangedHandler(this.handleBanksChanged); this.mounted = false; - this.updateBackButtonHooks(); } getSelectedIndex() { @@ -453,7 +406,7 @@ const BankDialog = withStyles(styles, { withTheme: true })( let defaultSelectedIndex = this.getSelectedIndex(); return ( - { this.handleDialogClose() }} TransitionComponent={Transition}>
@@ -601,7 +554,7 @@ const BankDialog = withStyles(styles, { withTheme: true })( -
+ ); diff --git a/react/src/DialogEx.tsx b/react/src/DialogEx.tsx index 1df06ae..38f0fa2 100644 --- a/react/src/DialogEx.tsx +++ b/react/src/DialogEx.tsx @@ -30,6 +30,38 @@ interface DialogExState { } +class DialogStackEntry { + constructor(tag: string) + { + this.tag = tag; + } + tag: string; +}; + +let dialogStack: DialogStackEntry[] = []; + +function peekDialogStack(): string { + if (dialogStack.length !== 0) { + return dialogStack[dialogStack.length-1].tag; + } + return ""; +} + +let ignoreNextPop = false; + +function popDialogStack(ignoreNextPop_: boolean): void { + if (dialogStack.length !== 0) + { + ignoreNextPop = ignoreNextPop_; + dialogStack.splice(dialogStack.length-1,1); + } +} +function pushDialogStack(tag: string): void { + dialogStack.push(new DialogStackEntry(tag)); +} + + + class DialogEx extends React.Component { constructor(props: DialogExProps) { @@ -47,14 +79,25 @@ class DialogEx extends React.Component { stateWasPopped: boolean = false; handlePopState(e: any): any { - this.stateWasPopped = true; - let shouldClose = (!e.state || !e.state[this.props.tag]); + let shouldClose = (peekDialogStack() === this.props.tag); if (shouldClose) { - if (this.props.onClose) + if (ignoreNextPop) { - this.props.onClose(e,"backdropClick"); + ignoreNextPop = false; + } else { + if (!this.stateWasPopped) + { + this.stateWasPopped = true; + + if (this.props.onClose) + { + popDialogStack(false); + this.props.onClose(e,"backdropClick"); + } + } } + e.stopPropagation(); } } @@ -76,28 +119,25 @@ class DialogEx extends React.Component { } state[this.props.tag] = true; + pushDialogStack(this.props.tag); // eslint-disable-next-line no-restricted-globals history.pushState( state, "", - "#" + this.props.tag + "" //"#" + this.props.tag ); } else { - window.removeEventListener("popstate",this.handlePopState); if (!this.stateWasPopped) { + this.stateWasPopped = true; + popDialogStack(true); // eslint-disable-next-line no-restricted-globals history.back(); } + window.removeEventListener("popstate",this.handlePopState); } - } } - onWindowSizeChanged(width: number, height: number): void - { - this.setState({fullScreen: height < 200}) - } - componentDidMount() { diff --git a/react/src/MidiBindingsDialog.tsx b/react/src/MidiBindingsDialog.tsx index 1b18fbc..880241b 100644 --- a/react/src/MidiBindingsDialog.tsx +++ b/react/src/MidiBindingsDialog.tsx @@ -18,7 +18,7 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import React, { SyntheticEvent } from 'react'; -import Dialog from '@material-ui/core/Dialog'; +import DialogEx from './DialogEx'; import ResizeResponsiveComponent from './ResizeResponsiveComponent'; import { PiPedalModel, PiPedalModelFactory, ListenHandle } from './PiPedalModel'; import Typography from '@material-ui/core/Typography'; @@ -86,21 +86,12 @@ export const MidiBindingDialog = listenSnackbarOpen: false }; this.model = PiPedalModelFactory.getInstance(); - this.handlePopState = this.handlePopState.bind(this); this.handleClose = this.handleClose.bind(this); } mounted: boolean = false; hasHooks: boolean = false; - stateWasPopped: boolean = false; - handlePopState(e: any): any { - this.stateWasPopped = true; - let shouldClose = (!e.state); - if (shouldClose) { - this.props.onClose(); - } - } handleClose() { this.props.onClose(); } @@ -163,39 +154,6 @@ export const MidiBindingDialog = } - updateHooks(): void { - let wantHooks = this.mounted && this.props.open; - if (wantHooks !== this.hasHooks) { - this.hasHooks = wantHooks; - - if (this.hasHooks) { - this.stateWasPopped = false; - window.addEventListener("popstate", this.handlePopState); - // eslint-disable-next-line no-restricted-globals - let state = history.state; - if (!state) { - state = {}; - } - state.midiBindingDialog = true; - - // eslint-disable-next-line no-restricted-globals - history.pushState( - state, - "Preset MIDI Bindings", - "#MidiBindingDialog" - ); - } else { - window.removeEventListener("popstate", this.handlePopState); - if (!this.stateWasPopped) { - // eslint-disable-next-line no-restricted-globals - history.back(); - } - this.cancelListenForControl(); - - } - - } - } onWindowSizeChanged(width: number, height: number): void { } @@ -203,18 +161,15 @@ export const MidiBindingDialog = componentDidMount() { super.componentDidMount(); this.mounted = true; - this.updateHooks(); } componentWillUnmount() { super.componentWillUnmount(); this.mounted = false; - this.updateHooks(); } componentDidUpdate() { - this.updateHooks(); } handleItemChanged(instanceId: number, newBinding: MidiBinding) { this.model.setMidiBinding(instanceId, newBinding); @@ -311,7 +266,7 @@ export const MidiBindingDialog = } return ( - @@ -353,7 +308,7 @@ export const MidiBindingDialog = onClose={() => this.setState({ listenSnackbarOpen: false })} message="Listening for MIDI input" /> - + ); } }); diff --git a/react/src/PresetDialog.tsx b/react/src/PresetDialog.tsx index 2d5e033..fcbcf9a 100644 --- a/react/src/PresetDialog.tsx +++ b/react/src/PresetDialog.tsx @@ -24,8 +24,8 @@ import { PiPedalModel, PiPedalModelFactory, PresetIndexEntry, PresetIndex } from import Button from "@material-ui/core/Button"; import ButtonBase from "@material-ui/core/ButtonBase"; import { TransitionProps } from '@material-ui/core/transitions/transition'; -import Slide from '@material-ui/core/Slide'; -import Dialog from '@material-ui/core/Dialog'; +import Slide from '@material-ui/core/Slide' ; +import DialogEx from './DialogEx'; import AppBar from '@material-ui/core/AppBar'; import Toolbar from '@material-ui/core/Toolbar'; import { Theme, withStyles, WithStyles, createStyles } from '@material-ui/core/styles'; @@ -334,7 +334,7 @@ const PresetDialog = withStyles(styles, { withTheme: true })( let defaultSelectedIndex = this.getSelectedIndex(); return ( - { this.handleDialogClose() }} TransitionComponent={Transition}>
@@ -456,7 +456,7 @@ const PresetDialog = withStyles(styles, { withTheme: true })( open={this.state.openUploadDialog} onClose={() => { this.setState({ openUploadDialog: false }) }} /> -
+ ); diff --git a/react/src/RenameDialog.tsx b/react/src/RenameDialog.tsx index a0db848..3b5a35b 100644 --- a/react/src/RenameDialog.tsx +++ b/react/src/RenameDialog.tsx @@ -20,7 +20,7 @@ import React from 'react'; import Button from '@material-ui/core/Button'; import TextField from '@material-ui/core/TextField'; -import Dialog from '@material-ui/core/Dialog'; +import DialogEx from './DialogEx'; import DialogActions from '@material-ui/core/DialogActions'; import DialogContent from '@material-ui/core/DialogContent'; import { nullCast } from './Utility'; @@ -49,57 +49,11 @@ export default class RenameDialog extends ResizeResponsiveComponent(); - this.handlePopState = this.handlePopState.bind(this); } mounted: boolean = false; - hasHooks: boolean = false; - stateWasPopped: boolean = false; - handlePopState(e: any): any { - this.stateWasPopped = true; - let shouldClose = (!e.state || !e.state.renameDialog); - if (shouldClose) - { - this.props.onClose(); - } - } - updateHooks() : void { - let wantHooks = this.mounted && this.props.open; - if (wantHooks !== this.hasHooks) - { - this.hasHooks = wantHooks; - - if (this.hasHooks) - { - this.stateWasPopped = false; - window.addEventListener("popstate",this.handlePopState); - // eslint-disable-next-line no-restricted-globals - let state = history.state; - if (!state) - { - state = {}; - } - state.renameDialog = true; - - // eslint-disable-next-line no-restricted-globals - history.pushState( - state, - this.props.acceptActionName, - "#RenameDialog" - ); - } else { - window.removeEventListener("popstate",this.handlePopState); - if (!this.stateWasPopped) - { - // eslint-disable-next-line no-restricted-globals - history.back(); - } - } - - } - } onWindowSizeChanged(width: number, height: number): void { this.setState({fullScreen: height < 200}) @@ -110,18 +64,15 @@ export default class RenameDialog extends ResizeResponsiveComponent @@ -169,7 +120,7 @@ export default class RenameDialog extends ResizeResponsiveComponent - + ); } } \ No newline at end of file diff --git a/react/src/UploadDialog.tsx b/react/src/UploadDialog.tsx index 71160e1..e544661 100644 --- a/react/src/UploadDialog.tsx +++ b/react/src/UploadDialog.tsx @@ -21,7 +21,7 @@ import React from 'react'; import Button from '@material-ui/core/Button'; import {PiPedalModel,PiPedalModelFactory} from './PiPedalModel'; -import Dialog from '@material-ui/core/Dialog'; +import DialogEx from './DialogEx'; import DialogTitle from '@material-ui/core/DialogTitle'; import DialogActions from '@material-ui/core/DialogActions'; import DialogContent from '@material-ui/core/DialogContent'; @@ -55,52 +55,11 @@ export default class UploadDialog extends ResizeResponsiveComponent this.handleClose()} style={{}} + this.handleClose()} style={{}} fullScreen={this.state.fullScreen} > Upload preset @@ -234,7 +193,7 @@ export default class UploadDialog extends ResizeResponsiveComponent - + ); } } \ No newline at end of file diff --git a/react/src/WifiConfigDialog.tsx b/react/src/WifiConfigDialog.tsx index 3dba339..dd9e124 100644 --- a/react/src/WifiConfigDialog.tsx +++ b/react/src/WifiConfigDialog.tsx @@ -20,7 +20,7 @@ import React from 'react'; import Button from '@material-ui/core/Button'; import TextField from '@material-ui/core/TextField'; -import Dialog from '@material-ui/core/Dialog'; +import DialogEx from './DialogEx'; import DialogActions from '@material-ui/core/DialogActions'; import DialogContent from '@material-ui/core/DialogContent'; import DialogTitle from '@material-ui/core/DialogTitle'; @@ -29,7 +29,6 @@ import FormControlLabel from '@material-ui/core/FormControlLabel'; import ResizeResponsiveComponent from './ResizeResponsiveComponent'; import WifiConfigSettings from './WifiConfigSettings'; import NoChangePassword from './NoChangePassword'; -import DialogEx from './DialogEx' import Typography from '@material-ui/core/Typography'; import { Theme, withStyles, WithStyles,createStyles } from '@material-ui/core/styles'; import Select from '@material-ui/core/Select'; @@ -104,60 +103,14 @@ const WifiConfigDialog = withStyles(styles, { withTheme: true })( this.refName = React.createRef(); this.refPassword = React.createRef(); - this.handlePopState = this.handlePopState.bind(this); } mounted: boolean = false; - hasHooks: boolean = false; - - stateWasPopped: boolean = false; - handleEnableChanged(e: any) { this.setState({ enabled: e.target.checked }); } - handlePopState(e: any): any { - this.stateWasPopped = true; - let shouldClose = (!e.state || !e.state.WifiConfig); - if (shouldClose) { - this.preventPasswordPrompt(); - setTimeout(()=> { - this.props.onClose(); - }); - } - } - - updateHooks(): void { - let wantHooks = this.mounted && this.props.open; - if (wantHooks !== this.hasHooks) { - this.hasHooks = wantHooks; - if (this.hasHooks) { - this.stateWasPopped = false; - window.addEventListener("popstate", this.handlePopState); - // eslint-disable-next-line no-restricted-globals - let state = history.state; - if (!state) { - state = {}; - } - state.WifiConfig = true; - - // eslint-disable-next-line no-restricted-globals - history.pushState( - state, - "Wi-Fi Configuration", - "#WifiConfig" - ); - } else { - window.removeEventListener("popstate", this.handlePopState); - if (!this.stateWasPopped) { - // eslint-disable-next-line no-restricted-globals - history.back(); - } - } - - } - } onWindowSizeChanged(width: number, height: number): void { this.setState({ fullScreen: height < 200 }) } @@ -166,16 +119,13 @@ const WifiConfigDialog = withStyles(styles, { withTheme: true })( componentDidMount() { super.componentDidMount(); this.mounted = true; - this.updateHooks(); } componentWillUnmount() { super.componentWillUnmount(); this.mounted = false; - this.updateHooks(); } componentDidUpdate(prevProps: WifiConfigProps) { - this.updateHooks(); if (this.props.open && !prevProps.open) { @@ -300,7 +250,7 @@ const WifiConfigDialog = withStyles(styles, { withTheme: true })( }; return ( - { this.state.fullScreen && ( @@ -407,7 +357,7 @@ const WifiConfigDialog = withStyles(styles, { withTheme: true })( - + ); } });