Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/deploy-web/env/.env.production
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,6 @@ NEXT_PUBLIC_GROWTH_CHANNEL_TRACKING_ENABLED=true
NEXT_PUBLIC_UNLEASH_FRONTEND_API_URL=https://features-edge.akash.network/api/frontend
NEXT_PUBLIC_UNLEASH_FRONTEND_API_TOKEN=*:production.3de2776029292b1860a520b1aa3ee9e417ae300283811fd77d231060

NEXT_PUBLIC_GTM_ID=GTM-WT4KSXNQ
NEXT_PUBLIC_GTM_ID=GTM-MSF384N3

NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_ytcG6PkH6KnmGR4Ge83ApYGr004iygcjjJ
1 change: 0 additions & 1 deletion apps/deploy-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@
"next-qrcode": "^2.1.0",
"next-seo": "^6.1.0",
"next-themes": "^0.2.1",
"nextjs-google-analytics": "^2.3.3",
"notistack": "^3.0.1",
"nprogress": "^0.2.0",
"react": "18.2.0",
Expand Down
18 changes: 0 additions & 18 deletions apps/deploy-web/src/components/layout/CustomGoogleAnalytics.tsx

This file was deleted.

3 changes: 0 additions & 3 deletions apps/deploy-web/src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import type { NextSeoProps } from "next-seo/lib/types";
import { ThemeProvider } from "next-themes";
import NProgress from "nprogress";

