@open-condo/miniapp-utils
Version:
A set of helper functions / components / hooks used to build new condo apps fast
143 lines (139 loc) • 5.19 kB
text/typescript
import { AnalyticsPlugin, PageData } from 'analytics';
export { AnalyticsPlugin } from 'analytics';
type AnalyticsConfig = {
app?: string;
version?: string | number;
debug?: boolean;
plugins?: AnalyticsPlugin[];
};
type AnyPayload = Record<string, any>;
/**
* Type-safe wrapper on top of "[analytics](https://www.npmjs.com/package/analytics)" npm package
* with support of group method (Posthog inspired) to group users into cohorts
*
* @example Init analytics in your app with type-guards
* import { Analytics } from '@open-condo/miniapp-utils/helpers/analytics'
* import { isDebug } from '@open-condo/miniapp-utils/helpers/environment'
*
* type MyAppEvents = {
* 'order_create': { orderId: string, itemIds: Array<string> }
* 'user_register': { userId: string, utmSource?: string }
* }
*
* type MyUserData = {
* 'name'?: string
* 'age'?: number
* }
*
* type MyAppGroups = 'organization' | 'country'
*
* export const analytics = new Analytics<MyAppEvents, MyUserData, MyAppGroups>({
* app: appName,
* version: revision,
* debug: isDebug(),
* })
*
* @example Use initialized analytics in your-app
* import { Router } from 'next/router'
* import { useEffect } from 'react'
*
* import { isValidCondoUIMessage } from '@open-condo/ui/events'
*
* import { analytics } from '@/domains/common/utils/analytics'
* import { useAuth } from '@/domains/user/utils/auth'
*
* import type { FC } from 'react'
*
* export const ResidentAppEventsHandler: FC = () => {
* const user = useAuth()
* const { activeResident } = useActiveResident()
*
* // User change tracking
* useEffect(() => {
* if (user) {
* analytics.identify(user.id, { name: user.name, type: user.type })
* }
* }, [user])
*
* // Page views tracking
* useEffect(() => {
* const handleRouteChange = () => analytics.pageView()
* Router.events.on('routeChangeComplete', handleRouteChange)
*
* return () => {
* Router.events.off('routeChangeComplete', handleRouteChange)
* }
* }, [])
*
* // Condo UI events tracking
* useEffect(() => {
* if (typeof window !== 'undefined') {
* const handleMessage = async (e: MessageEvent) => {
* if (isValidCondoUIMessage(e)) {
* const { params: { event, ...eventData } } = e.data
* await analytics.trackUntyped(event, eventData)
* }
*
* }
*
* window.addEventListener('message', handleMessage)
*
* return () => {
* window.removeEventListener('message', handleMessage)
* }
* }
* }, [])
*
* return null
* }
*/
declare class Analytics<Events extends Record<string, AnyPayload> = Record<string, never>, UserData extends AnyPayload = Record<string, never>, GroupNames extends string = never> {
private readonly _analytics;
private readonly _groups;
constructor(config: AnalyticsConfig);
/**
* Tracks type-safe business events. Recommended to use in most cases in app's codebase.
* To add an event, modify "Events" generic.
*/
track<EventName extends Extract<keyof Events, string>>(eventName: EventName, eventData: Events[EventName]): Promise<void>;
/**
* Tracks untyped analytics events, used mainly for external sources (bridge / ui-kit / messages, etc.)
* @deprecated It's not recommended to use this in your business logic, consider using typed "track" instead
*/
trackUntyped(eventName: string, eventData: AnyPayload): Promise<void>;
/**
* Tracks page changing in SPAs
*/
pageView(data?: PageData): Promise<void>;
/**
* Identifies user in analytics provider.
* To specify all possible shape of user's data, modify "UserData" generic
*
* NOTE: Analytics plugins don't have a fixed behavior on how to handle consecutive identify calls.
* Some of them affect only subsequent events, others affect all user events.
* Therefore, it is not recommended to put cohort-specific data (organization, address, language, etc.) here.
* Instead, use something like "group" method if your plugins supports it.
*/
identify<Key extends keyof UserData>(userId: string, userData?: Pick<UserData, Key>): Promise<void>;
/**
* Resets analytics providers
*/
reset(): Promise<void>;
static getGroupKey(groupName: string): string;
/**
* Associates the user with a group, adding the attributes `groups.${groupName} = groupId`
* to all subsequent analytic queries for the user
* @example
* analytics.setGroup('organization', organizationId)
*/
setGroup(groupName: GroupNames, groupId: string): void;
/**
* Removes the current user from the group, stripping the “groups.${groupName}”
* attribute from all subsequent eventualities
* @example
* deleteOrganization()
* .then(() => analytics.removeGroup('organization'))
*/
removeGroup(groupName: GroupNames): void;
}
export { Analytics, type AnalyticsConfig };