From acea593bc551aac14aebd982333be592ee4b298c Mon Sep 17 00:00:00 2001 From: Bero Date: Mon, 4 Nov 2024 16:15:08 +0100 Subject: [PATCH] Track install and error events --- .../website/src/components/layout/index.tsx | 73 +++++++++------- .../src/lib/state/redux/boot-site-client.ts | 23 +++-- .../playground/website/src/lib/tracking.ts | 87 +++++++++++++++++-- 3 files changed, 129 insertions(+), 54 deletions(-) diff --git a/packages/playground/website/src/components/layout/index.tsx b/packages/playground/website/src/components/layout/index.tsx index ee6906d1a5..b4ebf3bec4 100644 --- a/packages/playground/website/src/components/layout/index.tsx +++ b/packages/playground/website/src/components/layout/index.tsx @@ -33,6 +33,7 @@ import { setSiteManagerOpen, } from '../../lib/state/redux/slice-ui'; import { ImportFormModal } from '../import-form/modal'; +import { logErrorEvent } from '../../lib/tracking'; acquireOAuthTokenIfNeeded(); @@ -42,8 +43,8 @@ export const modalSlugs = { START_ERROR: 'start-error', IMPORT_FORM: 'import-form', GITHUB_IMPORT: 'github-import', - GITHUB_EXPORT: 'github-export' -} + GITHUB_EXPORT: 'github-export', +}; const displayMode = getDisplayModeFromQuery(); function getDisplayModeFromQuery(): DisplayMode { @@ -159,6 +160,8 @@ function Modals(blueprint: Blueprint) { useEffect(() => { addCrashListener(logger, (e) => { const error = e as CustomEvent; + logErrorEvent(error.detail?.source); + if (error.detail?.source === 'php-wasm') { dispatch(setActiveModal(modalSlugs.ERROR_REPORT)); } @@ -179,39 +182,43 @@ function Modals(blueprint: Blueprint) { } else if (currentModal === modalSlugs.IMPORT_FORM) { return ; } else if (currentModal === modalSlugs.GITHUB_IMPORT) { - return { - setGithubExportValues({ - repoUrl: url, - prNumber: pr?.toString(), - toPathInRepo: path, - prAction: pr ? 'update' : 'create', + return ( + ; + urlInformation: { owner, repo, type, pr }, + }) => { + setGithubExportValues({ + repoUrl: url, + prNumber: pr?.toString(), + toPathInRepo: path, + prAction: pr ? 'update' : 'create', + contentType, + plugin: pluginOrThemeName, + theme: pluginOrThemeName, + }); + setGithubExportFiles(files); + }} + /> + ); } else if (currentModal === modalSlugs.GITHUB_EXPORT) { - return { - setGithubExportValues(formValues); - setGithubExportFiles(undefined); - }} - />; + return ( + { + setGithubExportValues(formValues); + setGithubExportFiles(undefined); + }} + /> + ); } if (query.get('gh-ensure-auth') === 'yes') { diff --git a/packages/playground/website/src/lib/state/redux/boot-site-client.ts b/packages/playground/website/src/lib/state/redux/boot-site-client.ts index 6ae9b4664e..cdafb5ea87 100644 --- a/packages/playground/website/src/lib/state/redux/boot-site-client.ts +++ b/packages/playground/website/src/lib/state/redux/boot-site-client.ts @@ -10,8 +10,12 @@ import { removeClientInfo, updateClientInfo, } from './slice-clients'; -import { logTrackingEvent } from '../../tracking'; -import { Blueprint, StepDefinition } from '@wp-playground/blueprints'; +import { + logBlueprintStepEvent, + logErrorEvent, + logTrackingEvent, +} from '../../tracking'; +import { Blueprint } from '@wp-playground/blueprints'; import { logger } from '@php-wasm/logger'; import { setupPostMessageRelay } from '@php-wasm/web'; import { startPlaygroundWeb } from '@wp-playground/client'; @@ -100,17 +104,6 @@ export function bootSiteClient( blueprint = site.metadata.runtimeConfiguration; } else { blueprint = site.metadata.originalBlueprint; - // Log the names of provided Blueprint's steps. - // Only the names (e.g. "runPhp" or "login") are logged. Step options like - // code, password, URLs are never sent anywhere. - const steps = (blueprint?.steps || []) - ?.filter( - (step: any) => !!(typeof step === 'object' && step?.step) - ) - .map((step) => (step as StepDefinition).step); - for (const step of steps) { - logTrackingEvent('step', { step }); - } } let playground: PlaygroundClient; @@ -125,6 +118,9 @@ export function bootSiteClient( onClientConnected: (playground) => { (window as any)['playground'] = playground; }, + onBlueprintStepCompleted: (result, step) => { + logBlueprintStepEvent(step); + }, mounts: mountDescriptor ? [ { @@ -177,6 +173,7 @@ export function bootSiteClient( } } catch (e) { logger.error(e); + logErrorEvent('boot'); dispatch(setActiveSiteError('site-boot-failed')); dispatch(setActiveModal(modalSlugs.ERROR_REPORT)); return; diff --git a/packages/playground/website/src/lib/tracking.ts b/packages/playground/website/src/lib/tracking.ts index 1dbc0b85a0..1cf36f84f8 100644 --- a/packages/playground/website/src/lib/tracking.ts +++ b/packages/playground/website/src/lib/tracking.ts @@ -1,23 +1,94 @@ +import { StepDefinition } from '@wp-playground/blueprints'; + /** * Declare the global window.gtag function */ declare global { - interface Window { gtag: any; } + interface Window { + gtag: any; + } } /** * Google Analytics event names */ -type GAEvent = 'load' | 'step'; +type GAEvent = 'load' | 'step' | 'install' | 'error'; /** * Log a tracking event to Google Analytics * @param GAEvent The event name * @param Object Event data */ -export const logTrackingEvent = (event: GAEvent, data?: {[key: string]: string}) => { - if (typeof window === 'undefined' || !window.gtag) { - return; - } - window.gtag('event', event, data); -} +export const logTrackingEvent = ( + event: GAEvent, + data?: { [key: string]: string } +) => { + if (typeof window === 'undefined' || !window.gtag) { + return; + } + window.gtag('event', event, data); +}; + +/** + * Log Plugin install events + * @param step The Blueprint step + */ +export const logPluginInstallEvent = (step: StepDefinition) => { + const pluginData = (step as any).pluginData; + if (pluginData.slug) { + logTrackingEvent('install', { + plugin: pluginData.slug, + }); + } else if (pluginData.url) { + logTrackingEvent('install', { + plugin: pluginData.url, + }); + } +}; + +/** + * Log Theme install events + * @param step The Blueprint step + */ +export const logThemeInstallEvent = (step: StepDefinition) => { + const themeData = (step as any).themeData; + if (themeData.slug) { + logTrackingEvent('install', { + theme: themeData.slug, + }); + } else if (themeData.url) { + logTrackingEvent('install', { + theme: themeData.url, + }); + } +}; + +/** + * Log Blueprint step events + * @param step The Blueprint step + */ +export const logBlueprintStepEvent = (step: StepDefinition) => { + /** + * Log the names of provided Blueprint's steps. + * Only the names (e.g. "runPhp" or "login") are logged. Step options like + * code, password, URLs are never sent anywhere. + */ + logTrackingEvent('step', { step: step.step }); + + if (step.step === 'installPlugin') { + logPluginInstallEvent(step); + } else if (step.step === 'installTheme') { + logThemeInstallEvent(step); + } +}; + +/** + * Log error events + * + * @param error The error + */ +export const logErrorEvent = (source: string) => { + logTrackingEvent('error', { + source, + }); +};