@magicfeedback/popup-sdk 1.1.0 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -19
- package/dist/chunk-6WTOQGET.mjs +141 -0
- package/dist/chunk-6WTOQGET.mjs.map +1 -0
- package/dist/chunk-IOAZQPZE.mjs +120 -0
- package/dist/chunk-IOAZQPZE.mjs.map +1 -0
- package/dist/index.d.mts +417 -40
- package/dist/index.d.ts +417 -40
- package/dist/index.js +987 -106
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +841 -3411
- package/dist/index.mjs.map +1 -1
- package/dist/react-native.d.mts +486 -0
- package/dist/react-native.d.ts +486 -0
- package/dist/react-native.js +3286 -0
- package/dist/react-native.js.map +1 -0
- package/dist/react-native.mjs +1758 -0
- package/dist/react-native.mjs.map +1 -0
- package/dist/renderPopup-KBDWWSUU.mjs +1376 -0
- package/dist/renderPopup-KBDWWSUU.mjs.map +1 -0
- package/dist/renderPopup-YLFRGPQN.mjs +3303 -0
- package/dist/renderPopup-YLFRGPQN.mjs.map +1 -0
- package/package.json +36 -2
package/dist/index.js
CHANGED
|
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
8
11
|
var __commonJS = (cb, mod) => function __require() {
|
|
9
12
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
10
13
|
};
|
|
@@ -30,6 +33,117 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
30
33
|
));
|
|
31
34
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
35
|
|
|
36
|
+
// src/tracking/tracking-manager.ts
|
|
37
|
+
function createDefaultStorage() {
|
|
38
|
+
try {
|
|
39
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
40
|
+
const ls = window.localStorage;
|
|
41
|
+
const probe = "__deepdots_probe__";
|
|
42
|
+
ls.setItem(probe, "1");
|
|
43
|
+
ls.removeItem(probe);
|
|
44
|
+
return {
|
|
45
|
+
getItem: (k) => ls.getItem(k),
|
|
46
|
+
setItem: (k, v) => ls.setItem(k, v),
|
|
47
|
+
removeItem: (k) => ls.removeItem(k)
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
} catch {
|
|
51
|
+
}
|
|
52
|
+
return new InMemoryStorage();
|
|
53
|
+
}
|
|
54
|
+
function buildSurveyIdentity(userId, sessionId, miniService = null, analyticsFeedbackSessionId = null) {
|
|
55
|
+
const profile = userId ? [{ key: "external-user-id", value: [userId] }] : [];
|
|
56
|
+
const metadata = [];
|
|
57
|
+
if (sessionId) metadata.push({ key: "session_id", value: [sessionId] });
|
|
58
|
+
if (userId) metadata.push({ key: "user_id", value: [userId] });
|
|
59
|
+
if (miniService) metadata.push({ key: "mini_service", value: [miniService] });
|
|
60
|
+
if (analyticsFeedbackSessionId) metadata.push({ key: "deepdots_analytics_feedback_session_id", value: [analyticsFeedbackSessionId] });
|
|
61
|
+
return { profile, metadata };
|
|
62
|
+
}
|
|
63
|
+
function defaultUuid() {
|
|
64
|
+
const g = typeof globalThis !== "undefined" ? globalThis : {};
|
|
65
|
+
if (g.crypto?.randomUUID) return g.crypto.randomUUID();
|
|
66
|
+
const bytes = new Uint8Array(16);
|
|
67
|
+
if (g.crypto?.getRandomValues) {
|
|
68
|
+
g.crypto.getRandomValues(bytes);
|
|
69
|
+
} else {
|
|
70
|
+
for (let i = 0; i < 16; i++) bytes[i] = Math.floor(Math.random() * 256);
|
|
71
|
+
}
|
|
72
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
73
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
74
|
+
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0"));
|
|
75
|
+
return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10, 16).join("")}`;
|
|
76
|
+
}
|
|
77
|
+
var InMemoryStorage, STORAGE_KEYS, TrackingManager;
|
|
78
|
+
var init_tracking_manager = __esm({
|
|
79
|
+
"src/tracking/tracking-manager.ts"() {
|
|
80
|
+
"use strict";
|
|
81
|
+
InMemoryStorage = class {
|
|
82
|
+
constructor() {
|
|
83
|
+
this.map = /* @__PURE__ */ new Map();
|
|
84
|
+
}
|
|
85
|
+
getItem(key) {
|
|
86
|
+
return this.map.has(key) ? this.map.get(key) : null;
|
|
87
|
+
}
|
|
88
|
+
setItem(key, value) {
|
|
89
|
+
this.map.set(key, value);
|
|
90
|
+
}
|
|
91
|
+
removeItem(key) {
|
|
92
|
+
this.map.delete(key);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
STORAGE_KEYS = {
|
|
96
|
+
userId: "deepdots.user_id",
|
|
97
|
+
firstSeen: "deepdots.user.first_seen",
|
|
98
|
+
contactAttributes: "deepdots.contact.attributes"
|
|
99
|
+
};
|
|
100
|
+
TrackingManager = class {
|
|
101
|
+
constructor(options) {
|
|
102
|
+
/** session_id cacheado, provisto por el backend (no se genera ni se persiste). */
|
|
103
|
+
this.sessionId = null;
|
|
104
|
+
this.userWasJustCreated = false;
|
|
105
|
+
this.storage = options.storage;
|
|
106
|
+
this.clientUserId = options.clientUserId;
|
|
107
|
+
this.now = options.now ?? (() => Date.now());
|
|
108
|
+
this.uuid = options.uuid ?? defaultUuid;
|
|
109
|
+
this.enabled = options.enabled ?? true;
|
|
110
|
+
}
|
|
111
|
+
isTrackingEnabled() {
|
|
112
|
+
return this.enabled;
|
|
113
|
+
}
|
|
114
|
+
setTrackingEnabled(enabled) {
|
|
115
|
+
this.enabled = enabled;
|
|
116
|
+
}
|
|
117
|
+
/** Resuelve el user_id según la regla: cliente > persistido > generar+persistir. */
|
|
118
|
+
getUserId() {
|
|
119
|
+
if (!this.enabled) return null;
|
|
120
|
+
if (this.clientUserId) return this.clientUserId;
|
|
121
|
+
const persisted = this.storage.getItem(STORAGE_KEYS.userId);
|
|
122
|
+
if (persisted) return persisted;
|
|
123
|
+
const id = this.uuid();
|
|
124
|
+
this.storage.setItem(STORAGE_KEYS.userId, id);
|
|
125
|
+
this.storage.setItem(STORAGE_KEYS.firstSeen, String(this.now()));
|
|
126
|
+
this.userWasJustCreated = true;
|
|
127
|
+
return id;
|
|
128
|
+
}
|
|
129
|
+
/** `true` solo en la primera sesión tras crear el user_id propio del SDK. */
|
|
130
|
+
isNewUser() {
|
|
131
|
+
return this.userWasJustCreated;
|
|
132
|
+
}
|
|
133
|
+
/** session_id actual provisto por el backend, o null si aún no llegó. */
|
|
134
|
+
getSessionId() {
|
|
135
|
+
if (!this.enabled) return null;
|
|
136
|
+
return this.sessionId;
|
|
137
|
+
}
|
|
138
|
+
/** Cachea el session_id devuelto por el backend (respuesta de POST /sdk/popups). */
|
|
139
|
+
setSessionId(sessionId) {
|
|
140
|
+
if (!this.enabled) return;
|
|
141
|
+
this.sessionId = sessionId;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
33
147
|
// node_modules/@magicfeedback/native/dist/magicfeedback-sdk.browser.js
|
|
34
148
|
var require_magicfeedback_sdk_browser = __commonJS({
|
|
35
149
|
"node_modules/@magicfeedback/native/dist/magicfeedback-sdk.browser.js"(exports, module2) {
|
|
@@ -1955,26 +2069,11 @@ var require_magicfeedback_sdk_browser = __commonJS({
|
|
|
1955
2069
|
}
|
|
1956
2070
|
});
|
|
1957
2071
|
|
|
1958
|
-
// src/index.ts
|
|
1959
|
-
var index_exports = {};
|
|
1960
|
-
__export(index_exports, {
|
|
1961
|
-
DeepdotsPopups: () => DeepdotsPopups,
|
|
1962
|
-
POPUP_TRIGGER_CONDITION_STATUSES: () => POPUP_TRIGGER_CONDITION_STATUSES
|
|
1963
|
-
});
|
|
1964
|
-
module.exports = __toCommonJS(index_exports);
|
|
1965
|
-
|
|
1966
|
-
// src/types/index.ts
|
|
1967
|
-
var POPUP_TRIGGER_CONDITION_STATUSES = [
|
|
1968
|
-
"SHOWED",
|
|
1969
|
-
"PARTIAL",
|
|
1970
|
-
"COMPLETED"
|
|
1971
|
-
];
|
|
1972
|
-
|
|
1973
|
-
// src/ui/renderPopup.ts
|
|
1974
|
-
var import_native = __toESM(require_magicfeedback_sdk_browser());
|
|
1975
|
-
|
|
1976
2072
|
// src/assets/style.css
|
|
1977
|
-
var style_default
|
|
2073
|
+
var style_default;
|
|
2074
|
+
var init_style = __esm({
|
|
2075
|
+
"src/assets/style.css"() {
|
|
2076
|
+
style_default = `.magicfeedback-container {
|
|
1978
2077
|
width: 100%;
|
|
1979
2078
|
}
|
|
1980
2079
|
|
|
@@ -2828,8 +2927,14 @@ var style_default = `.magicfeedback-container {
|
|
|
2828
2927
|
.magicfeedback-error{
|
|
2829
2928
|
margin-top: 5px;
|
|
2830
2929
|
}`;
|
|
2930
|
+
}
|
|
2931
|
+
});
|
|
2831
2932
|
|
|
2832
2933
|
// src/ui/renderPopup.ts
|
|
2934
|
+
var renderPopup_exports = {};
|
|
2935
|
+
__export(renderPopup_exports, {
|
|
2936
|
+
renderPopup: () => renderPopup
|
|
2937
|
+
});
|
|
2833
2938
|
function ensureMagicFeedbackStyles(_popup) {
|
|
2834
2939
|
const STYLE_ID = "magicfeedback-sdk-styles";
|
|
2835
2940
|
if (document.getElementById(STYLE_ID)) return;
|
|
@@ -2887,7 +2992,7 @@ function ensureResponsiveStyles(_popup) {
|
|
|
2887
2992
|
`;
|
|
2888
2993
|
document.head.appendChild(style);
|
|
2889
2994
|
}
|
|
2890
|
-
async function renderPopup(container, surveyId, productId, actions, emit, onClose, env = "production", userId, style) {
|
|
2995
|
+
async function renderPopup(container, surveyId, productId, actions, emit, onClose, env = "production", userId, style, sessionId, miniService, analyticsFeedbackSessionId) {
|
|
2891
2996
|
let surveyCompletedEmitted = false;
|
|
2892
2997
|
let stylesInjected = false;
|
|
2893
2998
|
const isDark = style?.theme === "dark";
|
|
@@ -3198,8 +3303,8 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
3198
3303
|
env: env === "production" ? "prod" : "dev"
|
|
3199
3304
|
}
|
|
3200
3305
|
);
|
|
3201
|
-
const profile = userId
|
|
3202
|
-
formInstance = import_native.default.form(surveyId, productId, profile,
|
|
3306
|
+
const { profile, metadata } = buildSurveyIdentity(userId ?? null, sessionId ?? null, miniService ?? null, analyticsFeedbackSessionId ?? null);
|
|
3307
|
+
formInstance = import_native.default.form(surveyId, productId, profile, metadata);
|
|
3203
3308
|
const generateOptions = {
|
|
3204
3309
|
addButton: false,
|
|
3205
3310
|
getMetaData: true
|
|
@@ -3338,6 +3443,42 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
3338
3443
|
setLoading(false);
|
|
3339
3444
|
}
|
|
3340
3445
|
}
|
|
3446
|
+
var import_native;
|
|
3447
|
+
var init_renderPopup = __esm({
|
|
3448
|
+
"src/ui/renderPopup.ts"() {
|
|
3449
|
+
"use strict";
|
|
3450
|
+
init_tracking_manager();
|
|
3451
|
+
import_native = __toESM(require_magicfeedback_sdk_browser());
|
|
3452
|
+
init_style();
|
|
3453
|
+
}
|
|
3454
|
+
});
|
|
3455
|
+
|
|
3456
|
+
// src/index.ts
|
|
3457
|
+
var index_exports = {};
|
|
3458
|
+
__export(index_exports, {
|
|
3459
|
+
BrowserPopupRenderer: () => BrowserPopupRenderer,
|
|
3460
|
+
ContactManager: () => ContactManager,
|
|
3461
|
+
DeepdotsPopups: () => DeepdotsPopups,
|
|
3462
|
+
InMemoryStorage: () => InMemoryStorage,
|
|
3463
|
+
NoopPopupRenderer: () => NoopPopupRenderer,
|
|
3464
|
+
POPUP_TRIGGER_CONDITION_STATUSES: () => POPUP_TRIGGER_CONDITION_STATUSES,
|
|
3465
|
+
ReactNativePopupRenderer: () => ReactNativePopupRenderer,
|
|
3466
|
+
buildAnalyticsFeedbackBody: () => buildAnalyticsFeedbackBody,
|
|
3467
|
+
buildSurveyHtml: () => buildSurveyHtml,
|
|
3468
|
+
collectRnDevice: () => collectRnDevice,
|
|
3469
|
+
createDefaultRenderer: () => createDefaultRenderer,
|
|
3470
|
+
createFeedbackSink: () => createFeedbackSink,
|
|
3471
|
+
mmkvStorage: () => mmkvStorage,
|
|
3472
|
+
setupReactNative: () => setupReactNative
|
|
3473
|
+
});
|
|
3474
|
+
module.exports = __toCommonJS(index_exports);
|
|
3475
|
+
|
|
3476
|
+
// src/types/index.ts
|
|
3477
|
+
var POPUP_TRIGGER_CONDITION_STATUSES = [
|
|
3478
|
+
"SHOWED",
|
|
3479
|
+
"PARTIAL",
|
|
3480
|
+
"COMPLETED"
|
|
3481
|
+
];
|
|
3341
3482
|
|
|
3342
3483
|
// src/triggers/index.ts
|
|
3343
3484
|
function setupTrigger(popups, trigger) {
|
|
@@ -3516,41 +3657,125 @@ function resolveEnvironment(nodeEnv) {
|
|
|
3516
3657
|
return ENVIRONMENT_MAP[nodeEnv || "production"];
|
|
3517
3658
|
}
|
|
3518
3659
|
|
|
3660
|
+
// src/platform/react-native-renderer.ts
|
|
3661
|
+
init_tracking_manager();
|
|
3662
|
+
|
|
3663
|
+
// src/ui/surveyHtml.ts
|
|
3664
|
+
function buildSurveyHtml(opts) {
|
|
3665
|
+
const env = opts.env === "development" ? "dev" : "prod";
|
|
3666
|
+
const version = opts.version ?? "2.2.4";
|
|
3667
|
+
const cdn = `https://cdn.jsdelivr.net/npm/@magicfeedback/native@${version}/dist/magicfeedback-sdk.browser.js`;
|
|
3668
|
+
const sid = JSON.stringify(opts.surveyId);
|
|
3669
|
+
const pid = JSON.stringify(opts.productId);
|
|
3670
|
+
const profileJson = JSON.stringify(opts.profile ?? []);
|
|
3671
|
+
const metaJson = JSON.stringify(opts.metadata ?? []);
|
|
3672
|
+
return `<!DOCTYPE html><html><head>
|
|
3673
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
|
3674
|
+
<meta name="color-scheme" content="light"/>
|
|
3675
|
+
<style>html,body{margin:0;padding:0;background:transparent;font-family:-apple-system,system-ui,sans-serif}#mf{width:100%;box-sizing:border-box}#mf *{max-width:100%;box-sizing:border-box}</style>
|
|
3676
|
+
</head><body>
|
|
3677
|
+
<div id="mf"></div>
|
|
3678
|
+
<script>
|
|
3679
|
+
(function(){
|
|
3680
|
+
function emit(s){ try{ if(window.ReactNativeWebView){ window.ReactNativeWebView.postMessage(s); } }catch(e){} }
|
|
3681
|
+
function emitJSON(n,p){ try{ emit(JSON.stringify({name:n,payload:p||{}})); }catch(e){} }
|
|
3682
|
+
window.DeepdotsClose = function(){ emitJSON('popup_close'); };
|
|
3683
|
+
var initialized=false;
|
|
3684
|
+
function initMF(){
|
|
3685
|
+
if(initialized || !window.magicfeedback) return initialized;
|
|
3686
|
+
initialized=true;
|
|
3687
|
+
try{
|
|
3688
|
+
window.magicfeedback.init({ debug:false, env:'${env}' });
|
|
3689
|
+
var form = window.magicfeedback.form(${sid}, ${pid}, ${profileJson}, ${metaJson});
|
|
3690
|
+
window.DeepdotsForm = form;
|
|
3691
|
+
form.generate('mf', {
|
|
3692
|
+
addButton:false,
|
|
3693
|
+
getMetaData:true,
|
|
3694
|
+
onLoadedEvent:function(){ emitJSON('loaded'); },
|
|
3695
|
+
beforeSubmitEvent:function(){ emitJSON('before_submit'); },
|
|
3696
|
+
afterSubmitEvent:function(p){
|
|
3697
|
+
try{ if(p && p.completed){ emitJSON('survey_completed', p); } else { emitJSON('after_submit', p); } }
|
|
3698
|
+
catch(e){ emitJSON('after_submit'); }
|
|
3699
|
+
},
|
|
3700
|
+
onBackEvent:function(p){ emitJSON('back', p); }
|
|
3701
|
+
}).catch(function(e){ emit('error:init'); });
|
|
3702
|
+
}catch(e){ emit('error:exception'); }
|
|
3703
|
+
return true;
|
|
3704
|
+
}
|
|
3705
|
+
var s=document.createElement('script'); s.src=${JSON.stringify(cdn)}; s.async=true;
|
|
3706
|
+
s.onload=function(){ initMF(); };
|
|
3707
|
+
s.onerror=function(){ emit('error:load'); };
|
|
3708
|
+
document.head.appendChild(s);
|
|
3709
|
+
var tries=0, t=setInterval(function(){ if(initMF()||++tries>40){ clearInterval(t); } },150);
|
|
3710
|
+
})();
|
|
3711
|
+
<\/script>
|
|
3712
|
+
</body></html>`;
|
|
3713
|
+
}
|
|
3714
|
+
|
|
3519
3715
|
// src/platform/react-native-renderer.ts
|
|
3520
3716
|
var ReactNativePopupRenderer = class {
|
|
3521
|
-
constructor() {
|
|
3522
|
-
this.
|
|
3523
|
-
this.lastSurveyId = null;
|
|
3717
|
+
constructor(options = {}) {
|
|
3718
|
+
this.options = options;
|
|
3524
3719
|
this.emitFn = null;
|
|
3525
3720
|
this.onCloseFn = null;
|
|
3526
|
-
this.
|
|
3721
|
+
this.currentSurveyId = null;
|
|
3722
|
+
this.partialEmitted = false;
|
|
3527
3723
|
}
|
|
3528
3724
|
init() {
|
|
3529
3725
|
}
|
|
3530
|
-
show(surveyId, productId,
|
|
3531
|
-
this.mounted = true;
|
|
3532
|
-
this.lastSurveyId = surveyId;
|
|
3726
|
+
show(surveyId, productId, _actions, emit, onClose, env = "production", userId, _style, sessionId, miniService, analyticsFeedbackSessionId) {
|
|
3533
3727
|
this.emitFn = emit;
|
|
3534
3728
|
this.onCloseFn = onClose;
|
|
3535
|
-
this.
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
console.log("[ReactNativePopupRenderer] (stub) survey completed:", { surveyId: this.lastSurveyId, data });
|
|
3547
|
-
this.lastSurveyId = null;
|
|
3729
|
+
this.currentSurveyId = surveyId;
|
|
3730
|
+
this.partialEmitted = false;
|
|
3731
|
+
const { profile, metadata } = buildSurveyIdentity(userId ?? null, sessionId ?? null, miniService ?? null, analyticsFeedbackSessionId ?? null);
|
|
3732
|
+
const html = buildSurveyHtml({ surveyId, productId, env, profile, metadata });
|
|
3733
|
+
if (this.options.onShow) {
|
|
3734
|
+
this.options.onShow({ surveyId, productId, html });
|
|
3735
|
+
} else {
|
|
3736
|
+
console.warn(
|
|
3737
|
+
"[Deepdots] ReactNativePopupRenderer sin onShow: pasa { onShow } a new ReactNativePopupRenderer({...}) para montar el WebView del survey."
|
|
3738
|
+
);
|
|
3739
|
+
}
|
|
3548
3740
|
}
|
|
3549
3741
|
hide() {
|
|
3550
|
-
|
|
3551
|
-
this.
|
|
3552
|
-
|
|
3553
|
-
|
|
3742
|
+
this.options.onHide?.();
|
|
3743
|
+
this.currentSurveyId = null;
|
|
3744
|
+
}
|
|
3745
|
+
/**
|
|
3746
|
+
* El host la conecta al `onMessage` del WebView. Traduce los mensajes del survey a
|
|
3747
|
+
* eventos del SDK: primera interacción → `popup_clicked` (PARTIAL), completado →
|
|
3748
|
+
* `survey_completed` (COMPLETED) y cierra el popup.
|
|
3749
|
+
*/
|
|
3750
|
+
handleMessage(raw) {
|
|
3751
|
+
const surveyId = this.currentSurveyId;
|
|
3752
|
+
if (!surveyId || !this.emitFn) return;
|
|
3753
|
+
let name;
|
|
3754
|
+
try {
|
|
3755
|
+
name = JSON.parse(raw).name;
|
|
3756
|
+
} catch {
|
|
3757
|
+
name = raw;
|
|
3758
|
+
}
|
|
3759
|
+
switch (name) {
|
|
3760
|
+
case "loaded":
|
|
3761
|
+
case "before_submit":
|
|
3762
|
+
case "after_submit":
|
|
3763
|
+
case "back":
|
|
3764
|
+
if (!this.partialEmitted) {
|
|
3765
|
+
this.partialEmitted = true;
|
|
3766
|
+
this.emitFn("popup_clicked", surveyId, { action: "partial" });
|
|
3767
|
+
}
|
|
3768
|
+
break;
|
|
3769
|
+
case "survey_completed":
|
|
3770
|
+
this.emitFn("survey_completed", surveyId);
|
|
3771
|
+
this.onCloseFn?.();
|
|
3772
|
+
break;
|
|
3773
|
+
case "popup_close":
|
|
3774
|
+
this.onCloseFn?.();
|
|
3775
|
+
break;
|
|
3776
|
+
default:
|
|
3777
|
+
break;
|
|
3778
|
+
}
|
|
3554
3779
|
}
|
|
3555
3780
|
};
|
|
3556
3781
|
function isReactNativeEnv() {
|
|
@@ -3594,12 +3819,15 @@ var BrowserPopupRenderer = class {
|
|
|
3594
3819
|
document.body.appendChild(this.container);
|
|
3595
3820
|
}
|
|
3596
3821
|
}
|
|
3597
|
-
show(surveyId, productId, actions, emit, onClose, env = "production", userId, style) {
|
|
3822
|
+
show(surveyId, productId, actions, emit, onClose, env = "production", userId, style, sessionId, miniService, analyticsFeedbackSessionId) {
|
|
3598
3823
|
if (this.visible) return;
|
|
3599
3824
|
if (!this.container || !document.body.contains(this.container)) this.init();
|
|
3600
3825
|
if (!this.container) return;
|
|
3601
3826
|
this.visible = true;
|
|
3602
|
-
|
|
3827
|
+
const container = this.container;
|
|
3828
|
+
void Promise.resolve().then(() => (init_renderPopup(), renderPopup_exports)).then(({ renderPopup: renderPopup2 }) => {
|
|
3829
|
+
renderPopup2(container, surveyId, productId, actions, emit, onClose, env, userId, style, sessionId, miniService, analyticsFeedbackSessionId);
|
|
3830
|
+
});
|
|
3603
3831
|
}
|
|
3604
3832
|
hide() {
|
|
3605
3833
|
this.visible = false;
|
|
@@ -3618,9 +3846,418 @@ function createDefaultRenderer() {
|
|
|
3618
3846
|
return new BrowserPopupRenderer();
|
|
3619
3847
|
}
|
|
3620
3848
|
|
|
3849
|
+
// src/core/deepdots-popups.ts
|
|
3850
|
+
init_tracking_manager();
|
|
3851
|
+
|
|
3852
|
+
// src/analytics/analytics-manager.ts
|
|
3853
|
+
var dryRunSink = (payload) => {
|
|
3854
|
+
console.log(
|
|
3855
|
+
"[DeepdotsAnalytics] (dry-run \xB7 NO enviado \xB7 sin init.analytics) POST /sdk/feedback \u2192",
|
|
3856
|
+
JSON.stringify(payload, null, 2)
|
|
3857
|
+
);
|
|
3858
|
+
};
|
|
3859
|
+
var AnalyticsManager = class {
|
|
3860
|
+
constructor(options = {}) {
|
|
3861
|
+
this.events = [];
|
|
3862
|
+
this.attributes = {};
|
|
3863
|
+
this.miniService = null;
|
|
3864
|
+
this.miniServiceEnteredAt = 0;
|
|
3865
|
+
this.sink = options.sink ?? dryRunSink;
|
|
3866
|
+
this.now = options.now ?? (() => Date.now());
|
|
3867
|
+
this.publicKey = options.publicKey;
|
|
3868
|
+
this.language = options.language;
|
|
3869
|
+
this.device = options.device;
|
|
3870
|
+
this.platform = options.platform ?? "web";
|
|
3871
|
+
this.maxBatchSize = options.maxBatchSize ?? 20;
|
|
3872
|
+
this.onFlushNeeded = options.onFlushNeeded;
|
|
3873
|
+
}
|
|
3874
|
+
/** Actualiza campos de device en runtime (p.ej. country/city tras resolver geo). */
|
|
3875
|
+
updateDevice(partial) {
|
|
3876
|
+
this.device = { ...this.device, ...partial };
|
|
3877
|
+
}
|
|
3878
|
+
/** Mezcla user attributes (se coercionan a string). Mutable en runtime. */
|
|
3879
|
+
setUserAttributes(attrs) {
|
|
3880
|
+
for (const [k, v] of Object.entries(attrs)) {
|
|
3881
|
+
if (!k) continue;
|
|
3882
|
+
this.attributes[k] = String(v);
|
|
3883
|
+
}
|
|
3884
|
+
}
|
|
3885
|
+
/** Marca el inicio de un mini-service; etiqueta los eventos siguientes con `mini_service`. */
|
|
3886
|
+
enterMiniService(name, entryPointType) {
|
|
3887
|
+
this.miniService = name;
|
|
3888
|
+
this.miniServiceEnteredAt = this.now();
|
|
3889
|
+
this.track("mini_service_enter", { entry_point_type: entryPointType ?? null });
|
|
3890
|
+
}
|
|
3891
|
+
/** Cierra el mini-service activo emitiendo `mini_service_exit` con su duración (#27). No-op si no hay ninguno. */
|
|
3892
|
+
exitMiniService() {
|
|
3893
|
+
const name = this.miniService;
|
|
3894
|
+
if (name == null) return;
|
|
3895
|
+
const durationSeconds = Math.max(0, Math.round((this.now() - this.miniServiceEnteredAt) / 1e3));
|
|
3896
|
+
this.miniService = null;
|
|
3897
|
+
this.track("mini_service_exit", { mini_service: name, duration_seconds: durationSeconds });
|
|
3898
|
+
}
|
|
3899
|
+
/** Registra un evento de analítica (modelo GA: nombre + params). */
|
|
3900
|
+
track(name, params) {
|
|
3901
|
+
const merged = {
|
|
3902
|
+
...this.miniService ? { mini_service: this.miniService } : {},
|
|
3903
|
+
...params ?? {}
|
|
3904
|
+
};
|
|
3905
|
+
this.events.push({
|
|
3906
|
+
name,
|
|
3907
|
+
timestamp: this.now(),
|
|
3908
|
+
params: Object.keys(merged).length ? merged : void 0
|
|
3909
|
+
});
|
|
3910
|
+
if (this.events.length >= this.maxBatchSize) {
|
|
3911
|
+
this.onFlushNeeded?.();
|
|
3912
|
+
}
|
|
3913
|
+
}
|
|
3914
|
+
/** Mini-service activo (para inyectarlo en la metadata del survey, #33). */
|
|
3915
|
+
getMiniService() {
|
|
3916
|
+
return this.miniService;
|
|
3917
|
+
}
|
|
3918
|
+
/** Nº de eventos pendientes de flush. */
|
|
3919
|
+
pending() {
|
|
3920
|
+
return this.events.length;
|
|
3921
|
+
}
|
|
3922
|
+
/** Construye el envelope que se enviaría al endpoint de analytics. */
|
|
3923
|
+
buildPayload(identity) {
|
|
3924
|
+
return {
|
|
3925
|
+
publicKey: this.publicKey,
|
|
3926
|
+
userId: identity.userId,
|
|
3927
|
+
sessionId: identity.sessionId,
|
|
3928
|
+
context: {
|
|
3929
|
+
platform: this.platform,
|
|
3930
|
+
language: this.language,
|
|
3931
|
+
device: this.device,
|
|
3932
|
+
attributes: { ...this.attributes }
|
|
3933
|
+
},
|
|
3934
|
+
events: [...this.events]
|
|
3935
|
+
};
|
|
3936
|
+
}
|
|
3937
|
+
/** Envía (vía sink) el lote acumulado y vacía el buffer. No-op si no hay eventos. */
|
|
3938
|
+
flush(identity) {
|
|
3939
|
+
if (this.events.length === 0) return null;
|
|
3940
|
+
const payload = this.buildPayload(identity);
|
|
3941
|
+
this.sink(payload);
|
|
3942
|
+
this.events = [];
|
|
3943
|
+
return payload;
|
|
3944
|
+
}
|
|
3945
|
+
};
|
|
3946
|
+
|
|
3947
|
+
// src/analytics/feedback-payload.ts
|
|
3948
|
+
function pushKV(list, key, value) {
|
|
3949
|
+
if (value === void 0 || value === null || value === "") return;
|
|
3950
|
+
list.push({ key, value: [String(value)] });
|
|
3951
|
+
}
|
|
3952
|
+
function buildAnalyticsFeedbackBody(envelope, keys, feedbackSessionId) {
|
|
3953
|
+
const { context } = envelope;
|
|
3954
|
+
const profile = [];
|
|
3955
|
+
pushKV(profile, "external-user-id", envelope.userId);
|
|
3956
|
+
const metadata = [];
|
|
3957
|
+
pushKV(metadata, "deepdots_user_id", envelope.userId);
|
|
3958
|
+
pushKV(metadata, "deepdots_session_id", envelope.sessionId);
|
|
3959
|
+
pushKV(metadata, "deepdots_platform", context.platform);
|
|
3960
|
+
pushKV(metadata, "deepdots_language", context.language);
|
|
3961
|
+
if (context.device) {
|
|
3962
|
+
const d = context.device;
|
|
3963
|
+
pushKV(metadata, "deepdots_device_type", d.device_type);
|
|
3964
|
+
pushKV(metadata, "deepdots_os_version", d.os_version);
|
|
3965
|
+
pushKV(metadata, "deepdots_device_model", d.device_model);
|
|
3966
|
+
pushKV(metadata, "deepdots_app_version", d.app_version);
|
|
3967
|
+
pushKV(metadata, "deepdots_user_agent", d.user_agent);
|
|
3968
|
+
pushKV(metadata, "deepdots_timezone", d.timezone);
|
|
3969
|
+
pushKV(metadata, "deepdots_referrer", d.referrer);
|
|
3970
|
+
pushKV(metadata, "deepdots_viewport_size", d.viewport_size);
|
|
3971
|
+
pushKV(metadata, "deepdots_screen_resolution", d.screen_resolution);
|
|
3972
|
+
pushKV(metadata, "deepdots_pixel_ratio", d.pixel_ratio);
|
|
3973
|
+
pushKV(metadata, "deepdots_entry_type", d.entry_type);
|
|
3974
|
+
pushKV(metadata, "deepdots_page_load_ms", d.page_load_ms);
|
|
3975
|
+
pushKV(metadata, "deepdots_connection_type", d.connection_type);
|
|
3976
|
+
pushKV(metadata, "deepdots_country", d.country);
|
|
3977
|
+
pushKV(metadata, "deepdots_city", d.city);
|
|
3978
|
+
}
|
|
3979
|
+
for (const [k, v] of Object.entries(context.attributes ?? {})) {
|
|
3980
|
+
pushKV(metadata, k, v);
|
|
3981
|
+
}
|
|
3982
|
+
for (const e of envelope.events) {
|
|
3983
|
+
metadata.push({
|
|
3984
|
+
key: e.name,
|
|
3985
|
+
value: [JSON.stringify({ timestamp: e.timestamp, ...e.params ?? {} })]
|
|
3986
|
+
});
|
|
3987
|
+
}
|
|
3988
|
+
return {
|
|
3989
|
+
feedback: {
|
|
3990
|
+
text: "",
|
|
3991
|
+
answers: [],
|
|
3992
|
+
metadata,
|
|
3993
|
+
profile,
|
|
3994
|
+
finished: false
|
|
3995
|
+
},
|
|
3996
|
+
publicKey: keys.publicKey,
|
|
3997
|
+
integration: keys.integration,
|
|
3998
|
+
completed: false,
|
|
3999
|
+
...feedbackSessionId ? { sessionId: feedbackSessionId } : {}
|
|
4000
|
+
};
|
|
4001
|
+
}
|
|
4002
|
+
function createFeedbackSink(options) {
|
|
4003
|
+
let feedbackSessionId;
|
|
4004
|
+
return (envelope) => {
|
|
4005
|
+
const body = buildAnalyticsFeedbackBody(envelope, options.keys, feedbackSessionId);
|
|
4006
|
+
const f = options.fetchImpl ?? (typeof fetch !== "undefined" ? fetch : void 0);
|
|
4007
|
+
if (!f) {
|
|
4008
|
+
options.log?.("[DeepdotsAnalytics] no fetch disponible; payload no enviado", body);
|
|
4009
|
+
return;
|
|
4010
|
+
}
|
|
4011
|
+
options.log?.("[DeepdotsAnalytics] POST /sdk/feedback \u2192", body);
|
|
4012
|
+
f(`${options.baseUrl}/sdk/feedback`, {
|
|
4013
|
+
method: "POST",
|
|
4014
|
+
headers: { "Content-Type": "application/json" },
|
|
4015
|
+
body: JSON.stringify(body)
|
|
4016
|
+
}).then(async (res) => {
|
|
4017
|
+
if (res.ok) {
|
|
4018
|
+
try {
|
|
4019
|
+
const data = await res.json();
|
|
4020
|
+
if (data?.sessionId && data.sessionId !== feedbackSessionId) {
|
|
4021
|
+
feedbackSessionId = data.sessionId;
|
|
4022
|
+
options.log?.("[DeepdotsAnalytics] feedbackSessionId cacheado:", feedbackSessionId);
|
|
4023
|
+
options.onSessionId?.(feedbackSessionId);
|
|
4024
|
+
}
|
|
4025
|
+
} catch {
|
|
4026
|
+
}
|
|
4027
|
+
}
|
|
4028
|
+
}).catch((err) => options.log?.("[DeepdotsAnalytics] error enviando feedback", err));
|
|
4029
|
+
};
|
|
4030
|
+
}
|
|
4031
|
+
|
|
4032
|
+
// src/tracking/navigation-observer.ts
|
|
4033
|
+
var UUID_PREFIX = /^[0-9a-f]{8}-[0-9a-f]{4}-/i;
|
|
4034
|
+
function normalizeScreen(href) {
|
|
4035
|
+
let pathAndHash;
|
|
4036
|
+
try {
|
|
4037
|
+
const u = new URL(href, "http://deepdots.local");
|
|
4038
|
+
pathAndHash = u.pathname + (u.hash || "");
|
|
4039
|
+
} catch {
|
|
4040
|
+
pathAndHash = href;
|
|
4041
|
+
}
|
|
4042
|
+
pathAndHash = pathAndHash.replace(/\?[^#]*/g, "");
|
|
4043
|
+
const collapsed = pathAndHash.split("/").map((seg) => /^[0-9]+$/.test(seg) || UUID_PREFIX.test(seg) ? ":id" : seg).join("/");
|
|
4044
|
+
return collapsed || "/";
|
|
4045
|
+
}
|
|
4046
|
+
var NavigationObserver = class {
|
|
4047
|
+
constructor(options = {}) {
|
|
4048
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
4049
|
+
this.currentScreen = null;
|
|
4050
|
+
this.entryAt = 0;
|
|
4051
|
+
this.installed = false;
|
|
4052
|
+
this.domHandlers = [];
|
|
4053
|
+
this.now = options.now ?? (() => Date.now());
|
|
4054
|
+
}
|
|
4055
|
+
onVisit(listener) {
|
|
4056
|
+
this.listeners.add(listener);
|
|
4057
|
+
}
|
|
4058
|
+
/** Fija la pantalla inicial (sin emitir visita; su duración se cierra al salir). */
|
|
4059
|
+
begin(href) {
|
|
4060
|
+
this.currentScreen = normalizeScreen(href);
|
|
4061
|
+
this.entryAt = this.now();
|
|
4062
|
+
}
|
|
4063
|
+
/** Navega a una pantalla nueva: cierra la anterior (emite visita) y abre la nueva. */
|
|
4064
|
+
visit(href) {
|
|
4065
|
+
const next = normalizeScreen(href);
|
|
4066
|
+
if (next === this.currentScreen) return;
|
|
4067
|
+
this.closeCurrent();
|
|
4068
|
+
this.currentScreen = next;
|
|
4069
|
+
this.entryAt = this.now();
|
|
4070
|
+
}
|
|
4071
|
+
/** Cierra la pantalla actual emitiendo su visita (p. ej. al cerrar la app/pestaña). */
|
|
4072
|
+
stop() {
|
|
4073
|
+
this.closeCurrent();
|
|
4074
|
+
this.currentScreen = null;
|
|
4075
|
+
}
|
|
4076
|
+
closeCurrent() {
|
|
4077
|
+
if (this.currentScreen == null) return;
|
|
4078
|
+
const exit = this.now();
|
|
4079
|
+
const visit = {
|
|
4080
|
+
screen: this.currentScreen,
|
|
4081
|
+
entry: this.entryAt,
|
|
4082
|
+
exit,
|
|
4083
|
+
durationSeconds: Math.max(0, Math.round((exit - this.entryAt) / 1e3))
|
|
4084
|
+
};
|
|
4085
|
+
this.listeners.forEach((l) => {
|
|
4086
|
+
try {
|
|
4087
|
+
l(visit);
|
|
4088
|
+
} catch {
|
|
4089
|
+
}
|
|
4090
|
+
});
|
|
4091
|
+
}
|
|
4092
|
+
/** Instala los hooks de History en el navegador y registra la pantalla actual. */
|
|
4093
|
+
install() {
|
|
4094
|
+
if (this.installed || typeof window === "undefined" || typeof history === "undefined") return;
|
|
4095
|
+
this.installed = true;
|
|
4096
|
+
const handle = () => this.visit(window.location.href);
|
|
4097
|
+
const originalPush = history.pushState.bind(history);
|
|
4098
|
+
const originalReplace = history.replaceState.bind(history);
|
|
4099
|
+
history.pushState = (data, unused, url) => {
|
|
4100
|
+
const r = originalPush(data, unused, url);
|
|
4101
|
+
handle();
|
|
4102
|
+
return r;
|
|
4103
|
+
};
|
|
4104
|
+
history.replaceState = (data, unused, url) => {
|
|
4105
|
+
const r = originalReplace(data, unused, url);
|
|
4106
|
+
handle();
|
|
4107
|
+
return r;
|
|
4108
|
+
};
|
|
4109
|
+
window.addEventListener("popstate", handle);
|
|
4110
|
+
window.addEventListener("hashchange", handle);
|
|
4111
|
+
this.domHandlers.push(() => {
|
|
4112
|
+
history.pushState = originalPush;
|
|
4113
|
+
history.replaceState = originalReplace;
|
|
4114
|
+
window.removeEventListener("popstate", handle);
|
|
4115
|
+
window.removeEventListener("hashchange", handle);
|
|
4116
|
+
});
|
|
4117
|
+
this.begin(window.location.href);
|
|
4118
|
+
}
|
|
4119
|
+
uninstall() {
|
|
4120
|
+
this.domHandlers.forEach((off) => off());
|
|
4121
|
+
this.domHandlers = [];
|
|
4122
|
+
this.installed = false;
|
|
4123
|
+
}
|
|
4124
|
+
};
|
|
4125
|
+
|
|
4126
|
+
// src/analytics/device-info.ts
|
|
4127
|
+
function parseDeviceType(ua) {
|
|
4128
|
+
const s = (ua || "").toLowerCase();
|
|
4129
|
+
if (/ipad|tablet|playbook|silk/.test(s) || /android/.test(s) && !/mobile/.test(s)) {
|
|
4130
|
+
return "tablet";
|
|
4131
|
+
}
|
|
4132
|
+
if (/mobi|iphone|ipod|windows phone/.test(s) || /android/.test(s) && /mobile/.test(s)) {
|
|
4133
|
+
return "mobile";
|
|
4134
|
+
}
|
|
4135
|
+
return "desktop";
|
|
4136
|
+
}
|
|
4137
|
+
function getEntryType() {
|
|
4138
|
+
if (typeof performance === "undefined") return void 0;
|
|
4139
|
+
const entries = performance.getEntriesByType("navigation");
|
|
4140
|
+
if (!entries.length) return void 0;
|
|
4141
|
+
return entries[0].type;
|
|
4142
|
+
}
|
|
4143
|
+
function getPageLoadMs() {
|
|
4144
|
+
if (typeof performance === "undefined") return void 0;
|
|
4145
|
+
const entries = performance.getEntriesByType("navigation");
|
|
4146
|
+
if (!entries.length) return void 0;
|
|
4147
|
+
const load = entries[0].loadEventEnd;
|
|
4148
|
+
return load > 0 ? Math.round(load) : void 0;
|
|
4149
|
+
}
|
|
4150
|
+
function getConnectionType() {
|
|
4151
|
+
if (typeof navigator === "undefined") return void 0;
|
|
4152
|
+
return navigator.connection?.effectiveType ?? void 0;
|
|
4153
|
+
}
|
|
4154
|
+
function collectDeviceInfo(appVersion) {
|
|
4155
|
+
const ua = typeof navigator !== "undefined" ? navigator.userAgent : "";
|
|
4156
|
+
return {
|
|
4157
|
+
device_type: parseDeviceType(ua),
|
|
4158
|
+
user_agent: ua || void 0,
|
|
4159
|
+
app_version: appVersion,
|
|
4160
|
+
timezone: typeof Intl !== "undefined" ? Intl.DateTimeFormat().resolvedOptions().timeZone : void 0,
|
|
4161
|
+
referrer: typeof document !== "undefined" && document.referrer ? document.referrer : void 0,
|
|
4162
|
+
viewport_size: typeof window !== "undefined" ? `${window.innerWidth}x${window.innerHeight}` : void 0,
|
|
4163
|
+
screen_resolution: typeof screen !== "undefined" ? `${screen.width}x${screen.height}` : void 0,
|
|
4164
|
+
pixel_ratio: typeof window !== "undefined" ? window.devicePixelRatio : void 0,
|
|
4165
|
+
entry_type: getEntryType(),
|
|
4166
|
+
page_load_ms: getPageLoadMs(),
|
|
4167
|
+
connection_type: getConnectionType()
|
|
4168
|
+
};
|
|
4169
|
+
}
|
|
4170
|
+
|
|
4171
|
+
// src/analytics/geo-info.ts
|
|
4172
|
+
async function collectGeoInfo(fetchImpl) {
|
|
4173
|
+
const f = fetchImpl ?? (typeof fetch !== "undefined" ? fetch : void 0);
|
|
4174
|
+
if (!f) return null;
|
|
4175
|
+
try {
|
|
4176
|
+
const res = await f("https://ipapi.co/json/");
|
|
4177
|
+
if (!res.ok) return null;
|
|
4178
|
+
const data = await res.json();
|
|
4179
|
+
const country = data.country_code || void 0;
|
|
4180
|
+
const city = data.city || void 0;
|
|
4181
|
+
if (!country && !city) return null;
|
|
4182
|
+
return { country, city };
|
|
4183
|
+
} catch {
|
|
4184
|
+
return null;
|
|
4185
|
+
}
|
|
4186
|
+
}
|
|
4187
|
+
|
|
4188
|
+
// src/analytics/engagement-tracker.ts
|
|
4189
|
+
var EngagementTracker = class {
|
|
4190
|
+
constructor(now = () => Date.now()) {
|
|
4191
|
+
this.activeMs = 0;
|
|
4192
|
+
this.lastResumeAt = null;
|
|
4193
|
+
this.now = now;
|
|
4194
|
+
}
|
|
4195
|
+
/** Reanuda el conteo (pestaña visible / app en foreground). Idempotente. */
|
|
4196
|
+
resume() {
|
|
4197
|
+
if (this.lastResumeAt === null) this.lastResumeAt = this.now();
|
|
4198
|
+
}
|
|
4199
|
+
/** Pausa el conteo (pestaña oculta / app en background), acumulando el tramo activo. */
|
|
4200
|
+
pause() {
|
|
4201
|
+
if (this.lastResumeAt !== null) {
|
|
4202
|
+
this.activeMs += this.now() - this.lastResumeAt;
|
|
4203
|
+
this.lastResumeAt = null;
|
|
4204
|
+
}
|
|
4205
|
+
}
|
|
4206
|
+
/** Devuelve los ms activos acumulados desde la última lectura y reinicia (sin parar el timer). */
|
|
4207
|
+
consume() {
|
|
4208
|
+
if (this.lastResumeAt !== null) {
|
|
4209
|
+
const t = this.now();
|
|
4210
|
+
this.activeMs += t - this.lastResumeAt;
|
|
4211
|
+
this.lastResumeAt = t;
|
|
4212
|
+
}
|
|
4213
|
+
const ms = this.activeMs;
|
|
4214
|
+
this.activeMs = 0;
|
|
4215
|
+
return ms;
|
|
4216
|
+
}
|
|
4217
|
+
isActive() {
|
|
4218
|
+
return this.lastResumeAt !== null;
|
|
4219
|
+
}
|
|
4220
|
+
};
|
|
4221
|
+
|
|
4222
|
+
// src/contact/contact-manager.ts
|
|
4223
|
+
init_tracking_manager();
|
|
4224
|
+
function stableSerialize(attrs) {
|
|
4225
|
+
const sorted = {};
|
|
4226
|
+
for (const key of Object.keys(attrs).sort()) {
|
|
4227
|
+
sorted[key] = attrs[key];
|
|
4228
|
+
}
|
|
4229
|
+
return JSON.stringify(sorted);
|
|
4230
|
+
}
|
|
4231
|
+
var ContactManager = class {
|
|
4232
|
+
constructor(options) {
|
|
4233
|
+
this.storage = options.storage;
|
|
4234
|
+
this.publicKey = options.publicKey;
|
|
4235
|
+
this.userId = options.userId;
|
|
4236
|
+
this.post = options.post;
|
|
4237
|
+
}
|
|
4238
|
+
/**
|
|
4239
|
+
* Envía los atributos si cambiaron respecto a lo último enviado.
|
|
4240
|
+
* @returns `true` si se envió, `false` si no hubo cambios.
|
|
4241
|
+
*/
|
|
4242
|
+
async setAttributes(attributes) {
|
|
4243
|
+
const serialized = stableSerialize(attributes);
|
|
4244
|
+
const last = this.storage.getItem(STORAGE_KEYS.contactAttributes);
|
|
4245
|
+
if (last === serialized) return false;
|
|
4246
|
+
await this.post({
|
|
4247
|
+
publicKey: this.publicKey,
|
|
4248
|
+
userId: this.userId,
|
|
4249
|
+
userAttributes: attributes
|
|
4250
|
+
});
|
|
4251
|
+
this.storage.setItem(STORAGE_KEYS.contactAttributes, serialized);
|
|
4252
|
+
return true;
|
|
4253
|
+
}
|
|
4254
|
+
};
|
|
4255
|
+
|
|
3621
4256
|
// src/core/deepdots-popups.ts
|
|
3622
4257
|
var EXIT_QUEUE_STORAGE_KEY = "__deepdots_exit_popup_queue__";
|
|
3623
4258
|
var DAY_IN_MS = 24 * 60 * 60 * 1e3;
|
|
4259
|
+
var ANALYTICS_MAX_BATCH_SIZE = 20;
|
|
4260
|
+
var ANALYTICS_FLUSH_INTERVAL_MS = 3e4;
|
|
3624
4261
|
var DeepdotsPopups = class {
|
|
3625
4262
|
constructor() {
|
|
3626
4263
|
this.config = null;
|
|
@@ -3642,6 +4279,22 @@ var DeepdotsPopups = class {
|
|
|
3642
4279
|
this.deferredExitTimers = [];
|
|
3643
4280
|
this.baseUrl = "";
|
|
3644
4281
|
this.env = "production";
|
|
4282
|
+
/** Identidad + sesión (Fase 1 tracking). Null hasta init(). */
|
|
4283
|
+
this.tracking = null;
|
|
4284
|
+
/** Capa de analytics (canal separado del feedback). Null hasta init(). */
|
|
4285
|
+
this.analytics = null;
|
|
4286
|
+
/** feedbackSessionId cacheado del canal de analytics (devuelto por POST /sdk/feedback). */
|
|
4287
|
+
this.analyticsFeedbackSessionId = void 0;
|
|
4288
|
+
/** Timer del flush periódico de analytics (cada ANALYTICS_FLUSH_INTERVAL_MS). */
|
|
4289
|
+
this.analyticsFlushTimer = void 0;
|
|
4290
|
+
/** Observador de navegación (Fase 2): emite page_view por el canal de analytics. */
|
|
4291
|
+
this.navObserver = null;
|
|
4292
|
+
/** Tiempo activo (engagement time, #8). */
|
|
4293
|
+
this.engagement = null;
|
|
4294
|
+
/** Atributos internos del usuario identificado → POST /sdk/popups/contact. Null si no hay userId. */
|
|
4295
|
+
this.contact = null;
|
|
4296
|
+
/** Marca si ya se inició la navegación manual (setScreen) — para RN sin History API. */
|
|
4297
|
+
this.navStarted = false;
|
|
3645
4298
|
}
|
|
3646
4299
|
/** Initialize the SDK with configuration */
|
|
3647
4300
|
init(config) {
|
|
@@ -3654,33 +4307,207 @@ var DeepdotsPopups = class {
|
|
|
3654
4307
|
this.env = config.nodeEnv || "production";
|
|
3655
4308
|
this.config = {
|
|
3656
4309
|
apiKey: config.apiKey || void 0,
|
|
3657
|
-
mode: config.mode || "client",
|
|
3658
4310
|
debug: config.debug || false,
|
|
3659
|
-
popups: config.popups || [],
|
|
3660
4311
|
userId: config.userId || void 0
|
|
3661
4312
|
};
|
|
4313
|
+
const storage = config.storage ?? createDefaultStorage();
|
|
4314
|
+
this.tracking = new TrackingManager({
|
|
4315
|
+
storage,
|
|
4316
|
+
clientUserId: this.config.userId,
|
|
4317
|
+
enabled: config.trackingEnabled ?? true
|
|
4318
|
+
});
|
|
4319
|
+
if (this.config.userId) {
|
|
4320
|
+
const userId = this.config.userId;
|
|
4321
|
+
const publicKey = this.config.apiKey ?? "";
|
|
4322
|
+
this.contact = new ContactManager({
|
|
4323
|
+
storage,
|
|
4324
|
+
publicKey,
|
|
4325
|
+
userId,
|
|
4326
|
+
post: (body) => this.postContact(body)
|
|
4327
|
+
});
|
|
4328
|
+
if (config.contactAttributes) {
|
|
4329
|
+
void this.setContactAttributes(config.contactAttributes);
|
|
4330
|
+
}
|
|
4331
|
+
}
|
|
4332
|
+
this.log("tracking \xB7 user_id:", this.tracking.getUserId(), "\xB7 new_user:", this.tracking.isNewUser(), "\xB7 enabled:", this.tracking.isTrackingEnabled());
|
|
4333
|
+
const analyticsSink = config.analytics ? createFeedbackSink({
|
|
4334
|
+
baseUrl: this.baseUrl,
|
|
4335
|
+
keys: config.analytics,
|
|
4336
|
+
log: (...a) => this.log(...a),
|
|
4337
|
+
onSessionId: (id) => {
|
|
4338
|
+
this.analyticsFeedbackSessionId = id;
|
|
4339
|
+
}
|
|
4340
|
+
}) : void 0;
|
|
4341
|
+
this.analytics = new AnalyticsManager({
|
|
4342
|
+
sink: analyticsSink,
|
|
4343
|
+
publicKey: config.analytics?.publicKey ?? this.config.apiKey,
|
|
4344
|
+
language: typeof navigator !== "undefined" ? navigator.language : void 0,
|
|
4345
|
+
platform: config.platform ?? "web",
|
|
4346
|
+
device: config.device ?? collectDeviceInfo(config.appVersion),
|
|
4347
|
+
maxBatchSize: ANALYTICS_MAX_BATCH_SIZE,
|
|
4348
|
+
onFlushNeeded: () => this.flushAnalytics()
|
|
4349
|
+
});
|
|
4350
|
+
collectGeoInfo().then((geo) => {
|
|
4351
|
+
if (geo) this.analytics?.updateDevice(geo);
|
|
4352
|
+
}).catch(() => {
|
|
4353
|
+
});
|
|
4354
|
+
this.navObserver = new NavigationObserver();
|
|
4355
|
+
this.navObserver.onVisit((v) => this.track("page_view", { screen: v.screen, duration_seconds: v.durationSeconds }));
|
|
4356
|
+
this.navObserver.install();
|
|
4357
|
+
this.engagement = new EngagementTracker();
|
|
4358
|
+
this.engagement.resume();
|
|
4359
|
+
this.setupAnalyticsFlush();
|
|
3662
4360
|
this.initialized = true;
|
|
3663
4361
|
this.log("SDK initialized", this.config);
|
|
3664
4362
|
if (this.renderer.init) this.renderer.init();
|
|
3665
4363
|
this.setupPopupContainer();
|
|
3666
|
-
|
|
3667
|
-
this.popupDefinitions = this.validatePopupDefinitions(
|
|
4364
|
+
this.fetchPopupsFromServer().then((defs) => {
|
|
4365
|
+
this.popupDefinitions = this.validatePopupDefinitions(defs);
|
|
3668
4366
|
this.popupsLoaded = true;
|
|
4367
|
+
this.log("Popups loaded from API", this.popupDefinitions);
|
|
3669
4368
|
this.configureTriggersFromDefinitions();
|
|
3670
4369
|
this.processDeferredExitQueue();
|
|
4370
|
+
if (this.pendingAutoLaunch) {
|
|
4371
|
+
this.startTriggers();
|
|
4372
|
+
}
|
|
4373
|
+
});
|
|
4374
|
+
}
|
|
4375
|
+
/** User id actual (generado por el SDK o provisto por el cliente). Null si tracking off. */
|
|
4376
|
+
getUserId() {
|
|
4377
|
+
return this.tracking?.getUserId() ?? null;
|
|
4378
|
+
}
|
|
4379
|
+
/** Session id de navegación actual. Null si tracking off. */
|
|
4380
|
+
getSessionId() {
|
|
4381
|
+
return this.tracking?.getSessionId() ?? null;
|
|
4382
|
+
}
|
|
4383
|
+
/** Activa/desactiva el tracking (identidad + sesión + analytics). Kill-switch del contrato §7bis. */
|
|
4384
|
+
setTrackingEnabled(enabled) {
|
|
4385
|
+
this.tracking?.setTrackingEnabled(enabled);
|
|
4386
|
+
this.log("tracking \xB7 setTrackingEnabled:", enabled, "\xB7 session_id:", this.tracking?.getSessionId() ?? null);
|
|
4387
|
+
}
|
|
4388
|
+
// ───────── Analytics (canal separado del feedback, vinculado por user_id) ─────────
|
|
4389
|
+
/** Registra un evento de analítica (modelo GA: nombre + params). No-op si tracking off. */
|
|
4390
|
+
track(name, params) {
|
|
4391
|
+
if (!this.tracking?.isTrackingEnabled()) return;
|
|
4392
|
+
this.analytics?.track(name, params);
|
|
4393
|
+
this.log("analytics \xB7 track:", name, params ?? {});
|
|
4394
|
+
}
|
|
4395
|
+
/** User attributes para breakdowns (registration_status, pass_type, sector, pass_status…). */
|
|
4396
|
+
setUserAttributes(attributes) {
|
|
4397
|
+
if (!this.tracking?.isTrackingEnabled()) return;
|
|
4398
|
+
this.analytics?.setUserAttributes(attributes);
|
|
4399
|
+
}
|
|
4400
|
+
/**
|
|
4401
|
+
* Info interna del usuario que solo conoce el host (idioma, edad, plan…), persistida en el
|
|
4402
|
+
* Contact del backend para segmentación/targeting de popups. Identificada por el `userId` del
|
|
4403
|
+
* init. Solo envía si los atributos cambiaron (diff en storage). No-op si tracking off o sin userId.
|
|
4404
|
+
* @returns `true` si se envió al backend, `false` si no hubo cambios o está deshabilitado.
|
|
4405
|
+
*/
|
|
4406
|
+
async setContactAttributes(attributes) {
|
|
4407
|
+
if (!this.tracking?.isTrackingEnabled()) return false;
|
|
4408
|
+
if (!this.contact) return false;
|
|
4409
|
+
return this.contact.setAttributes(attributes);
|
|
4410
|
+
}
|
|
4411
|
+
/** Marca el inicio de un mini-service; etiqueta los eventos siguientes. No-op si tracking off. */
|
|
4412
|
+
enterMiniService(name, entryPointType) {
|
|
4413
|
+
if (!this.tracking?.isTrackingEnabled()) return;
|
|
4414
|
+
this.analytics?.enterMiniService(name, entryPointType);
|
|
4415
|
+
this.log("analytics \xB7 enterMiniService:", name, entryPointType ?? "");
|
|
4416
|
+
}
|
|
4417
|
+
/** Cierra el mini-service activo (emite `mini_service_exit` con duración, #27). No-op si tracking off. */
|
|
4418
|
+
exitMiniService() {
|
|
4419
|
+
if (!this.tracking?.isTrackingEnabled()) return;
|
|
4420
|
+
this.analytics?.exitMiniService();
|
|
4421
|
+
this.log("analytics \xB7 exitMiniService");
|
|
4422
|
+
}
|
|
4423
|
+
/** Findability (#31/#35): registra una búsqueda. `has_results` se deriva de `resultsCount`. */
|
|
4424
|
+
trackSearch(query, resultsCount, params) {
|
|
4425
|
+
this.track("search", { query, results_count: resultsCount, has_results: resultsCount > 0, ...params ?? {} });
|
|
4426
|
+
}
|
|
4427
|
+
/** Findability friction (#34/#35): señal de fricción con su `friction_topic`. */
|
|
4428
|
+
trackFindabilityFriction(frictionTopic, params) {
|
|
4429
|
+
this.track("findability_friction", { friction_topic: frictionTopic, ...params ?? {} });
|
|
4430
|
+
}
|
|
4431
|
+
/** Funnel: un paso del embudo, correlacionado por `taskId`. El backend reconstruye conversión/drop-off/tiempo. */
|
|
4432
|
+
trackFunnelStep(funnel, step, taskId, params) {
|
|
4433
|
+
this.track("funnel_step", { funnel, step, task_id: taskId, ...params ?? {} });
|
|
4434
|
+
}
|
|
4435
|
+
/**
|
|
4436
|
+
* Navegación MANUAL (entornos sin History API, p. ej. React Native con React Navigation).
|
|
4437
|
+
* El host la llama en cada cambio de pantalla; emite `page_view` al salir de la anterior.
|
|
4438
|
+
*/
|
|
4439
|
+
setScreen(name) {
|
|
4440
|
+
if (!this.navObserver) return;
|
|
4441
|
+
if (!this.navStarted) {
|
|
4442
|
+
this.navObserver.begin(name);
|
|
4443
|
+
this.navStarted = true;
|
|
3671
4444
|
} else {
|
|
3672
|
-
this.
|
|
3673
|
-
this.popupDefinitions = this.validatePopupDefinitions(defs);
|
|
3674
|
-
this.popupsLoaded = true;
|
|
3675
|
-
this.log("Popups loaded from server (fake)", this.popupDefinitions);
|
|
3676
|
-
this.configureTriggersFromDefinitions();
|
|
3677
|
-
this.processDeferredExitQueue();
|
|
3678
|
-
if (this.pendingAutoLaunch) {
|
|
3679
|
-
this.startTriggers();
|
|
3680
|
-
}
|
|
3681
|
-
});
|
|
4445
|
+
this.navObserver.visit(name);
|
|
3682
4446
|
}
|
|
3683
4447
|
}
|
|
4448
|
+
/** App a foreground (RN: AppState 'active'): reanuda el engagement time. */
|
|
4449
|
+
onForeground() {
|
|
4450
|
+
this.engagement?.resume();
|
|
4451
|
+
}
|
|
4452
|
+
/**
|
|
4453
|
+
* App a background (RN: AppState 'background'): cierra pantalla actual (page_view),
|
|
4454
|
+
* mini-service (mini_service_exit), emite engagement y hace flush del lote.
|
|
4455
|
+
*/
|
|
4456
|
+
onBackground() {
|
|
4457
|
+
this.navObserver?.stop();
|
|
4458
|
+
this.navStarted = false;
|
|
4459
|
+
this.exitMiniService();
|
|
4460
|
+
this.flushEngagement();
|
|
4461
|
+
this.engagement?.pause();
|
|
4462
|
+
this.flushAnalytics();
|
|
4463
|
+
}
|
|
4464
|
+
/** Payload que se ENVIARÍA al endpoint de analytics (no envía ni vacía el buffer). */
|
|
4465
|
+
previewAnalytics() {
|
|
4466
|
+
return this.analytics?.buildPayload(this.analyticsIdentity()) ?? {
|
|
4467
|
+
userId: null,
|
|
4468
|
+
sessionId: null,
|
|
4469
|
+
context: { platform: "web", attributes: {} },
|
|
4470
|
+
events: []
|
|
4471
|
+
};
|
|
4472
|
+
}
|
|
4473
|
+
/** Envía (hoy dry-run → console.log) el lote acumulado de analytics y vacía el buffer. */
|
|
4474
|
+
flushAnalytics() {
|
|
4475
|
+
if (!this.tracking?.isTrackingEnabled()) return;
|
|
4476
|
+
this.analytics?.flush(this.analyticsIdentity());
|
|
4477
|
+
}
|
|
4478
|
+
/** Emite un evento `user_engagement` con el tiempo activo acumulado (#8). */
|
|
4479
|
+
flushEngagement() {
|
|
4480
|
+
if (!this.tracking?.isTrackingEnabled()) return;
|
|
4481
|
+
const ms = this.engagement?.consume() ?? 0;
|
|
4482
|
+
if (ms > 0) this.track("user_engagement", { engagement_time_msec: ms });
|
|
4483
|
+
}
|
|
4484
|
+
analyticsIdentity() {
|
|
4485
|
+
return {
|
|
4486
|
+
userId: this.tracking?.getUserId() ?? null,
|
|
4487
|
+
sessionId: this.tracking?.getSessionId() ?? null
|
|
4488
|
+
};
|
|
4489
|
+
}
|
|
4490
|
+
/** Flush automático al ocultar/cerrar la página (no perder el lote pendiente). */
|
|
4491
|
+
setupAnalyticsFlush() {
|
|
4492
|
+
if (typeof document === "undefined" || typeof window === "undefined") return;
|
|
4493
|
+
this.analyticsFlushTimer = setInterval(() => this.flushAnalytics(), ANALYTICS_FLUSH_INTERVAL_MS);
|
|
4494
|
+
window.addEventListener("pagehide", () => {
|
|
4495
|
+
clearInterval(this.analyticsFlushTimer);
|
|
4496
|
+
this.navObserver?.stop();
|
|
4497
|
+
this.exitMiniService();
|
|
4498
|
+
this.flushEngagement();
|
|
4499
|
+
this.flushAnalytics();
|
|
4500
|
+
});
|
|
4501
|
+
document.addEventListener("visibilitychange", () => {
|
|
4502
|
+
if (document.visibilityState === "hidden") {
|
|
4503
|
+
this.flushEngagement();
|
|
4504
|
+
this.engagement?.pause();
|
|
4505
|
+
this.flushAnalytics();
|
|
4506
|
+
} else if (document.visibilityState === "visible") {
|
|
4507
|
+
this.engagement?.resume();
|
|
4508
|
+
}
|
|
4509
|
+
});
|
|
4510
|
+
}
|
|
3684
4511
|
/** Enable auto-launch functionality with configured triggers */
|
|
3685
4512
|
autoLaunch() {
|
|
3686
4513
|
if (!this.initialized) {
|
|
@@ -3698,35 +4525,6 @@ var DeepdotsPopups = class {
|
|
|
3698
4525
|
this.log("Auto-launch enabled");
|
|
3699
4526
|
this.triggers.forEach((trigger) => setupTrigger(this, trigger));
|
|
3700
4527
|
}
|
|
3701
|
-
/** Show a popup immediately (supports legacy ShowOptions and new PopupDefinition) */
|
|
3702
|
-
show(options) {
|
|
3703
|
-
if (!this.initialized) {
|
|
3704
|
-
throw new Error("SDK not initialized. Call init() first.");
|
|
3705
|
-
}
|
|
3706
|
-
const isDefinition = options.id !== void 0;
|
|
3707
|
-
if (isDefinition) {
|
|
3708
|
-
const def = options;
|
|
3709
|
-
this.log("Showing popup (definition)", def);
|
|
3710
|
-
this.surveyToPopupId.set(def.surveyId, def.id);
|
|
3711
|
-
this.lastShown.set(def.id, Date.now());
|
|
3712
|
-
this.renderPopup(def.surveyId, def.productId, def.actions, def.style);
|
|
3713
|
-
this.emitEvent("popup_shown", def.surveyId, { popupId: def.id });
|
|
3714
|
-
} else {
|
|
3715
|
-
const opt = options;
|
|
3716
|
-
this.log("Showing popup (legacy options)", opt);
|
|
3717
|
-
this.renderPopup(opt.surveyId, opt.productId, void 0);
|
|
3718
|
-
this.emitEvent("popup_shown", opt.surveyId);
|
|
3719
|
-
}
|
|
3720
|
-
}
|
|
3721
|
-
/** Mostrar popup por id de definición (usa surveyId interno) */
|
|
3722
|
-
showByPopupId(popupId) {
|
|
3723
|
-
const def = this.popupDefinitions.find((popup) => popup.id === popupId);
|
|
3724
|
-
if (!def) {
|
|
3725
|
-
this.log("Popup definition not found", popupId);
|
|
3726
|
-
return;
|
|
3727
|
-
}
|
|
3728
|
-
this.show(def);
|
|
3729
|
-
}
|
|
3730
4528
|
/** Configure triggers for auto-launching popups (manual) */
|
|
3731
4529
|
configureTriggers(triggers) {
|
|
3732
4530
|
if (!this.initialized) {
|
|
@@ -3795,7 +4593,11 @@ var DeepdotsPopups = class {
|
|
|
3795
4593
|
this.showDefinition(matched);
|
|
3796
4594
|
}
|
|
3797
4595
|
showDefinition(def) {
|
|
3798
|
-
this.
|
|
4596
|
+
this.log("Showing popup (definition)", def);
|
|
4597
|
+
this.surveyToPopupId.set(def.surveyId, def.id);
|
|
4598
|
+
this.lastShown.set(def.id, Date.now());
|
|
4599
|
+
this.renderPopup(def.surveyId, def.productId, def.actions, def.style);
|
|
4600
|
+
this.emitEvent("popup_shown", def.surveyId, { popupId: def.id });
|
|
3799
4601
|
}
|
|
3800
4602
|
shouldShow(def, pathUrl, skipPathCheck = false) {
|
|
3801
4603
|
if (!skipPathCheck && !this.matchesSegmentsPath(def, pathUrl)) {
|
|
@@ -4039,6 +4841,26 @@ var DeepdotsPopups = class {
|
|
|
4039
4841
|
return [];
|
|
4040
4842
|
}
|
|
4041
4843
|
}
|
|
4844
|
+
/** Envía los atributos de contact a la API (`POST /sdk/popups/contact`). */
|
|
4845
|
+
async postContact(body) {
|
|
4846
|
+
const baseUrl = this.baseUrl;
|
|
4847
|
+
if (!baseUrl) return;
|
|
4848
|
+
const endpoint = `${baseUrl}/sdk/popups/contact`;
|
|
4849
|
+
try {
|
|
4850
|
+
const response = await fetch(endpoint, {
|
|
4851
|
+
method: "POST",
|
|
4852
|
+
headers: { "Content-Type": "application/json" },
|
|
4853
|
+
body: JSON.stringify(body)
|
|
4854
|
+
});
|
|
4855
|
+
if (!response.ok) {
|
|
4856
|
+
this.log("Failed to post contact", response.status, response.statusText);
|
|
4857
|
+
return;
|
|
4858
|
+
}
|
|
4859
|
+
this.log("contact \xB7 sent attributes for user:", body.userId);
|
|
4860
|
+
} catch (error) {
|
|
4861
|
+
this.log("Error posting contact", error);
|
|
4862
|
+
}
|
|
4863
|
+
}
|
|
4042
4864
|
/** Notifica eventos de popup a la API */
|
|
4043
4865
|
async postPopupEvent(status, popupId, userId) {
|
|
4044
4866
|
const apiKey = this.config?.apiKey;
|
|
@@ -4060,6 +4882,15 @@ var DeepdotsPopups = class {
|
|
|
4060
4882
|
});
|
|
4061
4883
|
if (!response.ok) {
|
|
4062
4884
|
this.log("Failed to post popup event", response.status, response.statusText);
|
|
4885
|
+
return;
|
|
4886
|
+
}
|
|
4887
|
+
try {
|
|
4888
|
+
const data = await response.json();
|
|
4889
|
+
if (data && typeof data.sessionId === "string") {
|
|
4890
|
+
this.tracking?.setSessionId(data.sessionId);
|
|
4891
|
+
this.log("tracking \xB7 session_id from backend:", data.sessionId);
|
|
4892
|
+
}
|
|
4893
|
+
} catch {
|
|
4063
4894
|
}
|
|
4064
4895
|
} catch (error) {
|
|
4065
4896
|
this.log("Error posting popup event", error);
|
|
@@ -4102,7 +4933,11 @@ var DeepdotsPopups = class {
|
|
|
4102
4933
|
}
|
|
4103
4934
|
/** Render the popup UI */
|
|
4104
4935
|
renderPopup(surveyId, productId, actions, style) {
|
|
4105
|
-
const userId = this.config?.userId;
|
|
4936
|
+
const userId = this.tracking?.getUserId() ?? this.config?.userId;
|
|
4937
|
+
const sessionId = this.tracking?.getSessionId() ?? void 0;
|
|
4938
|
+
const miniService = this.analytics?.getMiniService() ?? void 0;
|
|
4939
|
+
const analyticsFeedbackSessionId = this.analyticsFeedbackSessionId;
|
|
4940
|
+
this.log("tracking \xB7 survey identity \u2192", { userId: userId ?? null, sessionId: sessionId ?? null, miniService: miniService ?? null, analyticsFeedbackSessionId: analyticsFeedbackSessionId ?? null });
|
|
4106
4941
|
if (this.renderer) {
|
|
4107
4942
|
this.renderer.show(
|
|
4108
4943
|
surveyId,
|
|
@@ -4112,22 +4947,31 @@ var DeepdotsPopups = class {
|
|
|
4112
4947
|
() => this.hidePopup(),
|
|
4113
4948
|
this.env,
|
|
4114
4949
|
userId,
|
|
4115
|
-
style
|
|
4950
|
+
style,
|
|
4951
|
+
sessionId,
|
|
4952
|
+
miniService,
|
|
4953
|
+
analyticsFeedbackSessionId
|
|
4116
4954
|
);
|
|
4117
4955
|
return;
|
|
4118
4956
|
}
|
|
4119
4957
|
if (!this.popupContainer) return;
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4958
|
+
const container = this.popupContainer;
|
|
4959
|
+
void Promise.resolve().then(() => (init_renderPopup(), renderPopup_exports)).then(({ renderPopup: renderPopup2 }) => {
|
|
4960
|
+
renderPopup2(
|
|
4961
|
+
container,
|
|
4962
|
+
surveyId,
|
|
4963
|
+
productId,
|
|
4964
|
+
actions,
|
|
4965
|
+
(type, id, payload) => this.emitEvent(type, id, payload),
|
|
4966
|
+
() => this.hidePopup(),
|
|
4967
|
+
this.env,
|
|
4968
|
+
userId,
|
|
4969
|
+
style,
|
|
4970
|
+
sessionId,
|
|
4971
|
+
miniService,
|
|
4972
|
+
analyticsFeedbackSessionId
|
|
4973
|
+
);
|
|
4974
|
+
});
|
|
4131
4975
|
}
|
|
4132
4976
|
/** Hide the popup */
|
|
4133
4977
|
hidePopup() {
|
|
@@ -4157,7 +5001,7 @@ var DeepdotsPopups = class {
|
|
|
4157
5001
|
if (popupId) {
|
|
4158
5002
|
const userIdFromData = data?.userId;
|
|
4159
5003
|
const status = type === "popup_shown" ? "SHOWED" /* SHOWED */ : type === "survey_completed" ? "COMPLETED" /* COMPLETED */ : "PARTIAL" /* PARTIAL */;
|
|
4160
|
-
void this.postPopupEvent(status, popupId, userIdFromData || this.config?.userId);
|
|
5004
|
+
void this.postPopupEvent(status, popupId, userIdFromData || this.tracking?.getUserId() || this.config?.userId);
|
|
4161
5005
|
} else {
|
|
4162
5006
|
this.debug("No popupId available to post event", { type, surveyId });
|
|
4163
5007
|
}
|
|
@@ -4293,4 +5137,41 @@ var DeepdotsPopups = class {
|
|
|
4293
5137
|
return defs.map((def) => this.normalizePopupDefinition(def)).filter((def) => !!def && this.isPopupDefinition(def));
|
|
4294
5138
|
}
|
|
4295
5139
|
};
|
|
5140
|
+
|
|
5141
|
+
// src/index.ts
|
|
5142
|
+
init_tracking_manager();
|
|
5143
|
+
|
|
5144
|
+
// src/react-native/setup.ts
|
|
5145
|
+
function mmkvStorage(mmkv) {
|
|
5146
|
+
return {
|
|
5147
|
+
getItem: (k) => mmkv.getString(k) ?? null,
|
|
5148
|
+
setItem: (k, v) => mmkv.set(k, v),
|
|
5149
|
+
removeItem: (k) => mmkv.delete(k)
|
|
5150
|
+
};
|
|
5151
|
+
}
|
|
5152
|
+
function collectRnDevice(d) {
|
|
5153
|
+
return {
|
|
5154
|
+
device_type: d.isTablet?.() ? "tablet" : "mobile",
|
|
5155
|
+
os_version: d.getSystemVersion?.(),
|
|
5156
|
+
device_model: d.getDeviceId?.(),
|
|
5157
|
+
app_version: d.getVersion?.()
|
|
5158
|
+
};
|
|
5159
|
+
}
|
|
5160
|
+
function setupReactNative(sdk, config, deps = {}) {
|
|
5161
|
+
if (deps.renderer) sdk.setRenderer(deps.renderer);
|
|
5162
|
+
const storage = deps.mmkv ? mmkvStorage(deps.mmkv) : config.storage;
|
|
5163
|
+
const device = deps.deviceInfo ? collectRnDevice(deps.deviceInfo) : config.device;
|
|
5164
|
+
const platform = deps.platform ?? config.platform;
|
|
5165
|
+
sdk.init({ ...config, storage, device, platform });
|
|
5166
|
+
let subscription;
|
|
5167
|
+
if (deps.appState?.addEventListener) {
|
|
5168
|
+
subscription = deps.appState.addEventListener("change", (state) => {
|
|
5169
|
+
if (state === "active") sdk.onForeground();
|
|
5170
|
+
else sdk.onBackground();
|
|
5171
|
+
});
|
|
5172
|
+
}
|
|
5173
|
+
return () => {
|
|
5174
|
+
if (subscription && typeof subscription.remove === "function") subscription.remove();
|
|
5175
|
+
};
|
|
5176
|
+
}
|
|
4296
5177
|
//# sourceMappingURL=index.js.map
|