@magicfeedback/popup-sdk 1.9.0 → 1.11.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/README.md +23 -1
- package/dist/assets/assets/style.css +303 -1
- package/dist/{chunk-MTV7T4OW.mjs → chunk-GXEPYT33.mjs} +304 -2
- package/dist/{chunk-MTV7T4OW.mjs.map → chunk-GXEPYT33.mjs.map} +1 -1
- package/dist/{chunk-AKVMUQXO.mjs → chunk-TSORXOQG.mjs} +304 -2
- package/dist/{chunk-AKVMUQXO.mjs.map → chunk-TSORXOQG.mjs.map} +1 -1
- package/dist/index.d.mts +57 -1
- package/dist/index.d.ts +57 -1
- package/dist/index.js +1752 -898
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +229 -16
- package/dist/index.mjs.map +1 -1
- package/dist/react-native.d.mts +52 -1
- package/dist/react-native.d.ts +52 -1
- package/dist/react-native.js +556 -21
- package/dist/react-native.js.map +1 -1
- package/dist/react-native.mjs +230 -16
- package/dist/react-native.mjs.map +1 -1
- package/dist/{renderPopup-LEM2MLWT.mjs → renderPopup-JF6IB6HL.mjs} +29 -10
- package/dist/renderPopup-JF6IB6HL.mjs.map +1 -0
- package/dist/{renderPopup-LEGTSBLD.mjs → renderPopup-KSZJ5AN4.mjs} +1231 -892
- package/dist/renderPopup-KSZJ5AN4.mjs.map +1 -0
- package/package.json +5 -3
- package/dist/renderPopup-LEGTSBLD.mjs.map +0 -1
- package/dist/renderPopup-LEM2MLWT.mjs.map +0 -1
package/dist/react-native.js
CHANGED
|
@@ -552,7 +552,7 @@ var init_style = __esm({
|
|
|
552
552
|
style_default = `/**
|
|
553
553
|
* NO EDITAR A MANO \u2014 generado por scripts/vendor-survey-css.mjs
|
|
554
554
|
*
|
|
555
|
-
* Copia de @magicfeedback/native@2.2.
|
|
555
|
+
* Copia de @magicfeedback/native@2.2.31 \u2192 dist/styles/magicfeedback-default.css
|
|
556
556
|
* (la hoja can\xF3nica del paquete, la que apunta su campo "style"), con estos cambios locales:
|
|
557
557
|
*
|
|
558
558
|
* 1. Sin el @import de Google Fonts (Nunito): no queremos una petici\xF3n externa desde la
|
|
@@ -1536,6 +1536,9 @@ div.magicfeedback-radio {
|
|
|
1536
1536
|
width: 100%;
|
|
1537
1537
|
overflow-x: auto;
|
|
1538
1538
|
border-radius: var(--mf-radius-md);
|
|
1539
|
+
/* the layout follows the space the survey gets (a pop-in, a sidebar),
|
|
1540
|
+
not the size of the window */
|
|
1541
|
+
container-type: inline-size;
|
|
1539
1542
|
}
|
|
1540
1543
|
|
|
1541
1544
|
.magicfeedback-multi-question-matrix-table {
|
|
@@ -1587,6 +1590,296 @@ div.magicfeedback-radio {
|
|
|
1587
1590
|
accent-color: var(--mf-primary);
|
|
1588
1591
|
}
|
|
1589
1592
|
|
|
1593
|
+
.magicfeedback-multi-question-matrix-question {
|
|
1594
|
+
min-width: 200px;
|
|
1595
|
+
padding: 10px;
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
/* the column header names the option in a table; the per-cell label is for
|
|
1599
|
+
screen readers until the rows stack */
|
|
1600
|
+
.magicfeedback-multi-question-matrix-option-label {
|
|
1601
|
+
position: absolute;
|
|
1602
|
+
width: 1px;
|
|
1603
|
+
height: 1px;
|
|
1604
|
+
overflow: hidden;
|
|
1605
|
+
clip: rect(0, 0, 0, 0);
|
|
1606
|
+
white-space: nowrap;
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
/* narrow container: every row becomes its own question with the options
|
|
1610
|
+
stacked underneath, instead of a table cut off at the side */
|
|
1611
|
+
@container (max-width: 599px) {
|
|
1612
|
+
.magicfeedback-multi-question-matrix-table,
|
|
1613
|
+
.magicfeedback-multi-question-matrix-table tbody,
|
|
1614
|
+
.magicfeedback-multi-question-matrix-table tr,
|
|
1615
|
+
.magicfeedback-multi-question-matrix-table td {
|
|
1616
|
+
display: block;
|
|
1617
|
+
width: 100%;
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
.magicfeedback-multi-question-matrix-table thead {
|
|
1621
|
+
display: none;
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
.magicfeedback-multi-question-matrix-row-tr,
|
|
1625
|
+
.magicfeedback-multi-question-matrix-row-tr:nth-child(even) {
|
|
1626
|
+
margin-bottom: var(--mf-space-lg);
|
|
1627
|
+
background: transparent;
|
|
1628
|
+
box-shadow: none;
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
@media (hover: hover) {
|
|
1632
|
+
.magicfeedback-multi-question-matrix-row-tr:hover {
|
|
1633
|
+
margin-bottom: var(--mf-space-lg);
|
|
1634
|
+
background: transparent;
|
|
1635
|
+
box-shadow: none;
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
.magicfeedback-multi-question-matrix-table td.magicfeedback-multi-question-matrix-question {
|
|
1640
|
+
min-width: 0;
|
|
1641
|
+
padding: 0 0 var(--mf-space-sm);
|
|
1642
|
+
text-align: start;
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
.magicfeedback-multi-question-matrix-question .magicfeedback-multi-question-matrix-label {
|
|
1646
|
+
font-weight: 600;
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1649
|
+
.magicfeedback-multi-question-matrix-table td.magicfeedback-multi-question-matrix-option {
|
|
1650
|
+
display: flex;
|
|
1651
|
+
align-items: center;
|
|
1652
|
+
gap: var(--mf-space-sm);
|
|
1653
|
+
margin-bottom: var(--mf-space-sm);
|
|
1654
|
+
padding: 0 var(--mf-space-md);
|
|
1655
|
+
text-align: start;
|
|
1656
|
+
background: var(--mf-bg-secondary);
|
|
1657
|
+
border: 2px solid var(--mf-border);
|
|
1658
|
+
border-radius: var(--mf-radius-md);
|
|
1659
|
+
cursor: pointer;
|
|
1660
|
+
transition: var(--mf-transition-fast);
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
@media (hover: hover) {
|
|
1664
|
+
.magicfeedback-multi-question-matrix-option:hover {
|
|
1665
|
+
border-color: var(--mf-primary-border);
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
.magicfeedback-multi-question-matrix-option:has(input:checked) {
|
|
1670
|
+
border-color: var(--mf-primary);
|
|
1671
|
+
background: var(--mf-primary-light);
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
.magicfeedback-multi-question-matrix-table .magicfeedback-multi-question-matrix-option input[type="radio"] {
|
|
1675
|
+
flex-shrink: 0;
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
/* the whole card is the click target */
|
|
1679
|
+
.magicfeedback-multi-question-matrix-option-label {
|
|
1680
|
+
position: static;
|
|
1681
|
+
flex: 1;
|
|
1682
|
+
width: auto;
|
|
1683
|
+
height: auto;
|
|
1684
|
+
padding: var(--mf-space-md) 0;
|
|
1685
|
+
overflow: visible;
|
|
1686
|
+
clip: auto;
|
|
1687
|
+
white-space: normal;
|
|
1688
|
+
font-size: var(--mf-font-size-base);
|
|
1689
|
+
color: var(--mf-text-primary);
|
|
1690
|
+
cursor: pointer;
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
/* ========================================
|
|
1695
|
+
MAXDIFF (BEST\u2013WORST)
|
|
1696
|
+
======================================== */
|
|
1697
|
+
.magicfeedback-max-diff {
|
|
1698
|
+
/* "least" gets its own hue, apart from the brand color "most" uses; dark
|
|
1699
|
+
enough to read as header text and to hold the white center dot */
|
|
1700
|
+
--mf-max-diff-worst: #b45309;
|
|
1701
|
+
width: 100%;
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
.magicfeedback-max-diff-container {
|
|
1705
|
+
width: 100%;
|
|
1706
|
+
/* the layout follows the space the survey gets (a pop-in, a sidebar),
|
|
1707
|
+
not the size of the window */
|
|
1708
|
+
container-type: inline-size;
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
.magicfeedback-max-diff-progress {
|
|
1712
|
+
display: flex;
|
|
1713
|
+
align-items: center;
|
|
1714
|
+
gap: var(--mf-space-md);
|
|
1715
|
+
margin-bottom: var(--mf-space-lg);
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
.magicfeedback-max-diff-progress-label {
|
|
1719
|
+
font-size: 0.8125rem;
|
|
1720
|
+
font-weight: var(--mf-font-weight-bold);
|
|
1721
|
+
letter-spacing: 0.04em;
|
|
1722
|
+
text-transform: uppercase;
|
|
1723
|
+
white-space: nowrap;
|
|
1724
|
+
color: var(--mf-text-secondary);
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
.magicfeedback-max-diff-progress-track {
|
|
1728
|
+
flex: 1;
|
|
1729
|
+
height: 6px;
|
|
1730
|
+
overflow: hidden;
|
|
1731
|
+
border-radius: var(--mf-radius-full);
|
|
1732
|
+
background: var(--mf-border);
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
.magicfeedback-max-diff-progress-fill {
|
|
1736
|
+
display: block;
|
|
1737
|
+
height: 100%;
|
|
1738
|
+
border-radius: var(--mf-radius-full);
|
|
1739
|
+
background: var(--mf-primary);
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
.magicfeedback-max-diff-table {
|
|
1743
|
+
width: 100%;
|
|
1744
|
+
border-collapse: collapse;
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
.magicfeedback-max-diff-head,
|
|
1748
|
+
.magicfeedback-max-diff-head-spacer {
|
|
1749
|
+
border-bottom: 1px solid var(--mf-border);
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
.magicfeedback-max-diff-head {
|
|
1753
|
+
width: 11rem;
|
|
1754
|
+
padding: 0 var(--mf-space-sm) var(--mf-space-sm);
|
|
1755
|
+
font-size: 0.8125rem;
|
|
1756
|
+
font-weight: var(--mf-font-weight-extrabold);
|
|
1757
|
+
letter-spacing: 0.04em;
|
|
1758
|
+
text-transform: uppercase;
|
|
1759
|
+
text-align: center;
|
|
1760
|
+
vertical-align: bottom;
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
.magicfeedback-max-diff-head-best {
|
|
1764
|
+
color: var(--mf-primary);
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
.magicfeedback-max-diff-head-worst {
|
|
1768
|
+
color: var(--mf-max-diff-worst);
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
.magicfeedback-max-diff-head-label {
|
|
1772
|
+
display: inline-flex;
|
|
1773
|
+
align-items: center;
|
|
1774
|
+
gap: 6px;
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
.magicfeedback-max-diff-row {
|
|
1778
|
+
transition: background-color 0.15s ease;
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
.magicfeedback-max-diff-row > * {
|
|
1782
|
+
border-bottom: 1px solid var(--mf-border);
|
|
1783
|
+
}
|
|
1784
|
+
|
|
1785
|
+
.magicfeedback-max-diff-row:has(.magicfeedback-max-diff-best:checked) {
|
|
1786
|
+
background-color: color-mix(in srgb, var(--mf-primary) 8%, transparent);
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
.magicfeedback-max-diff-row:has(.magicfeedback-max-diff-worst:checked) {
|
|
1790
|
+
background-color: color-mix(in srgb, var(--mf-max-diff-worst) 8%, transparent);
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
.magicfeedback-max-diff-item {
|
|
1794
|
+
padding: var(--mf-space-md) var(--mf-space-sm);
|
|
1795
|
+
font-size: 1.0625rem;
|
|
1796
|
+
font-weight: var(--mf-font-weight-semibold);
|
|
1797
|
+
line-height: 1.4;
|
|
1798
|
+
text-align: center;
|
|
1799
|
+
color: var(--mf-text-primary);
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
.magicfeedback-max-diff-row:has(input:checked) .magicfeedback-max-diff-item {
|
|
1803
|
+
font-weight: var(--mf-font-weight-extrabold);
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
.magicfeedback-max-diff-cell {
|
|
1807
|
+
padding: 0;
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
/* the whole cell is the click target */
|
|
1811
|
+
.magicfeedback-max-diff-pick {
|
|
1812
|
+
display: flex;
|
|
1813
|
+
align-items: center;
|
|
1814
|
+
justify-content: center;
|
|
1815
|
+
min-height: 64px;
|
|
1816
|
+
cursor: pointer;
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
.magicfeedback-max-diff-input {
|
|
1820
|
+
appearance: none;
|
|
1821
|
+
-webkit-appearance: none;
|
|
1822
|
+
flex-shrink: 0;
|
|
1823
|
+
width: 26px;
|
|
1824
|
+
height: 26px;
|
|
1825
|
+
margin: 0;
|
|
1826
|
+
border: 2px solid color-mix(in srgb, var(--mf-text-secondary) 80%, white);
|
|
1827
|
+
border-radius: var(--mf-radius-full);
|
|
1828
|
+
background: var(--mf-bg-secondary);
|
|
1829
|
+
cursor: pointer;
|
|
1830
|
+
transition: var(--mf-transition-fast);
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
@media (hover: hover) {
|
|
1834
|
+
.magicfeedback-max-diff-pick:hover .magicfeedback-max-diff-input {
|
|
1835
|
+
transform: scale(1.08);
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
.magicfeedback-max-diff-best:checked {
|
|
1840
|
+
border-color: var(--mf-primary);
|
|
1841
|
+
background: radial-gradient(circle, #FFFFFF 0 4px, var(--mf-primary) 4.5px);
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
.magicfeedback-max-diff-worst:checked {
|
|
1845
|
+
border-color: var(--mf-max-diff-worst);
|
|
1846
|
+
background: radial-gradient(circle, #FFFFFF 0 4px, var(--mf-max-diff-worst) 4.5px);
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
.magicfeedback-max-diff-input:focus-visible {
|
|
1850
|
+
outline: 3px solid var(--mf-primary-border);
|
|
1851
|
+
outline-offset: 3px;
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
/* narrow container: same table, slimmer pick columns and the header icon
|
|
1855
|
+
stacked over its label */
|
|
1856
|
+
@container (max-width: 599px) {
|
|
1857
|
+
.magicfeedback-max-diff-head {
|
|
1858
|
+
width: 4.75rem;
|
|
1859
|
+
padding-inline: 2px;
|
|
1860
|
+
font-size: 0.6875rem;
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
.magicfeedback-max-diff-head-label {
|
|
1864
|
+
flex-direction: column;
|
|
1865
|
+
gap: 2px;
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
.magicfeedback-max-diff-item {
|
|
1869
|
+
padding-inline: 4px;
|
|
1870
|
+
font-size: var(--mf-font-size-base);
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
.magicfeedback-max-diff-pick {
|
|
1874
|
+
min-height: 68px;
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
.magicfeedback-max-diff-input {
|
|
1878
|
+
width: 28px;
|
|
1879
|
+
height: 28px;
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1590
1883
|
/* ========================================
|
|
1591
1884
|
PRIORITY LIST
|
|
1592
1885
|
======================================== */
|
|
@@ -1777,6 +2070,15 @@ div.magicfeedback-radio {
|
|
|
1777
2070
|
}
|
|
1778
2071
|
}
|
|
1779
2072
|
|
|
2073
|
+
/* hidden on the first page; keep the submit button on the right */
|
|
2074
|
+
.magicfeedback-back[hidden] {
|
|
2075
|
+
display: none;
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2078
|
+
.magicfeedback-action-container .magicfeedback-submit {
|
|
2079
|
+
margin-left: auto;
|
|
2080
|
+
}
|
|
2081
|
+
|
|
1780
2082
|
/* ========================================
|
|
1781
2083
|
SKIP & COUNTER
|
|
1782
2084
|
======================================== */
|
|
@@ -2592,6 +2894,9 @@ function ensureFontFace(family, url) {
|
|
|
2592
2894
|
}
|
|
2593
2895
|
if (el.textContent !== css) el.textContent = css;
|
|
2594
2896
|
}
|
|
2897
|
+
function hasFinePointer() {
|
|
2898
|
+
return typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(pointer: fine)").matches;
|
|
2899
|
+
}
|
|
2595
2900
|
async function renderPopup(container, surveyId, productId, actions, emit, onClose, env = "production", userId, style, sessionId, miniService, analyticsFeedbackSessionId, options) {
|
|
2596
2901
|
let surveyCompletedEmitted = false;
|
|
2597
2902
|
let stylesInjected = false;
|
|
@@ -3022,6 +3327,13 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
3022
3327
|
if (pos.padding) container.style.padding = pos.padding;
|
|
3023
3328
|
let revealed = false;
|
|
3024
3329
|
let revealTimer = null;
|
|
3330
|
+
let surveyLoaded = false;
|
|
3331
|
+
let openFocusDone = false;
|
|
3332
|
+
function autofocusOnOpen() {
|
|
3333
|
+
if (openFocusDone || !revealed || !surveyLoaded) return;
|
|
3334
|
+
openFocusDone = true;
|
|
3335
|
+
if (hasFinePointer()) formInstance?.focusFirstQuestion?.();
|
|
3336
|
+
}
|
|
3025
3337
|
function reveal() {
|
|
3026
3338
|
if (revealed) return;
|
|
3027
3339
|
revealed = true;
|
|
@@ -3032,6 +3344,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
3032
3344
|
if (!container.contains(popup)) return;
|
|
3033
3345
|
container.style.visibility = "visible";
|
|
3034
3346
|
container.style.pointerEvents = "";
|
|
3347
|
+
autofocusOnOpen();
|
|
3035
3348
|
}
|
|
3036
3349
|
function revealWhenPainted2() {
|
|
3037
3350
|
if (revealed) return;
|
|
@@ -3081,10 +3394,18 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
3081
3394
|
// La pantalla final la pinta el popup: `renderSuccess` de @magicfeedback/native usa
|
|
3082
3395
|
// textContent, así que el mensaje de la plataforma (HTML con imagen) no se vería, y
|
|
3083
3396
|
// su fallback es un literal genérico que ignora `style.successMessage`.
|
|
3084
|
-
addSuccessScreen: false
|
|
3397
|
+
addSuccessScreen: false,
|
|
3398
|
+
// Surveys multi-idioma: native pide a la API este idioma (el del init o el del
|
|
3399
|
+
// dispositivo) y, si el survey no lo tiene, la API sirve el idioma por defecto.
|
|
3400
|
+
lang: options?.language,
|
|
3401
|
+
// Foco en la primera pregunta de escribir tras Start, Siguiente o Atrás. Native lo
|
|
3402
|
+
// aplica después de afterSubmitEvent/onBackEvent, cuando `setLoading(false)` ya ha
|
|
3403
|
+
// quitado el `inert`. La apertura va aparte (ver autofocusOnOpen).
|
|
3404
|
+
autofocus: "navigation"
|
|
3085
3405
|
};
|
|
3086
|
-
generateOptions.onLoadedEvent = ({ formData }) => {
|
|
3087
|
-
const
|
|
3406
|
+
generateOptions.onLoadedEvent = ({ formData, lang }) => {
|
|
3407
|
+
const shownLang = typeof lang === "string" && lang.trim().length > 0 ? lang : void 0;
|
|
3408
|
+
const surveyLang = shownLang ?? (Array.isArray(formData?.lang) ? formData.lang.find((l) => typeof l === "string" && l.trim().length > 0) : void 0);
|
|
3088
3409
|
if (surveyLang) applyLabels(surveyLang);
|
|
3089
3410
|
try {
|
|
3090
3411
|
} catch {
|
|
@@ -3135,12 +3456,14 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
3135
3456
|
updateButtons("in_progress_first");
|
|
3136
3457
|
}
|
|
3137
3458
|
updateProgress({ progress: formInstance.progress, total: formInstance.total });
|
|
3138
|
-
emit("popup_clicked", surveyId, { action: "loaded" });
|
|
3139
3459
|
setLoading(false);
|
|
3460
|
+
if (!surveyLoaded) {
|
|
3461
|
+
surveyLoaded = true;
|
|
3462
|
+
autofocusOnOpen();
|
|
3463
|
+
}
|
|
3140
3464
|
};
|
|
3141
3465
|
generateOptions.beforeSubmitEvent = () => {
|
|
3142
3466
|
setLoading(true);
|
|
3143
|
-
emit("popup_clicked", surveyId, { action: "before_submit" });
|
|
3144
3467
|
};
|
|
3145
3468
|
generateOptions.afterSubmitEvent = ({ error, completed, total, progress, followup }) => {
|
|
3146
3469
|
const errText = error ? typeof error === "string" ? error : error.message ?? String(error) : "";
|
|
@@ -3149,13 +3472,12 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
3149
3472
|
if (errText.toLowerCase().includes("no response")) {
|
|
3150
3473
|
errorHint.textContent = labels.errorRequired;
|
|
3151
3474
|
errorHint.style.display = "block";
|
|
3152
|
-
emit("popup_clicked", surveyId, { action: "validation_error_required" });
|
|
3153
3475
|
updateNavButtons();
|
|
3154
3476
|
return;
|
|
3155
3477
|
}
|
|
3156
3478
|
errorHint.textContent = labels.errorSubmit;
|
|
3157
3479
|
errorHint.style.display = "block";
|
|
3158
|
-
|
|
3480
|
+
sdkWarn("[Deepdots] survey submit error:", errText);
|
|
3159
3481
|
return;
|
|
3160
3482
|
}
|
|
3161
3483
|
errorHint.style.display = "none";
|
|
@@ -3174,7 +3496,6 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
3174
3496
|
updateProgress({ progress, total, followup });
|
|
3175
3497
|
};
|
|
3176
3498
|
generateOptions.onBackEvent = ({ progress, error, followup }) => {
|
|
3177
|
-
emit("popup_clicked", surveyId, { action: "back" });
|
|
3178
3499
|
if (!error) pageDepth = Math.max(0, pageDepth - 1);
|
|
3179
3500
|
updateNavButtons();
|
|
3180
3501
|
updateProgress({ progress, total: formInstance.total, followup });
|
|
@@ -3460,7 +3781,7 @@ function trimmedOrNull(value) {
|
|
|
3460
3781
|
}
|
|
3461
3782
|
function buildSurveyHtml(opts) {
|
|
3462
3783
|
const env = opts.env === "development" ? "dev" : "prod";
|
|
3463
|
-
const version = opts.version ?? "2.2.
|
|
3784
|
+
const version = opts.version ?? "2.2.31";
|
|
3464
3785
|
const cdn = `https://cdn.jsdelivr.net/npm/@magicfeedback/native@${version}/dist/magicfeedback-sdk.browser.js`;
|
|
3465
3786
|
const sid = jsonForScript(opts.surveyId);
|
|
3466
3787
|
const pid = jsonForScript(opts.productId);
|
|
@@ -3798,11 +4119,25 @@ ${customCss}
|
|
|
3798
4119
|
${PRIMARY_COLOR_JS}
|
|
3799
4120
|
${BUSY_JS}
|
|
3800
4121
|
${REVEAL_JS}
|
|
4122
|
+
// Foco en la primera pregunta si es de escribir. Al navegar lo pone @magicfeedback/native
|
|
4123
|
+
// (autofocus:'navigation'); la apertura la decide el popup, que es quien sabe cu\xE1ndo se revela.
|
|
4124
|
+
// Necesita el popup revelado y el survey cargado, que llegan en cualquier orden, y solo con
|
|
4125
|
+
// rat\xF3n o trackpad: en t\xE1ctil sacar\xEDa el teclado sin que el usuario haya tocado nada.
|
|
4126
|
+
var ddRevealed=false, ddSurveyLoaded=false, ddOpenFocusDone=false;
|
|
4127
|
+
function ddAutofocusOnOpen(){
|
|
4128
|
+
if(ddOpenFocusDone || !ddRevealed || !ddSurveyLoaded){ return; }
|
|
4129
|
+
ddOpenFocusDone=true;
|
|
4130
|
+
var fine=typeof window.matchMedia==='function' && window.matchMedia('(pointer: fine)').matches;
|
|
4131
|
+
var f=window.DeepdotsForm;
|
|
4132
|
+
if(fine && f && typeof f.focusFirstQuestion==='function'){ f.focusFirstQuestion(); }
|
|
4133
|
+
}
|
|
3801
4134
|
// El popup se ense\xF1a cuando el survey est\xE1 pintado; \`ready\` deja al host abrir su propio
|
|
3802
4135
|
// Modal en ese momento en vez de montar un WebView en blanco.
|
|
3803
4136
|
var ddReveal=ddCreateReveal(popup, function(){
|
|
3804
4137
|
document.body.classList.add('dd-ready');
|
|
3805
4138
|
emitJSON('ready');
|
|
4139
|
+
ddRevealed=true;
|
|
4140
|
+
ddAutofocusOnOpen();
|
|
3806
4141
|
}, ${REVEAL_TIMEOUT_MS});
|
|
3807
4142
|
|
|
3808
4143
|
function setLoading(isLoading){
|
|
@@ -3840,10 +4175,18 @@ ${REVEAL_JS}
|
|
|
3840
4175
|
// textContent, as\xED que el mensaje de la plataforma (HTML con imagen) no se ver\xEDa, y su
|
|
3841
4176
|
// fallback es un literal gen\xE9rico que ignora style.successMessage.
|
|
3842
4177
|
addSuccessScreen:false,
|
|
4178
|
+
// Surveys multi-idioma: native pide a la API este idioma (el del init o el del
|
|
4179
|
+
// dispositivo); si el survey no lo tiene, la API sirve el idioma por defecto.
|
|
4180
|
+
lang:${initLangJson}||undefined,
|
|
4181
|
+
// Foco en la primera pregunta de escribir tras Start, Siguiente o Atr\xE1s. Las versiones
|
|
4182
|
+
// de native sin esta opci\xF3n la ignoran.
|
|
4183
|
+
autofocus:'navigation',
|
|
3843
4184
|
onLoadedEvent:function(args){
|
|
3844
|
-
// Idioma
|
|
3845
|
-
// ingl\xE9s delante de un survey traducido.
|
|
3846
|
-
|
|
4185
|
+
// Idioma en el que native muestra el survey: manda sobre el del init para que el
|
|
4186
|
+
// chrome no se quede en ingl\xE9s delante de un survey traducido. Native < 2.3 no lo
|
|
4187
|
+
// env\xEDa: entonces el primero de formData.lang (el idioma por defecto).
|
|
4188
|
+
var shown=args && typeof args.lang==='string' && args.lang.trim() ? args.lang : null;
|
|
4189
|
+
var langs=shown ? [shown] : (args && args.formData ? args.formData.lang : null);
|
|
3847
4190
|
if(langs && langs.length){
|
|
3848
4191
|
for(var li=0;li<langs.length;li++){
|
|
3849
4192
|
if(typeof langs[li]==='string' && langs[li].trim()){ ddApplyLabels(langs[li]); break; }
|
|
@@ -3897,6 +4240,8 @@ ${REVEAL_JS}
|
|
|
3897
4240
|
updateProgress({ progress:form.progress, total:form.total, completed:false, followup:false });
|
|
3898
4241
|
emitJSON('loaded');
|
|
3899
4242
|
setLoading(false);
|
|
4243
|
+
// La primera carga es la apertura; las siguientes (tras Start) las enfoca native.
|
|
4244
|
+
if(!ddSurveyLoaded){ ddSurveyLoaded=true; ddAutofocusOnOpen(); }
|
|
3900
4245
|
},
|
|
3901
4246
|
beforeSubmitEvent:function(){
|
|
3902
4247
|
setLoading(true);
|
|
@@ -4049,6 +4394,7 @@ var ReactNativePopupRenderer = class {
|
|
|
4049
4394
|
this.emitReady(surveyId);
|
|
4050
4395
|
break;
|
|
4051
4396
|
case "loaded":
|
|
4397
|
+
break;
|
|
4052
4398
|
case "before_submit":
|
|
4053
4399
|
case "after_submit":
|
|
4054
4400
|
case "back":
|
|
@@ -4427,7 +4773,7 @@ async function safeBodyText(res) {
|
|
|
4427
4773
|
}
|
|
4428
4774
|
}
|
|
4429
4775
|
function createFeedbackSink(options) {
|
|
4430
|
-
let feedbackSessionId;
|
|
4776
|
+
let feedbackSessionId = options.initialSessionId || void 0;
|
|
4431
4777
|
let firstPostInFlight = null;
|
|
4432
4778
|
let generation = 0;
|
|
4433
4779
|
const url = `${options.baseUrl}/sdk/feedback`;
|
|
@@ -4520,6 +4866,50 @@ function createFeedbackSink(options) {
|
|
|
4520
4866
|
};
|
|
4521
4867
|
}
|
|
4522
4868
|
|
|
4869
|
+
// src/analytics/suspended-session.ts
|
|
4870
|
+
var SUSPENDED_SESSION_KEY = "deepdots.analytics.suspended";
|
|
4871
|
+
function getSessionStorage() {
|
|
4872
|
+
try {
|
|
4873
|
+
return typeof window !== "undefined" && window.sessionStorage ? window.sessionStorage : null;
|
|
4874
|
+
} catch {
|
|
4875
|
+
return null;
|
|
4876
|
+
}
|
|
4877
|
+
}
|
|
4878
|
+
function writeSuspendedSession(storage, session) {
|
|
4879
|
+
try {
|
|
4880
|
+
storage.setItem(SUSPENDED_SESSION_KEY, JSON.stringify(session));
|
|
4881
|
+
return true;
|
|
4882
|
+
} catch {
|
|
4883
|
+
return false;
|
|
4884
|
+
}
|
|
4885
|
+
}
|
|
4886
|
+
function clearSuspendedSession(storage) {
|
|
4887
|
+
try {
|
|
4888
|
+
storage.removeItem(SUSPENDED_SESSION_KEY);
|
|
4889
|
+
} catch {
|
|
4890
|
+
}
|
|
4891
|
+
}
|
|
4892
|
+
function takeSuspendedSession(storage, match, now, windowMs) {
|
|
4893
|
+
let raw = null;
|
|
4894
|
+
try {
|
|
4895
|
+
raw = storage.getItem(SUSPENDED_SESSION_KEY);
|
|
4896
|
+
} catch {
|
|
4897
|
+
return null;
|
|
4898
|
+
}
|
|
4899
|
+
if (!raw) return null;
|
|
4900
|
+
clearSuspendedSession(storage);
|
|
4901
|
+
try {
|
|
4902
|
+
const s = JSON.parse(raw);
|
|
4903
|
+
if (typeof s.sessionId !== "string" || !s.sessionId) return null;
|
|
4904
|
+
if (typeof s.at !== "number" || now - s.at < 0 || now - s.at > windowMs) return null;
|
|
4905
|
+
if (s.integration !== match.integration) return null;
|
|
4906
|
+
if ((s.userId ?? null) !== match.userId) return null;
|
|
4907
|
+
return { sessionId: s.sessionId, integration: s.integration, userId: s.userId ?? null, at: s.at };
|
|
4908
|
+
} catch {
|
|
4909
|
+
return null;
|
|
4910
|
+
}
|
|
4911
|
+
}
|
|
4912
|
+
|
|
4523
4913
|
// src/tracking/navigation-observer.ts
|
|
4524
4914
|
var UUID_PREFIX = /^[0-9a-f]{8}-[0-9a-f]{4}-/i;
|
|
4525
4915
|
function screenPath(href) {
|
|
@@ -5118,8 +5508,10 @@ var ANALYTICS_HEARTBEAT_MS = 5 * 6e4;
|
|
|
5118
5508
|
var ANALYTICS_SESSION_TIMEOUT_MS = 30 * 6e4;
|
|
5119
5509
|
var ANALYTICS_SERVER_IDLE_MS = 60 * 6e4;
|
|
5120
5510
|
var ANALYTICS_SESSION_DISCARD_MS = ANALYTICS_SERVER_IDLE_MS - 10 * 6e4;
|
|
5511
|
+
var ANALYTICS_RESUME_WINDOW_MS = 6e4;
|
|
5121
5512
|
var RESERVED_EVENT_PREFIX = "deepdots_";
|
|
5122
5513
|
var CUSTOM_EVENT_PREFIX = "deepdots_event_";
|
|
5514
|
+
var MAX_PENDING_TRIGGERS = 20;
|
|
5123
5515
|
var DeepdotsPopups = class {
|
|
5124
5516
|
constructor() {
|
|
5125
5517
|
this.config = null;
|
|
@@ -5133,6 +5525,14 @@ var DeepdotsPopups = class {
|
|
|
5133
5525
|
// deprecated: mantenido para compatibilidad interna
|
|
5134
5526
|
this.popupDefinitions = [];
|
|
5135
5527
|
this.popupsLoaded = false;
|
|
5528
|
+
/**
|
|
5529
|
+
* `true` mientras el `GET /sdk/{apiKey}/popups` de `init()` está en vuelo. En esa ventana los
|
|
5530
|
+
* disparos del host (`triggerEvent`, `triggerSurvey`) no tienen contra qué evaluarse, así que
|
|
5531
|
+
* se guardan en `pendingTriggers` y se reproducen al cargar. Click, scroll, time y exit no
|
|
5532
|
+
* hacen falta: son listeners que `autoLaunch` instala ya con las definiciones cargadas.
|
|
5533
|
+
*/
|
|
5534
|
+
this.awaitingPopups = false;
|
|
5535
|
+
this.pendingTriggers = [];
|
|
5136
5536
|
this.pendingAutoLaunch = false;
|
|
5137
5537
|
// Estado que alimenta las reglas de redisplay. Copia de trabajo en memoria; la fuente
|
|
5138
5538
|
// duradera es el storage (`popup-state-store`), rehidratada en `init()` — sin eso, una
|
|
@@ -5187,6 +5587,13 @@ var DeepdotsPopups = class {
|
|
|
5187
5587
|
this.lastEngagementAt = 0;
|
|
5188
5588
|
/** Cuándo se ocultó la pestaña por última vez (ms epoch); null si está visible. */
|
|
5189
5589
|
this.hiddenAt = null;
|
|
5590
|
+
/**
|
|
5591
|
+
* Sesión suspendida por un `pagehide` (ms epoch y `deepdots_user_id`); null si no hay.
|
|
5592
|
+
* `openSession` la reanuda si sigue a tiempo (ver ANALYTICS_RESUME_WINDOW_MS).
|
|
5593
|
+
*/
|
|
5594
|
+
this.suspended = null;
|
|
5595
|
+
/** Integración de analytics del init: la sesión suspendida solo la hereda la misma. */
|
|
5596
|
+
this.analyticsIntegration = void 0;
|
|
5190
5597
|
/**
|
|
5191
5598
|
* Idioma resuelto en init() (explícito > navigator.language > Intl). Única fuente de
|
|
5192
5599
|
* verdad para el context de analytics Y para la segmentación por idioma de los popups.
|
|
@@ -5246,10 +5653,17 @@ var DeepdotsPopups = class {
|
|
|
5246
5653
|
}
|
|
5247
5654
|
}
|
|
5248
5655
|
this.log("tracking \xB7 user_id:", this.tracking.getUserId(), "\xB7 new_user:", this.tracking.isNewUser(), "\xB7 enabled:", this.tracking.isTrackingEnabled());
|
|
5656
|
+
this.analyticsIntegration = config.analytics?.integration;
|
|
5657
|
+
const resumable = this.readResumableSession();
|
|
5658
|
+
if (resumable) {
|
|
5659
|
+
this.analyticsFeedbackSessionId = resumable.sessionId;
|
|
5660
|
+
this.suspended = { at: resumable.at, userId: resumable.userId };
|
|
5661
|
+
}
|
|
5249
5662
|
const analyticsSink = config.analytics ? createFeedbackSink({
|
|
5250
5663
|
baseUrl: this.baseUrl,
|
|
5251
5664
|
keys: config.analytics,
|
|
5252
5665
|
log: (...a) => this.log(...a),
|
|
5666
|
+
initialSessionId: resumable?.sessionId,
|
|
5253
5667
|
onSessionId: (id) => {
|
|
5254
5668
|
this.analyticsFeedbackSessionId = id;
|
|
5255
5669
|
this.notifyFeedbackSession(id, "open");
|
|
@@ -5299,6 +5713,9 @@ var DeepdotsPopups = class {
|
|
|
5299
5713
|
this.engagement = new EngagementTracker();
|
|
5300
5714
|
this.setupAnalyticsFlush();
|
|
5301
5715
|
this.openSession();
|
|
5716
|
+
if (resumable && this.analyticsFeedbackSessionId === resumable.sessionId) {
|
|
5717
|
+
this.notifyFeedbackSession(resumable.sessionId, "open");
|
|
5718
|
+
}
|
|
5302
5719
|
const pendingCrashes = this.crashReporter.drainPendingCrashes();
|
|
5303
5720
|
if (this.tracking?.isTrackingEnabled()) {
|
|
5304
5721
|
for (const rec of pendingCrashes) {
|
|
@@ -5309,8 +5726,10 @@ var DeepdotsPopups = class {
|
|
|
5309
5726
|
this.log("SDK initialized", this.config);
|
|
5310
5727
|
if (this.renderer.init) this.renderer.init();
|
|
5311
5728
|
this.setupPopupContainer();
|
|
5729
|
+
this.awaitingPopups = Boolean(this.config.apiKey && this.baseUrl);
|
|
5312
5730
|
this.fetchPopupsFromServer().then((defs) => {
|
|
5313
|
-
|
|
5731
|
+
if (!defs) this.dropPendingTriggers();
|
|
5732
|
+
this.popupDefinitions = this.validatePopupDefinitions(defs ?? []);
|
|
5314
5733
|
this.popupsLoaded = true;
|
|
5315
5734
|
this.log("Popups loaded from API", this.popupDefinitions);
|
|
5316
5735
|
if (this.popupDefinitions.length) this.renderer.preload?.();
|
|
@@ -5319,6 +5738,7 @@ var DeepdotsPopups = class {
|
|
|
5319
5738
|
if (this.pendingAutoLaunch) {
|
|
5320
5739
|
this.startTriggers();
|
|
5321
5740
|
}
|
|
5741
|
+
this.replayPendingTriggers();
|
|
5322
5742
|
});
|
|
5323
5743
|
}
|
|
5324
5744
|
/** User id actual (generado por el SDK o provisto por el cliente). Null si tracking off. */
|
|
@@ -5547,9 +5967,15 @@ var DeepdotsPopups = class {
|
|
|
5547
5967
|
openSession() {
|
|
5548
5968
|
if (this.sessionOpen) return;
|
|
5549
5969
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
5970
|
+
const suspendedMs = this.takeSuspendedSession();
|
|
5550
5971
|
this.sessionOpen = true;
|
|
5551
5972
|
this.lastEngagementAt = Date.now();
|
|
5552
|
-
|
|
5973
|
+
if (suspendedMs === null) {
|
|
5974
|
+
this.track("deepdots_session_start", {});
|
|
5975
|
+
} else {
|
|
5976
|
+
this.track("deepdots_session_resume", { suspended_ms: suspendedMs });
|
|
5977
|
+
this.log("tracking \xB7 session resumed after", suspendedMs, "ms:", this.analyticsFeedbackSessionId);
|
|
5978
|
+
}
|
|
5553
5979
|
this.navObserver?.restart();
|
|
5554
5980
|
if (this.isDocumentVisible()) this.engagement?.resume();
|
|
5555
5981
|
}
|
|
@@ -5614,6 +6040,75 @@ var DeepdotsPopups = class {
|
|
|
5614
6040
|
}
|
|
5615
6041
|
this.openSession();
|
|
5616
6042
|
}
|
|
6043
|
+
/**
|
|
6044
|
+
* `pagehide` en web: SUSPENDE la sesión en vez de cerrarla, porque el navegador lo lanza
|
|
6045
|
+
* igual al recargar que al cerrar la pestaña. Vuelca lo pendiente (page_view de la
|
|
6046
|
+
* pantalla, mini-services, engagement) con un `deepdots_session_suspend` y SIN
|
|
6047
|
+
* `completed`, y deja el registro en `sessionStorage` (ver `suspended-session.ts`):
|
|
6048
|
+
* - si la página vuelve a cargar en esta pestaña, `init()` sigue en el mismo registro;
|
|
6049
|
+
* - si la pestaña se cerró, el backend lo cierra a los pocos minutos sin otro lote.
|
|
6050
|
+
*
|
|
6051
|
+
* Devuelve false cuando no se puede suspender — aún no se conoce el registro (el primer
|
|
6052
|
+
* POST sigue en vuelo) o no hay `sessionStorage` — y entonces el caller cierra como antes.
|
|
6053
|
+
*/
|
|
6054
|
+
suspendSession() {
|
|
6055
|
+
if (!this.sessionOpen || !this.tracking?.isTrackingEnabled()) return false;
|
|
6056
|
+
const sessionId = this.analyticsFeedbackSessionId;
|
|
6057
|
+
const integration = this.analyticsIntegration;
|
|
6058
|
+
const storage = getSessionStorage();
|
|
6059
|
+
if (!sessionId || !integration || !storage) return false;
|
|
6060
|
+
const at = Date.now();
|
|
6061
|
+
const userId = this.tracking.getUserId();
|
|
6062
|
+
if (!writeSuspendedSession(storage, { sessionId, integration, userId, at })) return false;
|
|
6063
|
+
this.sessionOpen = false;
|
|
6064
|
+
this.suspended = { at, userId };
|
|
6065
|
+
this.navObserver?.stop();
|
|
6066
|
+
this.navStarted = false;
|
|
6067
|
+
this.analytics?.exitAllMiniServices();
|
|
6068
|
+
this.flushEngagement();
|
|
6069
|
+
this.engagement?.pause();
|
|
6070
|
+
this.track("deepdots_session_suspend", { reason: "page_hide" });
|
|
6071
|
+
this.flushAnalytics({ final: true });
|
|
6072
|
+
this.log("tracking \xB7 session suspended:", sessionId);
|
|
6073
|
+
return true;
|
|
6074
|
+
}
|
|
6075
|
+
/**
|
|
6076
|
+
* Recoge la sesión que dejó suspendida la carga anterior de esta pestaña (ver
|
|
6077
|
+
* `suspendSession`). Solo con analytics configurado y el tracking activo: sin ellos no hay
|
|
6078
|
+
* registro en el que seguir.
|
|
6079
|
+
*/
|
|
6080
|
+
readResumableSession() {
|
|
6081
|
+
const integration = this.analyticsIntegration;
|
|
6082
|
+
const storage = getSessionStorage();
|
|
6083
|
+
if (!integration || !storage || !this.tracking?.isTrackingEnabled()) return null;
|
|
6084
|
+
return takeSuspendedSession(
|
|
6085
|
+
storage,
|
|
6086
|
+
{ integration, userId: this.tracking.getUserId() },
|
|
6087
|
+
Date.now(),
|
|
6088
|
+
ANALYTICS_RESUME_WINDOW_MS
|
|
6089
|
+
);
|
|
6090
|
+
}
|
|
6091
|
+
/**
|
|
6092
|
+
* Consume la sesión suspendida al abrir: los ms que estuvo suspendida si se puede seguir en
|
|
6093
|
+
* ella (mismo usuario, dentro de ANALYTICS_RESUME_WINDOW_MS); null si no había o ya no toca.
|
|
6094
|
+
* En ese caso se olvida su registro sin enviar nada: lo cierra el backend, y la sesión que
|
|
6095
|
+
* se abre ahora va a uno nuevo.
|
|
6096
|
+
*/
|
|
6097
|
+
takeSuspendedSession() {
|
|
6098
|
+
const suspended = this.suspended;
|
|
6099
|
+
if (!suspended) return null;
|
|
6100
|
+
this.suspended = null;
|
|
6101
|
+
const storage = getSessionStorage();
|
|
6102
|
+
if (storage) clearSuspendedSession(storage);
|
|
6103
|
+
const suspendedMs = Math.max(0, Date.now() - suspended.at);
|
|
6104
|
+
if (this.analyticsFeedbackSessionId && suspendedMs <= ANALYTICS_RESUME_WINDOW_MS && suspended.userId === (this.tracking?.getUserId() ?? null)) {
|
|
6105
|
+
return suspendedMs;
|
|
6106
|
+
}
|
|
6107
|
+
this.analytics?.discardSession(this.analyticsIdentity());
|
|
6108
|
+
this.analyticsFeedbackSessionId = void 0;
|
|
6109
|
+
this.tracking?.setSessionId(null);
|
|
6110
|
+
return null;
|
|
6111
|
+
}
|
|
5617
6112
|
/** Olvida la sesión abierta sin enviar nada (ver onDocumentVisible). */
|
|
5618
6113
|
discardSession() {
|
|
5619
6114
|
this.sessionOpen = false;
|
|
@@ -5662,7 +6157,7 @@ var DeepdotsPopups = class {
|
|
|
5662
6157
|
if (typeof document === "undefined" || typeof window === "undefined") return;
|
|
5663
6158
|
window.addEventListener("pagehide", () => {
|
|
5664
6159
|
clearInterval(this.analyticsFlushTimer);
|
|
5665
|
-
this.closeSession("page_hide");
|
|
6160
|
+
if (!this.suspendSession()) this.closeSession("page_hide");
|
|
5666
6161
|
});
|
|
5667
6162
|
window.addEventListener("pageshow", (event) => {
|
|
5668
6163
|
if (!event.persisted) return;
|
|
@@ -5730,6 +6225,7 @@ var DeepdotsPopups = class {
|
|
|
5730
6225
|
}
|
|
5731
6226
|
/** Lógica para evaluar condiciones antes de mostrar una encuesta */
|
|
5732
6227
|
triggerSurvey(surveyId, popupId) {
|
|
6228
|
+
if (this.bufferIfAwaitingPopups({ kind: "survey", surveyId, popupId })) return;
|
|
5733
6229
|
const def = this.findPopupDefinition(surveyId, popupId);
|
|
5734
6230
|
if (!def) {
|
|
5735
6231
|
this.debug("No popup definition for trigger", { surveyId, popupId });
|
|
@@ -5751,6 +6247,10 @@ var DeepdotsPopups = class {
|
|
|
5751
6247
|
this.debug("Ignoring empty event trigger name");
|
|
5752
6248
|
return;
|
|
5753
6249
|
}
|
|
6250
|
+
if (this.bufferIfAwaitingPopups({ kind: "event", name: normalized })) return;
|
|
6251
|
+
this.evaluateEvent(normalized);
|
|
6252
|
+
}
|
|
6253
|
+
evaluateEvent(normalized) {
|
|
5754
6254
|
const candidates = this.popupDefinitions.filter((def) => {
|
|
5755
6255
|
return def.triggers.some((trigger) => {
|
|
5756
6256
|
const triggerValue = String(trigger.value ?? "").trim();
|
|
@@ -5768,6 +6268,40 @@ var DeepdotsPopups = class {
|
|
|
5768
6268
|
}
|
|
5769
6269
|
this.showDefinition(matched);
|
|
5770
6270
|
}
|
|
6271
|
+
/**
|
|
6272
|
+
* Guarda `trigger` si las definiciones aún no han llegado. Devuelve `true` si lo guardó (el
|
|
6273
|
+
* llamador no debe evaluarlo ahora) y `false` si no hay carga en vuelo (se evalúa ya, como
|
|
6274
|
+
* siempre: tras cargar, tras un fallo o sin apiKey).
|
|
6275
|
+
*/
|
|
6276
|
+
bufferIfAwaitingPopups(trigger) {
|
|
6277
|
+
if (!this.awaitingPopups) return false;
|
|
6278
|
+
if (this.pendingTriggers.length >= MAX_PENDING_TRIGGERS) {
|
|
6279
|
+
const dropped = this.pendingTriggers.shift();
|
|
6280
|
+
this.debug("Pending trigger buffer full; dropped oldest", dropped);
|
|
6281
|
+
}
|
|
6282
|
+
this.pendingTriggers.push(trigger);
|
|
6283
|
+
this.debug("Popups not loaded yet; trigger buffered", trigger);
|
|
6284
|
+
return true;
|
|
6285
|
+
}
|
|
6286
|
+
/** La carga falló: sin definiciones no hay nada que reproducir, se descarta todo. */
|
|
6287
|
+
dropPendingTriggers() {
|
|
6288
|
+
const dropped = this.pendingTriggers.length;
|
|
6289
|
+
this.awaitingPopups = false;
|
|
6290
|
+
this.pendingTriggers = [];
|
|
6291
|
+
if (dropped > 0) this.debug("Popups failed to load; dropped pending triggers", dropped);
|
|
6292
|
+
}
|
|
6293
|
+
/** Reproduce en orden por la evaluación normal (segmentos, cooldowns y condiciones de AHORA). */
|
|
6294
|
+
replayPendingTriggers() {
|
|
6295
|
+
this.awaitingPopups = false;
|
|
6296
|
+
const replay = this.pendingTriggers;
|
|
6297
|
+
this.pendingTriggers = [];
|
|
6298
|
+
if (!replay.length) return;
|
|
6299
|
+
this.debug("Replaying triggers received before popups loaded", replay.length);
|
|
6300
|
+
replay.forEach((trigger) => {
|
|
6301
|
+
if (trigger.kind === "event") this.evaluateEvent(trigger.name);
|
|
6302
|
+
else this.triggerSurvey(trigger.surveyId, trigger.popupId);
|
|
6303
|
+
});
|
|
6304
|
+
}
|
|
5771
6305
|
showDefinition(def) {
|
|
5772
6306
|
this.log("Showing popup (definition)", def);
|
|
5773
6307
|
this.surveyToPopupId.set(def.surveyId, def.id);
|
|
@@ -6038,7 +6572,7 @@ var DeepdotsPopups = class {
|
|
|
6038
6572
|
} catch {
|
|
6039
6573
|
}
|
|
6040
6574
|
}
|
|
6041
|
-
/** Fetch al servidor para obtener popups */
|
|
6575
|
+
/** Fetch al servidor para obtener popups. `null` si la carga falló (red, no-2xx, payload inválido). */
|
|
6042
6576
|
async fetchPopupsFromServer() {
|
|
6043
6577
|
const apiKey = this.config?.apiKey;
|
|
6044
6578
|
const baseUrl = this.baseUrl;
|
|
@@ -6054,19 +6588,19 @@ var DeepdotsPopups = class {
|
|
|
6054
6588
|
const response = await fetch(endpoint);
|
|
6055
6589
|
if (!response.ok) {
|
|
6056
6590
|
this.log("Failed to fetch popups", response.status, response.statusText);
|
|
6057
|
-
return
|
|
6591
|
+
return null;
|
|
6058
6592
|
}
|
|
6059
6593
|
const raw = await response.text();
|
|
6060
6594
|
const parsed = JSON.parse(raw);
|
|
6061
6595
|
if (!Array.isArray(parsed)) {
|
|
6062
6596
|
this.log("Unexpected popups payload", parsed);
|
|
6063
|
-
return
|
|
6597
|
+
return null;
|
|
6064
6598
|
}
|
|
6065
6599
|
this.debug("Fetched popups payload", parsed);
|
|
6066
6600
|
return parsed;
|
|
6067
6601
|
} catch (error) {
|
|
6068
6602
|
this.log("Error fetching popups", error);
|
|
6069
|
-
return
|
|
6603
|
+
return null;
|
|
6070
6604
|
}
|
|
6071
6605
|
}
|
|
6072
6606
|
/** Envía los atributos de contact a la API (`POST /sdk/popups/contact`). */
|
|
@@ -6490,6 +7024,7 @@ function DeepdotsProvider({ config, children }) {
|
|
|
6490
7024
|
style: { flex: 1 },
|
|
6491
7025
|
originWhitelist: ["*"],
|
|
6492
7026
|
javaScriptEnabled: true,
|
|
7027
|
+
keyboardDisplayRequiresUserAction: false,
|
|
6493
7028
|
source: { html: survey.html, baseUrl: "https://sdk.deepdots.com/" },
|
|
6494
7029
|
onMessage: (e) => rendererRef.current?.handleMessage(e.nativeEvent.data)
|
|
6495
7030
|
}
|