📈(frontend) abstract analytics classes
Add abstract classes for analytics services. We will be able to add easily any analytic services. Our first analytic service usecase is Posthog.
This commit is contained in:
@@ -3,8 +3,9 @@ import { PropsWithChildren, useEffect } from 'react';
|
||||
|
||||
import { Box } from '@/components';
|
||||
import { useCunninghamTheme } from '@/cunningham';
|
||||
import { useLanguageSynchronizer } from '@/features/language/hooks/useLanguageSynchronizer';
|
||||
import { CrispProvider, PostHogProvider } from '@/services';
|
||||
import { useLanguageSynchronizer } from '@/features/language/';
|
||||
import { useAnalytics } from '@/libs';
|
||||
import { CrispProvider, PostHogAnalytic } from '@/services';
|
||||
import { useSentryStore } from '@/stores/useSentryStore';
|
||||
|
||||
import { useConfig } from './api/useConfig';
|
||||
@@ -13,6 +14,7 @@ export const ConfigProvider = ({ children }: PropsWithChildren) => {
|
||||
const { data: conf } = useConfig();
|
||||
const { setSentry } = useSentryStore();
|
||||
const { setTheme } = useCunninghamTheme();
|
||||
const { AnalyticsProvider } = useAnalytics();
|
||||
const { synchronizeLanguage } = useLanguageSynchronizer();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -35,6 +37,14 @@ export const ConfigProvider = ({ children }: PropsWithChildren) => {
|
||||
void synchronizeLanguage();
|
||||
}, [synchronizeLanguage]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!conf?.POSTHOG_KEY) {
|
||||
return;
|
||||
}
|
||||
|
||||
new PostHogAnalytic(conf.POSTHOG_KEY);
|
||||
}, [conf?.POSTHOG_KEY]);
|
||||
|
||||
if (!conf) {
|
||||
return (
|
||||
<Box $height="100vh" $width="100vw" $align="center" $justify="center">
|
||||
@@ -44,10 +54,10 @@ export const ConfigProvider = ({ children }: PropsWithChildren) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<PostHogProvider conf={conf.POSTHOG_KEY}>
|
||||
<AnalyticsProvider>
|
||||
<CrispProvider websiteId={conf?.CRISP_WEBSITE_ID}>
|
||||
{children}
|
||||
</CrispProvider>
|
||||
</PostHogProvider>
|
||||
</AnalyticsProvider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export * from './hooks/useLanguageSynchronizer';
|
||||
export * from './LanguagePicker';
|
||||
|
||||
85
src/frontend/apps/impress/src/libs/Analytics.tsx
Normal file
85
src/frontend/apps/impress/src/libs/Analytics.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import { JSX, PropsWithChildren, ReactNode } from 'react';
|
||||
|
||||
type AnalyticEventClick = {
|
||||
eventName: 'click';
|
||||
};
|
||||
type AnalyticEventUser = {
|
||||
eventName: 'user';
|
||||
id: string;
|
||||
email: string;
|
||||
};
|
||||
|
||||
export type AnalyticEvent = AnalyticEventClick | AnalyticEventUser;
|
||||
|
||||
export abstract class AbstractAnalytic {
|
||||
public constructor() {
|
||||
Analytics.registerAnalytic(this);
|
||||
}
|
||||
|
||||
public abstract Provider(children?: ReactNode): JSX.Element;
|
||||
|
||||
public abstract trackEvent(evt: AnalyticEvent): void;
|
||||
|
||||
public abstract isFeatureFlagActivated(flagName: string): boolean;
|
||||
}
|
||||
|
||||
export class Analytics {
|
||||
private static instance: Analytics;
|
||||
private static analytics: AbstractAnalytic[] = [];
|
||||
|
||||
private constructor() {}
|
||||
|
||||
public static getInstance(): Analytics {
|
||||
if (!Analytics.instance) {
|
||||
Analytics.instance = new Analytics();
|
||||
}
|
||||
|
||||
return Analytics.instance;
|
||||
}
|
||||
|
||||
public static clearAnalytics(): void {
|
||||
Analytics.analytics = [];
|
||||
}
|
||||
|
||||
public static registerAnalytic(analytic: AbstractAnalytic): void {
|
||||
Analytics.analytics.push(analytic);
|
||||
}
|
||||
|
||||
public static trackEvent(evt: AnalyticEvent): void {
|
||||
Analytics.analytics.forEach((analytic) => analytic.trackEvent(evt));
|
||||
}
|
||||
|
||||
public static providers(children: ReactNode) {
|
||||
return Analytics.analytics.reduceRight(
|
||||
(acc, analytic) => analytic.Provider(acc),
|
||||
children,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a feature flag is activated
|
||||
*
|
||||
* Feature flags are activated if at least one analytic is activated
|
||||
* because we don't want to hide feature if the user does not
|
||||
* use analytics (AB testing, etc)
|
||||
*/
|
||||
public static isFeatureFlagActivated(flagName: string): boolean {
|
||||
if (!Analytics.analytics.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return Analytics.analytics.some((analytic) =>
|
||||
analytic.isFeatureFlagActivated(flagName),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const useAnalytics = () => {
|
||||
return {
|
||||
AnalyticsProvider: ({ children }: PropsWithChildren) =>
|
||||
Analytics.providers(children),
|
||||
isFeatureFlagActivated: (flagName: string) =>
|
||||
Analytics.isFeatureFlagActivated(flagName),
|
||||
trackEvent: (evt: AnalyticEvent) => Analytics.trackEvent(evt),
|
||||
};
|
||||
};
|
||||
1
src/frontend/apps/impress/src/libs/index.ts
Normal file
1
src/frontend/apps/impress/src/libs/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './Analytics';
|
||||
@@ -1,7 +1,36 @@
|
||||
import { Router } from 'next/router';
|
||||
import posthog from 'posthog-js';
|
||||
import { PostHogProvider as PHProvider } from 'posthog-js/react';
|
||||
import { PropsWithChildren, useEffect } from 'react';
|
||||
import { JSX, PropsWithChildren, ReactNode, useEffect } from 'react';
|
||||
|
||||
import { AbstractAnalytic } from '@/libs/';
|
||||
|
||||
export class PostHogAnalytic extends AbstractAnalytic {
|
||||
private conf?: PostHogConf = undefined;
|
||||
|
||||
public constructor(conf?: PostHogConf) {
|
||||
super();
|
||||
|
||||
this.conf = conf;
|
||||
}
|
||||
|
||||
public Provider(children?: ReactNode): JSX.Element {
|
||||
return <PostHogProvider conf={this.conf}>{children}</PostHogProvider>;
|
||||
}
|
||||
|
||||
public trackEvent(): void {}
|
||||
|
||||
public isFeatureFlagActivated(flagName: string): boolean {
|
||||
if (
|
||||
posthog.featureFlags.getFlags().includes(flagName) &&
|
||||
posthog.isFeatureEnabled(flagName) === false
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export interface PostHogConf {
|
||||
id: string;
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './Crisp';
|
||||
export * from './Posthog';
|
||||
export * from './PosthogAnalytic';
|
||||
|
||||
Reference in New Issue
Block a user