Skip to content

Commit

Permalink
Reduce the number of tutorial progress analytics events
Browse files Browse the repository at this point in the history
  • Loading branch information
4ian committed Dec 2, 2023
1 parent 87fa0a3 commit e994100
Showing 1 changed file with 71 additions and 9 deletions.
80 changes: 71 additions & 9 deletions newIDE/app/src/Utils/Analytics/EventSender.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ export const setCurrentlyRunningInAppTutorial = (tutorial: string | null) =>
* This function will retry to send the event if the analytics service is not ready.
*/
const recordEvent = (name: string, metadata?: { [string]: any }) => {
if (isDev) return;
if (isDev) {
// Uncomment to inspect analytics in development.
// console.log(`Should have sent analytics event "${name}"`, metadata);
return;
}

if (!posthogLoaded || !userIdentified) {
console.info(`App analytics not ready for an event - retrying in 2s.`);
Expand Down Expand Up @@ -433,16 +437,74 @@ export const sendEventsExtractedAsFunction = (metadata: {|
recordEvent('events-extracted-as-function', metadata);
};

export const sendInAppTutorialProgress = (metadata: {|
const inAppTutorialProgressLastFiredEvents: {
[string]: {
lastStep: number,
nextCheckTimeoutId: TimeoutID | null,
},
} = {};
const builtInTutorialIds = ['onboarding'];

/**
* Register the progress of a tutorial.
*
* To avoid sending too many events, we only send tutorial progress analytics events
* when some steps are reached (step index == multiple of 5), when the tutorial is completed,
* or after some inactivity (more than 30 seconds).
*/
export const sendInAppTutorialProgress = ({
step,
tutorialId,
isCompleted,
}: {|
tutorialId: string,
step: number,
isCompleted: boolean,
|}) => {
const builtInTutorialIds = ['onboarding'];
recordEvent(
builtInTutorialIds.includes(metadata.tutorialId)
? 'in-app-tutorial-built-in'
: 'in-app-tutorial-external',
metadata
);
const immediatelyRecordEvent = (
spentMoreThan30SecondsSinceLastStep: ?boolean
) => {
// Remember the last step we sent an event for.
inAppTutorialProgressLastFiredEvents[tutorialId] = {
lastStep: step,
nextCheckTimeoutId: null,
};
recordEvent(
builtInTutorialIds.includes(tutorialId)
? 'in-app-tutorial-built-in'
: 'in-app-tutorial-external',
{
tutorialId,
step,
isCompleted,
spentMoreThan30SecondsSinceLastStep: !!spentMoreThan30SecondsSinceLastStep,
}
);
};

// We receive a new progress event, so we can clear the timeout used
// to send the last event in case there is no progress.
const lastFiredEvent = inAppTutorialProgressLastFiredEvents[tutorialId];
if (lastFiredEvent && lastFiredEvent.nextCheckTimeoutId !== null)
clearTimeout(lastFiredEvent.nextCheckTimeoutId);

// Immediately send the event if the tutorial is ended or it's the first progress.
if (isCompleted || !lastFiredEvent) {
immediatelyRecordEvent();
return;
}

// Then, send an event every 5 steps, or if we had more than 5 steps since the last event.
// This last point is important because some steps might be hidden/skipped.
if (step % 5 === 0 || step >= lastFiredEvent.lastStep + 5) {
immediatelyRecordEvent();
return;
}

// Otherwise, continue to remember the last step that was sent, and force to send it 30 seconds
// later if there was no more progress.
inAppTutorialProgressLastFiredEvents[tutorialId] = {
lastStep: lastFiredEvent.lastStep,
nextCheckTimeoutId: setTimeout(() => immediatelyRecordEvent(true), 30000),
};
};

0 comments on commit e994100

Please sign in to comment.