diff options
Diffstat (limited to 'app/javascript')
11 files changed, 70 insertions, 17 deletions
diff --git a/app/javascript/flavours/glitch/components/error_boundary.js b/app/javascript/flavours/glitch/components/error_boundary.js index 62950a7d3..8998802b1 100644 --- a/app/javascript/flavours/glitch/components/error_boundary.js +++ b/app/javascript/flavours/glitch/components/error_boundary.js @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage } from 'react-intl'; import { preferencesLink } from 'flavours/glitch/util/backend_links'; +import StackTrace from 'stacktrace-js'; export default class ErrorBoundary extends React.PureComponent { @@ -11,15 +12,29 @@ export default class ErrorBoundary extends React.PureComponent { state = { hasError: false, + errorMessage: undefined, stackTrace: undefined, + mappedStackTrace: undefined, componentStack: undefined, } componentDidCatch(error, info) { this.setState({ hasError: true, + errorMessage: error.toString(), stackTrace: error.stack, componentStack: info && info.componentStack, + mappedStackTrace: undefined, + }); + + StackTrace.fromError(error).then((stackframes) => { + this.setState({ + mappedStackTrace: stackframes.map((sf) => sf.toString()).join('\n'), + }); + }).catch(() => { + this.setState({ + mappedStackTrace: undefined, + }); }); } @@ -29,13 +44,16 @@ export default class ErrorBoundary extends React.PureComponent { } render() { - const { hasError, stackTrace, componentStack } = this.state; + const { hasError, errorMessage, stackTrace, mappedStackTrace, componentStack } = this.state; if (!hasError) return this.props.children; let debugInfo = ''; if (stackTrace) { - debugInfo += 'Stack trace\n-----------\n\n```\n' + stackTrace.toString() + '\n```'; + debugInfo += 'Stack trace\n-----------\n\n```\n' + errorMessage + '\n' + stackTrace.toString() + '\n```'; + } + if (mappedStackTrace) { + debugInfo += 'Mapped stack trace\n-----------\n\n```\n' + errorMessage + '\n' + mappedStackTrace.toString() + '\n```'; } if (componentStack) { if (debugInfo) { diff --git a/app/javascript/flavours/glitch/features/compose/components/compose_form.js b/app/javascript/flavours/glitch/features/compose/components/compose_form.js index 6e07998ec..daacbb73f 100644 --- a/app/javascript/flavours/glitch/features/compose/components/compose_form.js +++ b/app/javascript/flavours/glitch/features/compose/components/compose_form.js @@ -93,7 +93,7 @@ class ComposeForm extends ImmutablePureComponent { } } - handleSubmit = () => { + handleSubmit = (overriddenVisibility = null) => { const { textarea: { value }, uploadForm } = this; const { onChange, @@ -106,6 +106,7 @@ class ComposeForm extends ImmutablePureComponent { text, mediaDescriptionConfirmation, onMediaDescriptionConfirm, + onChangeVisibility, } = this.props; // If something changes inside the textarea, then we update the @@ -124,6 +125,9 @@ class ComposeForm extends ImmutablePureComponent { const firstWithoutDescription = media.find(item => !item.get('description')); onMediaDescriptionConfirm(this.context.router ? this.context.router.history : null, firstWithoutDescription.get('id')); } else if (onSubmit) { + if (onChangeVisibility && overriddenVisibility) { + onChangeVisibility(overriddenVisibility); + } onSubmit(this.context.router ? this.context.router.history : null); } } @@ -152,13 +156,9 @@ class ComposeForm extends ImmutablePureComponent { // Handles the secondary submit button. handleSecondarySubmit = () => { const { - onChangeVisibility, sideArm, } = this.props; - if (sideArm !== 'none' && onChangeVisibility) { - onChangeVisibility(sideArm); - } - this.handleSubmit(); + this.handleSubmit(sideArm === 'none' ? null : sideArm); } // Selects a suggestion from the autofill. diff --git a/app/javascript/flavours/glitch/features/compose/components/publisher.js b/app/javascript/flavours/glitch/features/compose/components/publisher.js index b8d9d98bf..97890f40d 100644 --- a/app/javascript/flavours/glitch/features/compose/components/publisher.js +++ b/app/javascript/flavours/glitch/features/compose/components/publisher.js @@ -38,8 +38,12 @@ class Publisher extends ImmutablePureComponent { sideArm: PropTypes.oneOf(['none', 'direct', 'private', 'unlisted', 'public']), }; + handleSubmit = () => { + this.props.onSubmit(); + }; + render () { - const { countText, disabled, intl, onSecondarySubmit, onSubmit, privacy, sideArm } = this.props; + const { countText, disabled, intl, onSecondarySubmit, privacy, sideArm } = this.props; const diff = maxChars - length(countText || ''); const computedClass = classNames('composer--publisher', { @@ -105,7 +109,7 @@ class Publisher extends ImmutablePureComponent { } }()} title={`${intl.formatMessage(messages.publish)}: ${intl.formatMessage({ id: `privacy.${privacy}.short` })}`} - onClick={onSubmit} + onClick={this.handleSubmit} disabled={disabled || diff < 0} /> </div> diff --git a/app/javascript/flavours/glitch/features/keyboard_shortcuts/index.js b/app/javascript/flavours/glitch/features/keyboard_shortcuts/index.js index bc7571200..0bb71e872 100644 --- a/app/javascript/flavours/glitch/features/keyboard_shortcuts/index.js +++ b/app/javascript/flavours/glitch/features/keyboard_shortcuts/index.js @@ -114,6 +114,10 @@ class KeyboardShortcuts extends ImmutablePureComponent { <td><FormattedMessage id='keyboard_shortcuts.search' defaultMessage='to focus search' /></td> </tr> <tr> + <td><kbd>alt</kbd>+<kbd>enter</kbd></td> + <td><FormattedMessage id='keyboard_shortcuts.secondary_toot' defaultMessage='to send toot using secondary privacy setting' /></td> + </tr> + <tr> <td><kbd>esc</kbd></td> <td><FormattedMessage id='keyboard_shortcuts.unfocus' defaultMessage='to un-focus compose textarea/search' /></td> </tr> diff --git a/app/javascript/flavours/glitch/util/base_polyfills.js b/app/javascript/flavours/glitch/util/base_polyfills.js index ad023eb73..4b8123dba 100644 --- a/app/javascript/flavours/glitch/util/base_polyfills.js +++ b/app/javascript/flavours/glitch/util/base_polyfills.js @@ -6,6 +6,7 @@ import assign from 'object-assign'; import values from 'object.values'; import isNaN from 'is-nan'; import { decode as decodeBase64 } from './base64'; +import promiseFinally from 'promise.prototype.finally'; if (!Array.prototype.includes) { includes.shim(); @@ -23,6 +24,8 @@ if (!Number.isNaN) { Number.isNaN = isNaN; } +promiseFinally.shim(); + if (!HTMLCanvasElement.prototype.toBlob) { const BASE64_MARKER = ';base64,'; diff --git a/app/javascript/flavours/glitch/util/load_polyfills.js b/app/javascript/flavours/glitch/util/load_polyfills.js index 8cb81c1a6..73eedc9dc 100644 --- a/app/javascript/flavours/glitch/util/load_polyfills.js +++ b/app/javascript/flavours/glitch/util/load_polyfills.js @@ -18,7 +18,8 @@ function loadPolyfills() { Number.isNaN && Object.assign && Object.values && - window.Symbol + window.Symbol && + Promise.prototype.finally ); // Latest version of Firefox and Safari do not have IntersectionObserver. diff --git a/app/javascript/flavours/glitch/util/main.js b/app/javascript/flavours/glitch/util/main.js index b76826481..1fdb9ff2b 100644 --- a/app/javascript/flavours/glitch/util/main.js +++ b/app/javascript/flavours/glitch/util/main.js @@ -12,7 +12,7 @@ function main() { if (window.history && history.replaceState) { const { pathname, search, hash } = window.location; const path = pathname + search + hash; - if (!(/^\/web[$/]/).test(path)) { + if (!(/^\/web($|\/)/).test(path)) { history.replaceState(null, document.title, `/web${path}`); } } diff --git a/app/javascript/mastodon/base_polyfills.js b/app/javascript/mastodon/base_polyfills.js index 997813a04..12096d902 100644 --- a/app/javascript/mastodon/base_polyfills.js +++ b/app/javascript/mastodon/base_polyfills.js @@ -6,6 +6,7 @@ import assign from 'object-assign'; import values from 'object.values'; import isNaN from 'is-nan'; import { decode as decodeBase64 } from './utils/base64'; +import promiseFinally from 'promise.prototype.finally'; if (!Array.prototype.includes) { includes.shim(); @@ -23,6 +24,8 @@ if (!Number.isNaN) { Number.isNaN = isNaN; } +promiseFinally.shim(); + if (!HTMLCanvasElement.prototype.toBlob) { const BASE64_MARKER = ';base64,'; diff --git a/app/javascript/mastodon/components/error_boundary.js b/app/javascript/mastodon/components/error_boundary.js index 4e1c882e2..ca3012276 100644 --- a/app/javascript/mastodon/components/error_boundary.js +++ b/app/javascript/mastodon/components/error_boundary.js @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage } from 'react-intl'; import { version, source_url } from 'mastodon/initial_state'; +import StackTrace from 'stacktrace-js'; export default class ErrorBoundary extends React.PureComponent { @@ -11,24 +12,42 @@ export default class ErrorBoundary extends React.PureComponent { state = { hasError: false, + errorMessage: undefined, stackTrace: undefined, + mappedStackTrace: undefined, componentStack: undefined, }; componentDidCatch (error, info) { this.setState({ hasError: true, + errorMessage: error.toString(), stackTrace: error.stack, componentStack: info && info.componentStack, - copied: false, + mappedStackTrace: undefined, + }); + + StackTrace.fromError(error).then((stackframes) => { + this.setState({ + mappedStackTrace: stackframes.map((sf) => sf.toString()).join('\n'), + }); + }).catch(() => { + this.setState({ + mappedStackTrace: undefined, + }); }); } handleCopyStackTrace = () => { - const { stackTrace } = this.state; + const { errorMessage, stackTrace, mappedStackTrace } = this.state; const textarea = document.createElement('textarea'); - textarea.textContent = stackTrace; + let contents = [errorMessage, stackTrace]; + if (mappedStackTrace) { + contents.push(mappedStackTrace); + } + + textarea.textContent = contents.join('\n\n\n'); textarea.style.position = 'fixed'; document.body.appendChild(textarea); diff --git a/app/javascript/mastodon/load_polyfills.js b/app/javascript/mastodon/load_polyfills.js index 8cb81c1a6..73eedc9dc 100644 --- a/app/javascript/mastodon/load_polyfills.js +++ b/app/javascript/mastodon/load_polyfills.js @@ -18,7 +18,8 @@ function loadPolyfills() { Number.isNaN && Object.assign && Object.values && - window.Symbol + window.Symbol && + Promise.prototype.finally ); // Latest version of Firefox and Safari do not have IntersectionObserver. diff --git a/app/javascript/mastodon/main.js b/app/javascript/mastodon/main.js index 5d73caa10..da4884fd3 100644 --- a/app/javascript/mastodon/main.js +++ b/app/javascript/mastodon/main.js @@ -12,7 +12,7 @@ function main() { if (window.history && history.replaceState) { const { pathname, search, hash } = window.location; const path = pathname + search + hash; - if (!(/^\/web[$/]/).test(path)) { + if (!(/^\/web($|\/)/).test(path)) { history.replaceState(null, document.title, `/web${path}`); } } |