(core) Record new user sign-ups

Summary:
Adds Google Tag Manager snippet to all login pages, and a new user
preference, recordSignUpEvent, that's set to true on first sign-in. The
client now checks for this preference, and if true, dynamically loads
Google Tag Manager to record a sign-up event. Afterwards, it removes
the preference.

Test Plan: Tested manually.

Reviewers: dsagal

Reviewed By: dsagal

Subscribers: dsagal

Differential Revision: https://phab.getgrist.com/D3319
This commit is contained in:
George Gevoian
2022-03-11 12:35:29 -08:00
parent eff78ae2e1
commit ad1b4f3cff
7 changed files with 84 additions and 31 deletions

View File

@@ -722,11 +722,14 @@ export class FlexServer implements GristServer {
// Reset isFirstTimeUser flag.
await this._dbManager.updateUser(user.id, {isFirstTimeUser: false});
// This is a good time to set another flag (showNewUserQuestions), to show a popup with
// welcome question(s) to this new user. Both flags are scoped to the user, but
// isFirstTimeUser has a dedicated DB field because it predates userPrefs. Note that the
// updateOrg() method handles all levels of prefs (for user, user+org, or org).
await this._dbManager.updateOrg(getScope(req), 0, {userPrefs: {showNewUserQuestions: true}});
// This is a good time to set some other flags, for showing a popup with welcome question(s)
// to this new user and recording their sign-up with Google Tag Manager. These flags are also
// scoped to the user, but isFirstTimeUser has a dedicated DB field because it predates userPrefs.
// Note that the updateOrg() method handles all levels of prefs (for user, user+org, or org).
await this._dbManager.updateOrg(getScope(req), 0, {userPrefs: {
showNewUserQuestions: true,
recordSignUpEvent: true,
}});
// Redirect to teams page if users has access to more than one org. Otherwise, redirect to
// personal org.
@@ -1138,7 +1141,7 @@ export class FlexServer implements GristServer {
// These are some special-purpose welcome pages, with no middleware.
this.app.get(/\/welcome\/(signup|verify|teams|select-account)/, expressWrap(async (req, resp, next) => {
return this._sendAppPage(req, resp, {path: 'app.html', status: 200, config: {}, googleTagManager: true});
return this._sendAppPage(req, resp, {path: 'app.html', status: 200, config: {}, googleTagManager: 'anon'});
}));
this.app.post('/welcome/info', ...middleware, expressWrap(async (req, resp, next) => {

View File

@@ -1,4 +1,5 @@
import {GristLoadConfig} from 'app/common/gristUrls';
import {getTagManagerSnippet} from 'app/common/tagManager';
import {isAnonymousUser} from 'app/server/lib/Authorizer';
import {RequestWithOrg} from 'app/server/lib/extractOrg';
import {GristServer} from 'app/server/lib/GristServer';
@@ -47,6 +48,7 @@ export function makeGristConfig(homeUrl: string|null, extra: Partial<GristLoadCo
timestampMs: Date.now(),
enableWidgetRepository: Boolean(process.env.GRIST_WIDGET_LIST_URL),
survey: Boolean(process.env.DOC_ID_NEW_USER_INFO),
tagManagerId: process.env.GOOGLE_TAG_MANAGER_ID,
...extra,
};
}
@@ -85,7 +87,7 @@ export function makeSendAppPage(opts: {
const needTagManager = (options.googleTagManager === 'anon' && isAnonymousUser(req)) ||
options.googleTagManager === true;
const tagManagerSnippet = needTagManager ? getTagManagerSnippet() : '';
const tagManagerSnippet = needTagManager ? getTagManagerSnippet(process.env.GOOGLE_TAG_MANAGER_ID) : '';
const staticOrigin = process.env.APP_STATIC_URL || "";
const staticBaseUrl = `${staticOrigin}/v/${options.tag || tag}/`;
const warning = testLogin ? "<div class=\"dev_warning\">Authentication is not enforced</div>" : "";
@@ -101,25 +103,3 @@ function shouldSupportAnon() {
// Enable UI for anonymous access if a flag is explicitly set in the environment
return process.env.GRIST_SUPPORT_ANON === "true";
}
/**
* Returns the Google Tag Manager snippet to insert into <head> of the page, if
* GOOGLE_TAG_MANAGER_ID env var is set to a non-empty value. Otherwise returns the empty string.
*/
function getTagManagerSnippet() {
// Note also that we only insert the snippet for the <head>. The second recommended part (for
// <body>) is for <noscript> scenario, which doesn't apply to the Grist app (such visits, if
// any, wouldn't work and shouldn't be counted for any metrics we care about).
const tagId = process.env.GOOGLE_TAG_MANAGER_ID;
if (!tagId) { return ""; }
return `
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','${tagId}');</script>
<!-- End Google Tag Manager -->
`;
}