diff --git a/app/client/lib/helpScout.ts b/app/client/lib/helpScout.ts
index 110318ed..19c522cf 100644
--- a/app/client/lib/helpScout.ts
+++ b/app/client/lib/helpScout.ts
@@ -15,7 +15,8 @@
// tslint:disable:unified-signatures
-import {AppModel, reportError} from 'app/client/models/AppModel';
+import {AppModel} from 'app/client/models/AppModel';
+import {reportWarning} from 'app/client/models/errors';
import {IAppError} from 'app/client/models/NotifyModel';
import {GristLoadConfig} from 'app/common/gristUrls';
import {timeFormat} from 'app/common/timeFormat';
@@ -64,7 +65,8 @@ export function Beacon(method: 'navigate', route: string): void;
export function Beacon(method: 'identify', userObj: IUserObj): void;
export function Beacon(method: 'prefill', formObj: IFormObj): void;
export function Beacon(method: 'config', configObj: object): void;
-export function Beacon(method: 'on'|'off'|'once', event: 'open'|'close', callback: () => void): void;
+export function Beacon(method: 'on'|'once', event: string, callback: () => void): void;
+export function Beacon(method: 'off', event: string, callback?: () => void): void;
export function Beacon(method: 'session-data', data: ISessionData): void;
export function Beacon(method: BeaconCmd): void;
export function Beacon(method: BeaconCmd, options?: unknown, data?: unknown) {
@@ -85,21 +87,30 @@ function initBeacon(): void {
const beaconId = gristConfig && gristConfig.helpScoutBeaconId;
if (beaconId) {
(window as any).Beacon = _beacon;
- document.head.appendChild(dom('script', {
- type: 'text/javascript',
- src: 'https://beacon-v2.helpscout.net',
- async: true,
- }));
+ document.head.appendChild(dom('script',
+ {
+ type: 'text/javascript',
+ src: 'https://beacon-v2.helpscout.net',
+ async: true,
+ },
+ // Report when the beacon fails to load so that the user knows something is wrong, and we
+ // have a log of the error. (Note: might not report all failures due to ad-blockers.)
+ dom.on('error', (e) => {
+ reportWarning("Support form failed to load. " +
+ "Please email support@getgrist.com with questions instead.");
+ }),
+ ));
_beacon('init', beaconId);
_beacon('config', {display: {style: "manual"}});
} else {
(window as any).Beacon = () => null;
- reportError(new Error("Support form is not configured"));
+ reportWarning("Support form is not configured");
}
}
}
let lastOpenType: 'error' | 'message' = 'message';
+let lastRoute: BeaconRoute|null = null;
/**
* Helper to open a beacon, taking care of setting focus appropriately. Calls optional onOpen
@@ -118,31 +129,33 @@ function _beaconOpen(userObj: IUserObj|null, options: IBeaconOpenOptions) {
lastOpenType = openType;
}
+ const route: BeaconRoute = options.route || (errors?.length ? '/ask/message/' : '/answers/');
+ // If beacon was and still is being opened for help articles, avoid the 'navigate' call
+ // altogether, to keep the beacon at the last article it was on.
+ const skipNav = (route === lastRoute && route === '/answers/');
+ lastRoute = route;
+
Beacon('once', 'open', () => {
const iframe = document.querySelector('#beacon-container iframe') as HTMLIFrameElement;
if (iframe) { iframe.focus(); }
if (onOpen) { onOpen(); }
});
- Beacon('once', 'article-viewed' as any, () => {
- // HelpScout creates an iframe with an empty 'src' attribute, then writes to it. In such an
- // iframe, different browsers interpret relative links differently: Chrome's are relative to
- // the parent page's URL; Firefox's are relative to the parent page's .
- //
- // Here we set a explicitly in the iframe to get consistent behavior of links
- // relative to the top page's URL (HelpScout then seems to handle clicks on them correctly).
- const iframe = document.querySelector('#beacon-container iframe') as HTMLIFrameElement;
- iframe?.contentDocument?.head.appendChild(dom('base', {href: ''}));
- });
+ // Fix base-href tag when opening an article.
+ Beacon('once', 'article-viewed', () => fixBeaconBaseHref());
+ // We duplicate this check for 'ready' event, because 'open' and 'article-viewed' events don't
+ // trigger on page reload when a beacon article is already open (seems to be a HelpScout bug).
+ Beacon('once', 'ready', () => fixBeaconBaseHref());
+
Beacon('once', 'close', () => {
const iframe = document.querySelector('#beacon-container iframe') as HTMLIFrameElement;
if (iframe) { iframe.blur(); }
+ Beacon('off', 'article-viewed');
});
if (userObj) {
Beacon('identify', userObj);
}
const attrs: ISessionData = {};
- let route: BeaconRoute;
if (errors?.length) {
// If sending errors, prefill part of the message (the user sees this and can add to it), and
// include more detailed errors with stack traces into session-data.
@@ -162,10 +175,8 @@ function _beaconOpen(userObj: IUserObj|null, options: IBeaconOpenOptions) {
attrs[`error-${i}-stack`] = JSON.stringify(error.stack.trim().split('\n'));
}
});
- route = options.route || '/ask/message/';
} else {
Beacon('config', {messaging: {contactForm: {showSubject: true}}});
- route = options.route || '/answers/';
}
Beacon('session-data', {
@@ -173,7 +184,23 @@ function _beaconOpen(userObj: IUserObj|null, options: IBeaconOpenOptions) {
...attrs,
});
Beacon('open');
- Beacon('navigate', route);
+ if (!skipNav) {
+ Beacon('navigate', route);
+ }
+}
+
+function fixBeaconBaseHref() {
+ // HelpScout creates an iframe with an empty 'src' attribute, then writes to it. In such an
+ // iframe, different browsers interpret relative links differently: Chrome's are relative to
+ // the parent page's URL; Firefox's are relative to the parent page's .
+ //
+ // Here we set a explicitly in the iframe to get consistent behavior of links
+ // relative to the top page's URL (HelpScout then seems to handle clicks on them correctly).
+ const iframe = document.querySelector('#beacon-container iframe') as HTMLIFrameElement;
+ const iframeDoc = iframe?.contentDocument;
+ if (iframeDoc && !iframeDoc.querySelector('head > base')) {
+ iframeDoc.head.appendChild(dom('base', {href: ''}));
+ }
}
export interface IBeaconOpenOptions {
diff --git a/app/client/models/errors.ts b/app/client/models/errors.ts
index 72d25cdb..a6e0b207 100644
--- a/app/client/models/errors.ts
+++ b/app/client/models/errors.ts
@@ -53,10 +53,14 @@ export function reportMessage(msg: string, options?: Partial) {
}
/**
- * Shows warning toast notification (with yellow styling).
+ * Shows warning toast notification (with yellow styling), and log to server and to console. Pass
+ * {level: 'error'} for same behavior with adjusted styling.
*/
export function reportWarning(msg: string, options?: Partial) {
- reportMessage(msg, {level: 'warning', ...options});
+ options = {level: 'warning', ...options};
+ log.warn(`${options.level}: `, msg);
+ _logError(msg);
+ reportMessage(msg, options);
}
/**