import GoogleAnalytics from "@src/components/layout/CustomGoogleAnalytics";
import { CustomIntlProvider } from "@src/components/layout/CustomIntlProvider";
import { PageHead } from "@src/components/layout/PageHead";
import { LocalNoteManager } from "@src/components/LocalNoteManager";
Expand Down Expand Up @@ -52,8 +51,6 @@ const App: React.FunctionComponent<Props> = props => {
return (
<AppRoot {...props}>
<>
<GoogleAnalytics />

<UserProviders>
<FlagProvider>
<WalletProvider>
Expand Down
54 changes: 24 additions & 30 deletions apps/deploy-web/src/services/analytics/analytics.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { faker } from "@faker-js/faker";
import { describe, expect, it, type Mock, vi } from "vitest";

import type { Amplitude, AnalyticsOptions, GoogleAnalytics } from "./analytics.service";
import type { Amplitude, AnalyticsOptions } from "./analytics.service";
import { AnalyticsService } from "./analytics.service";

type Mocked<T> = {
Expand Down Expand Up @@ -81,7 +81,6 @@ describe(AnalyticsService.name, () => {
const setUserId = vi.fn();
const service = setup({
amplitude: { identify, setUserId },
ga: { event: vi.fn() },
options: {
amplitude: { enabled: true, apiKey: mockAmplitudeApiKey },
ga: { enabled: true, measurementId: mockGaMeasurementId }
Expand All @@ -96,10 +95,10 @@ describe(AnalyticsService.name, () => {
it("should only identify in enabled services", () => {
const identify = vi.fn();
const setUserId = vi.fn();
const gtag = vi.fn();
const dataLayer: Record<string, unknown>[] = [];
const service = setup({
amplitude: { identify, setUserId },
gtag,
dataLayer,
options: {
amplitude: { enabled: false, apiKey: mockAmplitudeApiKey },
ga: { enabled: true, measurementId: mockGaMeasurementId }
Expand All @@ -109,7 +108,7 @@ describe(AnalyticsService.name, () => {
const user = { id: faker.string.uuid() };
service.identify(user);

expect(gtag).toHaveBeenCalledWith("config", mockGaMeasurementId, { user_id: user.id });
expect(dataLayer).toContainEqual({ user_id: user.id });
expect(identify).not.toHaveBeenCalled();
expect(setUserId).not.toHaveBeenCalled();
});
Expand All @@ -118,10 +117,10 @@ describe(AnalyticsService.name, () => {
describe("track", () => {
it("should track events in both GA and Amplitude when no target specified", () => {
const track = vi.fn();
const event = vi.fn();
const dataLayer: Record<string, unknown>[] = [];
const service = setup({
amplitude: { track },
ga: { event },
dataLayer,
options: {
amplitude: { enabled: true, apiKey: mockAmplitudeApiKey },
ga: { enabled: true, measurementId: mockGaMeasurementId }
Expand All @@ -133,19 +132,19 @@ describe(AnalyticsService.name, () => {
someProperty: faker.word.sample()
};

service.identify({ id: faker.string.uuid() }); // Initialize sampling
service.identify({ id: faker.string.uuid() });
service.track("connect_wallet", properties);

expect(track).toHaveBeenCalledWith("connect_wallet", properties);
expect(event).toHaveBeenCalledWith("connect_wallet", properties);
expect(dataLayer).toContainEqual({ event: "connect_wallet", ...properties });
});

it("should track events only in specified target", () => {
const track = vi.fn();
const event = vi.fn();
const dataLayer: Record<string, unknown>[] = [];
const service = setup({
amplitude: { track },
ga: { event },
dataLayer,
options: {
amplitude: { enabled: true, apiKey: mockAmplitudeApiKey },
ga: { enabled: true, measurementId: mockGaMeasurementId }
Expand All @@ -154,17 +153,17 @@ describe(AnalyticsService.name, () => {

const properties = { category: "wallet" as const };

service.identify({ id: faker.string.uuid() }); // Initialize sampling
service.identify({ id: faker.string.uuid() });
service.track("connect_wallet", properties, "Amplitude");

expect(track).toHaveBeenCalledWith("connect_wallet", properties);
expect(event).not.toHaveBeenCalled();
expect(dataLayer).not.toContainEqual(expect.objectContaining({ event: "connect_wallet" }));
});

it("should transform GA event names correctly", () => {
const event = vi.fn();
const dataLayer: Record<string, unknown>[] = [];
const service = setup({
ga: { event },
dataLayer,
options: {
amplitude: { enabled: false, apiKey: mockAmplitudeApiKey },
ga: { enabled: true, measurementId: mockGaMeasurementId }
Expand All @@ -174,13 +173,13 @@ describe(AnalyticsService.name, () => {
const properties = { category: "transactions" as const };
service.track("successful_tx", properties);

expect(event).toHaveBeenCalledWith("successful_transaction", properties);
expect(dataLayer).toContainEqual({ event: "successful_transaction", ...properties });
});

it("should handle navigate_tab events specially for GA", () => {
const event = vi.fn();
const dataLayer: Record<string, unknown>[] = [];
const service = setup({
ga: { event },
dataLayer,
options: {
amplitude: { enabled: false, apiKey: mockAmplitudeApiKey },
ga: { enabled: true, measurementId: mockGaMeasurementId }
Expand All @@ -193,15 +192,15 @@ describe(AnalyticsService.name, () => {
};

service.track("navigate_tab", properties);
expect(event).toHaveBeenCalledWith("navigate_tab_settings", properties);
expect(dataLayer).toContainEqual({ event: "navigate_tab_settings", ...properties });
});

it("should only track in enabled services", () => {
const track = vi.fn();
const event = vi.fn();
const dataLayer: Record<string, unknown>[] = [];
const service = setup({
amplitude: { track },
ga: { event },
dataLayer,
options: {
amplitude: { enabled: false, apiKey: mockAmplitudeApiKey },
ga: { enabled: true, measurementId: mockGaMeasurementId }
Expand All @@ -212,14 +211,13 @@ describe(AnalyticsService.name, () => {
service.track("connect_wallet", properties);

expect(track).not.toHaveBeenCalled();
expect(event).toHaveBeenCalled();
expect(dataLayer).toContainEqual({ event: "connect_wallet", ...properties });
});
});

function setup(params: {
amplitude?: Mocked<Amplitude>;
ga?: Mocked<GoogleAnalytics>;
gtag?: Gtag.Gtag;
dataLayer?: Record<string, unknown>[];
options?: AnalyticsOptions;
storage?: Pick<Storage, "getItem" | "setItem">;
}) {
Expand All @@ -234,23 +232,19 @@ describe(AnalyticsService.name, () => {
add: vi.fn(),
...(params.amplitude ?? {})
};
const ga = {
event: vi.fn(),
...(params.ga ?? {})
};
const storage = params.storage ?? {
getItem: vi.fn(),
setItem: vi.fn()
};
const dataLayer = params.dataLayer ?? [];

return new AnalyticsService(
params.options ?? {
amplitude: { enabled: false, apiKey: mockAmplitudeApiKey },
ga: { enabled: false, measurementId: mockGaMeasurementId }
},
amplitude,
ga,
() => params.gtag ?? vi.fn(),
() => dataLayer,
storage
);
}
Expand Down
17 changes: 5 additions & 12 deletions apps/deploy-web/src/services/analytics/analytics.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

import * as amplitude from "@amplitude/analytics-browser";
import { sessionReplayPlugin } from "@amplitude/plugin-session-replay-browser";
import { event } from "nextjs-google-analytics";

export type AnalyticsUser = {
id?: string;
anonymous?: boolean;
Expand Down Expand Up @@ -143,7 +141,6 @@ const AMPLITUDE_USER_PROPERTIES_MAP = {
const isBrowser = typeof window !== "undefined";

export type Amplitude = Pick<typeof amplitude, "init" | "Identify" | "identify" | "track" | "setUserId" | "add">;
export type GoogleAnalytics = { event: typeof event };

export class AnalyticsService {
private readonly STORAGE_KEY = "analytics_values_cache";
Expand All @@ -153,15 +150,10 @@ export class AnalyticsService {
private readonly isAmplitudeEnabled: boolean;
private amplitudeInitialized = false;

private get gtag() {
return this.getGtag();
}

constructor(
private readonly options: AnalyticsOptions,
private readonly amplitudeClient: Amplitude = amplitude,
private readonly ga: GoogleAnalytics = { event },
private readonly getGtag: () => Gtag.Gtag | undefined = () => (isBrowser ? window.gtag : undefined),
private readonly getDataLayer: () => Record<string, unknown>[] | undefined = () => (isBrowser ? window.dataLayer : undefined),
private readonly storage: Pick<Storage, "getItem" | "setItem"> | undefined = isBrowser ? window.localStorage : undefined
) {
this.isAmplitudeEnabled = this.options.amplitude.enabled;
Expand All @@ -184,8 +176,8 @@ export class AnalyticsService {
return;
}

if (this.options.ga.enabled && this.gtag && user.id) {
this.gtag("config", this.options.ga.measurementId, { user_id: user.id });
if (this.options.ga.enabled && user.id) {
this.getDataLayer()?.push({ user_id: user.id });
}

if (!this.isAmplitudeEnabled) {
Expand Down Expand Up @@ -260,7 +252,8 @@ export class AnalyticsService {
}

if (this.options.ga.enabled && (!analyticsTarget || analyticsTarget === "GA")) {
this.ga?.event(...this.transformGaEvent(eventName, eventProperties));
const [name, props] = this.transformGaEvent(eventName, eventProperties);
this.getDataLayer()?.push({ ...props, event: name });
}
}

Expand Down
1 change: 1 addition & 0 deletions apps/deploy-web/src/types/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { Keplr, Window as KeplrWindow } from "@keplr-wallet/types";
declare global {
interface Window extends KeplrWindow {
wallet: Keplr | undefined;
dataLayer?: Record<string, unknown>[];
LeapElements?: {
mountElements: (args: any) => void;
WalletType: any;
Expand Down
1 change: 0 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading