@osano/osano-cmp-react-native 0.18.6 → 0.19.1
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/lib/commonjs/ConsentManager.js +32 -1
- package/lib/commonjs/ConsentManager.js.map +1 -1
- package/lib/commonjs/components/PoweredByOsano.js +72 -0
- package/lib/commonjs/components/PoweredByOsano.js.map +1 -0
- package/lib/commonjs/iab/tcf/iab-storage-utils.js +53 -49
- package/lib/commonjs/iab/tcf/iab-storage-utils.js.map +1 -1
- package/lib/commonjs/iab/tcf/middleware.js +20 -23
- package/lib/commonjs/iab/tcf/middleware.js.map +1 -1
- package/lib/commonjs/iab/tcf/reducer.js +0 -22
- package/lib/commonjs/iab/tcf/reducer.js.map +1 -1
- package/lib/commonjs/index.js +16 -2
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/reducer.js +5 -1
- package/lib/commonjs/reducer.js.map +1 -1
- package/lib/commonjs/services/consent.js +178 -77
- package/lib/commonjs/services/consent.js.map +1 -1
- package/lib/commonjs/views/Dialog/index.js +15 -2
- package/lib/commonjs/views/Dialog/index.js.map +1 -1
- package/lib/commonjs/views/Drawer/Drawer.ConsentCategories.js +160 -12
- package/lib/commonjs/views/Drawer/Drawer.ConsentCategories.js.map +1 -1
- package/lib/module/ConsentManager.js +32 -1
- package/lib/module/ConsentManager.js.map +1 -1
- package/lib/module/components/PoweredByOsano.js +68 -0
- package/lib/module/components/PoweredByOsano.js.map +1 -0
- package/lib/module/iab/tcf/iab-storage-utils.js +53 -49
- package/lib/module/iab/tcf/iab-storage-utils.js.map +1 -1
- package/lib/module/iab/tcf/middleware.js +22 -25
- package/lib/module/iab/tcf/middleware.js.map +1 -1
- package/lib/module/iab/tcf/reducer.js +1 -23
- package/lib/module/iab/tcf/reducer.js.map +1 -1
- package/lib/module/index.js +17 -3
- package/lib/module/index.js.map +1 -1
- package/lib/module/reducer.js +5 -1
- package/lib/module/reducer.js.map +1 -1
- package/lib/module/services/consent.js +174 -75
- package/lib/module/services/consent.js.map +1 -1
- package/lib/module/views/Dialog/index.js +16 -3
- package/lib/module/views/Dialog/index.js.map +1 -1
- package/lib/module/views/Drawer/Drawer.ConsentCategories.js +162 -14
- package/lib/module/views/Drawer/Drawer.ConsentCategories.js.map +1 -1
- package/lib/typescript/src/ConsentManager.d.ts +13 -1
- package/lib/typescript/src/ConsentManager.d.ts.map +1 -1
- package/lib/typescript/src/components/PoweredByOsano.d.ts +5 -0
- package/lib/typescript/src/components/PoweredByOsano.d.ts.map +1 -0
- package/lib/typescript/src/iab/tcf/iab-storage-utils.d.ts +7 -7
- package/lib/typescript/src/iab/tcf/iab-storage-utils.d.ts.map +1 -1
- package/lib/typescript/src/iab/tcf/middleware.d.ts.map +1 -1
- package/lib/typescript/src/iab/tcf/reducer.d.ts +1 -0
- package/lib/typescript/src/iab/tcf/reducer.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/reducer.d.ts.map +1 -1
- package/lib/typescript/src/services/consent.d.ts +28 -0
- package/lib/typescript/src/services/consent.d.ts.map +1 -1
- package/lib/typescript/src/views/Dialog/index.d.ts.map +1 -1
- package/lib/typescript/src/views/Drawer/Drawer.ConsentCategories.d.ts.map +1 -1
- package/package.json +5 -4
- package/src/ConsentManager.ts +33 -2
- package/src/components/PoweredByOsano.tsx +79 -0
- package/src/iab/tcf/iab-storage-utils.ts +101 -56
- package/src/iab/tcf/middleware.ts +36 -32
- package/src/iab/tcf/reducer.ts +2 -29
- package/src/index.tsx +15 -1
- package/src/reducer.ts +4 -0
- package/src/services/consent.ts +220 -86
- package/src/types/consent-config.d.ts +11 -1
- package/src/types/store.d.ts +5 -0
- package/src/views/Dialog/index.tsx +19 -1
- package/src/views/Drawer/Drawer.ConsentCategories.tsx +168 -11
|
@@ -4,13 +4,16 @@ import React, {
|
|
|
4
4
|
useEffect,
|
|
5
5
|
useMemo,
|
|
6
6
|
useRef,
|
|
7
|
+
useState,
|
|
7
8
|
} from 'react';
|
|
8
9
|
import {
|
|
9
10
|
ActivityIndicator,
|
|
10
11
|
FlatList,
|
|
11
12
|
type LayoutChangeEvent,
|
|
13
|
+
Linking,
|
|
12
14
|
type ListRenderItemInfo,
|
|
13
15
|
Platform,
|
|
16
|
+
Pressable,
|
|
14
17
|
type StyleProp,
|
|
15
18
|
StyleSheet,
|
|
16
19
|
Text,
|
|
@@ -37,6 +40,7 @@ import { Divider } from '../../components/Divider';
|
|
|
37
40
|
import { ExpansionPanel } from '../../components/ExpansionPanel';
|
|
38
41
|
import { Link } from '../../components/Link';
|
|
39
42
|
import Locale from '../../components/Locale';
|
|
43
|
+
import PoweredByOsano from '../../components/PoweredByOsano';
|
|
40
44
|
import StoragePolicyLink from '../../components/StoragePolicyLink';
|
|
41
45
|
import Toggle from '../../components/Toggle';
|
|
42
46
|
import { ConsentCategory, type ConsentCategoryType } from '../../constants';
|
|
@@ -56,7 +60,7 @@ import type { Disclosure } from '../../types/store';
|
|
|
56
60
|
import DrawerHeader from './Drawer.Header';
|
|
57
61
|
|
|
58
62
|
type ConsentCategoryItem = {
|
|
59
|
-
type: 'info' | 'category' | 'specialFeature';
|
|
63
|
+
type: 'info' | 'category' | 'specialFeature' | 'uuid';
|
|
60
64
|
id: string;
|
|
61
65
|
headerHeight?: number;
|
|
62
66
|
};
|
|
@@ -512,21 +516,144 @@ const CategoryListHeader: React.FC<{ style: StyleProp<ViewStyle> }> = ({
|
|
|
512
516
|
);
|
|
513
517
|
};
|
|
514
518
|
|
|
519
|
+
const OSANO_URL =
|
|
520
|
+
'https://www.osano.com/?utm_campaign=cmp&utm_source=cmp-dialog&utm_medium=drawer';
|
|
521
|
+
|
|
515
522
|
const CategoryListFooter: React.FC = () => {
|
|
516
|
-
const { cmp, dispatch } = useContext(OsanoPrivateContext);
|
|
523
|
+
const { cmp, dispatch, translate } = useContext(OsanoPrivateContext);
|
|
517
524
|
|
|
518
525
|
const handleSave = useCallback(() => {
|
|
519
526
|
cmp && dispatch(saveConsent(cmp));
|
|
520
527
|
dispatch(close());
|
|
521
|
-
}, [cmp]);
|
|
528
|
+
}, [cmp, dispatch]);
|
|
522
529
|
|
|
530
|
+
// Always shown here; showBannerLogo only gates the logo on the consent banner.
|
|
523
531
|
return (
|
|
524
532
|
<View style={[styles.listItem, styles.listFooter]}>
|
|
525
533
|
<SaveButton onPress={handleSave} />
|
|
526
|
-
<
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
534
|
+
<Pressable
|
|
535
|
+
accessibilityRole="link"
|
|
536
|
+
accessibilityLabel={`${translate('messaging.poweredBy')}`}
|
|
537
|
+
onPress={() => Linking.openURL(OSANO_URL)}
|
|
538
|
+
style={styles.poweredBy}
|
|
539
|
+
>
|
|
540
|
+
<PoweredByOsano testID="drawer-banner-logo" />
|
|
541
|
+
</Pressable>
|
|
542
|
+
</View>
|
|
543
|
+
);
|
|
544
|
+
};
|
|
545
|
+
|
|
546
|
+
// Falls back to translucent black when hex is missing or malformed.
|
|
547
|
+
const hexToRgba = (hex: string | undefined, alpha: number): string => {
|
|
548
|
+
if (!hex || !hex.startsWith('#')) {
|
|
549
|
+
return `rgba(0, 0, 0, ${alpha})`;
|
|
550
|
+
}
|
|
551
|
+
let h = hex.slice(1);
|
|
552
|
+
if (h.length === 3) {
|
|
553
|
+
h = h
|
|
554
|
+
.split('')
|
|
555
|
+
.map((c) => c + c)
|
|
556
|
+
.join('');
|
|
557
|
+
}
|
|
558
|
+
// Reject any non-hex input up front so parseInt can't yield NaN channels.
|
|
559
|
+
if (!/^[0-9a-fA-F]{6}$/.test(h)) {
|
|
560
|
+
return `rgba(0, 0, 0, ${alpha})`;
|
|
561
|
+
}
|
|
562
|
+
const r = parseInt(h.slice(0, 2), 16);
|
|
563
|
+
const g = parseInt(h.slice(2, 4), 16);
|
|
564
|
+
const b = parseInt(h.slice(4, 6), 16);
|
|
565
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
566
|
+
};
|
|
567
|
+
|
|
568
|
+
// Resolve the clipboard native module lazily. The require is guarded so the
|
|
569
|
+
// drawer still renders where the module is not linked (e.g. Expo Go), instead
|
|
570
|
+
// of throwing at import time; the Copy button is hidden when it is unavailable.
|
|
571
|
+
const getClipboard = (): { setString: (value: string) => void } | null => {
|
|
572
|
+
try {
|
|
573
|
+
// Guarded require (not a static import) so an unlinked module degrades
|
|
574
|
+
// instead of throwing when this file loads.
|
|
575
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
576
|
+
return require('@react-native-clipboard/clipboard').default;
|
|
577
|
+
} catch {
|
|
578
|
+
return null;
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
|
|
582
|
+
// Shows the visitor's consent UUID with a Copy button that writes it to the
|
|
583
|
+
// clipboard. Renders nothing when no UUID is available.
|
|
584
|
+
const ConsentUuidRow: React.FC<{ style: StyleProp<ViewStyle> }> = ({
|
|
585
|
+
style,
|
|
586
|
+
}) => {
|
|
587
|
+
const { cmp, config, translate } = useContext(OsanoPrivateContext);
|
|
588
|
+
const uuid = cmp?.deviceId ?? '';
|
|
589
|
+
const palette = config?.palette;
|
|
590
|
+
const foregroundColor = palette?.infoDialogForegroundColor;
|
|
591
|
+
const buttonBackgroundColor = palette?.buttonBackgroundColor;
|
|
592
|
+
const buttonForegroundColor = palette?.buttonForegroundColor;
|
|
593
|
+
const clipboard = useMemo(() => getClipboard(), []);
|
|
594
|
+
const [copied, setCopied] = useState(false);
|
|
595
|
+
const copiedTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
596
|
+
|
|
597
|
+
useEffect(
|
|
598
|
+
() => () => {
|
|
599
|
+
if (copiedTimeout.current) {
|
|
600
|
+
clearTimeout(copiedTimeout.current);
|
|
601
|
+
}
|
|
602
|
+
},
|
|
603
|
+
[],
|
|
604
|
+
);
|
|
605
|
+
|
|
606
|
+
const handleCopy = useCallback(() => {
|
|
607
|
+
if (!uuid || !clipboard) {
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
clipboard.setString(uuid);
|
|
611
|
+
setCopied(true);
|
|
612
|
+
if (copiedTimeout.current) {
|
|
613
|
+
clearTimeout(copiedTimeout.current);
|
|
614
|
+
}
|
|
615
|
+
// Revert the label back to "Copy" so the button is reusable.
|
|
616
|
+
copiedTimeout.current = setTimeout(() => setCopied(false), 2000);
|
|
617
|
+
}, [uuid, clipboard]);
|
|
618
|
+
|
|
619
|
+
if (!uuid) {
|
|
620
|
+
return null;
|
|
621
|
+
}
|
|
622
|
+
const copyLabel = `${translate('drawer.copy')}`;
|
|
623
|
+
const copiedLabel = `${translate('drawer.copied')}`;
|
|
624
|
+
return (
|
|
625
|
+
<View style={[styles.listItem, styles.uuidRow, style]}>
|
|
626
|
+
<View
|
|
627
|
+
style={[
|
|
628
|
+
styles.uuidBox,
|
|
629
|
+
{ backgroundColor: hexToRgba(foregroundColor, 0.05) },
|
|
630
|
+
]}
|
|
631
|
+
>
|
|
632
|
+
<Text
|
|
633
|
+
selectable
|
|
634
|
+
numberOfLines={1}
|
|
635
|
+
ellipsizeMode="tail"
|
|
636
|
+
style={[styles.uuidText, { color: foregroundColor }]}
|
|
637
|
+
testID="drawer-consent-uuid"
|
|
638
|
+
>
|
|
639
|
+
{`UUID: ${uuid}`}
|
|
640
|
+
</Text>
|
|
641
|
+
{clipboard ? (
|
|
642
|
+
<Pressable
|
|
643
|
+
accessibilityRole="button"
|
|
644
|
+
accessibilityLabel={copyLabel}
|
|
645
|
+
onPress={handleCopy}
|
|
646
|
+
style={[
|
|
647
|
+
styles.copyButton,
|
|
648
|
+
{ backgroundColor: buttonBackgroundColor },
|
|
649
|
+
]}
|
|
650
|
+
testID="drawer-consent-uuid-copy"
|
|
651
|
+
>
|
|
652
|
+
<Text style={[styles.copyText, { color: buttonForegroundColor }]}>
|
|
653
|
+
{copied ? copiedLabel : copyLabel}
|
|
654
|
+
</Text>
|
|
655
|
+
</Pressable>
|
|
656
|
+
) : null}
|
|
530
657
|
</View>
|
|
531
658
|
</View>
|
|
532
659
|
);
|
|
@@ -551,6 +678,8 @@ const ListItemRenderer = ({
|
|
|
551
678
|
);
|
|
552
679
|
case 'specialFeature':
|
|
553
680
|
return <SpecialFeatureListItem style={style} specialFeatureId={id} />;
|
|
681
|
+
case 'uuid':
|
|
682
|
+
return <ConsentUuidRow style={style} />;
|
|
554
683
|
default:
|
|
555
684
|
return null;
|
|
556
685
|
}
|
|
@@ -627,6 +756,9 @@ export const ConsentCategoriesDrawer: React.FC = () => {
|
|
|
627
756
|
}),
|
|
628
757
|
);
|
|
629
758
|
}
|
|
759
|
+
if (config?.showConsentUuid) {
|
|
760
|
+
newCategories.push({ type: 'uuid', id: 'uuid', headerHeight });
|
|
761
|
+
}
|
|
630
762
|
newCategories.unshift({
|
|
631
763
|
type: 'info',
|
|
632
764
|
id: 'info',
|
|
@@ -675,9 +807,8 @@ const styles = StyleSheet.create({
|
|
|
675
807
|
poweredBy: {
|
|
676
808
|
alignItems: 'center',
|
|
677
809
|
justifyContent: 'center',
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
marginVertical: 16,
|
|
810
|
+
marginTop: 24,
|
|
811
|
+
marginBottom: 16,
|
|
681
812
|
},
|
|
682
813
|
switch: {
|
|
683
814
|
marginBottom: 8,
|
|
@@ -698,12 +829,38 @@ const styles = StyleSheet.create({
|
|
|
698
829
|
paddingVertical: 32,
|
|
699
830
|
},
|
|
700
831
|
listFooter: {
|
|
701
|
-
|
|
832
|
+
paddingTop: 16,
|
|
833
|
+
paddingBottom: 8,
|
|
702
834
|
},
|
|
703
835
|
listItem: {
|
|
704
836
|
fontSize: 12,
|
|
705
837
|
paddingHorizontal: 24,
|
|
706
838
|
},
|
|
839
|
+
uuidRow: {
|
|
840
|
+
paddingVertical: 8,
|
|
841
|
+
},
|
|
842
|
+
uuidBox: {
|
|
843
|
+
borderRadius: 8,
|
|
844
|
+
paddingHorizontal: 12,
|
|
845
|
+
paddingVertical: 8,
|
|
846
|
+
flexDirection: 'row',
|
|
847
|
+
alignItems: 'center',
|
|
848
|
+
justifyContent: 'space-between',
|
|
849
|
+
gap: 12,
|
|
850
|
+
},
|
|
851
|
+
uuidText: {
|
|
852
|
+
fontSize: 14,
|
|
853
|
+
flex: 1,
|
|
854
|
+
},
|
|
855
|
+
copyButton: {
|
|
856
|
+
borderRadius: 8,
|
|
857
|
+
paddingHorizontal: 16,
|
|
858
|
+
paddingVertical: 8,
|
|
859
|
+
},
|
|
860
|
+
copyText: {
|
|
861
|
+
fontSize: 14,
|
|
862
|
+
fontWeight: '700',
|
|
863
|
+
},
|
|
707
864
|
paragraph: {
|
|
708
865
|
marginBottom: 24,
|
|
709
866
|
},
|