@redacto.io/consent-sdk-react-native 4.0.0 → 5.0.0-beta.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.mjs CHANGED
@@ -131,6 +131,7 @@ var fetchConsentContent = async ({
131
131
  var submitConsentEvent = async ({
132
132
  accessToken,
133
133
  baseUrl,
134
+ ledgerBaseUrl,
134
135
  noticeUuid,
135
136
  purposes,
136
137
  declined,
@@ -150,7 +151,7 @@ var submitConsentEvent = async ({
150
151
  if (!ORGANISATION_UUID || !WORKSPACE_UUID) {
151
152
  throw new Error("Invalid token: missing organization or workspace UUID");
152
153
  }
153
- const apiBaseUrl = baseUrl || DEFAULT_CONSENT_API_BASE_URL;
154
+ const apiBaseUrl = ledgerBaseUrl || baseUrl || DEFAULT_CONSENT_API_BASE_URL;
154
155
  const validatedPurposes = purposes.map((purpose) => {
155
156
  if (!purpose.uuid) {
156
157
  throw new Error("Invalid purpose data: missing uuid");
@@ -399,6 +400,122 @@ var verifyGuardianStatus = async ({
399
400
  };
400
401
  };
401
402
 
403
+ // src/shared/notice-consent-status.ts
404
+ import { jwtDecode as jwtDecode2 } from "jwt-decode";
405
+ var decodeNoticeConsentToken = (token) => {
406
+ try {
407
+ return jwtDecode2(token);
408
+ } catch {
409
+ return null;
410
+ }
411
+ };
412
+ var fetchNoticeConsentStatusForScope = async ({
413
+ accessToken,
414
+ ledgerBaseUrl,
415
+ organisationUuid,
416
+ workspaceUuid,
417
+ noticeUuid,
418
+ signal
419
+ }) => {
420
+ if (!accessToken || !ledgerBaseUrl || !organisationUuid || !workspaceUuid || !noticeUuid) {
421
+ return null;
422
+ }
423
+ try {
424
+ const response = await fetch(
425
+ `${ledgerBaseUrl}/public/organisations/${organisationUuid}/workspaces/${workspaceUuid}/notices/${noticeUuid}/check-consent`,
426
+ {
427
+ method: "POST",
428
+ headers: {
429
+ Authorization: `Bearer ${accessToken}`,
430
+ "Content-Type": "application/json"
431
+ },
432
+ body: "{}",
433
+ signal
434
+ }
435
+ );
436
+ if (!response.ok) {
437
+ return null;
438
+ }
439
+ const data = await response.json();
440
+ const body = data && typeof data === "object" && "detail" in data && data.detail ? data.detail : data;
441
+ if (!body || !Array.isArray(body.purposes)) {
442
+ return null;
443
+ }
444
+ return body;
445
+ } catch {
446
+ return null;
447
+ }
448
+ };
449
+ var fetchNoticeConsentStatus = async ({
450
+ accessToken,
451
+ ledgerBaseUrl,
452
+ noticeUuid,
453
+ signal
454
+ }) => {
455
+ if (!accessToken || !ledgerBaseUrl || !noticeUuid) {
456
+ return null;
457
+ }
458
+ const decodedToken = decodeNoticeConsentToken(accessToken);
459
+ if (!decodedToken) {
460
+ return null;
461
+ }
462
+ const {
463
+ organisation_uuid: ORGANISATION_UUID,
464
+ workspace_uuid: WORKSPACE_UUID
465
+ } = decodedToken;
466
+ if (!ORGANISATION_UUID || !WORKSPACE_UUID) {
467
+ return null;
468
+ }
469
+ return fetchNoticeConsentStatusForScope({
470
+ accessToken,
471
+ ledgerBaseUrl,
472
+ organisationUuid: ORGANISATION_UUID,
473
+ workspaceUuid: WORKSPACE_UUID,
474
+ noticeUuid,
475
+ signal
476
+ });
477
+ };
478
+ var isNoticeFullyConsented = (status) => status.consented && status.all_mandatory_active;
479
+ var buildLedgerConsentOverlay = (status, configPurposes) => {
480
+ var _a, _b;
481
+ const byUuid = new Map(status.purposes.map((p) => [p.uuid, p]));
482
+ const hasHistory = status.purposes.some(
483
+ (p) => (p.status || "INACTIVE") !== "INACTIVE"
484
+ );
485
+ if (!hasHistory) {
486
+ return null;
487
+ }
488
+ const purpose_selections = {};
489
+ for (const cp of configPurposes) {
490
+ const lp = byUuid.get(cp.uuid);
491
+ const purposeStatus = (lp == null ? void 0 : lp.status) || "INACTIVE";
492
+ const selectedByUuid = new Map(
493
+ ((lp == null ? void 0 : lp.data_elements) || []).map((de) => [de.uuid, de.selected])
494
+ );
495
+ const data_elements = {};
496
+ for (const de of cp.data_elements) {
497
+ data_elements[de.uuid] = {
498
+ selected: (_a = selectedByUuid.get(de.uuid)) != null ? _a : false,
499
+ enabled: de.enabled,
500
+ required: de.required
501
+ };
502
+ }
503
+ purpose_selections[cp.uuid] = {
504
+ selected: (_b = lp == null ? void 0 : lp.selected) != null ? _b : false,
505
+ status: purposeStatus,
506
+ needs_reconsent: purposeStatus !== "ACTIVE",
507
+ data_elements
508
+ };
509
+ }
510
+ const reconsent_required = configPurposes.some(
511
+ (cp) => {
512
+ var _a2;
513
+ return (((_a2 = purpose_selections[cp.uuid]) == null ? void 0 : _a2.status) || "INACTIVE") !== "ACTIVE";
514
+ }
515
+ );
516
+ return { reconsent_required, purpose_selections };
517
+ };
518
+
402
519
  // src/RedactoNoticeConsent/components/Checkbox.tsx
403
520
  import { TouchableOpacity, View, StyleSheet } from "react-native";
404
521
  import Svg, { Path } from "react-native-svg";
@@ -1629,6 +1746,7 @@ var RedactoNoticeConsent = ({
1629
1746
  accessToken,
1630
1747
  refreshToken: _refreshToken,
1631
1748
  baseUrl,
1749
+ ledgerBaseUrl,
1632
1750
  settings,
1633
1751
  language = "en",
1634
1752
  blockUI = false,
@@ -1688,6 +1806,7 @@ var RedactoNoticeConsent = ({
1688
1806
  const abortControllerRef = useRef2(null);
1689
1807
  const hasBootstrappedLanguageRef = useRef2(false);
1690
1808
  const fetchContent = useCallback(async () => {
1809
+ var _a2, _b2;
1691
1810
  if (abortControllerRef.current) {
1692
1811
  abortControllerRef.current.abort();
1693
1812
  }
@@ -1706,8 +1825,23 @@ var RedactoNoticeConsent = ({
1706
1825
  signal: abortControllerRef.current.signal
1707
1826
  });
1708
1827
  const activeConfig2 = data.detail.active_config;
1709
- const purposeSelections = data.detail.purpose_selections;
1710
- const reconsentRequired = data.detail.reconsent_required || false;
1828
+ let purposeSelections = data.detail.purpose_selections;
1829
+ let reconsentRequired = data.detail.reconsent_required || false;
1830
+ if (ledgerBaseUrl && !applicationId) {
1831
+ const ledgerStatus = await fetchNoticeConsentStatus({
1832
+ accessToken,
1833
+ ledgerBaseUrl,
1834
+ noticeUuid: noticeId,
1835
+ signal: (_a2 = abortControllerRef.current) == null ? void 0 : _a2.signal
1836
+ });
1837
+ if (ledgerStatus) {
1838
+ const overlay = buildLedgerConsentOverlay(ledgerStatus, activeConfig2.purposes);
1839
+ reconsentRequired = (_b2 = overlay == null ? void 0 : overlay.reconsent_required) != null ? _b2 : false;
1840
+ purposeSelections = overlay == null ? void 0 : overlay.purpose_selections;
1841
+ data.detail.purpose_selections = purposeSelections;
1842
+ data.detail.reconsent_required = reconsentRequired;
1843
+ }
1844
+ }
1711
1845
  const noticeIsMinor = data.detail.is_minor || false;
1712
1846
  setIsMinor(noticeIsMinor);
1713
1847
  const initialCollapsed = {};
@@ -1720,9 +1854,9 @@ var RedactoNoticeConsent = ({
1720
1854
  const purposeSelected = sel ? sel.selected : false;
1721
1855
  initialPurposeState[purpose.uuid] = purposeSelected;
1722
1856
  purpose.data_elements.forEach((el) => {
1723
- var _a2, _b2, _c2;
1857
+ var _a3, _b3, _c2;
1724
1858
  const combinedId = `${purpose.uuid}-${el.uuid}`;
1725
- const elementSel = (_c2 = (_b2 = (_a2 = sel == null ? void 0 : sel.data_elements) == null ? void 0 : _a2[el.uuid]) == null ? void 0 : _b2.selected) != null ? _c2 : false;
1859
+ const elementSel = (_c2 = (_b3 = (_a3 = sel == null ? void 0 : sel.data_elements) == null ? void 0 : _a3[el.uuid]) == null ? void 0 : _b3.selected) != null ? _c2 : false;
1726
1860
  initialDataElementState[combinedId] = elementSel;
1727
1861
  initialDataElementSelectionsMap[combinedId] = elementSel;
1728
1862
  });
@@ -1770,7 +1904,7 @@ var RedactoNoticeConsent = ({
1770
1904
  } finally {
1771
1905
  setIsLoading(false);
1772
1906
  }
1773
- }, [noticeId, accessToken, baseUrl, validateAgainst, includeFullyConsentedData, applicationId, onAccept, onError]);
1907
+ }, [noticeId, accessToken, baseUrl, ledgerBaseUrl, validateAgainst, includeFullyConsentedData, applicationId, onAccept, onError]);
1774
1908
  useEffect2(() => {
1775
1909
  hasBootstrappedLanguageRef.current = false;
1776
1910
  }, [noticeId, accessToken]);
@@ -2173,6 +2307,7 @@ var RedactoNoticeConsent = ({
2173
2307
  await submitConsentEvent({
2174
2308
  accessToken,
2175
2309
  baseUrl,
2310
+ ledgerBaseUrl,
2176
2311
  noticeUuid: activeConfig2.notice_uuid,
2177
2312
  purposes: activeConfig2.purposes.map((purpose) => ({
2178
2313
  ...purpose,
@@ -2200,7 +2335,7 @@ var RedactoNoticeConsent = ({
2200
2335
  } finally {
2201
2336
  setIsSubmitting(false);
2202
2337
  }
2203
- }, [content, accessToken, baseUrl, applicationId, selectedPurposes, selectedDataElements, stopAudio, onAccept, onError]);
2338
+ }, [content, accessToken, baseUrl, ledgerBaseUrl, applicationId, selectedPurposes, selectedDataElements, stopAudio, onAccept, onError]);
2204
2339
  const handleDecline = useCallback(() => {
2205
2340
  stopAudio();
2206
2341
  stopPolling();
@@ -2861,13 +2996,14 @@ var submitConsentEvent2 = async ({
2861
2996
  workspace_uuid,
2862
2997
  accessToken,
2863
2998
  baseUrl,
2999
+ ledgerBaseUrl,
2864
3000
  noticeUuid,
2865
3001
  purposes,
2866
3002
  declined,
2867
3003
  meta_data
2868
3004
  }) => {
2869
3005
  var _a, _b;
2870
- const apiBaseUrl = baseUrl || DEFAULT_CONSENT_API_BASE_URL;
3006
+ const apiBaseUrl = ledgerBaseUrl || baseUrl || DEFAULT_CONSENT_API_BASE_URL;
2871
3007
  const payload = {
2872
3008
  notice_uuid: noticeUuid,
2873
3009
  purposes: purposes.map((purpose) => {
@@ -3189,6 +3325,7 @@ var RedactoConsentInlineComponent = ({
3189
3325
  notice_uuid,
3190
3326
  accessToken,
3191
3327
  baseUrl,
3328
+ ledgerBaseUrl,
3192
3329
  language = "en",
3193
3330
  onAccept,
3194
3331
  onDecline: _onDecline,
@@ -3207,6 +3344,9 @@ var RedactoConsentInlineComponent = ({
3207
3344
  const [selectedPurposes, setSelectedPurposes] = useState2({});
3208
3345
  const [selectedDataElements, setSelectedDataElements] = useState2({});
3209
3346
  const [hasAlreadyConsented, setHasAlreadyConsented] = useState2(false);
3347
+ const [isCheckingConsent, setIsCheckingConsent] = useState2(
3348
+ Boolean(ledgerBaseUrl) && !applicationId
3349
+ );
3210
3350
  const [fetchError, setFetchError] = useState2(null);
3211
3351
  const [errorMessage, setErrorMessage] = useState2(null);
3212
3352
  const [isLanguageDropdownOpen, setIsLanguageDropdownOpen] = useState2(false);
@@ -3327,6 +3467,40 @@ var RedactoConsentInlineComponent = ({
3327
3467
  controller.abort();
3328
3468
  };
3329
3469
  }, [fetchNotice]);
3470
+ useEffect3(() => {
3471
+ if (!ledgerBaseUrl || applicationId) {
3472
+ return;
3473
+ }
3474
+ if (!accessToken || !content) {
3475
+ return;
3476
+ }
3477
+ let active = true;
3478
+ const controller = new AbortController();
3479
+ const checkLedgerConsent = async () => {
3480
+ try {
3481
+ const ledgerStatus = await fetchNoticeConsentStatusForScope({
3482
+ accessToken,
3483
+ ledgerBaseUrl,
3484
+ organisationUuid: org_uuid,
3485
+ workspaceUuid: workspace_uuid,
3486
+ noticeUuid: notice_uuid,
3487
+ signal: controller.signal
3488
+ });
3489
+ if (active && ledgerStatus && isNoticeFullyConsented(ledgerStatus)) {
3490
+ setHasAlreadyConsented(true);
3491
+ }
3492
+ } finally {
3493
+ if (active) {
3494
+ setIsCheckingConsent(false);
3495
+ }
3496
+ }
3497
+ };
3498
+ void checkLedgerConsent();
3499
+ return () => {
3500
+ active = false;
3501
+ controller.abort();
3502
+ };
3503
+ }, [ledgerBaseUrl, applicationId, accessToken, content, org_uuid, workspace_uuid, notice_uuid]);
3330
3504
  const togglePurposeCollapse = useCallback2((purposeUuid) => {
3331
3505
  setCollapsedPurposes((prev) => ({ ...prev, [purposeUuid]: !prev[purposeUuid] }));
3332
3506
  }, []);
@@ -3377,6 +3551,7 @@ var RedactoConsentInlineComponent = ({
3377
3551
  workspace_uuid,
3378
3552
  accessToken: tokenToUse,
3379
3553
  baseUrl,
3554
+ ledgerBaseUrl,
3380
3555
  noticeUuid: content.notice_uuid,
3381
3556
  purposes: content.purposes.map((purpose) => {
3382
3557
  const hasSelectedRequiredElement = purpose.data_elements.some(
@@ -3415,6 +3590,7 @@ var RedactoConsentInlineComponent = ({
3415
3590
  org_uuid,
3416
3591
  workspace_uuid,
3417
3592
  baseUrl,
3593
+ ledgerBaseUrl,
3418
3594
  selectedDataElements,
3419
3595
  selectedPurposes,
3420
3596
  applicationId,
@@ -3422,7 +3598,7 @@ var RedactoConsentInlineComponent = ({
3422
3598
  ]
3423
3599
  );
3424
3600
  useEffect3(() => {
3425
- if (accessToken && content && areAllRequiredElementsChecked && !isSubmitting && !hasSubmittedRef.current) {
3601
+ if (accessToken && content && areAllRequiredElementsChecked && !isSubmitting && !isCheckingConsent && !hasAlreadyConsented && !hasSubmittedRef.current) {
3426
3602
  hasSubmittedRef.current = true;
3427
3603
  const autoSubmit = async () => {
3428
3604
  try {
@@ -3434,7 +3610,7 @@ var RedactoConsentInlineComponent = ({
3434
3610
  };
3435
3611
  autoSubmit();
3436
3612
  }
3437
- }, [accessToken, content, areAllRequiredElementsChecked, isSubmitting, handleAcceptWithToken, onError]);
3613
+ }, [accessToken, content, areAllRequiredElementsChecked, isSubmitting, isCheckingConsent, hasAlreadyConsented, handleAcceptWithToken, onError]);
3438
3614
  useEffect3(() => {
3439
3615
  if (!accessToken) {
3440
3616
  hasSubmittedRef.current = false;