mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
(core) Add sign-up and sharing/invite telemetry
Summary: Enhances sign-up telemetry with login and verification method metadata, and adds UTM parameters to SendGrid invite email links and Grist document links. Test Plan: Server and manual. Reviewers: dsagal Reviewed By: dsagal Subscribers: dsagal Differential Revision: https://phab.getgrist.com/D4169
This commit is contained in:
parent
dba3a59486
commit
b77c762358
@ -311,6 +311,8 @@ DetailView.prototype.buildFieldDom = function(field, row) {
|
||||
kd.toggleClass('scissors', isCopyActive),
|
||||
kd.toggleClass('record-add', row._isAddRow),
|
||||
dom.autoDispose(isCopyActive),
|
||||
// Optional icon. Currently only use to show formula icon.
|
||||
dom('div.field-icon'),
|
||||
fieldBuilder.buildDomWithCursor(row, isCellActive, isCellSelected)
|
||||
)
|
||||
);
|
||||
|
@ -15,6 +15,7 @@ import {buildUpgradeButton} from 'app/client/ui/ProductUpgrades';
|
||||
import {buildTutorialCard} from 'app/client/ui/TutorialCard';
|
||||
import {buildPinnedDoc, createPinnedDocs} from 'app/client/ui/PinnedDocs';
|
||||
import {shadowScroll} from 'app/client/ui/shadowScroll';
|
||||
import {makeShareDocUrl} from 'app/client/ui/ShareMenu';
|
||||
import {transition} from 'app/client/ui/transitions';
|
||||
import {shouldShowWelcomeCoachingCall, showWelcomeCoachingCall} from 'app/client/ui/WelcomeCoachingCall';
|
||||
import {shouldShowWelcomeQuestions, showWelcomeQuestions} from 'app/client/ui/WelcomeQuestions';
|
||||
@ -496,7 +497,7 @@ export function makeDocOptionsMenu(home: HomeModel, doc: Document, renaming: Obs
|
||||
resourceType: 'document',
|
||||
resourceId: doc.id,
|
||||
resource: doc,
|
||||
linkToCopy: urlState().makeUrl(docUrl(doc)),
|
||||
linkToCopy: makeShareDocUrl(doc),
|
||||
reload: () => api.getDocAccess(doc.id),
|
||||
appModel: home.app,
|
||||
});
|
||||
|
@ -305,7 +305,7 @@ async function manageUsers(doc: DocInfo, docPageModel: DocPageModel) {
|
||||
resource: doc,
|
||||
docPageModel,
|
||||
appModel: docPageModel.appModel,
|
||||
linkToCopy: urlState().makeUrl(docUrl(doc)),
|
||||
linkToCopy: makeShareDocUrl(doc),
|
||||
// On save, re-fetch the document info, to toggle the "Public Access" icon if it changed.
|
||||
// Skip if personal, since personal cannot affect "Public Access", and the only
|
||||
// change possible is to remove the user (which would make refreshCurrentDoc fail)
|
||||
@ -314,6 +314,12 @@ async function manageUsers(doc: DocInfo, docPageModel: DocPageModel) {
|
||||
});
|
||||
}
|
||||
|
||||
export function makeShareDocUrl(doc: Document) {
|
||||
const url = new URL(urlState().makeUrl(docUrl(doc)));
|
||||
url.searchParams.set('utm_id', 'share-doc');
|
||||
return url.href;
|
||||
}
|
||||
|
||||
const cssShareButton = styled('div', `
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -770,10 +770,14 @@ export const TelemetryContracts: TelemetryContracts = {
|
||||
},
|
||||
signupFirstVisit: {
|
||||
category: 'ProductVisits',
|
||||
description: 'Triggered when a new user first opens the Grist app',
|
||||
description: 'Triggered when a new user first opens the Grist app.',
|
||||
minimumTelemetryLevel: Level.full,
|
||||
retentionPeriod: 'indefinitely',
|
||||
metadataContracts: {
|
||||
loginMethod: {
|
||||
description: 'The login method on getgrist.com. May be "Email + Password" or "Google".',
|
||||
dataType: 'string',
|
||||
},
|
||||
siteId: {
|
||||
description: 'The site id of first visit after signup.',
|
||||
dataType: 'number',
|
||||
@ -798,6 +802,10 @@ export const TelemetryContracts: TelemetryContracts = {
|
||||
minimumTelemetryLevel: Level.full,
|
||||
retentionPeriod: 'indefinitely',
|
||||
metadataContracts: {
|
||||
verificationMethod: {
|
||||
description: 'The verification method. May be "code" or "link".',
|
||||
dataType: 'string',
|
||||
},
|
||||
isAnonymousTemplateSignup: {
|
||||
description: 'Whether the user viewed any templates before signing up.',
|
||||
dataType: 'boolean',
|
||||
|
@ -144,7 +144,7 @@ describe('ApiServerAccess', function() {
|
||||
// We should send mail about this one, since Kiwi had no access previously.
|
||||
if (notificationsConfig) {
|
||||
const mail = await assertLastMail();
|
||||
assert.match(mail.description, /^invite kiwi@getgrist.com to http.*\/o\/docs\/$/);
|
||||
assert.match(mail.description, /^invite kiwi@getgrist.com to http.*\/o\/docs\/\?utm_id=invite-org$/);
|
||||
const env = mail.payload.personalizations[0].dynamic_template_data;
|
||||
assert.deepEqual(pick(env, ['resource.name', 'resource.kind', 'resource.kindUpperFirst',
|
||||
'resource.isTeamSite', 'resource.isWorkspace', 'resource.isDocument',
|
||||
@ -159,7 +159,7 @@ describe('ApiServerAccess', function() {
|
||||
user: {name: 'Kiwi', email: 'kiwi@getgrist.com'},
|
||||
access: {role: 'editors', canEdit: true, canView: true}
|
||||
} as any);
|
||||
assert.match(env.resource.url, /^http.*\/o\/docs\/$/);
|
||||
assert.match(env.resource.url, /^http.*\/o\/docs\/\?utm_id=invite-org$/);
|
||||
assert.deepEqual(mail.payload.personalizations[0].to[0], {email: 'kiwi@getgrist.com', name: 'Kiwi'});
|
||||
assert.deepEqual(mail.payload.from, {email: 'support@getgrist.com', name: 'Chimpy (via Grist)'});
|
||||
assert.deepEqual(mail.payload.reply_to, {email: 'chimpy@getgrist.com', name: 'Chimpy'});
|
||||
@ -207,7 +207,10 @@ describe('ApiServerAccess', function() {
|
||||
const resp4 = await axios.patch(`${homeUrl}/api/orgs/${nasaOrgId}/access`, {delta: delta4}, chimpy);
|
||||
assert.equal(resp4.status, 200);
|
||||
if (notificationsConfig) {
|
||||
assert.match((await assertLastMail()).description, /^invite kiwi@getgrist.com to http.*\/o\/nasa\/$/);
|
||||
assert.match(
|
||||
(await assertLastMail()).description,
|
||||
/^invite kiwi@getgrist.com to http.*\/o\/nasa\/\?utm_id=invite-org$/
|
||||
);
|
||||
}
|
||||
// Assert that the number of users in the org has updated (Kiwi was added).
|
||||
assert.deepEqual(userCountUpdates[nasaOrgId as number], [2]);
|
||||
@ -349,9 +352,9 @@ describe('ApiServerAccess', function() {
|
||||
// Check we would sent an email to Kiwi about this
|
||||
if (notificationsConfig) {
|
||||
const mail = await assertLastMail();
|
||||
assert.match(mail.description, /^invite kiwi@getgrist.com to http.*\/o\/docs\/ws\/[0-9]+\/$/);
|
||||
assert.match(mail.description, /^invite kiwi@getgrist.com to http.*\/o\/docs\/ws\/[0-9]+\/\?utm_id=invite-ws$/);
|
||||
const env = mail.payload.personalizations[0].dynamic_template_data;
|
||||
assert.match(env.resource.url, /^http.*\/o\/docs\/ws\/[0-9]+\/$/);
|
||||
assert.match(env.resource.url, /^http.*\/o\/docs\/ws\/[0-9]+\/\?utm_id=invite-ws$/);
|
||||
assert.equal(env.resource.kind, 'workspace');
|
||||
assert.equal(env.resource.kindUpperFirst, 'Workspace');
|
||||
assert.equal(env.resource.isTeamSite, false);
|
||||
|
Loading…
Reference in New Issue
Block a user