@magicfeedback/popup-sdk 1.1.8 → 1.2.0
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/dist/index.d.mts +119 -9
- package/dist/index.d.ts +119 -9
- package/dist/index.js +191 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +187 -21
- package/dist/index.mjs.map +1 -1
- package/dist/react-native.d.mts +59 -7
- package/dist/react-native.d.ts +59 -7
- package/dist/react-native.js +188 -19
- package/dist/react-native.js.map +1 -1
- package/dist/react-native.mjs +184 -21
- package/dist/react-native.mjs.map +1 -1
- package/dist/{renderPopup-PS3NJDUR.mjs → renderPopup-NHWJ7YSI.mjs} +7 -1
- package/dist/renderPopup-NHWJ7YSI.mjs.map +1 -0
- package/dist/{renderPopup-EZKDMLVZ.mjs → renderPopup-R53C7DB3.mjs} +7 -1
- package/dist/renderPopup-R53C7DB3.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/renderPopup-EZKDMLVZ.mjs.map +0 -1
- package/dist/renderPopup-PS3NJDUR.mjs.map +0 -1
package/dist/index.js
CHANGED
|
@@ -3077,6 +3077,11 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
3077
3077
|
const isDark = style?.theme === "dark";
|
|
3078
3078
|
const theme = {
|
|
3079
3079
|
popupBg: isDark ? "#1e1e1e" : "#fff",
|
|
3080
|
+
// El popup fija su propio fondo, así que debe fijar también el esquema de color.
|
|
3081
|
+
// Sin esto los controles de formulario (input/textarea/select) caen al estilo por
|
|
3082
|
+
// defecto del navegador: en un host con `prefers-color-scheme: dark` se pintan
|
|
3083
|
+
// oscuros con texto blanco sobre el fondo claro del popup.
|
|
3084
|
+
colorScheme: isDark ? "dark" : "light",
|
|
3080
3085
|
textPrimary: isDark ? "#f0f0f0" : "#111",
|
|
3081
3086
|
closeBtnHoverBg: isDark ? "rgba(255,255,255,0.10)" : "rgba(0,0,0,0.06)",
|
|
3082
3087
|
closeBtnHoverColor: isDark ? "#fff" : "#000000"
|
|
@@ -3099,6 +3104,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
3099
3104
|
flex-direction: column;
|
|
3100
3105
|
justify-content: flex-start;
|
|
3101
3106
|
background: ${theme.popupBg};
|
|
3107
|
+
color-scheme: ${theme.colorScheme};
|
|
3102
3108
|
border-radius: 8px;
|
|
3103
3109
|
padding: 24px;
|
|
3104
3110
|
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
|
@@ -3550,6 +3556,9 @@ __export(index_exports, {
|
|
|
3550
3556
|
CrashReporter: () => CrashReporter,
|
|
3551
3557
|
DeepdotsPopups: () => DeepdotsPopups,
|
|
3552
3558
|
InMemoryStorage: () => InMemoryStorage,
|
|
3559
|
+
MAX_TRACKED_MESSAGES: () => MAX_TRACKED_MESSAGES,
|
|
3560
|
+
MESSAGE_CHANNELS: () => MESSAGE_CHANNELS,
|
|
3561
|
+
MessageGuard: () => MessageGuard,
|
|
3553
3562
|
NoopPopupRenderer: () => NoopPopupRenderer,
|
|
3554
3563
|
POPUP_TRIGGER_CONDITION_STATUSES: () => POPUP_TRIGGER_CONDITION_STATUSES,
|
|
3555
3564
|
ReactNativePopupRenderer: () => ReactNativePopupRenderer,
|
|
@@ -4017,6 +4026,15 @@ var AnalyticsManager = class {
|
|
|
4017
4026
|
this.exitMiniService(name);
|
|
4018
4027
|
}
|
|
4019
4028
|
}
|
|
4029
|
+
/**
|
|
4030
|
+
* Olvida lo que pertenecía al usuario anterior (user attributes + métricas) al cambiar de
|
|
4031
|
+
* usuario. NO toca el buffer de eventos: el cierre de sesión ya lo vació con la identidad
|
|
4032
|
+
* vieja, y lo que llegue después es del usuario nuevo.
|
|
4033
|
+
*/
|
|
4034
|
+
resetUserScope() {
|
|
4035
|
+
this.attributes = {};
|
|
4036
|
+
this.metrics = {};
|
|
4037
|
+
}
|
|
4020
4038
|
/** Registra un evento de analítica (modelo GA: nombre + params). */
|
|
4021
4039
|
track(name, params) {
|
|
4022
4040
|
const current = this.getMiniService();
|
|
@@ -4104,7 +4122,7 @@ function pushKV(list, key, value) {
|
|
|
4104
4122
|
if (value === void 0 || value === null || value === "") return;
|
|
4105
4123
|
list.push({ key, value: [String(value)] });
|
|
4106
4124
|
}
|
|
4107
|
-
function buildAnalyticsFeedbackBody(envelope, keys, feedbackSessionId) {
|
|
4125
|
+
function buildAnalyticsFeedbackBody(envelope, keys, feedbackSessionId, options) {
|
|
4108
4126
|
const { context } = envelope;
|
|
4109
4127
|
const profile = [];
|
|
4110
4128
|
pushKV(profile, "external-user-id", envelope.userId);
|
|
@@ -4155,7 +4173,8 @@ function buildAnalyticsFeedbackBody(envelope, keys, feedbackSessionId) {
|
|
|
4155
4173
|
},
|
|
4156
4174
|
publicKey: keys.publicKey,
|
|
4157
4175
|
integration: keys.integration,
|
|
4158
|
-
|
|
4176
|
+
// Único marcador de cierre acordado con backend: el último lote de la sesión.
|
|
4177
|
+
completed: options?.sessionEnd === true,
|
|
4159
4178
|
...feedbackSessionId ? { sessionId: feedbackSessionId } : {}
|
|
4160
4179
|
};
|
|
4161
4180
|
}
|
|
@@ -4174,7 +4193,7 @@ function createFeedbackSink(options) {
|
|
|
4174
4193
|
let feedbackSessionId;
|
|
4175
4194
|
let firstPostInFlight = null;
|
|
4176
4195
|
const url = `${options.baseUrl}/sdk/feedback`;
|
|
4177
|
-
const post = async (body, final) => {
|
|
4196
|
+
const post = async (body, final, sessionEnd = false) => {
|
|
4178
4197
|
const json = JSON.stringify(body);
|
|
4179
4198
|
const small = json.length <= KEEPALIVE_MAX_BYTES;
|
|
4180
4199
|
if (final && options.sendBeaconImpl && small) {
|
|
@@ -4208,6 +4227,7 @@ function createFeedbackSink(options) {
|
|
|
4208
4227
|
);
|
|
4209
4228
|
return;
|
|
4210
4229
|
}
|
|
4230
|
+
if (sessionEnd) return;
|
|
4211
4231
|
try {
|
|
4212
4232
|
const data = await res.json();
|
|
4213
4233
|
if (data?.sessionId && data.sessionId !== feedbackSessionId) {
|
|
@@ -4220,13 +4240,18 @@ function createFeedbackSink(options) {
|
|
|
4220
4240
|
};
|
|
4221
4241
|
return async (envelope, meta) => {
|
|
4222
4242
|
const final = meta?.final === true;
|
|
4243
|
+
const sessionEnd = meta?.sessionEnd === true;
|
|
4223
4244
|
if (!feedbackSessionId && firstPostInFlight && !final) {
|
|
4224
4245
|
await firstPostInFlight;
|
|
4225
4246
|
}
|
|
4226
|
-
const body = buildAnalyticsFeedbackBody(envelope, options.keys, feedbackSessionId);
|
|
4247
|
+
const body = buildAnalyticsFeedbackBody(envelope, options.keys, feedbackSessionId, { sessionEnd });
|
|
4227
4248
|
options.log?.("[DeepdotsAnalytics] POST /sdk/feedback \u2192", body);
|
|
4228
|
-
const sent = post(body, final);
|
|
4229
|
-
if (
|
|
4249
|
+
const sent = post(body, final, sessionEnd);
|
|
4250
|
+
if (sessionEnd) {
|
|
4251
|
+
feedbackSessionId = void 0;
|
|
4252
|
+
firstPostInFlight = null;
|
|
4253
|
+
options.onSessionReset?.();
|
|
4254
|
+
} else if (!feedbackSessionId) {
|
|
4230
4255
|
firstPostInFlight = sent.then(
|
|
4231
4256
|
() => void 0,
|
|
4232
4257
|
() => void 0
|
|
@@ -4665,6 +4690,7 @@ var CrashReporter = class {
|
|
|
4665
4690
|
init_logger();
|
|
4666
4691
|
|
|
4667
4692
|
// src/analytics/messaging.ts
|
|
4693
|
+
var MESSAGE_CHANNELS = ["push", "in_app"];
|
|
4668
4694
|
function buildMessageParams(stage, o) {
|
|
4669
4695
|
const p = {
|
|
4670
4696
|
stage,
|
|
@@ -4677,6 +4703,57 @@ function buildMessageParams(stage, o) {
|
|
|
4677
4703
|
if (o.currency) p.currency = o.currency;
|
|
4678
4704
|
return { ...p, ...o.params ?? {} };
|
|
4679
4705
|
}
|
|
4706
|
+
var MAX_TRACKED_MESSAGES = 500;
|
|
4707
|
+
var MessageGuard = class {
|
|
4708
|
+
constructor(maxTrackedMessages = MAX_TRACKED_MESSAGES) {
|
|
4709
|
+
this.maxTrackedMessages = maxTrackedMessages;
|
|
4710
|
+
/** message_id → canal fijado + stages ya emitidos. El orden de inserción marca la eviction. */
|
|
4711
|
+
this.seen = /* @__PURE__ */ new Map();
|
|
4712
|
+
}
|
|
4713
|
+
evaluate(stage, options) {
|
|
4714
|
+
const channel = options.channel;
|
|
4715
|
+
if (!MESSAGE_CHANNELS.includes(channel)) {
|
|
4716
|
+
return {
|
|
4717
|
+
emit: false,
|
|
4718
|
+
reason: "invalid_channel",
|
|
4719
|
+
detail: `channel "${channel}" no v\xE1lido (esperado ${MESSAGE_CHANNELS.join(" | ")})`
|
|
4720
|
+
};
|
|
4721
|
+
}
|
|
4722
|
+
const entry = this.seen.get(options.id);
|
|
4723
|
+
if (!entry) {
|
|
4724
|
+
this.remember(options.id, channel, stage);
|
|
4725
|
+
return { emit: true };
|
|
4726
|
+
}
|
|
4727
|
+
if (entry.channel !== channel) {
|
|
4728
|
+
return {
|
|
4729
|
+
emit: false,
|
|
4730
|
+
reason: "channel_conflict",
|
|
4731
|
+
detail: `message_id "${options.id}" ya se report\xF3 en channel "${entry.channel}"; se descarta "${channel}"`
|
|
4732
|
+
};
|
|
4733
|
+
}
|
|
4734
|
+
if (entry.stages.has(stage)) {
|
|
4735
|
+
return {
|
|
4736
|
+
emit: false,
|
|
4737
|
+
reason: "duplicate_stage",
|
|
4738
|
+
detail: `stage "${stage}" ya emitido para message_id "${options.id}"`
|
|
4739
|
+
};
|
|
4740
|
+
}
|
|
4741
|
+
entry.stages.add(stage);
|
|
4742
|
+
return { emit: true };
|
|
4743
|
+
}
|
|
4744
|
+
/** Olvida todo lo vigilado (nueva sesión). */
|
|
4745
|
+
reset() {
|
|
4746
|
+
this.seen.clear();
|
|
4747
|
+
}
|
|
4748
|
+
remember(id, channel, stage) {
|
|
4749
|
+
this.seen.set(id, { channel, stages: /* @__PURE__ */ new Set([stage]) });
|
|
4750
|
+
while (this.seen.size > this.maxTrackedMessages) {
|
|
4751
|
+
const oldest = this.seen.keys().next();
|
|
4752
|
+
if (oldest.done) break;
|
|
4753
|
+
this.seen.delete(oldest.value);
|
|
4754
|
+
}
|
|
4755
|
+
}
|
|
4756
|
+
};
|
|
4680
4757
|
|
|
4681
4758
|
// src/core/deepdots-popups.ts
|
|
4682
4759
|
var EXIT_QUEUE_STORAGE_KEY = "__deepdots_exit_popup_queue__";
|
|
@@ -4718,14 +4795,24 @@ var DeepdotsPopups = class {
|
|
|
4718
4795
|
this.analyticsFlushTimer = void 0;
|
|
4719
4796
|
/** Crash & error reporting (#14–17). Null hasta init(). */
|
|
4720
4797
|
this.crashReporter = null;
|
|
4798
|
+
/** Protecciones del funnel de Messaging (#18–22). Vigencia de sesión: se reinicia en init(). */
|
|
4799
|
+
this.messageGuard = new MessageGuard();
|
|
4721
4800
|
/** Observador de navegación (Fase 2): emite page_view por el canal de analytics. */
|
|
4722
4801
|
this.navObserver = null;
|
|
4723
4802
|
/** Tiempo activo (engagement time, #8). */
|
|
4724
4803
|
this.engagement = null;
|
|
4725
4804
|
/** Atributos internos del usuario identificado → POST /sdk/popups/contact. Null si no hay userId. */
|
|
4726
4805
|
this.contact = null;
|
|
4806
|
+
/** Storage resuelto en init() (inyectado por el host o localStorage). Reusado al cambiar de usuario. */
|
|
4807
|
+
this.storage = null;
|
|
4727
4808
|
/** Marca si ya se inició la navegación manual (setScreen) — para RN sin History API. */
|
|
4728
4809
|
this.navStarted = false;
|
|
4810
|
+
/**
|
|
4811
|
+
* `true` entre `session_start` y `session_end`. Hace idempotente tanto la apertura como
|
|
4812
|
+
* el cierre (p. ej. `visibilitychange` + `pagehide` seguidos) y permite abrir una sesión
|
|
4813
|
+
* nueva al volver a foreground o al conceder el consentimiento más tarde.
|
|
4814
|
+
*/
|
|
4815
|
+
this.sessionOpen = false;
|
|
4729
4816
|
/**
|
|
4730
4817
|
* Idioma resuelto en init() (explícito > navigator.language > Intl). Única fuente de
|
|
4731
4818
|
* verdad para el context de analytics Y para la segmentación por idioma de los popups.
|
|
@@ -4737,6 +4824,11 @@ var DeepdotsPopups = class {
|
|
|
4737
4824
|
this.logger = config.logger ?? console;
|
|
4738
4825
|
setLogger(this.logger);
|
|
4739
4826
|
if (this.initialized) {
|
|
4827
|
+
const nextUserId = config.userId || void 0;
|
|
4828
|
+
if (nextUserId !== this.config?.userId) {
|
|
4829
|
+
this.setUserId(nextUserId);
|
|
4830
|
+
return;
|
|
4831
|
+
}
|
|
4740
4832
|
this.log("SDK already initialized");
|
|
4741
4833
|
return;
|
|
4742
4834
|
}
|
|
@@ -4749,6 +4841,7 @@ var DeepdotsPopups = class {
|
|
|
4749
4841
|
userId: config.userId || void 0
|
|
4750
4842
|
};
|
|
4751
4843
|
const storage = config.storage ?? createDefaultStorage();
|
|
4844
|
+
this.storage = storage;
|
|
4752
4845
|
this.tracking = new TrackingManager({
|
|
4753
4846
|
storage,
|
|
4754
4847
|
clientUserId: this.config.userId,
|
|
@@ -4776,6 +4869,9 @@ var DeepdotsPopups = class {
|
|
|
4776
4869
|
sendBeaconImpl: typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function" ? (url, body) => navigator.sendBeacon(url, body) : void 0,
|
|
4777
4870
|
onSessionId: (id) => {
|
|
4778
4871
|
this.analyticsFeedbackSessionId = id;
|
|
4872
|
+
},
|
|
4873
|
+
onSessionReset: () => {
|
|
4874
|
+
this.analyticsFeedbackSessionId = void 0;
|
|
4779
4875
|
}
|
|
4780
4876
|
}) : createDryRunSink((...a) => this.logger.log(...a));
|
|
4781
4877
|
const device = config.device ?? collectDeviceInfo(config.appVersion);
|
|
@@ -4784,6 +4880,7 @@ var DeepdotsPopups = class {
|
|
|
4784
4880
|
navigator: typeof navigator !== "undefined" ? navigator : void 0,
|
|
4785
4881
|
intl: typeof Intl !== "undefined" ? Intl : void 0
|
|
4786
4882
|
});
|
|
4883
|
+
this.messageGuard.reset();
|
|
4787
4884
|
this.analytics = new AnalyticsManager({
|
|
4788
4885
|
sink: analyticsSink,
|
|
4789
4886
|
publicKey: config.analytics?.publicKey ?? this.config.apiKey,
|
|
@@ -4819,7 +4916,7 @@ var DeepdotsPopups = class {
|
|
|
4819
4916
|
this.engagement = new EngagementTracker();
|
|
4820
4917
|
this.engagement.resume();
|
|
4821
4918
|
this.setupAnalyticsFlush();
|
|
4822
|
-
this.
|
|
4919
|
+
this.openSession();
|
|
4823
4920
|
const pendingCrashes = this.crashReporter.drainPendingCrashes();
|
|
4824
4921
|
if (this.tracking?.isTrackingEnabled()) {
|
|
4825
4922
|
for (const rec of pendingCrashes) {
|
|
@@ -4851,7 +4948,9 @@ var DeepdotsPopups = class {
|
|
|
4851
4948
|
}
|
|
4852
4949
|
/** Activa/desactiva el tracking (identidad + sesión + analytics). Kill-switch del contrato §7bis. */
|
|
4853
4950
|
setTrackingEnabled(enabled) {
|
|
4951
|
+
if (!enabled) this.closeSession("tracking_disabled");
|
|
4854
4952
|
this.tracking?.setTrackingEnabled(enabled);
|
|
4953
|
+
if (enabled) this.openSession();
|
|
4855
4954
|
this.log("tracking \xB7 setTrackingEnabled:", enabled, "\xB7 session_id:", this.tracking?.getSessionId() ?? null);
|
|
4856
4955
|
}
|
|
4857
4956
|
// ───────── Analytics (canal separado del feedback, vinculado por user_id) ─────────
|
|
@@ -4921,8 +5020,18 @@ var DeepdotsPopups = class {
|
|
|
4921
5020
|
trackFunnelStep(funnel, step, taskId, params) {
|
|
4922
5021
|
this.track("deepdots_funnel_step", { funnel, step, task_id: taskId, ...params ?? {} });
|
|
4923
5022
|
}
|
|
4924
|
-
/**
|
|
5023
|
+
/**
|
|
5024
|
+
* Messaging (#18–22): registra una etapa del funnel de una notificación (push/in-app).
|
|
5025
|
+
* No-op si tracking off. El evento se descarta (con warning) si el `channel` no es válido,
|
|
5026
|
+
* si ese `(message_id, stage)` ya se emitió en la sesión, o si el `message_id` ya se reportó
|
|
5027
|
+
* en otro canal — ver `MessageGuard`.
|
|
5028
|
+
*/
|
|
4925
5029
|
trackMessage(stage, options) {
|
|
5030
|
+
const verdict = this.messageGuard.evaluate(stage, options);
|
|
5031
|
+
if (!verdict.emit) {
|
|
5032
|
+
console.warn(`[DeepdotsPopups] trackMessage descartado (${verdict.reason}): ${verdict.detail}`);
|
|
5033
|
+
return;
|
|
5034
|
+
}
|
|
4926
5035
|
this.track("deepdots_message", buildMessageParams(stage, options));
|
|
4927
5036
|
}
|
|
4928
5037
|
/**
|
|
@@ -4938,21 +5047,86 @@ var DeepdotsPopups = class {
|
|
|
4938
5047
|
this.navObserver.visit(name);
|
|
4939
5048
|
}
|
|
4940
5049
|
}
|
|
4941
|
-
/** App a foreground (RN: AppState 'active'): reanuda el engagement time. */
|
|
5050
|
+
/** App a foreground (RN: AppState 'active'): reanuda el engagement time y abre sesión nueva. */
|
|
4942
5051
|
onForeground() {
|
|
4943
5052
|
this.engagement?.resume();
|
|
5053
|
+
this.openSession();
|
|
4944
5054
|
}
|
|
4945
5055
|
/**
|
|
4946
|
-
* App a background (RN: AppState 'background'):
|
|
4947
|
-
* mini-
|
|
5056
|
+
* App a background (RN: AppState 'background'): FIN DE SESIÓN. Cierra pantalla actual
|
|
5057
|
+
* (page_view), mini-services (mini_service_exit), emite engagement + `session_end` y
|
|
5058
|
+
* envía el último lote con `completed:true`.
|
|
5059
|
+
*
|
|
5060
|
+
* Es la única señal disponible en móvil: el kill de la app (swipe) NO da callback.
|
|
4948
5061
|
*/
|
|
4949
5062
|
onBackground() {
|
|
5063
|
+
this.closeSession("background");
|
|
5064
|
+
this.engagement?.pause();
|
|
5065
|
+
}
|
|
5066
|
+
/**
|
|
5067
|
+
* Cierre EXPLÍCITO de la sesión por parte del host (logout, fin de flujo…): emite
|
|
5068
|
+
* `session_end` y envía el último lote con `completed:true`.
|
|
5069
|
+
*/
|
|
5070
|
+
endSession() {
|
|
5071
|
+
this.closeSession("manual");
|
|
5072
|
+
}
|
|
5073
|
+
/**
|
|
5074
|
+
* Cambio de usuario (login / logout / cambio de cuenta). Cierra la sesión del usuario
|
|
5075
|
+
* anterior (`session_end` con `reason: 'user_change'` + `completed:true`), cambia la
|
|
5076
|
+
* identidad y abre una sesión nueva. Sin `userId` vuelve al id anónimo del SDK.
|
|
5077
|
+
*
|
|
5078
|
+
* Equivale a llamar a `init()` otra vez con otro `userId`.
|
|
5079
|
+
*/
|
|
5080
|
+
setUserId(userId) {
|
|
5081
|
+
const next = userId || void 0;
|
|
5082
|
+
if (!this.initialized || !this.config || next === this.config.userId) return;
|
|
5083
|
+
this.closeSession("user_change");
|
|
5084
|
+
const enabled = this.tracking?.isTrackingEnabled() ?? true;
|
|
5085
|
+
const storage = this.storage ?? createDefaultStorage();
|
|
5086
|
+
this.config.userId = next;
|
|
5087
|
+
this.tracking = new TrackingManager({ storage, clientUserId: next, enabled });
|
|
5088
|
+
this.contact = next ? new ContactManager({
|
|
5089
|
+
storage,
|
|
5090
|
+
publicKey: this.config.apiKey ?? "",
|
|
5091
|
+
userId: next,
|
|
5092
|
+
post: (body) => this.postContact(body)
|
|
5093
|
+
}) : null;
|
|
5094
|
+
this.analytics?.resetUserScope();
|
|
5095
|
+
this.messageGuard.reset();
|
|
5096
|
+
this.log("tracking \xB7 user_change \xB7 user_id:", this.tracking.getUserId());
|
|
5097
|
+
this.openSession();
|
|
5098
|
+
}
|
|
5099
|
+
/**
|
|
5100
|
+
* Cierra la sesión actual: vuelca todo lo que quedaba abierto y envía el último lote
|
|
5101
|
+
* marcado con `completed:true`, para que el backend cierre el registro. El `sessionId`
|
|
5102
|
+
* cacheado se olvida → el lote siguiente abre un registro nuevo.
|
|
5103
|
+
*
|
|
5104
|
+
* Idempotente: dos cierres seguidos (p. ej. `visibilitychange` + `pagehide`) no duplican
|
|
5105
|
+
* el `session_end`.
|
|
5106
|
+
*/
|
|
5107
|
+
closeSession(reason) {
|
|
5108
|
+
if (!this.sessionOpen) return;
|
|
5109
|
+
if (!this.tracking?.isTrackingEnabled()) return;
|
|
5110
|
+
this.sessionOpen = false;
|
|
4950
5111
|
this.navObserver?.stop();
|
|
4951
5112
|
this.navStarted = false;
|
|
4952
|
-
|
|
5113
|
+
this.analytics?.exitAllMiniServices();
|
|
4953
5114
|
this.flushEngagement();
|
|
4954
|
-
this.
|
|
4955
|
-
this.flushAnalytics();
|
|
5115
|
+
this.track("deepdots_session_end", { reason });
|
|
5116
|
+
this.flushAnalytics({ final: reason === "page_hide", sessionEnd: true });
|
|
5117
|
+
this.analyticsFeedbackSessionId = void 0;
|
|
5118
|
+
this.tracking?.setSessionId(null);
|
|
5119
|
+
this.log("tracking \xB7 session_end:", reason);
|
|
5120
|
+
}
|
|
5121
|
+
/**
|
|
5122
|
+
* Abre sesión (`session_start`) si no hay una abierta y el tracking está activo. Idempotente:
|
|
5123
|
+
* se llama en init(), al volver a foreground y al conceder el consentimiento.
|
|
5124
|
+
*/
|
|
5125
|
+
openSession() {
|
|
5126
|
+
if (this.sessionOpen) return;
|
|
5127
|
+
if (!this.tracking?.isTrackingEnabled()) return;
|
|
5128
|
+
this.sessionOpen = true;
|
|
5129
|
+
this.track("deepdots_session_start", {});
|
|
4956
5130
|
}
|
|
4957
5131
|
/** Payload que se ENVIARÍA al endpoint de analytics (no envía ni vacía el buffer). */
|
|
4958
5132
|
previewAnalytics() {
|
|
@@ -4990,10 +5164,7 @@ var DeepdotsPopups = class {
|
|
|
4990
5164
|
this.analyticsFlushTimer = setInterval(() => this.flushAnalytics(), ANALYTICS_FLUSH_INTERVAL_MS);
|
|
4991
5165
|
window.addEventListener("pagehide", () => {
|
|
4992
5166
|
clearInterval(this.analyticsFlushTimer);
|
|
4993
|
-
this.
|
|
4994
|
-
if (this.tracking?.isTrackingEnabled()) this.analytics?.exitAllMiniServices();
|
|
4995
|
-
this.flushEngagement();
|
|
4996
|
-
this.flushAnalytics({ final: true });
|
|
5167
|
+
this.closeSession("page_hide");
|
|
4997
5168
|
});
|
|
4998
5169
|
document.addEventListener("visibilitychange", () => {
|
|
4999
5170
|
if (document.visibilityState === "hidden") {
|
|
@@ -5671,7 +5842,8 @@ function setupReactNative(sdk, config, deps = {}) {
|
|
|
5671
5842
|
if (deps.appState?.addEventListener) {
|
|
5672
5843
|
subscription = deps.appState.addEventListener("change", (state) => {
|
|
5673
5844
|
if (state === "active") sdk.onForeground();
|
|
5674
|
-
else sdk.onBackground();
|
|
5845
|
+
else if (state === "background") sdk.onBackground();
|
|
5846
|
+
else sdk.flushAnalytics();
|
|
5675
5847
|
});
|
|
5676
5848
|
}
|
|
5677
5849
|
return () => {
|