From 4ca8b3215e2dbd09459000fad2b6b16c2aa01c0e Mon Sep 17 00:00:00 2001 From: AliHashish Date: Mon, 21 Aug 2023 14:35:40 +0300 Subject: [PATCH 1/4] Live Signals Actions --- packages/clarity-devtools/src/config.ts | 3 + packages/clarity-devtools/src/content.ts | 5 +- packages/clarity-js/src/clarity.ts | 2 +- packages/clarity-js/src/core/action.ts | 127 +++++++++++++++++++++++ packages/clarity-js/src/core/config.ts | 5 +- packages/clarity-js/src/core/index.ts | 5 +- packages/clarity-js/src/core/signal.ts | 22 ++++ packages/clarity-js/src/data/index.ts | 1 + packages/clarity-js/src/data/signal.ts | 7 ++ packages/clarity-js/src/data/upload.ts | 12 ++- packages/clarity-js/types/core.d.ts | 3 + packages/clarity-js/types/data.d.ts | 8 ++ packages/clarity-js/types/index.d.ts | 1 + 13 files changed, 193 insertions(+), 8 deletions(-) create mode 100644 packages/clarity-js/src/core/action.ts create mode 100644 packages/clarity-js/src/core/signal.ts create mode 100644 packages/clarity-js/src/data/signal.ts diff --git a/packages/clarity-devtools/src/config.ts b/packages/clarity-devtools/src/config.ts index cd4711a9..31c0d238 100644 --- a/packages/clarity-devtools/src/config.ts +++ b/packages/clarity-devtools/src/config.ts @@ -18,5 +18,8 @@ export default function(): Core.Config { ], fraud: true, checksum: [], + liveSignalsActionParams: [], + defaultAction: null, + customAction: (): void => { }, }; } diff --git a/packages/clarity-devtools/src/content.ts b/packages/clarity-devtools/src/content.ts index 72c55815..350ec3cd 100644 --- a/packages/clarity-devtools/src/content.ts +++ b/packages/clarity-devtools/src/content.ts @@ -66,7 +66,10 @@ function wireup(settings: any): string { unmask: "$__unmask__$", content: "$__showText__$", upload: (data: string): void => { window.postMessage({ action: "upload", payload: data }, "*"); }, - projectId: "devtools" + projectId: "devtools", + liveSignalsActionParams: [], + defaultAction: 0, + customAction: () => {} }); }).toString(); Object.keys(settings).forEach(s => code = code.replace(`"$__${s}__$"`, JSON.stringify(settings[s]))); diff --git a/packages/clarity-js/src/clarity.ts b/packages/clarity-js/src/clarity.ts index b1b12528..a871d4bb 100644 --- a/packages/clarity-js/src/clarity.ts +++ b/packages/clarity-js/src/clarity.ts @@ -11,7 +11,7 @@ import * as interaction from "@src/interaction"; import * as layout from "@src/layout"; import * as performance from "@src/performance"; export { version }; -export { consent, event, identify, set, upgrade, metadata } from "@src/data"; +export { consent, event, identify, set, upgrade, metadata, signal } from "@src/data"; export { hashText } from "@src/layout"; const modules: Module[] = [diagnostic, layout, interaction, performance]; diff --git a/packages/clarity-js/src/core/action.ts b/packages/clarity-js/src/core/action.ts new file mode 100644 index 00000000..9ce72041 --- /dev/null +++ b/packages/clarity-js/src/core/action.ts @@ -0,0 +1,127 @@ +import config from "@src/core/config"; +import { signal } from "@src/data/signal"; + + +export function determineAction(): void { + switch (config.defaultAction) { + case 0: + // A custom action written by the client + signal(config.customAction); + break; + case 1: + signal(showDialog); + break; + case 2: + signal(showAlert); + break; + default: + signal(null); + break; + } +} + +function showAlert(): void { + // Shows a blocking warning + var str: string = "It seems you might be facing some troubles. Please contact us at: "; + if (config.liveSignalsActionParams?.length > 0) + { + // The client email for example + str += config.liveSignalsActionParams[0]; + } + alert(str); +} + +function showDialog(): void { + // This function may be further optimized and cleaned up + try { + // Tries to get the dialog element if it exists + const dialog = document.getElementById("zdialog") as HTMLDialogElement; // using a weird ID to avoid potential conflict with existing IDs + var str: string = "It seems you might be facing some troubles. Please contact us at: "; + if (config.liveSignalsActionParams?.length > 0) + { + // possibly the email + str += config.liveSignalsActionParams[0]; + } + dialog.innerHTML = str; + + var lightmode : boolean = false; + if (config.liveSignalsActionParams?.length > 1) + { + lightmode = (config.liveSignalsActionParams[1].toLowerCase() == "true")? true : false; + } + + styleDialog(dialog, lightmode); + dialog.style.display = "block"; + } catch (err) { + // If not, creates a new dialog element + const dialog = document.createElement("dialog") as HTMLDialogElement; + // dialog.innerHTML = "Non-Blocking test dialog 2"; + var str: string = "It seems you might be facing some troubles. Please contact us at: "; + if (config.liveSignalsActionParams?.length > 0) + { + str += config.liveSignalsActionParams[0]; + } + dialog.innerHTML = str; + var lightmode : boolean = false; + if (config.liveSignalsActionParams?.length > 1) + { + lightmode = (config.liveSignalsActionParams[1].toLowerCase() == "true")? true : false; + } + styleDialog(dialog, lightmode); + } +} + +function closeDialog(): void { + // No need to try catch, as this will only be called after + // showDialog, which means the dialog element exists + const dialog = document.getElementById("zdialog") as HTMLDialogElement; + dialog.style.display = "none"; +} + +function styleDialog(dialog: HTMLDialogElement, lightMode = false): void { + dialog.style.display = "block"; + dialog.id = "zdialog"; // Choosing a weird name to avoid any potential conflicts with other elements + + // Apply common styling + dialog.style.position = "fixed"; + dialog.style.top = "50%"; + dialog.style.left = "50%"; + dialog.style.transform = "translate(-50%, -50%)"; + dialog.style.margin = "0"; + dialog.style.padding = "20px"; + dialog.style.width = "80%"; + dialog.style.textAlign = "center"; + dialog.style.zIndex = "10000"; // Set the z-index to 10000 to make sure it is on top of everything + + // Apply mode-specific styling + if (lightMode) { + dialog.style.backgroundColor = "rgba(39, 54, 223, 0.93)"; + dialog.style.color = "rgb(245, 245, 245)"; + dialog.style.border = "2px solid #ddd"; // Thicker border in light mode + } else { + dialog.style.backgroundColor = "rgba(0, 0, 0, 0.9)"; + dialog.style.color = "#fff"; + dialog.style.border = "2px solid #666"; // Thicker border in dark mode + } + + // Create a close button + const close = document.createElement("button"); + close.innerHTML = "OK"; + close.onclick = closeDialog; + dialog.appendChild(close); + + // Apply styling to the close button + close.style.backgroundColor = "#4CAF50"; + close.style.color = "#fff"; + close.style.padding = "10px 20px"; + close.style.border = "none"; + close.style.borderRadius = "4px"; + close.style.fontSize = "16px"; + close.style.cursor = "pointer"; + close.style.textShadow = "1px 1px #000"; + close.style.display = "block"; + close.style.margin = "auto"; + close.style.marginTop = "20px"; + + document.body.appendChild(dialog); // Append dialog to the body element +} \ No newline at end of file diff --git a/packages/clarity-js/src/core/config.ts b/packages/clarity-js/src/core/config.ts index 45b1dc9f..f7357047 100644 --- a/packages/clarity-js/src/core/config.ts +++ b/packages/clarity-js/src/core/config.ts @@ -18,7 +18,10 @@ let config: Config = { fallback: null, upgrade: null, action: null, - dob: null + dob: null, + liveSignalsActionParams: [], + defaultAction: null, + customAction: null }; export default config; diff --git a/packages/clarity-js/src/core/index.ts b/packages/clarity-js/src/core/index.ts index 1bae028a..af063cfa 100644 --- a/packages/clarity-js/src/core/index.ts +++ b/packages/clarity-js/src/core/index.ts @@ -51,7 +51,10 @@ export function config(override: Config): boolean { // Process custom configuration overrides, if available if (override === null || status) { return false; } for (let key in override) { - if (key in configuration) { configuration[key] = override[key]; } + if (key in configuration) { + if (key === "customAction") { configuration[key] = eval(String(override[key])); continue; } + configuration[key] = override[key]; + } } return true; } diff --git a/packages/clarity-js/src/core/signal.ts b/packages/clarity-js/src/core/signal.ts new file mode 100644 index 00000000..2f83ff42 --- /dev/null +++ b/packages/clarity-js/src/core/signal.ts @@ -0,0 +1,22 @@ +import { ClaritySignal } from "@clarity-types/data"; +import { signalCallback } from "@src/data/signal"; +import { determineAction } from "@src/core/action"; + +function parseSignals(signalsString: string): ClaritySignal[] { + const signalsJson: ClaritySignal[] = JSON.parse(signalsString) + return signalsJson; +} + +export function signalEvent(signalsString: string) { + try { + const signals = parseSignals(signalsString); + + // Check whether user is frustrated or not + // if yes, execute an action based on configuration + if(!signalCallback) determineAction(); + signals.forEach(signal => { + signalCallback(signal) + }) + } catch (err) { + } +} diff --git a/packages/clarity-js/src/data/index.ts b/packages/clarity-js/src/data/index.ts index 4010efd8..4c5fc040 100644 --- a/packages/clarity-js/src/data/index.ts +++ b/packages/clarity-js/src/data/index.ts @@ -16,6 +16,7 @@ export { event } from "@src/data/custom"; export { consent, metadata } from "@src/data/metadata"; export { upgrade } from "@src/data/upgrade"; export { set, identify } from "@src/data/variable"; +export { signal } from "@src/data/signal"; const modules: Module[] = [baseline, dimension, variable, limit, summary, metadata, envelope, upload, ping, upgrade, extract]; diff --git a/packages/clarity-js/src/data/signal.ts b/packages/clarity-js/src/data/signal.ts new file mode 100644 index 00000000..c5eb647a --- /dev/null +++ b/packages/clarity-js/src/data/signal.ts @@ -0,0 +1,7 @@ +import { SignalCallback } from "@clarity-types/data"; + +export let signalCallback: SignalCallback = null; + +export function signal(cb: SignalCallback): void { + signalCallback = cb +} \ No newline at end of file diff --git a/packages/clarity-js/src/data/upload.ts b/packages/clarity-js/src/data/upload.ts index cd9c7191..bec68bc3 100644 --- a/packages/clarity-js/src/data/upload.ts +++ b/packages/clarity-js/src/data/upload.ts @@ -3,6 +3,7 @@ import { BooleanFlag, Check, Constant, EncodedPayload, Event, Metric, Setting, T import * as clarity from "@src/clarity"; import config from "@src/core/config"; import measure from "@src/core/measure"; +import { signalEvent } from "@src/core/signal"; import { time } from "@src/core/time"; import { clearTimeout, setTimeout } from "@src/core/timeout"; import compress from "@src/data/compress"; @@ -145,7 +146,7 @@ function stringify(encoded: EncodedPayload): string { function send(payload: string, zipped: Uint8Array, sequence: number, beacon: boolean = false): void { // Upload data if a valid URL is defined in the config - if (typeof config.upload === Constant.String) { + if (typeof config.upload === Constant.String) { const url = config.upload as string; let dispatched = false; @@ -166,7 +167,7 @@ function send(payload: string, zipped: Uint8Array, sequence: number, beacon: boo // a) It's not the last payload, and therefore we didn't attempt sending sendBeacon // b) It's the last payload, however, we failed to queue sendBeacon call and need to now fall back to XHR. // E.g. if data is over 64KB, several user agents (like Chrome) will reject to queue the sendBeacon call. - if (dispatched === false) { + if (dispatched === false) { // While tracking payload for retry, we only track string value of the payload to err on the safe side // Not all browsers support compression API and the support for it in supported browsers is still experimental if (sequence in transit) { transit[sequence].attempts++; } else { transit[sequence] = { data: payload, attempts: 1 }; } @@ -185,7 +186,7 @@ function send(payload: string, zipped: Uint8Array, sequence: number, beacon: boo xhr.send(payload); } } - } else if (config.upload) { + } else if (config.upload) { const callback = config.upload as UploadCallback; callback(payload); done(sequence); @@ -250,7 +251,7 @@ function response(payload: string): void { let lines = payload && payload.length > 0 ? payload.split("\n") : []; for (var line of lines) { - let parts = line && line.length > 0 ? line.split(/ (.*)/) : [Constant.Empty]; + let parts = line && line.length > 0 ? line.split(/ (.*)/) : [Constant.Empty]; switch (parts[0]) { case Constant.End: // Clear out session storage and end the session so we can start fresh the next time @@ -267,6 +268,9 @@ function response(payload: string): void { case Constant.Extract: if (parts.length > 1) { extract.trigger(parts[1]); } break; + case Constant.Signal: + if (parts.length > 1) { signalEvent(parts[1]) } + break; } } } diff --git a/packages/clarity-js/types/core.d.ts b/packages/clarity-js/types/core.d.ts index 7bf34406..2b16c330 100644 --- a/packages/clarity-js/types/core.d.ts +++ b/packages/clarity-js/types/core.d.ts @@ -137,6 +137,9 @@ export interface Config { upgrade?: (key: string) => void; action?: (key: string) => void; dob?: number; + liveSignalsActionParams?: string[]; + defaultAction?: number; + customAction?: Data.SignalCallback; } export const enum Constant { diff --git a/packages/clarity-js/types/data.d.ts b/packages/clarity-js/types/data.d.ts index c1b93348..ab1e4edc 100644 --- a/packages/clarity-js/types/data.d.ts +++ b/packages/clarity-js/types/data.d.ts @@ -8,6 +8,7 @@ export interface MetadataCallbackOptions { callback: MetadataCallback, wait: boolean } +export type SignalCallback = (data: ClaritySignal) => void; /* Enum */ export const enum Event { @@ -273,6 +274,7 @@ export const enum Constant { End = "END", Upgrade = "UPGRADE", Action = "ACTION", + Signal = "SIGNAL", Extract = "EXTRACT", UserHint = "userHint", UserType = "userType", @@ -439,3 +441,9 @@ export interface UploadData { attempts: number; status: number; } + +export interface ClaritySignal { + type: string + timestamp?: number + value?: number +} \ No newline at end of file diff --git a/packages/clarity-js/types/index.d.ts b/packages/clarity-js/types/index.d.ts index 8c3d66a5..79c1dfe1 100644 --- a/packages/clarity-js/types/index.d.ts +++ b/packages/clarity-js/types/index.d.ts @@ -16,6 +16,7 @@ interface Clarity { set: (variable: string, value: string | string[]) => void; identify: (userId: string, sessionId?: string, pageId?: string, userHint?: string) => void; metadata: (callback: Data.MetadataCallback, wait?: boolean) => void; + signal: (callback: Data.SignalCallback) => void; } interface Selector { From 0c749f24915a8aaa4a7eba5ef8d308915acfa031 Mon Sep 17 00:00:00 2001 From: AliHashish Date: Mon, 21 Aug 2023 15:02:03 +0300 Subject: [PATCH 2/4] Update upload.ts --- packages/clarity-js/src/data/upload.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/clarity-js/src/data/upload.ts b/packages/clarity-js/src/data/upload.ts index bec68bc3..eeab6033 100644 --- a/packages/clarity-js/src/data/upload.ts +++ b/packages/clarity-js/src/data/upload.ts @@ -146,7 +146,7 @@ function stringify(encoded: EncodedPayload): string { function send(payload: string, zipped: Uint8Array, sequence: number, beacon: boolean = false): void { // Upload data if a valid URL is defined in the config - if (typeof config.upload === Constant.String) { + if (typeof config.upload === Constant.String) { const url = config.upload as string; let dispatched = false; @@ -167,7 +167,7 @@ function send(payload: string, zipped: Uint8Array, sequence: number, beacon: boo // a) It's not the last payload, and therefore we didn't attempt sending sendBeacon // b) It's the last payload, however, we failed to queue sendBeacon call and need to now fall back to XHR. // E.g. if data is over 64KB, several user agents (like Chrome) will reject to queue the sendBeacon call. - if (dispatched === false) { + if (dispatched === false) { // While tracking payload for retry, we only track string value of the payload to err on the safe side // Not all browsers support compression API and the support for it in supported browsers is still experimental if (sequence in transit) { transit[sequence].attempts++; } else { transit[sequence] = { data: payload, attempts: 1 }; } @@ -186,7 +186,7 @@ function send(payload: string, zipped: Uint8Array, sequence: number, beacon: boo xhr.send(payload); } } - } else if (config.upload) { + } else if (config.upload) { const callback = config.upload as UploadCallback; callback(payload); done(sequence); @@ -251,7 +251,7 @@ function response(payload: string): void { let lines = payload && payload.length > 0 ? payload.split("\n") : []; for (var line of lines) { - let parts = line && line.length > 0 ? line.split(/ (.*)/) : [Constant.Empty]; + let parts = line && line.length > 0 ? line.split(/ (.*)/) : [Constant.Empty]; switch (parts[0]) { case Constant.End: // Clear out session storage and end the session so we can start fresh the next time From 330d4c3e0553666b3c0866ed6860ec1406176767 Mon Sep 17 00:00:00 2001 From: AliHashish Date: Thu, 24 Aug 2023 21:25:53 +0300 Subject: [PATCH 3/4] Live Signals Actions update - Parameters are now passed as a dictionary instead of an array of strings - Client no longer has to make their function an arrow one, as this is handled automatically This makes clarity-js more compatible with the other PR made for extra parameters. This has been tested locally. --- packages/clarity-devtools/src/config.ts | 6 ++-- packages/clarity-devtools/src/content.ts | 6 ++-- packages/clarity-js/src/core/action.ts | 35 ++++++++++++++---------- packages/clarity-js/src/core/config.ts | 6 ++-- packages/clarity-js/src/core/index.ts | 2 +- packages/clarity-js/types/core.d.ts | 6 ++-- 6 files changed, 33 insertions(+), 28 deletions(-) diff --git a/packages/clarity-devtools/src/config.ts b/packages/clarity-devtools/src/config.ts index 31c0d238..5b4d484c 100644 --- a/packages/clarity-devtools/src/config.ts +++ b/packages/clarity-devtools/src/config.ts @@ -18,8 +18,8 @@ export default function(): Core.Config { ], fraud: true, checksum: [], - liveSignalsActionParams: [], - defaultAction: null, - customAction: (): void => { }, + liveSignalsActionConfigs: new Map(), + liveSignalsActionType: null, + liveSignalsCustomAction: (): void => { }, }; } diff --git a/packages/clarity-devtools/src/content.ts b/packages/clarity-devtools/src/content.ts index 350ec3cd..6fa6ec67 100644 --- a/packages/clarity-devtools/src/content.ts +++ b/packages/clarity-devtools/src/content.ts @@ -67,9 +67,9 @@ function wireup(settings: any): string { content: "$__showText__$", upload: (data: string): void => { window.postMessage({ action: "upload", payload: data }, "*"); }, projectId: "devtools", - liveSignalsActionParams: [], - defaultAction: 0, - customAction: () => {} + liveSignalsActionConfigs: new Map(), + liveSignalsActionType: 0, + liveSignalsCustomAction: () => {} }); }).toString(); Object.keys(settings).forEach(s => code = code.replace(`"$__${s}__$"`, JSON.stringify(settings[s]))); diff --git a/packages/clarity-js/src/core/action.ts b/packages/clarity-js/src/core/action.ts index 9ce72041..795548b4 100644 --- a/packages/clarity-js/src/core/action.ts +++ b/packages/clarity-js/src/core/action.ts @@ -3,15 +3,19 @@ import { signal } from "@src/data/signal"; export function determineAction(): void { - switch (config.defaultAction) { + switch (config.liveSignalsActionType) { case 0: - // A custom action written by the client - signal(config.customAction); + // No action + signal(null); break; case 1: - signal(showDialog); + // A custom action written by the client + signal(config.liveSignalsCustomAction); break; case 2: + signal(showDialog); + break; + case 3: signal(showAlert); break; default: @@ -23,10 +27,10 @@ export function determineAction(): void { function showAlert(): void { // Shows a blocking warning var str: string = "It seems you might be facing some troubles. Please contact us at: "; - if (config.liveSignalsActionParams?.length > 0) + if ("email" in config.liveSignalsActionConfigs) { // The client email for example - str += config.liveSignalsActionParams[0]; + str += config.liveSignalsActionConfigs["email"]; } alert(str); } @@ -37,17 +41,17 @@ function showDialog(): void { // Tries to get the dialog element if it exists const dialog = document.getElementById("zdialog") as HTMLDialogElement; // using a weird ID to avoid potential conflict with existing IDs var str: string = "It seems you might be facing some troubles. Please contact us at: "; - if (config.liveSignalsActionParams?.length > 0) + if ("email" in config.liveSignalsActionConfigs) { - // possibly the email - str += config.liveSignalsActionParams[0]; + // The client email for example + str += config.liveSignalsActionConfigs["email"]; } dialog.innerHTML = str; var lightmode : boolean = false; - if (config.liveSignalsActionParams?.length > 1) + if ("lightmode" in config.liveSignalsActionConfigs) { - lightmode = (config.liveSignalsActionParams[1].toLowerCase() == "true")? true : false; + lightmode = (config.liveSignalsActionConfigs["lightmode"] == "true")? true : false; } styleDialog(dialog, lightmode); @@ -57,15 +61,16 @@ function showDialog(): void { const dialog = document.createElement("dialog") as HTMLDialogElement; // dialog.innerHTML = "Non-Blocking test dialog 2"; var str: string = "It seems you might be facing some troubles. Please contact us at: "; - if (config.liveSignalsActionParams?.length > 0) + if ("email" in config.liveSignalsActionConfigs) { - str += config.liveSignalsActionParams[0]; + // The client email for example + str += config.liveSignalsActionConfigs["email"]; } dialog.innerHTML = str; var lightmode : boolean = false; - if (config.liveSignalsActionParams?.length > 1) + if ("lightmode" in config.liveSignalsActionConfigs) { - lightmode = (config.liveSignalsActionParams[1].toLowerCase() == "true")? true : false; + lightmode = (config.liveSignalsActionConfigs["lightmode"] == "true")? true : false; } styleDialog(dialog, lightmode); } diff --git a/packages/clarity-js/src/core/config.ts b/packages/clarity-js/src/core/config.ts index f7357047..1528b142 100644 --- a/packages/clarity-js/src/core/config.ts +++ b/packages/clarity-js/src/core/config.ts @@ -19,9 +19,9 @@ let config: Config = { upgrade: null, action: null, dob: null, - liveSignalsActionParams: [], - defaultAction: null, - customAction: null + liveSignalsActionConfigs: new Map(), + liveSignalsActionType: null, + liveSignalsCustomAction: null }; export default config; diff --git a/packages/clarity-js/src/core/index.ts b/packages/clarity-js/src/core/index.ts index af063cfa..1999385a 100644 --- a/packages/clarity-js/src/core/index.ts +++ b/packages/clarity-js/src/core/index.ts @@ -52,7 +52,7 @@ export function config(override: Config): boolean { if (override === null || status) { return false; } for (let key in override) { if (key in configuration) { - if (key === "customAction") { configuration[key] = eval(String(override[key])); continue; } + if (key === "liveSignalsCustomAction") { configuration[key] = eval("() => { " + String(override[key]) + "}"); continue; } configuration[key] = override[key]; } } diff --git a/packages/clarity-js/types/core.d.ts b/packages/clarity-js/types/core.d.ts index 2b16c330..39eee13b 100644 --- a/packages/clarity-js/types/core.d.ts +++ b/packages/clarity-js/types/core.d.ts @@ -137,9 +137,9 @@ export interface Config { upgrade?: (key: string) => void; action?: (key: string) => void; dob?: number; - liveSignalsActionParams?: string[]; - defaultAction?: number; - customAction?: Data.SignalCallback; + liveSignalsActionConfigs?: Map; + liveSignalsActionType?: number; + liveSignalsCustomAction?: Data.SignalCallback; } export const enum Constant { From daa58dfb2c897700f398103061c29192333ba243 Mon Sep 17 00:00:00 2001 From: AliHashish Date: Thu, 31 Aug 2023 19:52:15 +0300 Subject: [PATCH 4/4] Types and Thresholds Handling different signal types, each with their own threshold --- packages/clarity-js/src/core/action.ts | 66 +++++++++++--------------- packages/clarity-js/src/core/index.ts | 9 +++- packages/clarity-js/src/core/signal.ts | 18 +++++-- packages/clarity-js/types/data.d.ts | 9 ++++ 4 files changed, 61 insertions(+), 41 deletions(-) diff --git a/packages/clarity-js/src/core/action.ts b/packages/clarity-js/src/core/action.ts index 795548b4..20257911 100644 --- a/packages/clarity-js/src/core/action.ts +++ b/packages/clarity-js/src/core/action.ts @@ -1,21 +1,21 @@ import config from "@src/core/config"; import { signal } from "@src/data/signal"; - +import { LiveSignalsActionType } from "@clarity-types/data"; export function determineAction(): void { switch (config.liveSignalsActionType) { - case 0: + case LiveSignalsActionType.NoAction: // No action signal(null); break; - case 1: + case LiveSignalsActionType.CustomAction: // A custom action written by the client signal(config.liveSignalsCustomAction); break; - case 2: + case LiveSignalsActionType.ContactUsDialogueBox: signal(showDialog); break; - case 3: + case LiveSignalsActionType.ContactUsAlert: signal(showAlert); break; default: @@ -26,52 +26,44 @@ export function determineAction(): void { function showAlert(): void { // Shows a blocking warning - var str: string = "It seems you might be facing some troubles. Please contact us at: "; + var alertMessage: string = "It seems you might be facing some troubles. Please contact us at: "; if ("email" in config.liveSignalsActionConfigs) { // The client email for example - str += config.liveSignalsActionConfigs["email"]; + alertMessage += config.liveSignalsActionConfigs["email"]; } - alert(str); + alert(alertMessage); } function showDialog(): void { + // Shows a non-blocking dialog box // This function may be further optimized and cleaned up - try { - // Tries to get the dialog element if it exists - const dialog = document.getElementById("zdialog") as HTMLDialogElement; // using a weird ID to avoid potential conflict with existing IDs - var str: string = "It seems you might be facing some troubles. Please contact us at: "; - if ("email" in config.liveSignalsActionConfigs) - { - // The client email for example - str += config.liveSignalsActionConfigs["email"]; - } - dialog.innerHTML = str; - - var lightmode : boolean = false; - if ("lightmode" in config.liveSignalsActionConfigs) + var dialogMessage: string = "It seems you might be facing some troubles. Please contact us at: "; + if ("email" in config.liveSignalsActionConfigs) + { + // The client email for example + dialogMessage += config.liveSignalsActionConfigs["email"]; + } + + var lightmode : boolean = false; + if ("lightmode" in config.liveSignalsActionConfigs) + { + if (typeof(config.liveSignalsActionConfigs["lightmode"]) == "string") { - lightmode = (config.liveSignalsActionConfigs["lightmode"] == "true")? true : false; + lightmode = (config.liveSignalsActionConfigs["lightmode"].toLowerCase() == "true")? true : false; } + } + try { + // Tries to get the dialog element if it exists + const dialog = document.getElementById("zdialog") as HTMLDialogElement; // using a weird ID to avoid potential conflict with existing IDs + dialog.innerHTML = dialogMessage; styleDialog(dialog, lightmode); - dialog.style.display = "block"; } catch (err) { // If not, creates a new dialog element const dialog = document.createElement("dialog") as HTMLDialogElement; // dialog.innerHTML = "Non-Blocking test dialog 2"; - var str: string = "It seems you might be facing some troubles. Please contact us at: "; - if ("email" in config.liveSignalsActionConfigs) - { - // The client email for example - str += config.liveSignalsActionConfigs["email"]; - } - dialog.innerHTML = str; - var lightmode : boolean = false; - if ("lightmode" in config.liveSignalsActionConfigs) - { - lightmode = (config.liveSignalsActionConfigs["lightmode"] == "true")? true : false; - } + dialog.innerHTML = dialogMessage; styleDialog(dialog, lightmode); } } @@ -102,11 +94,11 @@ function styleDialog(dialog: HTMLDialogElement, lightMode = false): void { if (lightMode) { dialog.style.backgroundColor = "rgba(39, 54, 223, 0.93)"; dialog.style.color = "rgb(245, 245, 245)"; - dialog.style.border = "2px solid #ddd"; // Thicker border in light mode + dialog.style.border = "2px solid #ddd"; } else { dialog.style.backgroundColor = "rgba(0, 0, 0, 0.9)"; dialog.style.color = "#fff"; - dialog.style.border = "2px solid #666"; // Thicker border in dark mode + dialog.style.border = "2px solid #666"; } // Create a close button diff --git a/packages/clarity-js/src/core/index.ts b/packages/clarity-js/src/core/index.ts index 1999385a..85aafabe 100644 --- a/packages/clarity-js/src/core/index.ts +++ b/packages/clarity-js/src/core/index.ts @@ -52,7 +52,14 @@ export function config(override: Config): boolean { if (override === null || status) { return false; } for (let key in override) { if (key in configuration) { - if (key === "liveSignalsCustomAction") { configuration[key] = eval("() => { " + String(override[key]) + "}"); continue; } + if (key === "liveSignalsCustomAction") { + // Check if there are some configs sent by the client + let liveSignalsConfigs: string = ('liveSignalsActionConfigs' in override)? "variables = override['liveSignalsActionConfigs'];" : ""; + // This way, the client can use their parameters in their custom action through variables["keyName"] + let liveSignalsCustomCode: string = String(override[key]).replace(/\n/g, "\\n");; + configuration[key] = eval("() => {" + liveSignalsConfigs + liveSignalsCustomCode + "}"); + continue; + } configuration[key] = override[key]; } } diff --git a/packages/clarity-js/src/core/signal.ts b/packages/clarity-js/src/core/signal.ts index 2f83ff42..883130a1 100644 --- a/packages/clarity-js/src/core/signal.ts +++ b/packages/clarity-js/src/core/signal.ts @@ -7,7 +7,18 @@ function parseSignals(signalsString: string): ClaritySignal[] { return signalsJson; } + +const LiveSignalType: Map = new Map([ + ["BotSignal", 0], + ["FrustrationScore", 1], + ["PurchaseProbability", 2] + ]); + export function signalEvent(signalsString: string) { + let botThreshold : number = 0.5; + let frustrationThreshold : number = 0.65; + let purchaseThreshold : number = 0.5; + const thresholds : number[] = [botThreshold, frustrationThreshold, purchaseThreshold]; try { const signals = parseSignals(signalsString); @@ -15,8 +26,9 @@ export function signalEvent(signalsString: string) { // if yes, execute an action based on configuration if(!signalCallback) determineAction(); signals.forEach(signal => { - signalCallback(signal) + if (signal["value"] > thresholds[LiveSignalType.get(signal["type"])]) { + signalCallback(signal); + } }) - } catch (err) { - } + } catch (err) { } } diff --git a/packages/clarity-js/types/data.d.ts b/packages/clarity-js/types/data.d.ts index ab1e4edc..8b29867e 100644 --- a/packages/clarity-js/types/data.d.ts +++ b/packages/clarity-js/types/data.d.ts @@ -315,6 +315,15 @@ export const enum XMLReadyState { Done = 4 } +/* Live Signals */ + +export const enum LiveSignalsActionType { + NoAction = 0, + CustomAction = 1, + ContactUsDialogueBox = 2, + ContactUsAlert = 3 +} + /* Helper Interfaces */ export interface Payload {