]> cat aescling's git repositories - mastodon.git/commitdiff
Show confirmation dialog on leaving WebUI while composing (#5616)
authorunarist <m.unarist@gmail.com>
Thu, 9 Nov 2017 13:34:41 +0000 (22:34 +0900)
committerEugen Rochko <eugen@zeonfederated.com>
Thu, 9 Nov 2017 13:34:41 +0000 (14:34 +0100)
* Show confirmation dialog on leaving WebUI while composing

Currently, Back button and Back hotkey can cause leaving from WebUI, as well as browser's back button. Users may hit those buttons accidentally, and their composing text will be lost.

So this prevents it by showing confirmation dialog from `onbeforeunload` event.

* Fix message and comments

app/javascript/mastodon/features/ui/index.js

index 8a0b00fc1c9680b96740c113c2e93446039a24d2..f28b3709926a0971335f14991d566bf3a8e1de7b 100644 (file)
@@ -39,13 +39,19 @@ import {
 } from './util/async-components';
 import { HotKeys } from 'react-hotkeys';
 import { me } from '../../initial_state';
+import { defineMessages, injectIntl } from 'react-intl';
 
 // Dummy import, to make sure that <Status /> ends up in the application bundle.
 // Without this it ends up in ~8 very commonly used bundles.
 import '../../components/status';
 
+const messages = defineMessages({
+  beforeUnload: { id: 'ui.beforeunload', defaultMessage: 'Your draft will be lost if you leave Mastodon.' },
+});
+
 const mapStateToProps = state => ({
   isComposing: state.getIn(['compose', 'is_composing']),
+  hasComposingText: state.getIn(['compose', 'text']) !== '',
 });
 
 const keyMap = {
@@ -75,6 +81,7 @@ const keyMap = {
 };
 
 @connect(mapStateToProps)
+@injectIntl
 @withRouter
 export default class UI extends React.Component {
 
@@ -86,7 +93,9 @@ export default class UI extends React.Component {
     dispatch: PropTypes.func.isRequired,
     children: PropTypes.node,
     isComposing: PropTypes.bool,
+    hasComposingText: PropTypes.bool,
     location: PropTypes.object,
+    intl: PropTypes.object.isRequired,
   };
 
   state = {
@@ -94,6 +103,17 @@ export default class UI extends React.Component {
     draggingOver: false,
   };
 
+  handleBeforeUnload = (e) => {
+    const { intl, isComposing, hasComposingText } = this.props;
+
+    if (isComposing && hasComposingText) {
+      // Setting returnValue to any string causes confirmation dialog.
+      // Many browsers no longer display this text to users,
+      // but we set user-friendly message for other browsers, e.g. Edge.
+      e.returnValue = intl.formatMessage(messages.beforeUnload);
+    }
+  }
+
   handleResize = debounce(() => {
     // The cached heights are no longer accurate, invalidate
     this.props.dispatch(clearHeight());
@@ -168,6 +188,7 @@ export default class UI extends React.Component {
   }
 
   componentWillMount () {
+    window.addEventListener('beforeunload', this.handleBeforeUnload, false);
     window.addEventListener('resize', this.handleResize, { passive: true });
     document.addEventListener('dragenter', this.handleDragEnter, false);
     document.addEventListener('dragover', this.handleDragOver, false);
@@ -209,6 +230,7 @@ export default class UI extends React.Component {
   }
 
   componentWillUnmount () {
+    window.removeEventListener('beforeunload', this.handleBeforeUnload);
     window.removeEventListener('resize', this.handleResize);
     document.removeEventListener('dragenter', this.handleDragEnter);
     document.removeEventListener('dragover', this.handleDragOver);