mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Add additional telemetry events
Summary: The new events capture usage of forms, widgets, access rules, and onboarding tours and tips. Test Plan: Manual. Reviewers: dsagal Reviewed By: dsagal Subscribers: dsagal Differential Revision: https://phab.getgrist.com/D4189
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import {showBehavioralPrompt} from 'app/client/components/modals';
|
||||
import {logTelemetryEvent} from 'app/client/lib/telemetry';
|
||||
import {AppModel} from 'app/client/models/AppModel';
|
||||
import {getUserPrefObs} from 'app/client/models/UserPrefs';
|
||||
import {GristBehavioralPrompts} from 'app/client/ui/GristTooltips';
|
||||
@@ -159,6 +160,8 @@ export class BehavioralPromptsManager extends Disposable {
|
||||
});
|
||||
dom.onElem(refElement, 'click', () => close());
|
||||
dom.onDisposeElem(refElement, () => close());
|
||||
|
||||
logTelemetryEvent('viewedTip', {full: {tipName: prompt}});
|
||||
}
|
||||
|
||||
private _showNextQueuedTip() {
|
||||
|
||||
@@ -2,7 +2,7 @@ import {cssBannerLink} from 'app/client/components/Banner';
|
||||
import {DocPageModel} from 'app/client/models/DocPageModel';
|
||||
import {urlState} from 'app/client/models/gristUrlState';
|
||||
import {docListHeader} from 'app/client/ui/DocMenuCss';
|
||||
import {GristTooltips, TooltipContentFunc} from 'app/client/ui/GristTooltips';
|
||||
import {Tooltip} from 'app/client/ui/GristTooltips';
|
||||
import {withInfoTooltip} from 'app/client/ui/tooltips';
|
||||
import {mediaXSmall, theme} from 'app/client/ui2018/cssVars';
|
||||
import {icon} from 'app/client/ui2018/icons';
|
||||
@@ -81,7 +81,7 @@ export class DocumentUsage extends Disposable {
|
||||
maximumValue: maxValue ?? DEFAULT_MAX_DATA_SIZE,
|
||||
unit: 'MB',
|
||||
shouldHideLimits: maxValue === undefined,
|
||||
tooltipContentFunc: GristTooltips.dataSize,
|
||||
tooltip: 'dataSize',
|
||||
formatValue: (val) => {
|
||||
// To display a nice, round number for `maximumValue`, we first convert
|
||||
// to KiBs (base-2), and then convert to MBs (base-10). Normally, we wouldn't
|
||||
@@ -271,7 +271,7 @@ interface MetricOptions {
|
||||
// If true, limits will always be hidden, even if `maximumValue` is a positive number.
|
||||
shouldHideLimits?: boolean;
|
||||
// Shows an icon next to the metric name that displays a tooltip on hover.
|
||||
tooltipContentFunc?: TooltipContentFunc;
|
||||
tooltip?: Tooltip;
|
||||
formatValue?(value: number): string;
|
||||
}
|
||||
|
||||
@@ -281,14 +281,11 @@ interface MetricOptions {
|
||||
* close `currentValue` is to hitting `maximumValue`.
|
||||
*/
|
||||
function buildUsageMetric(options: MetricOptions, ...domArgs: DomElementArg[]) {
|
||||
const {name, tooltipContentFunc} = options;
|
||||
const {name, tooltip} = options;
|
||||
return cssUsageMetric(
|
||||
cssMetricName(
|
||||
tooltipContentFunc
|
||||
? withInfoTooltip(
|
||||
cssOverflowableText(name, testId('name')),
|
||||
tooltipContentFunc()
|
||||
)
|
||||
tooltip
|
||||
? withInfoTooltip(cssOverflowableText(name, testId('name')), tooltip)
|
||||
: cssOverflowableText(name, testId('name')),
|
||||
),
|
||||
buildUsageProgressBar(options),
|
||||
|
||||
@@ -11,6 +11,7 @@ import {Disposable} from 'app/client/lib/dispose';
|
||||
import {AsyncComputed, makeTestId, stopEvent} from 'app/client/lib/domUtils';
|
||||
import {makeT} from 'app/client/lib/localization';
|
||||
import {localStorageBoolObs} from 'app/client/lib/localStorageObs';
|
||||
import {logTelemetryEvent} from 'app/client/lib/telemetry';
|
||||
import DataTableModel from 'app/client/models/DataTableModel';
|
||||
import {ViewFieldRec, ViewSectionRec} from 'app/client/models/DocModel';
|
||||
import {ShareRec} from 'app/client/models/entities/ShareRec';
|
||||
@@ -514,6 +515,13 @@ export class FormView extends Disposable {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
logTelemetryEvent('publishedForm', {
|
||||
full: {
|
||||
docIdDigest: this.gristDoc.docId(),
|
||||
},
|
||||
});
|
||||
|
||||
await this.gristDoc.docModel.docData.bundleActions('Publish form', async () => {
|
||||
if (!validShare) {
|
||||
const shareRef = await this.gristDoc.docModel.docData.sendAction([
|
||||
@@ -573,6 +581,12 @@ export class FormView extends Disposable {
|
||||
}
|
||||
|
||||
private async _unpublishForm() {
|
||||
logTelemetryEvent('unpublishedForm', {
|
||||
full: {
|
||||
docIdDigest: this.gristDoc.docId(),
|
||||
},
|
||||
});
|
||||
|
||||
await this.gristDoc.docModel.docData.bundleActions('Unpublish form', async () => {
|
||||
this.viewSection.shareOptionsObj.update({
|
||||
publish: false,
|
||||
|
||||
@@ -47,9 +47,10 @@ import {DocTutorial} from 'app/client/ui/DocTutorial';
|
||||
import {DocSettingsPage} from 'app/client/ui/DocumentSettings';
|
||||
import {isTourActive} from "app/client/ui/OnBoardingPopups";
|
||||
import {DefaultPageWidget, IPageWidget, toPageWidget} from 'app/client/ui/PageWidgetPicker';
|
||||
import {linkFromId, selectBy} from 'app/client/ui/selectBy';
|
||||
import {linkFromId, NoLink, selectBy} from 'app/client/ui/selectBy';
|
||||
import {WebhookPage} from 'app/client/ui/WebhookPage';
|
||||
import {startWelcomeTour} from 'app/client/ui/WelcomeTour';
|
||||
import {getTelemetryWidgetTypeFromPageWidget} from 'app/client/ui/widgetTypesMap';
|
||||
import {PlayerState, YouTubePlayer} from 'app/client/ui/YouTubePlayer';
|
||||
import {isNarrowScreen, mediaSmall, mediaXSmall, testId, theme} from 'app/client/ui2018/cssVars';
|
||||
import {IconName} from 'app/client/ui2018/IconList';
|
||||
@@ -882,6 +883,13 @@ export class GristDoc extends DisposableWithEvents {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const widgetType = getTelemetryWidgetTypeFromPageWidget(val);
|
||||
logTelemetryEvent('addedWidget', {full: {docIdDigest: this.docId(), widgetType}});
|
||||
if (val.link !== NoLink) {
|
||||
logTelemetryEvent('linkedWidget', {full: {docIdDigest: this.docId(), widgetType}});
|
||||
}
|
||||
|
||||
const res: {sectionRef: number} = await docData.bundleActions(
|
||||
t("Added new linked section to view {{viewName}}", {viewName}),
|
||||
() => this.addWidgetToPageImpl(val, tableId ?? null)
|
||||
@@ -932,6 +940,14 @@ export class GristDoc extends DisposableWithEvents {
|
||||
* Adds a new page (aka: view) with a single view section (aka: page widget) described by `val`.
|
||||
*/
|
||||
public async addNewPage(val: IPageWidget) {
|
||||
logTelemetryEvent('addedPage', {full: {docIdDigest: this.docId()}});
|
||||
logTelemetryEvent('addedWidget', {
|
||||
full: {
|
||||
docIdDigest: this.docId(),
|
||||
widgetType: getTelemetryWidgetTypeFromPageWidget(val),
|
||||
},
|
||||
});
|
||||
|
||||
if (val.table === 'New Table') {
|
||||
const name = await this._promptForName();
|
||||
if (name === undefined) {
|
||||
|
||||
@@ -4,12 +4,14 @@ import {DocumentUsage} from 'app/client/components/DocumentUsage';
|
||||
import {GristDoc} from 'app/client/components/GristDoc';
|
||||
import {printViewSection} from 'app/client/components/Printing';
|
||||
import {ViewSectionHelper} from 'app/client/components/ViewLayout';
|
||||
import {logTelemetryEvent} from 'app/client/lib/telemetry';
|
||||
import {mediaSmall, theme, vars} from 'app/client/ui2018/cssVars';
|
||||
import {icon} from 'app/client/ui2018/icons';
|
||||
import {Computed, Disposable, dom, fromKo, makeTestId, Observable, styled} from 'grainjs';
|
||||
import {reportError} from 'app/client/models/errors';
|
||||
import {ViewSectionRec} from 'app/client/models/DocModel';
|
||||
import {buildViewSectionDom} from 'app/client/components/buildViewSectionDom';
|
||||
import {getTelemetryWidgetTypeFromVS} from 'app/client/ui/widgetTypesMap';
|
||||
|
||||
const testId = makeTestId('test-raw-data-');
|
||||
|
||||
@@ -82,6 +84,10 @@ export class RawDataPopup extends Disposable {
|
||||
if (this._viewSection.isRaw.peek()) {
|
||||
throw new Error("Can't delete a raw section");
|
||||
}
|
||||
|
||||
const widgetType = getTelemetryWidgetTypeFromVS(this._viewSection);
|
||||
logTelemetryEvent('deletedWidget', {full: {docIdDigest: this._gristDoc.docId(), widgetType}});
|
||||
|
||||
this._gristDoc.docData.sendAction(['RemoveViewSection', this._viewSection.id.peek()]).catch(reportError);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -14,8 +14,10 @@ import {LayoutTray} from 'app/client/components/LayoutTray';
|
||||
import {printViewSection} from 'app/client/components/Printing';
|
||||
import {Delay} from 'app/client/lib/Delay';
|
||||
import {createObsArray} from 'app/client/lib/koArrayWrap';
|
||||
import {logTelemetryEvent} from 'app/client/lib/telemetry';
|
||||
import {ViewRec, ViewSectionRec} from 'app/client/models/DocModel';
|
||||
import {reportError} from 'app/client/models/errors';
|
||||
import {getTelemetryWidgetTypeFromVS} from 'app/client/ui/widgetTypesMap';
|
||||
import {isNarrowScreen, mediaSmall, testId, theme} from 'app/client/ui2018/cssVars';
|
||||
import {icon} from 'app/client/ui2018/icons';
|
||||
import {DisposableWithEvents} from 'app/common/DisposableWithEvents';
|
||||
@@ -279,6 +281,14 @@ export class ViewLayout extends DisposableWithEvents implements IDomComponent {
|
||||
// more than one viewsection in the view.
|
||||
public removeViewSection(viewSectionRowId: number) {
|
||||
this.maximized.set(null);
|
||||
const viewSection = this.viewModel.viewSections().all().find(s => s.getRowId() === viewSectionRowId);
|
||||
if (!viewSection) {
|
||||
throw new Error(`Section not found: ${viewSectionRowId}`);
|
||||
}
|
||||
|
||||
const widgetType = getTelemetryWidgetTypeFromVS(viewSection);
|
||||
logTelemetryEvent('deletedWidget', {full: {docIdDigest: this.gristDoc.docId(), widgetType}});
|
||||
|
||||
this.gristDoc.docData.sendAction(['RemoveViewSection', viewSectionRowId]).catch(reportError);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { GristDoc } from 'app/client/components/GristDoc';
|
||||
import { logTelemetryEvent } from 'app/client/lib/telemetry';
|
||||
import { ViewFieldRec, ViewSectionRec } from 'app/client/models/DocModel';
|
||||
import { cssInput } from 'app/client/ui/cssInput';
|
||||
import { cssField, cssLabel } from 'app/client/ui/MakeCopyMenu';
|
||||
@@ -43,6 +44,8 @@ async function makeDuplicate(gristDoc: GristDoc, pageId: number, pageName: strin
|
||||
await gristDoc.docData.bundleActions(
|
||||
t("Duplicate page {{pageName}}", {pageName}),
|
||||
async () => {
|
||||
logTelemetryEvent('addedPage', {full: {docIdDigest: gristDoc.docId()}});
|
||||
|
||||
// create new view and new sections
|
||||
const results = await createNewViewSections(gristDoc.docData, viewSections);
|
||||
viewRef = results[0].viewRef;
|
||||
|
||||
Reference in New Issue
Block a user