UNPKG

ttf-api

Version:

The TrueToForm API SDK

171 lines (151 loc) 5.33 kB
import EventQueue from "./eventQueue"; import { sessionIdKey, userIdKey } from "./client"; const eventCategories = { WIDGET_INTERACTION: "widget_interaction", HOST_INTERACTION: "host_interaction", USER_UPDATE: "user_update", CONVERSION: "conversion", CUSTOM: "custom", }; const getBrowserName = (userAgent) => { if (userAgent.includes("Firefox")) { return "Firefox"; } else if (userAgent.includes("Edg")) { return "Edge"; } else if (userAgent.includes("Chrome") && !userAgent.includes("Chromium")) { return "Chrome"; } else if (userAgent.includes("Safari") && !userAgent.includes("Chrome")) { return "Safari"; } else if (userAgent.includes("Opera") || userAgent.includes("OPR")) { return "Opera"; } else if (userAgent.includes("MSIE") || userAgent.includes("Trident")) { return "Internet Explorer"; } else { return "Unknown"; } }; const getTimeOfDay = () => { const hour = new Date().getHours(); if (hour >= 5 && hour < 12) { return "morning"; } else if (hour >= 12 && hour < 14) { return "noon"; } else if (hour >= 14 && hour < 18) { return "afternoon"; } else if (hour >= 18 && hour < 21) { return "evening"; } else { return "night"; } }; const getTimeZoneData = () => { const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; const timeZoneOffset = new Date().getTimezoneOffset(); return { timeZone, timeZoneOffset }; }; function replaceUndefinedWithNull(obj) { for (const key in obj) { if (obj[key] === undefined) { obj[key] = null; } else if (typeof obj[key] === "object" && obj[key] !== null) { replaceUndefinedWithNull(obj[key]); } } } const eventCache = {}; const analyticsEventsWrapper = (client) => { const eventQueue = EventQueue.getInstance(client); const THROTTLE_WINDOW = 500; // milliseconds const getAnalyticsEvents = (widgetState = {}) => { const createEvent = (category, name, props) => { if (typeof name !== "string") { name = String(name); } if (typeof props !== "object" || props === null) { props = {}; } let duration = null; if (props.startTime && typeof props.startTime === "number") { duration = Date.now() - props.startTime; delete props.startTime; } props.duration = duration; const sortObject = (obj) => { if (obj !== null && typeof obj === "object" && !Array.isArray(obj)) { const sortedObj = {}; const keys = Object.keys(obj).sort(); keys.forEach((key) => { sortedObj[key] = sortObject(obj[key]); }); return sortedObj; } else if (Array.isArray(obj)) { return obj.map(sortObject); } else { return obj; } }; const sortedProps = sortObject(props); // since props obj is small, we can afford to sort it const eventKey = `${category}_${name}_${JSON.stringify(sortedProps)}`; if ( eventCache[eventKey] && Date.now() - eventCache[eventKey] < THROTTLE_WINDOW ) { // Throttle the event (do not enqueue) return; } else { // Update the cache eventCache[eventKey] = Date.now(); // Proceed to create and enqueue the event } const event = { category, name, props, // these props will be validated by the server // but we still need to include them in the event in case these events // are indeed should be added to the previous session even if the session is expired analyticsUserId: localStorage.getItem(userIdKey), analyticsSessionId: localStorage.getItem(sessionIdKey), metadata: { timestamp: new Date().toISOString(), localTimestamp: new Date().toLocaleString(), timeZone: getTimeZoneData().timeZone, timeZoneOffset: getTimeZoneData().timeZoneOffset, timeOfDay: getTimeOfDay(), pageUrl: window.location.href, referrerUrl: document.referrer, device: /Mobi|Android/i.test(navigator.userAgent) ? "mobile" : "desktop", screenWidth: window.screen.width, screenHeight: window.screen.height, browserLanguage: navigator.language, browser: getBrowserName(navigator.userAgent), widgetState, }, }; replaceUndefinedWithNull(event); eventQueue.enqueueEvent(event); return event; }; const addWidgetInteractionEvent = (name, props = {}) => createEvent(eventCategories.WIDGET_INTERACTION, name, props); const addHostInteractionEvent = (name, props = {}) => createEvent(eventCategories.HOST_INTERACTION, name, props); const addUserUpdateEvent = (name, props = {}) => createEvent(eventCategories.USER_UPDATE, name, props); const addConversionEvent = (name, props = {}) => createEvent(eventCategories.CONVERSION, name, props); const addCustomEvent = (name, props = {}) => createEvent(eventCategories.CUSTOM, name, props); return { addWidgetInteractionEvent, addHostInteractionEvent, addUserUpdateEvent, addConversionEvent, addCustomEvent, }; }; return getAnalyticsEvents; }; export { analyticsEventsWrapper };