mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-05-28 18:26:54 +00:00
122 lines
3.2 KiB
TypeScript
122 lines
3.2 KiB
TypeScript
import React, { useEffect, useMemo } from 'react';
|
|
import {
|
|
FeatureDefinition,
|
|
GrowthBook,
|
|
GrowthBookProvider
|
|
} from '@growthbook/growthbook-react';
|
|
import { connect } from 'react-redux';
|
|
import { createSelector } from 'reselect';
|
|
import { userSelector, userFetchStateSelector } from '../../redux/selectors';
|
|
import envData from '../../../config/env.json';
|
|
import defaultGrowthBookFeatures from '../../../config/growthbook-features-default.json';
|
|
import type { User, UserFetchState } from '../../redux/prop-types';
|
|
import { getUUID } from '../../utils/growthbook-cookie';
|
|
import callGA from '../../analytics/call-ga';
|
|
import GrowthBookReduxConnector from './growth-book-redux-connector';
|
|
|
|
const { clientLocale, growthbookUri } = envData as {
|
|
clientLocale: string;
|
|
growthbookUri: string | null;
|
|
};
|
|
|
|
declare global {
|
|
interface Window {
|
|
dataLayer: [Record<string, number | string>];
|
|
}
|
|
}
|
|
|
|
const mapStateToProps = createSelector(
|
|
userSelector,
|
|
userFetchStateSelector,
|
|
(user: User | null, userFetchState: UserFetchState) => ({
|
|
user,
|
|
userFetchState
|
|
})
|
|
);
|
|
|
|
type StateProps = ReturnType<typeof mapStateToProps>;
|
|
interface GrowthBookWrapper extends StateProps {
|
|
children: JSX.Element;
|
|
}
|
|
|
|
interface UserAttributes {
|
|
id: string;
|
|
clientLocal: string;
|
|
staff?: boolean;
|
|
joinDateUnix?: number;
|
|
completedChallengesLength?: number;
|
|
signedIn?: true;
|
|
}
|
|
|
|
const GrowthBookWrapper = ({
|
|
children,
|
|
user,
|
|
userFetchState
|
|
}: GrowthBookWrapper) => {
|
|
const growthbook = useMemo(
|
|
() =>
|
|
new GrowthBook({
|
|
trackingCallback: (experiment, result) => {
|
|
callGA({
|
|
event: 'experiment_viewed',
|
|
event_category: 'experiment',
|
|
experiment_id: experiment.key,
|
|
variation_id: result.variationId
|
|
});
|
|
}
|
|
}),
|
|
|
|
[]
|
|
);
|
|
|
|
useEffect(() => {
|
|
async function setGrowthBookFeatures() {
|
|
if (!growthbookUri) {
|
|
// Defaults are added to facilitate testing, and avoid passing the related env
|
|
growthbook.setFeatures(defaultGrowthBookFeatures);
|
|
} else {
|
|
try {
|
|
const res = await fetch(growthbookUri);
|
|
const data = (await res.json()) as {
|
|
features: Record<string, FeatureDefinition>;
|
|
};
|
|
growthbook.setFeatures(data.features);
|
|
} catch (e) {
|
|
// TODO: report to sentry when it's enabled
|
|
console.error(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
void setGrowthBookFeatures();
|
|
}, [growthbook]);
|
|
|
|
useEffect(() => {
|
|
if (userFetchState.complete) {
|
|
let userAttributes: UserAttributes = {
|
|
id: getUUID() as string,
|
|
clientLocal: clientLocale
|
|
};
|
|
|
|
if (user) {
|
|
userAttributes = {
|
|
...userAttributes,
|
|
staff: user.email.includes('@freecodecamp'),
|
|
joinDateUnix: Date.parse(user.joinDate),
|
|
completedChallengesLength: user.completedChallenges.length,
|
|
signedIn: true
|
|
};
|
|
}
|
|
growthbook.setAttributes(userAttributes);
|
|
}
|
|
}, [user, userFetchState, growthbook]);
|
|
|
|
return (
|
|
<GrowthBookProvider growthbook={growthbook}>
|
|
<GrowthBookReduxConnector>{children}</GrowthBookReduxConnector>
|
|
</GrowthBookProvider>
|
|
);
|
|
};
|
|
|
|
export default connect(mapStateToProps)(GrowthBookWrapper);
|