@magicfeedback/popup-sdk 1.1.3 → 1.1.5
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/assets/assets/style.css +10 -1
- package/dist/{chunk-6WTOQGET.mjs → chunk-7TOLYAFL.mjs} +35 -2
- package/dist/chunk-7TOLYAFL.mjs.map +1 -0
- package/dist/{chunk-IOAZQPZE.mjs → chunk-LSTKGFU7.mjs} +35 -2
- package/dist/chunk-LSTKGFU7.mjs.map +1 -0
- package/dist/index.d.mts +38 -2
- package/dist/index.d.ts +38 -2
- package/dist/index.js +98 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +34 -8
- package/dist/index.mjs.map +1 -1
- package/dist/react-native.d.mts +34 -1
- package/dist/react-native.d.ts +34 -1
- package/dist/react-native.js +97 -6
- package/dist/react-native.js.map +1 -1
- package/dist/react-native.mjs +33 -8
- package/dist/react-native.mjs.map +1 -1
- package/dist/{renderPopup-YLFRGPQN.mjs → renderPopup-MES7IINW.mjs} +34 -3
- package/dist/renderPopup-MES7IINW.mjs.map +1 -0
- package/dist/{renderPopup-KBDWWSUU.mjs → renderPopup-YNH2TD3B.mjs} +34 -3
- package/dist/renderPopup-YNH2TD3B.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-6WTOQGET.mjs.map +0 -1
- package/dist/chunk-IOAZQPZE.mjs.map +0 -1
- package/dist/renderPopup-KBDWWSUU.mjs.map +0 -1
- package/dist/renderPopup-YLFRGPQN.mjs.map +0 -1
package/dist/react-native.d.mts
CHANGED
|
@@ -195,12 +195,21 @@ interface PopupActions {
|
|
|
195
195
|
start?: PopupActionStart;
|
|
196
196
|
back?: PopupActionDecline;
|
|
197
197
|
}
|
|
198
|
+
/** Fuente personalizable del popup + survey (viene de la API por popup) */
|
|
199
|
+
interface PopupFont {
|
|
200
|
+
/** Nombre de la familia. Ej: "Inter" (nombre limpio, NO un stack). */
|
|
201
|
+
family: string;
|
|
202
|
+
/** Opcional: URL a un archivo de fuente (woff2/ttf/otf) para armar un @font-face. */
|
|
203
|
+
url?: string;
|
|
204
|
+
}
|
|
198
205
|
/** Configurable popup styles */
|
|
199
206
|
interface PopupStyle {
|
|
200
207
|
theme: 'light' | 'dark';
|
|
201
208
|
position: 'bottom' | 'bottom-right' | 'bottom-left' | 'top' | 'top-right' | 'top-left' | 'center';
|
|
202
209
|
/** @deprecated Not used by the SDK; kept only for API compatibility */
|
|
203
210
|
imageUrl?: string | null;
|
|
211
|
+
/** Fuente personalizada. Si falta, se mantiene el comportamiento actual. */
|
|
212
|
+
font?: PopupFont;
|
|
204
213
|
}
|
|
205
214
|
|
|
206
215
|
interface PopupRenderer {
|
|
@@ -240,6 +249,28 @@ interface ReactNativeErrorUtils {
|
|
|
240
249
|
setGlobalHandler(handler: (error: unknown, isFatal?: boolean) => void): void;
|
|
241
250
|
}
|
|
242
251
|
|
|
252
|
+
/**
|
|
253
|
+
* Messaging (#18–22): tracking de notificaciones del host (push / in-app).
|
|
254
|
+
* Un único evento reservado `deepdots_message` con un campo `stage` discriminador;
|
|
255
|
+
* el funnel se correlaciona por `message_id` y se agrupa por `message_title`.
|
|
256
|
+
*/
|
|
257
|
+
type MessageStage = 'delivered' | 'clicked' | 'converted';
|
|
258
|
+
interface TrackMessageOptions {
|
|
259
|
+
/** Identificador del mensaje/notificación — correlaciona el funnel delivered→clicked→converted. */
|
|
260
|
+
id: string;
|
|
261
|
+
/** Título del mensaje — dimensión de agrupación de #18–22. */
|
|
262
|
+
title: string;
|
|
263
|
+
/** Canal de entrega. */
|
|
264
|
+
channel: 'push' | 'in_app';
|
|
265
|
+
/** Nombre de la campaña (opcional). */
|
|
266
|
+
campaign?: string;
|
|
267
|
+
/** Valor de conversión (opcional, típico en `stage: 'converted'`). */
|
|
268
|
+
value?: number;
|
|
269
|
+
currency?: string;
|
|
270
|
+
/** Parámetros libres adicionales. */
|
|
271
|
+
params?: Record<string, unknown>;
|
|
272
|
+
}
|
|
273
|
+
|
|
243
274
|
/**
|
|
244
275
|
* Main class for managing survey popups
|
|
245
276
|
*/
|
|
@@ -311,6 +342,8 @@ declare class DeepdotsPopups {
|
|
|
311
342
|
trackFindabilityFriction(frictionTopic: string, params?: Record<string, unknown>): void;
|
|
312
343
|
/** Funnel: un paso del embudo, correlacionado por `taskId`. El backend reconstruye conversión/drop-off/tiempo. */
|
|
313
344
|
trackFunnelStep(funnel: string, step: string, taskId: string, params?: Record<string, unknown>): void;
|
|
345
|
+
/** Messaging (#18–22): registra una etapa del funnel de una notificación (push/in-app). No-op si tracking off. */
|
|
346
|
+
trackMessage(stage: MessageStage, options: TrackMessageOptions): void;
|
|
314
347
|
/**
|
|
315
348
|
* Navegación MANUAL (entornos sin History API, p. ej. React Native con React Navigation).
|
|
316
349
|
* El host la llama en cada cambio de pantalla; emite `page_view` al salir de la anterior.
|
|
@@ -485,7 +518,7 @@ declare class ReactNativePopupRenderer implements PopupRenderer {
|
|
|
485
518
|
private partialEmitted;
|
|
486
519
|
constructor(options?: ReactNativeRendererOptions);
|
|
487
520
|
init(): void;
|
|
488
|
-
show(surveyId: string, productId: string, _actions: PopupActions | undefined, emit: EmitFn, onClose: () => void, env?: string, userId?: string,
|
|
521
|
+
show(surveyId: string, productId: string, _actions: PopupActions | undefined, emit: EmitFn, onClose: () => void, env?: string, userId?: string, style?: PopupStyle, sessionId?: string, miniService?: string, analyticsFeedbackSessionId?: string): void;
|
|
489
522
|
hide(): void;
|
|
490
523
|
/**
|
|
491
524
|
* El host la conecta al `onMessage` del WebView. Traduce los mensajes del survey a
|
package/dist/react-native.d.ts
CHANGED
|
@@ -195,12 +195,21 @@ interface PopupActions {
|
|
|
195
195
|
start?: PopupActionStart;
|
|
196
196
|
back?: PopupActionDecline;
|
|
197
197
|
}
|
|
198
|
+
/** Fuente personalizable del popup + survey (viene de la API por popup) */
|
|
199
|
+
interface PopupFont {
|
|
200
|
+
/** Nombre de la familia. Ej: "Inter" (nombre limpio, NO un stack). */
|
|
201
|
+
family: string;
|
|
202
|
+
/** Opcional: URL a un archivo de fuente (woff2/ttf/otf) para armar un @font-face. */
|
|
203
|
+
url?: string;
|
|
204
|
+
}
|
|
198
205
|
/** Configurable popup styles */
|
|
199
206
|
interface PopupStyle {
|
|
200
207
|
theme: 'light' | 'dark';
|
|
201
208
|
position: 'bottom' | 'bottom-right' | 'bottom-left' | 'top' | 'top-right' | 'top-left' | 'center';
|
|
202
209
|
/** @deprecated Not used by the SDK; kept only for API compatibility */
|
|
203
210
|
imageUrl?: string | null;
|
|
211
|
+
/** Fuente personalizada. Si falta, se mantiene el comportamiento actual. */
|
|
212
|
+
font?: PopupFont;
|
|
204
213
|
}
|
|
205
214
|
|
|
206
215
|
interface PopupRenderer {
|
|
@@ -240,6 +249,28 @@ interface ReactNativeErrorUtils {
|
|
|
240
249
|
setGlobalHandler(handler: (error: unknown, isFatal?: boolean) => void): void;
|
|
241
250
|
}
|
|
242
251
|
|
|
252
|
+
/**
|
|
253
|
+
* Messaging (#18–22): tracking de notificaciones del host (push / in-app).
|
|
254
|
+
* Un único evento reservado `deepdots_message` con un campo `stage` discriminador;
|
|
255
|
+
* el funnel se correlaciona por `message_id` y se agrupa por `message_title`.
|
|
256
|
+
*/
|
|
257
|
+
type MessageStage = 'delivered' | 'clicked' | 'converted';
|
|
258
|
+
interface TrackMessageOptions {
|
|
259
|
+
/** Identificador del mensaje/notificación — correlaciona el funnel delivered→clicked→converted. */
|
|
260
|
+
id: string;
|
|
261
|
+
/** Título del mensaje — dimensión de agrupación de #18–22. */
|
|
262
|
+
title: string;
|
|
263
|
+
/** Canal de entrega. */
|
|
264
|
+
channel: 'push' | 'in_app';
|
|
265
|
+
/** Nombre de la campaña (opcional). */
|
|
266
|
+
campaign?: string;
|
|
267
|
+
/** Valor de conversión (opcional, típico en `stage: 'converted'`). */
|
|
268
|
+
value?: number;
|
|
269
|
+
currency?: string;
|
|
270
|
+
/** Parámetros libres adicionales. */
|
|
271
|
+
params?: Record<string, unknown>;
|
|
272
|
+
}
|
|
273
|
+
|
|
243
274
|
/**
|
|
244
275
|
* Main class for managing survey popups
|
|
245
276
|
*/
|
|
@@ -311,6 +342,8 @@ declare class DeepdotsPopups {
|
|
|
311
342
|
trackFindabilityFriction(frictionTopic: string, params?: Record<string, unknown>): void;
|
|
312
343
|
/** Funnel: un paso del embudo, correlacionado por `taskId`. El backend reconstruye conversión/drop-off/tiempo. */
|
|
313
344
|
trackFunnelStep(funnel: string, step: string, taskId: string, params?: Record<string, unknown>): void;
|
|
345
|
+
/** Messaging (#18–22): registra una etapa del funnel de una notificación (push/in-app). No-op si tracking off. */
|
|
346
|
+
trackMessage(stage: MessageStage, options: TrackMessageOptions): void;
|
|
314
347
|
/**
|
|
315
348
|
* Navegación MANUAL (entornos sin History API, p. ej. React Native con React Navigation).
|
|
316
349
|
* El host la llama en cada cambio de pantalla; emite `page_view` al salir de la anterior.
|
|
@@ -485,7 +518,7 @@ declare class ReactNativePopupRenderer implements PopupRenderer {
|
|
|
485
518
|
private partialEmitted;
|
|
486
519
|
constructor(options?: ReactNativeRendererOptions);
|
|
487
520
|
init(): void;
|
|
488
|
-
show(surveyId: string, productId: string, _actions: PopupActions | undefined, emit: EmitFn, onClose: () => void, env?: string, userId?: string,
|
|
521
|
+
show(surveyId: string, productId: string, _actions: PopupActions | undefined, emit: EmitFn, onClose: () => void, env?: string, userId?: string, style?: PopupStyle, sessionId?: string, miniService?: string, analyticsFeedbackSessionId?: string): void;
|
|
489
522
|
hide(): void;
|
|
490
523
|
/**
|
|
491
524
|
* El host la conecta al `onMessage` del WebView. Traduce los mensajes del survey a
|
package/dist/react-native.js
CHANGED
|
@@ -141,6 +141,43 @@ var init_tracking_manager = __esm({
|
|
|
141
141
|
}
|
|
142
142
|
});
|
|
143
143
|
|
|
144
|
+
// src/ui/font.ts
|
|
145
|
+
function fontFormatFromUrl(url) {
|
|
146
|
+
const clean = url.split("?")[0].split("#")[0];
|
|
147
|
+
const ext = clean.slice(clean.lastIndexOf(".") + 1).toLowerCase();
|
|
148
|
+
return FORMAT_BY_EXT[ext];
|
|
149
|
+
}
|
|
150
|
+
function sanitizeFamily(family) {
|
|
151
|
+
return family.replace(/[^A-Za-z0-9 ._-]/g, "").trim();
|
|
152
|
+
}
|
|
153
|
+
function isSafeFontUrl(url) {
|
|
154
|
+
if (!/^(https?:|data:)/i.test(url)) return false;
|
|
155
|
+
return !/[\x00-\x20"<>\\]/.test(url);
|
|
156
|
+
}
|
|
157
|
+
function buildFontFamilyValue(family) {
|
|
158
|
+
const safe = sanitizeFamily(family);
|
|
159
|
+
return `"${safe}", -apple-system, system-ui, sans-serif`;
|
|
160
|
+
}
|
|
161
|
+
function buildFontFaceCss(family, url) {
|
|
162
|
+
if (!url || !isSafeFontUrl(url)) return "";
|
|
163
|
+
const safe = sanitizeFamily(family);
|
|
164
|
+
const fmt = fontFormatFromUrl(url);
|
|
165
|
+
const src = fmt ? `url("${url}") format("${fmt}")` : `url("${url}")`;
|
|
166
|
+
return `@font-face{font-family:"${safe}";src:${src};font-display:swap;}`;
|
|
167
|
+
}
|
|
168
|
+
var FORMAT_BY_EXT;
|
|
169
|
+
var init_font = __esm({
|
|
170
|
+
"src/ui/font.ts"() {
|
|
171
|
+
"use strict";
|
|
172
|
+
FORMAT_BY_EXT = {
|
|
173
|
+
woff2: "woff2",
|
|
174
|
+
woff: "woff",
|
|
175
|
+
ttf: "truetype",
|
|
176
|
+
otf: "opentype"
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
144
181
|
// src/assets/style.css
|
|
145
182
|
var style_default;
|
|
146
183
|
var init_style = __esm({
|
|
@@ -285,13 +322,22 @@ var init_style = __esm({
|
|
|
285
322
|
padding: 0;
|
|
286
323
|
font-weight: 900;
|
|
287
324
|
color: #222;
|
|
288
|
-
font-family: 'Montserrat';
|
|
325
|
+
font-family: var(--deepdots-font, 'Montserrat');
|
|
289
326
|
font-size: 18px;
|
|
290
327
|
text-transform: uppercase;
|
|
291
328
|
text-align: center;
|
|
292
329
|
margin-bottom: 40px;
|
|
293
330
|
}
|
|
294
331
|
|
|
332
|
+
/* Los controles de formulario no heredan font-family por defecto: cuando hay
|
|
333
|
+
fuente custom (clase deepdots-has-font), los forzamos a usar la del contenedor. */
|
|
334
|
+
.deepdots-popup.deepdots-has-font button,
|
|
335
|
+
.deepdots-popup.deepdots-has-font input,
|
|
336
|
+
.deepdots-popup.deepdots-has-font textarea,
|
|
337
|
+
.deepdots-popup.deepdots-has-font select {
|
|
338
|
+
font-family: var(--deepdots-font, inherit);
|
|
339
|
+
}
|
|
340
|
+
|
|
295
341
|
.deepdots-popup h6 {
|
|
296
342
|
margin-bottom: 1rem;
|
|
297
343
|
}
|
|
@@ -1064,6 +1110,18 @@ function ensureResponsiveStyles(_popup) {
|
|
|
1064
1110
|
`;
|
|
1065
1111
|
document.head.appendChild(style);
|
|
1066
1112
|
}
|
|
1113
|
+
function ensureFontFace(family, url) {
|
|
1114
|
+
const css = buildFontFaceCss(family, url);
|
|
1115
|
+
if (!css) return;
|
|
1116
|
+
const STYLE_ID = "deepdots-font-face";
|
|
1117
|
+
let el = document.getElementById(STYLE_ID);
|
|
1118
|
+
if (!el) {
|
|
1119
|
+
el = document.createElement("style");
|
|
1120
|
+
el.id = STYLE_ID;
|
|
1121
|
+
document.head.appendChild(el);
|
|
1122
|
+
}
|
|
1123
|
+
if (el.textContent !== css) el.textContent = css;
|
|
1124
|
+
}
|
|
1067
1125
|
async function renderPopup(container, surveyId, productId, actions, emit, onClose, env = "production", userId, style, sessionId, miniService, analyticsFeedbackSessionId) {
|
|
1068
1126
|
let surveyCompletedEmitted = false;
|
|
1069
1127
|
let stylesInjected = false;
|
|
@@ -1099,6 +1157,14 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
1099
1157
|
width: 90%;
|
|
1100
1158
|
min-height: 200px;
|
|
1101
1159
|
`;
|
|
1160
|
+
const font = style?.font;
|
|
1161
|
+
if (font?.family) {
|
|
1162
|
+
const familyValue = buildFontFamilyValue(font.family);
|
|
1163
|
+
popup.style.fontFamily = familyValue;
|
|
1164
|
+
popup.style.setProperty("--deepdots-font", familyValue);
|
|
1165
|
+
popup.classList.add("deepdots-has-font");
|
|
1166
|
+
if (font.url) ensureFontFace(font.family, font.url);
|
|
1167
|
+
}
|
|
1102
1168
|
const header = document.createElement("div");
|
|
1103
1169
|
header.className = "deepdots-popup-header";
|
|
1104
1170
|
header.style.cssText = "display:flex; justify-content:flex-end; align-items:center; width:100%;";
|
|
@@ -1520,6 +1586,7 @@ var init_renderPopup = __esm({
|
|
|
1520
1586
|
"src/ui/renderPopup.ts"() {
|
|
1521
1587
|
"use strict";
|
|
1522
1588
|
init_tracking_manager();
|
|
1589
|
+
init_font();
|
|
1523
1590
|
import_native = __toESM(require("@magicfeedback/native"));
|
|
1524
1591
|
init_style();
|
|
1525
1592
|
}
|
|
@@ -1726,6 +1793,7 @@ function resolveEnvironment(nodeEnv) {
|
|
|
1726
1793
|
init_tracking_manager();
|
|
1727
1794
|
|
|
1728
1795
|
// src/ui/surveyHtml.ts
|
|
1796
|
+
init_font();
|
|
1729
1797
|
function buildSurveyHtml(opts) {
|
|
1730
1798
|
const env = opts.env === "development" ? "dev" : "prod";
|
|
1731
1799
|
const version = opts.version ?? "2.2.4";
|
|
@@ -1734,10 +1802,12 @@ function buildSurveyHtml(opts) {
|
|
|
1734
1802
|
const pid = JSON.stringify(opts.productId);
|
|
1735
1803
|
const profileJson = JSON.stringify(opts.profile ?? []);
|
|
1736
1804
|
const metaJson = JSON.stringify(opts.metadata ?? []);
|
|
1805
|
+
const fontFaceCss = opts.font ? buildFontFaceCss(opts.font.family, opts.font.url) : "";
|
|
1806
|
+
const fontFamilyValue = opts.font ? buildFontFamilyValue(opts.font.family) : "-apple-system,system-ui,sans-serif";
|
|
1737
1807
|
return `<!DOCTYPE html><html><head>
|
|
1738
1808
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
|
1739
1809
|
<meta name="color-scheme" content="light"/>
|
|
1740
|
-
<style
|
|
1810
|
+
<style>${fontFaceCss}html,body{margin:0;padding:0;background:transparent;font-family:${fontFamilyValue}}#mf{width:100%;box-sizing:border-box}#mf *{max-width:100%;box-sizing:border-box}</style>
|
|
1741
1811
|
</head><body>
|
|
1742
1812
|
<div id="mf"></div>
|
|
1743
1813
|
<script>
|
|
@@ -1788,13 +1858,13 @@ var ReactNativePopupRenderer = class {
|
|
|
1788
1858
|
}
|
|
1789
1859
|
init() {
|
|
1790
1860
|
}
|
|
1791
|
-
show(surveyId, productId, _actions, emit, onClose, env = "production", userId,
|
|
1861
|
+
show(surveyId, productId, _actions, emit, onClose, env = "production", userId, style, sessionId, miniService, analyticsFeedbackSessionId) {
|
|
1792
1862
|
this.emitFn = emit;
|
|
1793
1863
|
this.onCloseFn = onClose;
|
|
1794
1864
|
this.currentSurveyId = surveyId;
|
|
1795
1865
|
this.partialEmitted = false;
|
|
1796
1866
|
const { profile, metadata } = buildSurveyIdentity(userId ?? null, sessionId ?? null, miniService ?? null, analyticsFeedbackSessionId ?? null);
|
|
1797
|
-
const html = buildSurveyHtml({ surveyId, productId, env, profile, metadata });
|
|
1867
|
+
const html = buildSurveyHtml({ surveyId, productId, env, profile, metadata, font: style?.font });
|
|
1798
1868
|
if (this.options.onShow) {
|
|
1799
1869
|
this.options.onShow({ surveyId, productId, html });
|
|
1800
1870
|
} else {
|
|
@@ -2460,11 +2530,27 @@ var CrashReporter = class {
|
|
|
2460
2530
|
}
|
|
2461
2531
|
};
|
|
2462
2532
|
|
|
2533
|
+
// src/analytics/messaging.ts
|
|
2534
|
+
function buildMessageParams(stage, o) {
|
|
2535
|
+
const p = {
|
|
2536
|
+
stage,
|
|
2537
|
+
message_id: o.id,
|
|
2538
|
+
message_title: o.title,
|
|
2539
|
+
channel: o.channel
|
|
2540
|
+
};
|
|
2541
|
+
if (o.campaign) p.campaign = o.campaign;
|
|
2542
|
+
if (o.value !== void 0) p.value = o.value;
|
|
2543
|
+
if (o.currency) p.currency = o.currency;
|
|
2544
|
+
return { ...p, ...o.params ?? {} };
|
|
2545
|
+
}
|
|
2546
|
+
|
|
2463
2547
|
// src/core/deepdots-popups.ts
|
|
2464
2548
|
var EXIT_QUEUE_STORAGE_KEY = "__deepdots_exit_popup_queue__";
|
|
2465
2549
|
var DAY_IN_MS = 24 * 60 * 60 * 1e3;
|
|
2466
2550
|
var ANALYTICS_MAX_BATCH_SIZE = 20;
|
|
2467
2551
|
var ANALYTICS_FLUSH_INTERVAL_MS = 3e4;
|
|
2552
|
+
var RESERVED_EVENT_PREFIX = "deepdots_";
|
|
2553
|
+
var CUSTOM_EVENT_PREFIX = "deepdots_event_";
|
|
2468
2554
|
var DeepdotsPopups = class {
|
|
2469
2555
|
constructor() {
|
|
2470
2556
|
this.config = null;
|
|
@@ -2617,8 +2703,9 @@ var DeepdotsPopups = class {
|
|
|
2617
2703
|
/** Registra un evento de analítica (modelo GA: nombre + params). No-op si tracking off. */
|
|
2618
2704
|
track(name, params) {
|
|
2619
2705
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
2620
|
-
|
|
2621
|
-
this.
|
|
2706
|
+
const eventName = name.startsWith(RESERVED_EVENT_PREFIX) ? name : `${CUSTOM_EVENT_PREFIX}${name}`;
|
|
2707
|
+
this.analytics?.track(eventName, params);
|
|
2708
|
+
this.log("analytics \xB7 track:", eventName, params ?? {});
|
|
2622
2709
|
}
|
|
2623
2710
|
/** User attributes para breakdowns (registration_status, pass_type, sector, pass_status…). */
|
|
2624
2711
|
setUserAttributes(attributes) {
|
|
@@ -2670,6 +2757,10 @@ var DeepdotsPopups = class {
|
|
|
2670
2757
|
trackFunnelStep(funnel, step, taskId, params) {
|
|
2671
2758
|
this.track("deepdots_funnel_step", { funnel, step, task_id: taskId, ...params ?? {} });
|
|
2672
2759
|
}
|
|
2760
|
+
/** Messaging (#18–22): registra una etapa del funnel de una notificación (push/in-app). No-op si tracking off. */
|
|
2761
|
+
trackMessage(stage, options) {
|
|
2762
|
+
this.track("deepdots_message", buildMessageParams(stage, options));
|
|
2763
|
+
}
|
|
2673
2764
|
/**
|
|
2674
2765
|
* Navegación MANUAL (entornos sin History API, p. ej. React Native con React Navigation).
|
|
2675
2766
|
* El host la llama en cada cambio de pantalla; emite `page_view` al salir de la anterior.
|