Merged in feat/tracking-page-load-events (pull request #434)

Feat(SW-289): tracking page load events

Approved-by: Michael Zetterberg
This commit is contained in:
Christel Westerberg
2024-08-23 12:19:15 +00:00
committed by Michael Zetterberg
5 changed files with 143 additions and 31 deletions

View File

@@ -1,9 +1,11 @@
"use client" "use client"
import NextLink from "next/link" import NextLink from "next/link"
import { usePathname } from "next/navigation" import { usePathname, useRouter } from "next/navigation"
import { useCallback, useEffect } from "react" import { startTransition, useCallback, useEffect } from "react"
import { trackClick } from "@/utils/tracking" import useRouterTransitionStore from "@/stores/router-transition"
import { trackClick, trackPageViewStart } from "@/utils/tracking"
import { linkVariants } from "./variants" import { linkVariants } from "./variants"
@@ -21,6 +23,7 @@ export default function Link({
prefetch, prefetch,
variant, variant,
trackingId, trackingId,
onClick,
...props ...props
}: LinkProps) { }: LinkProps) {
const currentPageSlug = usePathname() const currentPageSlug = usePathname()
@@ -39,28 +42,36 @@ export default function Link({
variant, variant,
}) })
const router = useRouter()
const startRouterTransition = useRouterTransitionStore(
(state) => state.startRouterTransition
)
const trackClickById = useCallback(() => { const trackClickById = useCallback(() => {
if (trackingId) { if (trackingId) {
trackClick(trackingId) trackClick(trackingId)
} }
}, [trackingId]) }, [trackingId])
useEffect(() => {
if (trackingId) {
const linkComponent = document.getElementById(trackingId)
linkComponent?.addEventListener("click", trackClickById)
return () => {
linkComponent?.removeEventListener("click", trackClickById)
}
}
}, [trackClickById, trackingId])
return ( return (
<NextLink <NextLink
scroll={scroll} scroll={scroll}
prefetch={prefetch} prefetch={prefetch}
className={classNames} className={classNames}
onClick={(e) => {
trackPageViewStart()
startTransition(() => {
startRouterTransition()
if (trackingId) {
trackClickById()
}
if (onClick) {
onClick(e)
}
router.push(href)
})
}}
href={href} href={href}
id={trackingId} id={trackingId}
{...props} {...props}

View File

@@ -1,7 +1,7 @@
"use client" "use client"
import { usePathname } from "next/navigation" import { usePathname } from "next/navigation"
import { useCallback, useEffect } from "react" import { useCallback, useEffect, useState } from "react"
import { webviews } from "@/constants/routes/webviews" import { webviews } from "@/constants/routes/webviews"
@@ -16,8 +16,6 @@ function createSDKPageObject(trackingData: TrackingSDKData) {
const { host: domain } = window.location const { host: domain } = window.location
const page_obj = { const page_obj = {
event: "pageView",
pageInfo: {
pageType: trackingData.pageType, pageType: trackingData.pageType,
pageName: joinedSegments, pageName: joinedSegments,
pageId: trackingData.pageId, pageId: trackingData.pageId,
@@ -29,7 +27,6 @@ function createSDKPageObject(trackingData: TrackingSDKData) {
createDate: trackingData.createdDate, createDate: trackingData.createdDate,
publishDate: trackingData.publishedDate, publishDate: trackingData.publishedDate,
// sessionid: "<unique identifier of session>", // base on what? // sessionid: "<unique identifier of session>", // base on what?
},
} }
return page_obj return page_obj
} }
@@ -37,6 +34,7 @@ function createSDKPageObject(trackingData: TrackingSDKData) {
export default function TrackingSDK({ pageData, userData }: TrackingSDKProps) { export default function TrackingSDK({ pageData, userData }: TrackingSDKProps) {
const pathName = usePathname() const pathName = usePathname()
const isWebview = webviews.includes(pathName) const isWebview = webviews.includes(pathName)
const [initPerformanceTracking, setInitPerformanceTracking] = useState(true)
const CookiebotCallbackOnAccept = useCallback(() => { const CookiebotCallbackOnAccept = useCallback(() => {
const cookie = window._satellite.cookie.get("CookieConsent") const cookie = window._satellite.cookie.get("CookieConsent")
@@ -62,11 +60,55 @@ export default function TrackingSDK({ pageData, userData }: TrackingSDKProps) {
} }
} }
useEffect(() => {
if (initPerformanceTracking) {
const perfObserver = new PerformanceObserver((observedEntries) => {
const entry = observedEntries.getEntriesByType("navigation")[0]
if (entry && window.adobeDataLayer) {
const trackingData = { ...pageData, pathName }
const pageObject = createSDKPageObject(trackingData)
const { loadEventEnd, startTime, duration } =
entry as PerformanceNavigationTiming
window.adobeDataLayer.push({
event: "pageViewEnd",
pageInfo: pageObject,
userInfo: userData,
timing: {
duration,
loadEventEnd,
startTime,
},
})
}
})
perfObserver.observe({
type: "navigation",
buffered: true,
})
setInitPerformanceTracking(false)
// Cleanup function to disconnect the observer
return () => {
perfObserver.disconnect()
}
}
}, [pathName, pageData, userData, initPerformanceTracking])
useEffect(() => { useEffect(() => {
if (window.adobeDataLayer) { if (window.adobeDataLayer) {
const trackingData = { ...pageData, pathName } const trackingData = { ...pageData, pathName }
const pageObject = createSDKPageObject(trackingData) const pageObject = createSDKPageObject(trackingData)
window.adobeDataLayer.push({ ...pageObject, userInfo: userData })
window.adobeDataLayer.push({
event: "pageView",
pageInfo: pageObject,
userInfo: userData,
})
} }
}, [pathName, pageData, userData]) }, [pathName, pageData, userData])

View File

@@ -0,0 +1,35 @@
"use client"
import { startTransition, useEffect, useOptimistic } from "react"
import useRouterTransitionStore from "@/stores/router-transition"
export default function RouterTransition() {
const [loading, setLoading] = useOptimistic(false)
const { isTransitioning, stopRouterTransition } = useRouterTransitionStore()
useEffect(() => {
if (!isTransitioning) {
return
}
if (isTransitioning) {
startTransition(() => {
setLoading(true)
})
}
if (!loading && isTransitioning) {
stopRouterTransition()
// Send event to adobe that navigation transition is completed
if (window.adobeDataLayer) {
window.adobeDataLayer.push({
event: "pageViewEnd",
})
}
}
}, [isTransitioning, setLoading, loading, stopRouterTransition])
return null
}

View File

@@ -1,5 +1,7 @@
import { serverClient } from "@/lib/trpc/server" import { serverClient } from "@/lib/trpc/server"
import RouterTransition from "@/components/TrackingSDK/RouterTransition"
import TrackingSDKClient from "./Client" import TrackingSDKClient from "./Client"
import { TrackingSDKPageData } from "@/types/components/tracking" import { TrackingSDKPageData } from "@/types/components/tracking"
@@ -15,5 +17,10 @@ export default async function TrackingSDK({
}) { }) {
const userTrackingData = await serverClient().user.tracking() const userTrackingData = await serverClient().user.tracking()
return <TrackingSDKClient pageData={pageData} userData={userTrackingData} /> return (
<>
<TrackingSDKClient pageData={pageData} userData={userTrackingData} />
<RouterTransition />
</>
)
} }

View File

@@ -0,0 +1,17 @@
"use client"
import { create } from "zustand"
interface RouterTransitionState {
isTransitioning: boolean
startRouterTransition: () => void
stopRouterTransition: () => void
}
const useRouterTransitionStore = create<RouterTransitionState>((set) => ({
isTransitioning: false,
startRouterTransition: () => set(() => ({ isTransitioning: true })),
stopRouterTransition: () => set(() => ({ isTransitioning: false })),
}))
export default useRouterTransitionStore