@magicfeedback/popup-sdk 1.8.4 → 1.9.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 CHANGED
@@ -123,6 +123,10 @@ Config fields:
123
123
  - `analytics?: { publicKey: string; integration: string }` — enables the real analytics
124
124
  delivery. Without it the analytics channel stays in dry-run: nothing is sent, and the
125
125
  payload is only logged when `debug` is on.
126
+ - `onFeedbackSession?: (session: { sessionId: string; status: 'open' | 'closed' }) => void` —
127
+ called when an analytics session opens or closes, with the id the Deepdots API stores as
128
+ `sdkSessionId` on the feedback the session becomes. See
129
+ [Linking your backend to a session](#linking-your-backend-to-a-session).
126
130
  - `geolocation?: boolean` — IP geolocation (country/city) for analytics, default `true`. It
127
131
  calls third-party services (`ipapi.co`, then `ipwho.is`, then `ipinfo.io` as fallbacks, 3 s
128
132
  timeout each, stopping at the first answer). It only runs when `analytics` is set, tracking
@@ -281,6 +285,42 @@ session start/end and unhandled errors are automatic. The rest is instrumented b
281
285
  | `setTrackingEnabled(enabled)` | Kill switch (consent) |
282
286
  | `reportError(error, options?)` | A handled error |
283
287
  | `previewAnalytics()` / `flushAnalytics()` | Inspect the pending payload / send it now |
288
+ | `getFeedbackSessionId()` / `init({ onFeedbackSession })` | The id of the feedback the current session becomes |
289
+
290
+ ### Linking your backend to a session
291
+
292
+ Every analytics session becomes one feedback in Deepdots, and the API stores the session's id
293
+ on it as `sdkSessionId`. Send that id to your backend and it can find the feedback
294
+ (`GET /feedbacks?filter={"where":{"sdkSessionId":"<id>"}}`) and add data to it later, such as
295
+ the push deliveries the app never sees. A session keeps that one feedback even when it closes
296
+ again (the app retries a closing batch whose response it lost, or a late batch lands after the
297
+ close): the API updates the feedback instead of creating another one.
298
+
299
+ ```ts
300
+ popups.init({
301
+ apiKey: 'YOUR_PUBLIC_API_KEY',
302
+ analytics: { publicKey: 'YOUR_PUBLIC_API_KEY', integration: 'YOUR_INTEGRATION_ID' },
303
+ onFeedbackSession: ({ sessionId, status }) => {
304
+ // 'open': first batch accepted. 'closed': the feedback exists within seconds.
305
+ myBackend.reportSession(sessionId, status);
306
+ },
307
+ });
308
+ ```
309
+
310
+ - `open` comes when the API accepts the session's first batch, `closed` when it accepts the
311
+ closing one (`completed: true`). A session whose only batch is the closing one reports just
312
+ `closed`.
313
+ - `closed` does not come when the page unloads before the closing request returns, for a
314
+ session dropped after 30 minutes hidden, nor when the closing request fails (network error,
315
+ 5xx): its events are re-sent with the next session and the API closes the old record itself,
316
+ later. In all three cases the id already came with `open`.
317
+ - Key what you store by `sessionId`, not by arrival order: the closing request and the next
318
+ session's first one go out in parallel, so the new session's `open` can arrive before the
319
+ previous one's `closed`.
320
+ - `getFeedbackSessionId()` returns the open session's id, or `null` before its first batch is
321
+ accepted and after it closes.
322
+ - This is not `getSessionId()`, the SDK's own session id. That one travels in the metadata as
323
+ `deepdots_session_id`, and the feedback can't be looked up by it.
284
324
 
285
325
  ### Messaging — `trackMessage(stage, options)`
286
326
 
@@ -12,7 +12,9 @@
12
12
  * repintaría su web.
13
13
  * 4. El grupo .magicfeedback-radio se cualifica como div.magicfeedback-radio (la clase acaba
14
14
  * también en el <input> del boolean).
15
- * 5. Al final, un bloque de reglas propias del popup (fuente en los controles y padding del
15
+ * 5. Todo :hover va dentro de @media (hover: hover): en táctil WebKit deja el hover pegado
16
+ * en el último punto tocado y la opción de la página siguiente parecía seleccionada.
17
+ * 6. Al final, un bloque de reglas propias del popup (fuente en los controles y padding del
16
18
  * radio nativo).
17
19
  *
18
20
  * Las variables --mf-* se dejan en :root a propósito: el modal del priority-list se portea a
@@ -250,9 +252,11 @@
250
252
  text-underline-offset: 2px;
251
253
  }
252
254
 
253
- .magicfeedback-info-message a:hover,
254
- .magicfeedback-start-message a:hover {
255
- color: var(--mf-primary-hover);
255
+ @media (hover: hover) {
256
+ .magicfeedback-info-message a:hover,
257
+ .magicfeedback-start-message a:hover {
258
+ color: var(--mf-primary-hover);
259
+ }
256
260
  }
257
261
 
258
262
  .magicfeedback-info-message ul,
@@ -478,10 +482,12 @@ div.magicfeedback-radio {
478
482
  transition: var(--mf-transition-fast);
479
483
  }
480
484
 
481
- .magicfeedback-checkbox-container:hover,
482
- .magicfeedback-radio-container:hover {
483
- background-color: var(--mf-bg-hover);
484
- border-color: var(--mf-primary);
485
+ @media (hover: hover) {
486
+ .magicfeedback-checkbox-container:hover,
487
+ .magicfeedback-radio-container:hover {
488
+ background-color: var(--mf-bg-hover);
489
+ border-color: var(--mf-primary);
490
+ }
485
491
  }
486
492
 
487
493
  .magicfeedback-checkbox-container:has(input:checked),
@@ -548,9 +554,11 @@ div.magicfeedback-radio {
548
554
  height: inherit;
549
555
  }
550
556
 
551
- .magicfeedback-boolean-option label:hover {
552
- background-color: var(--mf-bg-hover);
553
- border-color: var(--mf-primary);
557
+ @media (hover: hover) {
558
+ .magicfeedback-boolean-option label:hover {
559
+ background-color: var(--mf-bg-hover);
560
+ border-color: var(--mf-primary);
561
+ }
554
562
  }
555
563
 
556
564
  .magicfeedback-boolean-option input[type="radio"]:checked + label {
@@ -621,9 +629,11 @@ div.magicfeedback-radio {
621
629
  border: 2px solid transparent;
622
630
  }
623
631
 
624
- .magicfeedback-rating-option-label-container img:hover {
625
- transform: scale(1.1);
626
- border-color: var(--mf-primary);
632
+ @media (hover: hover) {
633
+ .magicfeedback-rating-option-label-container img:hover {
634
+ transform: scale(1.1);
635
+ border-color: var(--mf-primary);
636
+ }
627
637
  }
628
638
 
629
639
  .magicfeedback-rating-option-label-container input[type="radio"]:checked + img {
@@ -775,9 +785,11 @@ div.magicfeedback-radio {
775
785
  font-size: 0.8rem;
776
786
  }
777
787
 
778
- .magicfeedback-rating-number-option-row .magicfeedback-rating-number-option-label-container:hover .magicfeedback-rating-number-value,
779
- .magicfeedback-rating-number-option-row-reverse .magicfeedback-rating-number-option-label-container:hover .magicfeedback-rating-number-value {
780
- background: var(--mf-primary-light);
788
+ @media (hover: hover) {
789
+ .magicfeedback-rating-number-option-row .magicfeedback-rating-number-option-label-container:hover .magicfeedback-rating-number-value,
790
+ .magicfeedback-rating-number-option-row-reverse .magicfeedback-rating-number-option-label-container:hover .magicfeedback-rating-number-value {
791
+ background: var(--mf-primary-light);
792
+ }
781
793
  }
782
794
 
783
795
  .magicfeedback-rating-number-option-row .magicfeedback-rating-number-option-label-container input[type="radio"]:checked + .magicfeedback-rating-number-value,
@@ -814,8 +826,10 @@ div.magicfeedback-radio {
814
826
  font-size: var(--mf-font-size-sm);
815
827
  }
816
828
 
817
- .magicfeedback-rating-number-option--extra .magicfeedback-rating-number-option-label-container:hover .magicfeedback-rating-number-value {
818
- background: var(--mf-primary-light);
829
+ @media (hover: hover) {
830
+ .magicfeedback-rating-number-option--extra .magicfeedback-rating-number-option-label-container:hover .magicfeedback-rating-number-value {
831
+ background: var(--mf-primary-light);
832
+ }
819
833
  }
820
834
 
821
835
  .magicfeedback-rating-number-option--extra .magicfeedback-rating-number-option-label-container input[type="radio"]:checked + .magicfeedback-rating-number-value {
@@ -848,9 +862,11 @@ div.magicfeedback-radio {
848
862
  color: var(--mf-primary);
849
863
  }
850
864
 
851
- .magicfeedback-rating-number-option-column .magicfeedback-rating-number-option-label-container:hover .magicfeedback-rating-number-value,
852
- .magicfeedback-rating-number-option-column-reverse .magicfeedback-rating-number-option-label-container:hover .magicfeedback-rating-number-value {
853
- background: var(--mf-primary-light);
865
+ @media (hover: hover) {
866
+ .magicfeedback-rating-number-option-column .magicfeedback-rating-number-option-label-container:hover .magicfeedback-rating-number-value,
867
+ .magicfeedback-rating-number-option-column-reverse .magicfeedback-rating-number-option-label-container:hover .magicfeedback-rating-number-value {
868
+ background: var(--mf-primary-light);
869
+ }
854
870
  }
855
871
 
856
872
  .magicfeedback-rating-number-option-column .magicfeedback-rating-number-option-label-container input[type="radio"]:checked + .magicfeedback-rating-number-value,
@@ -904,10 +920,12 @@ div.magicfeedback-radio {
904
920
  transition: var(--mf-transition-fast);
905
921
  }
906
922
 
907
- .magicfeedback-rating-star-option:not(:checked) > .rating__star:hover,
908
- .magicfeedback-rating-star-option:not(:checked) > .rating__star:hover ~ span {
909
- color: #fbbf24;
910
- transform: scale(1.1);
923
+ @media (hover: hover) {
924
+ .magicfeedback-rating-star-option:not(:checked) > .rating__star:hover,
925
+ .magicfeedback-rating-star-option:not(:checked) > .rating__star:hover ~ span {
926
+ color: #fbbf24;
927
+ transform: scale(1.1);
928
+ }
911
929
  }
912
930
 
913
931
  .magicfeedback-rating-star-selected {
@@ -948,9 +966,11 @@ div.magicfeedback-radio {
948
966
  transition: var(--mf-transition-fast);
949
967
  }
950
968
 
951
- .magicfeedback-image-option-label-container:hover img {
952
- border-color: var(--mf-primary);
953
- box-shadow: var(--mf-shadow-md);
969
+ @media (hover: hover) {
970
+ .magicfeedback-image-option-label-container:hover img {
971
+ border-color: var(--mf-primary);
972
+ box-shadow: var(--mf-shadow-md);
973
+ }
954
974
  }
955
975
 
956
976
  .magicfeedback-image-option-label-container input:checked + img {
@@ -991,9 +1011,11 @@ div.magicfeedback-radio {
991
1011
  background-color: var(--mf-bg-secondary);
992
1012
  }
993
1013
 
994
- .magicfeedback-multi-question-matrix-row-tr:hover {
995
- background-color: var(--mf-bg-hover);
996
- box-shadow: var(--mf-shadow-sm);
1014
+ @media (hover: hover) {
1015
+ .magicfeedback-multi-question-matrix-row-tr:hover {
1016
+ background-color: var(--mf-bg-hover);
1017
+ box-shadow: var(--mf-shadow-sm);
1018
+ }
997
1019
  }
998
1020
 
999
1021
  .magicfeedback-multi-question-matrix-table td {
@@ -1029,9 +1051,11 @@ div.magicfeedback-radio {
1029
1051
  transition: var(--mf-transition-fast);
1030
1052
  }
1031
1053
 
1032
- .magicfeedback-priority-list-item:hover {
1033
- box-shadow: var(--mf-shadow-sm);
1034
- border-color: var(--mf-primary);
1054
+ @media (hover: hover) {
1055
+ .magicfeedback-priority-list-item:hover {
1056
+ box-shadow: var(--mf-shadow-sm);
1057
+ border-color: var(--mf-primary);
1058
+ }
1035
1059
  }
1036
1060
 
1037
1061
  .magicfeedback-priority-list-item-label {
@@ -1052,10 +1076,12 @@ div.magicfeedback-radio {
1052
1076
  user-select: none;
1053
1077
  }
1054
1078
 
1055
- .magicfeedback-priority-list-arrow-up:hover,
1056
- .magicfeedback-priority-list-arrow-down:hover {
1057
- color: var(--mf-primary);
1058
- transform: scale(1.2);
1079
+ @media (hover: hover) {
1080
+ .magicfeedback-priority-list-arrow-up:hover,
1081
+ .magicfeedback-priority-list-arrow-down:hover {
1082
+ color: var(--mf-primary);
1083
+ transform: scale(1.2);
1084
+ }
1059
1085
  }
1060
1086
 
1061
1087
  /* ========================================
@@ -1162,10 +1188,12 @@ div.magicfeedback-radio {
1162
1188
  box-shadow: var(--mf-shadow-md);
1163
1189
  }
1164
1190
 
1165
- .magicfeedback-submit:hover {
1166
- background-color: var(--mf-primary-hover);
1167
- box-shadow: var(--mf-shadow-lg);
1168
- transform: translateY(-2px);
1191
+ @media (hover: hover) {
1192
+ .magicfeedback-submit:hover {
1193
+ background-color: var(--mf-primary-hover);
1194
+ box-shadow: var(--mf-shadow-lg);
1195
+ transform: translateY(-2px);
1196
+ }
1169
1197
  }
1170
1198
 
1171
1199
  .magicfeedback-submit:active {
@@ -1191,9 +1219,11 @@ div.magicfeedback-radio {
1191
1219
  transition: var(--mf-transition-fast);
1192
1220
  }
1193
1221
 
1194
- .magicfeedback-back:hover {
1195
- border-color: var(--mf-primary);
1196
- background-color: var(--mf-bg-hover);
1222
+ @media (hover: hover) {
1223
+ .magicfeedback-back:hover {
1224
+ border-color: var(--mf-primary);
1225
+ background-color: var(--mf-bg-hover);
1226
+ }
1197
1227
  }
1198
1228
 
1199
1229
  /* ========================================
@@ -1213,8 +1243,10 @@ div.magicfeedback-radio {
1213
1243
  transition: var(--mf-transition-fast);
1214
1244
  }
1215
1245
 
1216
- .magicfeedback-skip-container:hover {
1217
- color: var(--mf-primary);
1246
+ @media (hover: hover) {
1247
+ .magicfeedback-skip-container:hover {
1248
+ color: var(--mf-primary);
1249
+ }
1218
1250
  }
1219
1251
 
1220
1252
  .magicfeedback-skip-container input[type="checkbox"] {
@@ -1355,9 +1387,11 @@ div.magicfeedback-radio {
1355
1387
  box-shadow: var(--mf-shadow-sm);
1356
1388
  }
1357
1389
 
1358
- .magicfeedback-button:hover {
1359
- background: var(--mf-bg-hover);
1360
- border-color: var(--mf-primary);
1390
+ @media (hover: hover) {
1391
+ .magicfeedback-button:hover {
1392
+ background: var(--mf-bg-hover);
1393
+ border-color: var(--mf-primary);
1394
+ }
1361
1395
  }
1362
1396
 
1363
1397
  .magicfeedback-button:active {
@@ -1371,8 +1405,10 @@ div.magicfeedback-radio {
1371
1405
  box-shadow: var(--mf-shadow-md);
1372
1406
  }
1373
1407
 
1374
- .magicfeedback-button-primary:hover {
1375
- background: var(--mf-primary-hover);
1408
+ @media (hover: hover) {
1409
+ .magicfeedback-button-primary:hover {
1410
+ background: var(--mf-primary-hover);
1411
+ }
1376
1412
  }
1377
1413
 
1378
1414
  .magicfeedback-button-primary:active {
@@ -1398,8 +1434,10 @@ div.magicfeedback-radio {
1398
1434
  box-shadow: var(--mf-shadow-md);
1399
1435
  }
1400
1436
 
1401
- .magicfeedback-start-message-button:hover {
1402
- background: var(--mf-primary-hover);
1437
+ @media (hover: hover) {
1438
+ .magicfeedback-start-message-button:hover {
1439
+ background: var(--mf-primary-hover);
1440
+ }
1403
1441
  }
1404
1442
 
1405
1443
  /* ========================================
@@ -1535,12 +1573,18 @@ div.magicfeedback-radio {
1535
1573
  color: #666;
1536
1574
  }
1537
1575
 
1538
- .magicfeedback-modal-close:hover,
1539
1576
  .magicfeedback-modal-close:focus {
1540
1577
  color: #000;
1541
1578
  outline: none;
1542
1579
  }
1543
1580
 
1581
+ @media (hover: hover) {
1582
+ .magicfeedback-modal-close:hover {
1583
+ color: #000;
1584
+ outline: none;
1585
+ }
1586
+ }
1587
+
1544
1588
  /* ========================================
1545
1589
  FILE / IMAGE UPLOAD
1546
1590
  ======================================== */
@@ -1584,9 +1628,11 @@ div.magicfeedback-radio {
1584
1628
  transition: var(--mf-transition);
1585
1629
  }
1586
1630
 
1587
- .magicfeedback-upload-dropzone:hover {
1588
- border-color: var(--mf-primary);
1589
- background-color: var(--mf-bg-hover);
1631
+ @media (hover: hover) {
1632
+ .magicfeedback-upload-dropzone:hover {
1633
+ border-color: var(--mf-primary);
1634
+ background-color: var(--mf-bg-hover);
1635
+ }
1590
1636
  }
1591
1637
 
1592
1638
  .magicfeedback-upload-dropzone:focus-within {
@@ -1724,13 +1770,20 @@ div.magicfeedback-radio {
1724
1770
  transition: var(--mf-transition-fast);
1725
1771
  }
1726
1772
 
1727
- .magicfeedback-upload-remove:hover,
1728
1773
  .magicfeedback-upload-remove:focus-visible {
1729
1774
  color: var(--mf-error);
1730
1775
  background-color: var(--mf-primary-light);
1731
1776
  outline: none;
1732
1777
  }
1733
1778
 
1779
+ @media (hover: hover) {
1780
+ .magicfeedback-upload-remove:hover {
1781
+ color: var(--mf-error);
1782
+ background-color: var(--mf-primary-light);
1783
+ outline: none;
1784
+ }
1785
+ }
1786
+
1734
1787
  .magicfeedback-upload-remove svg {
1735
1788
  width: 16px;
1736
1789
  height: 16px;