ttf-api
Version:
The TrueToForm API SDK
171 lines (151 loc) • 5.33 kB
JavaScript
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 };