about summary refs log tree commit diff
path: root/app/javascript
diff options
context:
space:
mode:
Diffstat (limited to 'app/javascript')
-rw-r--r--app/javascript/flavours/glitch/components/error_boundary.js22
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/compose_form.js12
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/publisher.js8
-rw-r--r--app/javascript/flavours/glitch/features/keyboard_shortcuts/index.js4
-rw-r--r--app/javascript/flavours/glitch/util/base_polyfills.js3
-rw-r--r--app/javascript/flavours/glitch/util/load_polyfills.js3
-rw-r--r--app/javascript/flavours/glitch/util/main.js2
-rw-r--r--app/javascript/mastodon/base_polyfills.js3
-rw-r--r--app/javascript/mastodon/components/error_boundary.js25
-rw-r--r--app/javascript/mastodon/load_polyfills.js3
-rw-r--r--app/javascript/mastodon/main.js2
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}`);
     }
   }