diff --git a/packages/sdks/nextjs/hook.ts b/packages/sdks/nextjs/hook.ts new file mode 100644 index 00000000..bae913e5 --- /dev/null +++ b/packages/sdks/nextjs/hook.ts @@ -0,0 +1,51 @@ +import type { + DecrementPayload, + IdentifyPayload, + IncrementPayload, + TrackProperties, +} from '@openpanel/web'; + +export function useOpenPanel() { + return { + track, + screenView, + identify, + increment, + decrement, + clear, + setGlobalProperties, + }; +} + +function setGlobalProperties(properties: Record) { + window.op?.('setGlobalProperties', properties); +} + +function track(name: string, properties?: TrackProperties) { + window.op?.('track', name, properties); +} + +function screenView(properties?: TrackProperties): void; +function screenView(path: string, properties?: TrackProperties): void; +function screenView( + pathOrProperties?: string | TrackProperties, + propertiesOrUndefined?: TrackProperties, +) { + window.op?.('screenView', pathOrProperties, propertiesOrUndefined); +} + +function identify(payload: IdentifyPayload) { + window.op?.('identify', payload); +} + +function increment(payload: IncrementPayload) { + window.op?.('increment', payload); +} + +function decrement(payload: DecrementPayload) { + window.op('decrement', payload); +} + +function clear() { + window.op?.('clear'); +} diff --git a/packages/sdks/nextjs/index.tsx b/packages/sdks/nextjs/index.tsx index 97d77d25..2d78e1f7 100644 --- a/packages/sdks/nextjs/index.tsx +++ b/packages/sdks/nextjs/index.tsx @@ -1,18 +1,16 @@ // adding .js next/script import fixes an issues // with esm and nextjs (when using pages dir) import Script from 'next/script.js'; -import React from 'react'; import type { - DecrementPayload, IdentifyPayload, - IncrementPayload, OpenPanelMethodNames, OpenPanelOptions, - TrackProperties, } from '@openpanel/web'; +import { ReactiveProfile } from './reactive'; export * from '@openpanel/web'; +export * from './hook'; const CDN_URL = 'https://openpanel.dev/op1.js'; @@ -80,6 +78,7 @@ export function OpenPanelComponent({ .join('\n')}`, }} /> + {profileId && } ); } @@ -94,6 +93,7 @@ export function IdentifyComponent(props: IdentifyComponentProps) { __html: `window.op('identify', ${JSON.stringify(props)});`, }} /> + ); } @@ -109,48 +109,3 @@ export function SetGlobalPropertiesComponent(props: Record) { ); } - -export function useOpenPanel() { - return { - track, - screenView, - identify, - increment, - decrement, - clear, - setGlobalProperties, - }; -} - -function setGlobalProperties(properties: Record) { - window.op?.('setGlobalProperties', properties); -} - -function track(name: string, properties?: TrackProperties) { - window.op?.('track', name, properties); -} - -function screenView(properties?: TrackProperties): void; -function screenView(path: string, properties?: TrackProperties): void; -function screenView( - pathOrProperties?: string | TrackProperties, - propertiesOrUndefined?: TrackProperties, -) { - window.op?.('screenView', pathOrProperties, propertiesOrUndefined); -} - -function identify(payload: IdentifyPayload) { - window.op?.('identify', payload); -} - -function increment(payload: IncrementPayload) { - window.op?.('increment', payload); -} - -function decrement(payload: DecrementPayload) { - window.op('decrement', payload); -} - -function clear() { - window.op?.('clear'); -} diff --git a/packages/sdks/nextjs/reactive.tsx b/packages/sdks/nextjs/reactive.tsx new file mode 100644 index 00000000..de62e403 --- /dev/null +++ b/packages/sdks/nextjs/reactive.tsx @@ -0,0 +1,34 @@ +'use client'; + +import type { IdentifyPayload } from '@openpanel/web'; +import { useEffect, useRef } from 'react'; +import { useOpenPanel } from './hook'; + +const fastJsonEqual = (a: unknown, b: unknown) => { + if (typeof a !== 'object' || typeof b !== 'object') { + return a === b; + } + + return JSON.stringify(a) === JSON.stringify(b); +}; + +export function ReactiveProfile(props: IdentifyPayload) { + const op = useOpenPanel(); + const prev = useRef(props); + + useEffect(() => { + if ( + props.profileId !== prev.current?.profileId || + props.firstName !== prev.current?.firstName || + props.lastName !== prev.current?.lastName || + props.email !== prev.current?.email || + props.avatar !== prev.current?.avatar || + !fastJsonEqual(props.properties, prev.current?.properties) + ) { + op.identify(props); + prev.current = props; + } + }, [props]); + + return null; +}