-
Notifications
You must be signed in to change notification settings - Fork 1.1k
fix: conversion not being tracked properly with builder action #4153
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 6 commits
86eee54
3c76be2
ffc9157
d3787e7
6267e52
835c560
9245f6d
2bacf49
c018f6f
ecdad57
a4ae75a
a07ac26
9475c04
2ff9c7f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| --- | ||
| "@builder.io/react": patch | ||
| "@builder.io/sdk": patch | ||
| "@builder.io/sdk-angular": patch | ||
| "@builder.io/sdk-react-nextjs": patch | ||
| "@builder.io/sdk-qwik": patch | ||
| "@builder.io/sdk-react": patch | ||
| "@builder.io/sdk-react-native": patch | ||
| "@builder.io/sdk-solid": patch | ||
| "@builder.io/sdk-svelte": patch | ||
| "@builder.io/sdk-vue": patch | ||
| --- | ||
|
|
||
| fix: handle conversion tracking for gen1 and gen2 sdks |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| import { getCookieSync, setCookie } from '../helpers/cookie.js'; | ||
|
|
||
| export const testCookiePrefix = 'builder.tests'; | ||
|
|
||
| export function getTestCookie(name: string) { | ||
| return getCookieSync({ | ||
| name: `${testCookiePrefix}.${name}`, | ||
| canTrack: true, | ||
| }); | ||
| } | ||
|
|
||
| function parseUrlParams(url: string): Map<string, string> { | ||
| const result = new Map<string, string>(); | ||
|
|
||
| try { | ||
| const urlObj = new URL(url); | ||
| const params = urlObj.searchParams; | ||
|
|
||
| for (const [key, value] of params) { | ||
| result.set(key, value); | ||
| } | ||
| } catch (error) { | ||
| console.debug('Error parsing URL parameters:', error); | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| export function setTestCookie(contentId: string, variationId: string) { | ||
| // 30 days from now | ||
| const future = new Date(); | ||
| future.setDate(future.getDate() + 30); | ||
|
|
||
| // Use the native setCookie function directly | ||
| if (typeof window !== 'undefined') { | ||
| setCookie({ | ||
| name: `${testCookiePrefix}.${contentId}`, | ||
| value: variationId, | ||
| expires: future, | ||
| canTrack: true, | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| export function setTestsFromUrl() { | ||
| if (typeof window === 'undefined') return; | ||
|
|
||
| try { | ||
| // Use native URL object to parse current page URL | ||
| const params = parseUrlParams(window.location.href); | ||
|
|
||
| // Look for parameters that start with 'builder.tests.' | ||
| for (const [key, value] of params) { | ||
| if (key.startsWith(`${testCookiePrefix}.`)) { | ||
| const testKey = key.replace(`${testCookiePrefix}.`, ''); | ||
| setTestCookie(testKey, value); | ||
| } | ||
| } | ||
| } catch (e) { | ||
| console.debug('Error parsing tests from URL', e); | ||
| } | ||
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,9 +2,14 @@ import type { | |
| BuilderContextInterface, | ||
| BuilderRenderState, | ||
| } from '../../context/types.js'; | ||
| import { getDefaultCanTrack } from '../../helpers/canTrack.js'; | ||
| import { getTestCookie } from '../content-variants.js'; | ||
| import { getGlobalBuilderContext } from '../global-context.js'; | ||
| import { isBrowser } from '../is-browser.js'; | ||
| import { isEditing } from '../is-editing.js'; | ||
| import { getUserAttributes } from '../track/helpers.js'; | ||
| import type { EventProps } from '../track/index.js'; | ||
| import { _track } from '../track/index.js'; | ||
|
|
||
| export type EvaluatorArgs = Omit<ExecutorArgs, 'builder' | 'event'> & { | ||
| event?: Event; | ||
|
|
@@ -15,6 +20,18 @@ export type BuilderGlobals = { | |
| isBrowser: boolean | undefined; | ||
| isServer: boolean | undefined; | ||
| getUserAttributes: typeof getUserAttributes; | ||
| track: ( | ||
| eventName: string, | ||
| properties: Partial<EventProps & { apiHost?: string }>, | ||
| context?: any | ||
| ) => void; | ||
| trackConversion: ( | ||
| amount?: number, | ||
| contentId?: string | any, | ||
| variationId?: string, | ||
| customProperties?: any, | ||
| context?: any | ||
| ) => void; | ||
| }; | ||
|
|
||
| export type ExecutorArgs = Pick< | ||
|
|
@@ -52,6 +69,56 @@ export const getBuilderGlobals = (): BuilderGlobals => ({ | |
| isBrowser: isBrowser(), | ||
| isServer: !isBrowser(), | ||
| getUserAttributes: () => getUserAttributes(), | ||
| track: ( | ||
| eventName: string, | ||
| properties: Partial<EventProps & { apiHost?: string }> = {}, | ||
| context?: any | ||
| ) => { | ||
| const builderContext = getGlobalBuilderContext(); | ||
| _track({ | ||
| type: eventName, | ||
| ...properties, | ||
| apiHost: builderContext?.apiHost, | ||
| apiKey: builderContext?.apiKey || '', | ||
| context, | ||
| canTrack: getDefaultCanTrack(properties.canTrack), | ||
| }); | ||
| }, | ||
| trackConversion: ( | ||
| amount?: number, | ||
| contentId?: string, | ||
| variationId?: string, | ||
| customProperties?: any, | ||
| context?: any | ||
| ) => { | ||
| const meta = typeof contentId === 'object' ? contentId : customProperties; | ||
| let useContentId = typeof contentId === 'string' ? contentId : undefined; | ||
| const builderContext = getGlobalBuilderContext(); | ||
|
|
||
| if (!useContentId && builderContext?.contentId) { | ||
| useContentId = builderContext.contentId; | ||
| } | ||
|
|
||
| let useVariationId = variationId; | ||
| if (!useVariationId && useContentId) { | ||
| useVariationId = getTestCookie(useContentId) || undefined; | ||
| } | ||
midhunadarvin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| _track({ | ||
| type: 'conversion', | ||
| apiHost: builderContext?.apiHost, | ||
| apiKey: builderContext?.apiKey || '', | ||
| amount: amount || undefined, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| contentId: useContentId, | ||
| variationId: | ||
| useVariationId && useContentId && useVariationId !== useContentId | ||
| ? useVariationId | ||
| : undefined, | ||
| meta, | ||
| context: context || undefined, | ||
| canTrack: getDefaultCanTrack(), | ||
| }); | ||
| }, | ||
| }); | ||
|
|
||
| export const parseCode = ( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| /** | ||
| * Global Builder context singleton to store and retrieve Builder configuration | ||
| * across the application without prop drilling. | ||
| */ | ||
|
|
||
| export interface GlobalBuilderContext { | ||
| apiKey?: string; | ||
| apiHost?: string; | ||
| contentId?: string; | ||
| } | ||
|
|
||
| /** | ||
| * Singleton instance to store the global Builder context | ||
| */ | ||
| class BuilderGlobalContext { | ||
| private static instance: BuilderGlobalContext; | ||
| private context: GlobalBuilderContext = {}; | ||
|
|
||
| private constructor() {} | ||
|
|
||
| /** | ||
| * Get the singleton instance | ||
| */ | ||
| public static getInstance(): BuilderGlobalContext { | ||
| if (!BuilderGlobalContext.instance) { | ||
| BuilderGlobalContext.instance = new BuilderGlobalContext(); | ||
| } | ||
| return BuilderGlobalContext.instance; | ||
| } | ||
|
|
||
| /** | ||
| * Set the global context values | ||
| */ | ||
| public setContext(context: GlobalBuilderContext): void { | ||
| this.context = { ...this.context, ...context }; | ||
| } | ||
|
|
||
| /** | ||
| * Get the current global context | ||
| */ | ||
| public getContext(): GlobalBuilderContext { | ||
| return { ...this.context }; | ||
| } | ||
|
|
||
| /** | ||
| * Clear the global context | ||
| */ | ||
| public clearContext(): void { | ||
| this.context = {}; | ||
| } | ||
|
|
||
| /** | ||
| * Get a specific value from the context | ||
| */ | ||
| public getValue<K extends keyof GlobalBuilderContext>( | ||
| key: K | ||
| ): GlobalBuilderContext[K] { | ||
| return this.context[key]; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Set the global Builder context | ||
| * @param context - The context values to set | ||
| */ | ||
| export function setGlobalBuilderContext(context: GlobalBuilderContext): void { | ||
| BuilderGlobalContext.getInstance().setContext(context); | ||
| } | ||
|
|
||
| /** | ||
| * Get the global Builder context | ||
| * @returns The current global Builder context | ||
| */ | ||
| export function getGlobalBuilderContext(): GlobalBuilderContext { | ||
| return BuilderGlobalContext.getInstance().getContext(); | ||
| } | ||
|
|
||
| /** | ||
| * Get a specific value from the global Builder context | ||
| * @param key - The key to retrieve | ||
| * @returns The value for the specified key | ||
| */ | ||
| export function getGlobalBuilderValue<K extends keyof GlobalBuilderContext>( | ||
| key: K | ||
| ): GlobalBuilderContext[K] { | ||
| return BuilderGlobalContext.getInstance().getValue(key); | ||
| } | ||
|
|
||
| /** | ||
| * Clear the global Builder context | ||
| */ | ||
| export function clearGlobalBuilderContext(): void { | ||
| BuilderGlobalContext.getInstance().clearContext(); | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.