diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js index 8a0b00fc1c9..f28b3709926 100644 --- a/app/javascript/mastodon/features/ui/index.js +++ b/app/javascript/mastodon/features/ui/index.js @@ -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 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);