Begin tracking more events

This commit is contained in:
Brandon Thomas committed 2023-01-12 01:59:52 -05:00
1 parent f5fec15b01
commit 59a8b16db5
5 files changed
+96 -7

No files matched your search

@@ -0,0 +1,78 @@
/**
* Send Analytics to Google Analytics.
*
* Keeping track of events in a singular space lets us easily update them and keep track of what we're analyzing at a high level.
*
* Currently we use Universal Analytics (UA), but support for that ends July 2023. We need to update to Google Analytics 4 (GA4).
*
* Here is some reading material:
* - [Events usage in UA - really good resource on how to bucket events](https://support.google.com/analytics/answer/1033068)
* - [GTag events for multiple Google products: UA, GA4, etc.](https://developers.google.com/tag-platform/devguides/events)
* - [Sending events to both UA and GA4](https://support.google.com/analytics/answer/11091026)
*/
class Analytics {
// ========== USER ==========
static accountSignupAttempt() {
Analytics.sendCategorizedEvent('account', 'signup_attempt');
}
static accountSignupComplete() {
Analytics.sendCategorizedEvent('account', 'signup_complete');
}
static accountLoginAttempt() {
Analytics.sendCategorizedEvent('account', 'login_attempt');
}
static accountLoginSuccess() {
Analytics.sendCategorizedEvent('account', 'login_success');
}
// ========== PREMIUM ==========
static premiumForwardToStripeCheckout() {
Analytics.sendCategorizedEvent('premium', 'forward_to_stripe_checkout');
}
static premiumForwardToStripePortal() {
Analytics.sendCategorizedEvent('premium', 'forward_to_stripe_portal');
}
static premiumBounceToSignup() {
Analytics.sendCategorizedEvent('premium', 'bounce_to_signup');
}
// ========== TTS ==========
static ttsGenerate(modelToken: string, ttsLength: number) {
Analytics.sendCategorizedEvent('TTS', 'generate_tts', modelToken, ttsLength);
}
static ttsClear(modelToken?: string) {
Analytics.sendCategorizedEvent('TTS', 'clear', modelToken);
}
// ========== (impl) ==========
private static sendEvent(actionName: string, eventLabel?: string, value?: number) {
gtag('event', actionName, {
'event_category': undefined,
'event_label': eventLabel,
'value': value,
});
}
private static sendCategorizedEvent(eventCategory: string, actionName: string, eventLabel?: string, value?: number) {
gtag('event', actionName, {
'event_category': eventCategory,
'event_label': eventLabel,
'value': value,
});
}
}
export { Analytics }
@@ -11,6 +11,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faUser, faKey } from "@fortawesome/free-solid-svg-icons";
import { motion } from "framer-motion";
import { container, panel } from "../../../data/animation";
import { Analytics } from "../../../common/Analytics";
interface Props {
sessionWrapper: SessionWrapper;
@@ -57,14 +58,16 @@ function LoginPage(props: Props) {
password: password,
};
Analytics.accountLoginAttempt();
const response = await CreateSession(request);
if (CreateSessionIsError(response)) {
setErrorMessage(response.error_message);
} else if (CreateSessionIsSuccess(response)) {
console.log("querying new session");
props.querySessionAction();
props.querySessionSubscriptionsAction();
Analytics.accountLoginSuccess();
history.push("/");
}
@@ -18,6 +18,7 @@ import {
import { motion } from "framer-motion";
import { container, item, panel } from "../../../data/animation";
import { FakeYouFrontendEnvironment } from "@storyteller/components/src/env/FakeYouFrontendEnvironment";
import { Analytics } from "../../../common/Analytics";
interface Props {
sessionWrapper: SessionWrapper;
@@ -54,11 +55,14 @@ function PricingPage(props: Props) {
): Promise<boolean> => {
if (!props.sessionWrapper.isLoggedIn()) {
// TODO: This needs to bring the user back to purchase flow.
Analytics.premiumBounceToSignup();
history.push("/signup");
return false;
} else if (props.sessionSubscriptionsWrapper.hasPaidFeatures()) {
Analytics.premiumForwardToStripePortal();
return await beginStripePortalFlow();
} else {
Analytics.premiumForwardToStripeCheckout();
return await beginStripeCheckoutFlow(internal_plan_key);
}
};
@@ -10,6 +10,7 @@ import { Link } from "react-router-dom";
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
import { motion } from "framer-motion";
import { container, panel } from "../../../data/animation";
import { Analytics } from "../../../common/Analytics";
enum FieldTriState {
EMPTY_FALSE,
@@ -166,6 +167,8 @@ function SignupPage(props: Props) {
): Promise<boolean> => {
ev.preventDefault();
Analytics.accountSignupAttempt();
if (
!usernameValid ||
!emailValid ||
@@ -194,9 +197,10 @@ function SignupPage(props: Props) {
setUsernameInvalidReason(response.error_fields["username"] || "");
}
} else if (CreateAccountIsSuccess(response)) {
console.log("querying new session");
props.querySessionCallback();
Analytics.accountSignupComplete();
// TODO: Switch to functional component.
window.location.href = "/";
}
@@ -48,6 +48,7 @@ import { motion } from "framer-motion";
import { container, panel } from "../../../../data/animation";
import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session/SessionSubscriptionsWrapper";
import { TtsPageHero } from "./TtsPageHero";
import { Analytics } from "../../../../common/Analytics";
export interface EnqueueJobResponsePayload {
success: boolean;
@@ -192,11 +193,7 @@ function TtsModelListPage(props: Props) {
const response = await GenerateTtsAudio(request);
gtag('event', 'generate_tts', {
'event_category': 'TTS',
'event_label': modelToken,
'value': props.textBuffer.length,
});
Analytics.ttsGenerate(modelToken, props.textBuffer.length);
if (GenerateTtsAudioIsOk(response)) {
setMaybeTtsError(undefined);
@@ -211,6 +208,9 @@ function TtsModelListPage(props: Props) {
const handleClearClick = (ev: React.FormEvent<HTMLButtonElement>) => {
ev.preventDefault();
props.clearTextBuffer();
Analytics.ttsClear(props.maybeSelectedTtsModel?.model_token);
return false;
};