Files
myeasycms-v2/apps/web/components/analytics-provider.tsx
Giancarlo Buomprisco 5b9285a575 Next.js 15 Update (#26)
* Update Next.js and React versions in all packages
* Replace onRedirect function with next/link in BillingSessionStatus, since it's no longer cached by default
* Remove unused revalidatePath import in billing return page, since it's no longer cached by default
* Add Turbopack module aliases to improve development server speed
* Converted new Dynamic APIs to be Promise-based
* Adjust mobile layout
* Use ENABLE_REACT_COMPILER to enable the React Compiler in Next.js 15
* Report Errors using the new onRequestError hook
2024-10-22 14:39:21 +08:00

96 lines
2.4 KiB
TypeScript

'use client';
import { useEffect } from 'react';
import { usePathname, useSearchParams } from 'next/navigation';
import { analytics } from '@kit/analytics';
import {
AppEvent,
AppEventType,
ConsumerProvidedEventTypes,
useAppEvents,
} from '@kit/shared/events';
type AnalyticsMapping<
T extends ConsumerProvidedEventTypes = NonNullable<unknown>,
> = {
[K in AppEventType<T>]?: (event: AppEvent<T, K>) => unknown;
};
/**
* Hook to subscribe to app events and map them to analytics actions
* @param mapping
*/
function useAnalyticsMapping<T extends ConsumerProvidedEventTypes>(
mapping: AnalyticsMapping<T>,
) {
const appEvents = useAppEvents<T>();
useEffect(() => {
const subscriptions = Object.entries(mapping).map(
([eventType, handler]) => {
appEvents.on(eventType as AppEventType<T>, handler);
return () => appEvents.off(eventType as AppEventType<T>, handler);
},
);
return () => {
subscriptions.forEach((unsubscribe) => unsubscribe());
};
}, [appEvents, mapping]);
}
/**
* Define a mapping of app events to analytics actions
* Add new mappings here to track new events in the analytics service from app events
*/
const analyticsMapping: AnalyticsMapping = {
'user.signedIn': (event) => {
const { userId, ...traits } = event.payload;
if (userId) {
return analytics.identify(userId, traits);
}
},
'user.signedUp': (event) => {
return analytics.trackEvent(event.type, event.payload);
},
'checkout.started': (event) => {
return analytics.trackEvent(event.type, event.payload);
},
'user.updated': (event) => {
return analytics.trackEvent(event.type, event.payload);
},
};
/**
* Provider for the analytics service
*/
export function AnalyticsProvider(props: React.PropsWithChildren) {
// Subscribe to app events and map them to analytics actions
useAnalyticsMapping(analyticsMapping);
// Report page views to the analytics service
useReportPageView((url) => analytics.trackPageView(url));
// Render children
return props.children;
}
/**
* Hook to report page views to the analytics service
* @param reportAnalyticsFn
*/
function useReportPageView(reportAnalyticsFn: (url: string) => unknown) {
const pathname = usePathname();
const searchParams = useSearchParams();
useEffect(() => {
const url = [pathname, searchParams.toString()].filter(Boolean).join('?');
reportAnalyticsFn(url);
}, [pathname, reportAnalyticsFn, searchParams]);
}