diff options
Diffstat (limited to 'app/javascript/mastodon/features/ui/index.js')
-rw-r--r-- | app/javascript/mastodon/features/ui/index.js | 30 |
1 files changed, 25 insertions, 5 deletions
diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js index 9f77ab5aa..69eb1bbf7 100644 --- a/app/javascript/mastodon/features/ui/index.js +++ b/app/javascript/mastodon/features/ui/index.js @@ -40,18 +40,23 @@ import { PinnedStatuses, } 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 '../../../glitch/components/status'; +const messages = defineMessages({ + beforeUnload: { id: 'ui.beforeunload', defaultMessage: 'Your draft will be lost if you leave Mastodon.' }, +}); + const mapStateToProps = state => ({ - systemFontUi: state.getIn(['meta', 'system_font_ui']), + isComposing: state.getIn(['compose', 'is_composing']), + hasComposingText: state.getIn(['compose', 'text']) !== '', layout: state.getIn(['local_settings', 'layout']), isWide: state.getIn(['local_settings', 'stretch']), navbarUnder: state.getIn(['local_settings', 'navbar_under']), - me: state.getIn(['meta', 'me']), - isComposing: state.getIn(['compose', 'is_composing']), }); const keyMap = { @@ -82,6 +87,7 @@ const keyMap = { }; @connect(mapStateToProps) +@injectIntl @withRouter export default class UI extends React.Component { @@ -97,8 +103,9 @@ export default class UI extends React.Component { systemFontUi: PropTypes.bool, navbarUnder: PropTypes.bool, isComposing: PropTypes.bool, - me: PropTypes.string, + hasComposingText: PropTypes.bool, location: PropTypes.object, + intl: PropTypes.object.isRequired, }; state = { @@ -106,6 +113,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()); @@ -180,6 +198,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); @@ -222,6 +241,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); @@ -321,7 +341,7 @@ export default class UI extends React.Component { } handleHotkeyGoToProfile = () => { - this.context.router.history.push(`/accounts/${this.props.me}`); + this.context.router.history.push(`/accounts/${me}`); } handleHotkeyGoToBlocked = () => { |