"use client" import { useEffect } from "react" import { useSessionId } from "@scandic-hotels/common/hooks/useSessionId" import { logger } from "@scandic-hotels/common/logger" import { promiseWithTimeout } from "@scandic-hotels/common/utils/promiseWithTimeout" import { createSDKPageObject, trackPageView } from "./pageview" import { useTrackingUserData } from "./useTrackingUserData" import type { TrackingSDKAncillaries, TrackingSDKHotelInfo, TrackingSDKPageData, TrackingSDKPaymentInfo, TrackingSDKUserData, } from "./types" type TrackingSDKProps = { pageData: TrackingSDKPageData hotelInfo?: TrackingSDKHotelInfo paymentInfo?: TrackingSDKPaymentInfo ancillaries?: TrackingSDKAncillaries pathName: string } let hasTrackedHardNavigation = false export const useTrackHardNavigation = ({ pageData, hotelInfo, paymentInfo, ancillaries, pathName, }: TrackingSDKProps) => { const sessionId = useSessionId() const { userData, isPending } = useTrackingUserData() useEffect(() => { if (isPending) { return } if (!userData) { return } const track = () => { if (hasTrackedHardNavigation) { return } trackPerformance({ pathName, sessionId, paymentInfo, hotelInfo, userData, pageData, ancillaries, }) hasTrackedHardNavigation = true } if (document.readyState === "complete") { track() return } window.addEventListener("load", track) return () => window.removeEventListener("load", track) }, [ pathName, hotelInfo, pageData, sessionId, paymentInfo, userData, ancillaries, ]) } const trackPerformance = async ({ pathName, sessionId, paymentInfo, hotelInfo, userData, pageData, ancillaries, }: { pathName: string sessionId: string | null paymentInfo: TrackingSDKProps["paymentInfo"] hotelInfo: TrackingSDKProps["hotelInfo"] userData: TrackingSDKUserData pageData: TrackingSDKProps["pageData"] ancillaries: TrackingSDKProps["ancillaries"] }) => { let pageLoadTime: number | undefined = undefined let lcpTime: number | undefined = undefined try { pageLoadTime = await promiseWithTimeout(getPageLoadTimeEntry(), 3000) } catch (error) { logger.error("Error obtaining pageLoadTime:", error) } try { lcpTime = await promiseWithTimeout(getLCPTimeEntry(), 3000) } catch (error) { logger.error("Error obtaining lcpTime:", error) } const trackingData = { ...pageData, sessionId, pathName, pageLoadTime, lcpTime, } const pageObject = createSDKPageObject(trackingData) trackPageView({ event: "pageView", pageInfo: pageObject, userInfo: userData, hotelInfo, paymentInfo, ancillaries, }) } const getLCPTimeEntry = () => { return new Promise((resolve) => { const observer = new PerformanceObserver((entries) => { const lastEntry = entries.getEntries().at(-1) if (lastEntry) { observer.disconnect() resolve(lastEntry.startTime / 1000) } }) const lcpSupported = PerformanceObserver.supportedEntryTypes?.includes( "largest-contentful-paint" ) if (lcpSupported) { observer.observe({ type: "largest-contentful-paint", buffered: true, }) } else { resolve(undefined) } }) } const getPageLoadTimeEntry = () => { return new Promise((resolve) => { const observer = new PerformanceObserver((entries) => { const navEntry = entries.getEntriesByType("navigation")[0] if (navEntry) { observer.disconnect() resolve(navEntry.duration / 1000) } }) observer.observe({ type: "navigation", buffered: true }) }) }