From 01d6ea7ca55c000534cac4e6b57f48d42b2e0af1 Mon Sep 17 00:00:00 2001 From: Yair Dovrat Date: Fri, 14 Jun 2024 13:47:23 +0200 Subject: [PATCH] ZRZ-1318 engagement time changes --- src/index.ts | 60 ++++++++++++++++++------------------------- src/requestBuilder.ts | 35 +++++++++++++++++++++++-- src/utils.ts | 30 +++++++++++++++++----- 3 files changed, 81 insertions(+), 44 deletions(-) diff --git a/src/index.ts b/src/index.ts index 356d53e..e708f37 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,10 +4,9 @@ import { computeEngagementDuration, countConversion, countPageview, + sendUserEngagementEvent, } from './utils' -export const SESSION_DURATION_IN_MIN = 30 - const sendGaAudiences = ( event: MCEvent, settings: ComponentSettings, @@ -70,7 +69,7 @@ const sendGaAudiences = ( client.fetch(finalDoubleClickURL) } } -const sendEvent = async ( +export const sendEvent = async ( eventType: string, event: MCEvent, settings: ComponentSettings, @@ -78,7 +77,11 @@ const sendEvent = async ( ) => { const { client } = event const { finalURL, requestBody } = getFinalURL(eventType, event, settings) - + console.log( + '🚀🚀🚀🚀🚀🚀🚀🚀 final URL is here and send event fires', + finalURL + ) + console.log('🚀🚀🚀🚀🚀🚀🚀🚀 also manager.fetch is working: ', manager.fetch) manager.fetch(finalURL, { headers: { 'User-Agent': client.userAgent }, }) @@ -86,29 +89,16 @@ const sendEvent = async ( if (settings['ga-audiences'] || event.payload['ga-audiences']) { sendGaAudiences(event, settings, requestBody) } - - client.set('let', Date.now().toString()) // reset the last event time } + const onVisibilityChange = (settings: ComponentSettings, manager: Manager) => (event: MCEvent) => { const { client, payload } = event - if (payload.visibilityChange[0].state == 'visible') { - event.client.set('engagementStart', payload.visibilityChange[0].timestamp) + client.set('engagementStart', payload.visibilityChange[0].timestamp) } else if (payload.visibilityChange[0].state == 'hidden') { - // on pageblur - computeEngagementDuration(event) - - const msSinceLastEvent = Date.now() - parseInt(client.get('let') || '0') // _let = "_lastEventTime" - if (msSinceLastEvent > 10000) { - // order matters so engagement duration is set before dispatching the hit - computeEngagementDuration(event) - - sendEvent('user_engagement', event, settings, manager) - - // Reset engagementDuration after event has been dispatched so it does not accumulate - event.client.set('engagementDuration', '0') - } + // when visibilityChange status changes to hidden, fire `user_engagement` event + sendUserEngagementEvent(event, settings, manager) } } @@ -119,30 +109,33 @@ export default async function (manager: Manager, settings: ComponentSettings) { ) manager.addEventListener('pageview', event => { + // this line does not trigger visibilityChange after a pagview, it will start triggering events only on the fist change to hidden event.client.attachEvent('visibilityChange') + // if engagement duration is >1 send a user_engagement event before pageview, to count the time on previous page properly + const engagementDuration = + parseInt(String(event.client.get('engagementDuration')), 10) || 0 + if (engagementDuration >= 1) { + sendUserEngagementEvent(event, settings, manager) + } + // engagement start gets reset on every new pageview or event + const now = new Date(Date.now()).getTime() + event.client.set('engagementStart', `${now}`) + // Reset engagementDuration after pageview has been dispatched so it restarts the count + event.client.set('engagementDuration', '0') // count pageviews for 'seg' value countPageview(event.client) - // order matters so engagement duration is set before dispatching the hit - - computeEngagementDuration(event) sendEvent('page_view', event, settings, manager) - - // Reset engagementDuration after event has been dispatched so it does not accumulate - event.client.set('engagementDuration', '0') }) manager.addEventListener('event', event => { // count conversion events for 'seg' value countConversion(event) // order matters so engagement duration is set before dispatching the hit - computeEngagementDuration(event) + computeEngagementDuration(event, settings) sendEvent('event', event, settings, manager) - - // Reset engagementDuration after event has been dispatched so it does not accumulate - event.client.set('engagementDuration', '0') }) manager.addEventListener('ecommerce', async event => { @@ -150,11 +143,8 @@ export default async function (manager: Manager, settings: ComponentSettings) { // count conversion events for 'seg' value countConversion(event) // order matters so engagement duration is set before dispatching the hit - computeEngagementDuration(event) + computeEngagementDuration(event, settings) sendEvent('ecommerce', event, settings, manager) - - // Reset engagementDuration after event has been dispatched so it does not accumulate - event.client.set('engagementDuration', '0') }) } diff --git a/src/requestBuilder.ts b/src/requestBuilder.ts index 58fa8b7..5ad7984 100644 --- a/src/requestBuilder.ts +++ b/src/requestBuilder.ts @@ -1,4 +1,4 @@ -import { ComponentSettings, MCEvent } from '@managed-components/types' +import { Client, ComponentSettings, MCEvent } from '@managed-components/types' import { buildProductRequest, EVENTS, @@ -9,6 +9,16 @@ import { flattenKeys, getParamSafely } from './utils' const getRandomInt = () => Math.floor(2147483647 * Math.random()) +const firstPageEvent = (client: Client) => { + const countedEvent = client.get('countedEvent') + if (countedEvent) { + console.log('🥑🥑🥑🥑🥑🥑🥑 this is not the first event!') + return false + } else { + console.log('🥑🥑🥑🥑🥑🥑🥑 this is the FIRST event!') + return true + } +} function getToolRequest( eventType: string, event: MCEvent, @@ -108,8 +118,29 @@ function getToolRequest( //const notTheFirstSession = parseInt(requestBody['_s'] as string) > 1 const engagementDuration = parseInt(String(client.get('engagementDuration')), 10) || 0 - if (engagementDuration) { + + // include _et parameter for engagement time metrics + if ( + (eventType === 'event' || eventType === 'ecommerce') && + firstPageEvent(client) + ) { + requestBody._et = engagementDuration + console.log( + '💡💡💡💡💡💡💡💡 event/ecommerce includes _et: ', + requestBody._et + ) + // mark first event to avoid sending _et with upcoming events on that page + event.client.set('countedEvent', '1', { scope: 'page' }) + // Reset engagementDuration after event has been dispatched so it does not accumulate + event.client.set('engagementDuration', '0') + } else if (eventType === 'user_engagement') { requestBody._et = engagementDuration + // Reset engagementDuration after event has been dispatched so it does not accumulate + event.client.set('engagementDuration', '0') + console.log( + '🐝🐝🐝🐝🐝🐝🐝 user_engagement includes _et: ', + requestBody._et + ) } /* Start of gclid treating */ diff --git a/src/utils.ts b/src/utils.ts index 27cafe0..c99cdb9 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,11 @@ -import { Client, MCEvent } from '@managed-components/types' -import { SESSION_DURATION_IN_MIN } from '.' +import { + Client, + ComponentSettings, + MCEvent, + Manager, +} from '@managed-components/types' +import { sendEvent } from '.' +import { Settings } from 'http2' export const flattenKeys = (obj: { [k: string]: unknown } = {}, prefix = '') => Object.keys(obj).reduce((acc: { [k: string]: unknown }, k) => { @@ -72,7 +78,12 @@ export const countConversion = (event: MCEvent) => { } } -export const computeEngagementDuration = (event: MCEvent) => { +export const computeEngagementDuration = ( + event: MCEvent, + settings: ComponentSettings +) => { + const SESSION_DURATION_IN_MIN = settings.sessionLength || 30 // inactivity time gap between sessions (in min) + const now = new Date(Date.now()).getTime() let engagementDuration = @@ -87,10 +98,15 @@ export const computeEngagementDuration = (event: MCEvent) => { } engagementDuration += now - engagementStart - event.client.set('engagementDuration', `${engagementDuration}`) - - // engagement start gets reset on every new pageview or event - event.client.set('engagementStart', `${now}`) } +export const sendUserEngagementEvent = ( + event: MCEvent, + settings: Settings, + manager: Manager +) => { + computeEngagementDuration(event, settings) + + sendEvent('user_engagement', event, settings, manager) +}