@osano/osano-cmp-react-native 0.18.5 → 0.19.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/lib/commonjs/ConsentManager.js +28 -2
- 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/constants.js +2 -3
- package/lib/commonjs/constants.js.map +1 -1
- package/lib/commonjs/doNotSell.js +95 -0
- package/lib/commonjs/doNotSell.js.map +1 -0
- package/lib/commonjs/index.js +9 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/reducer.js +41 -41
- package/lib/commonjs/reducer.js.map +1 -1
- package/lib/commonjs/selectors.js +20 -2
- package/lib/commonjs/selectors.js.map +1 -1
- package/lib/commonjs/services/consent.js +34 -5
- 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 +161 -13
- package/lib/commonjs/views/Drawer/Drawer.ConsentCategories.js.map +1 -1
- package/lib/module/ConsentManager.js +28 -2
- 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/constants.js +2 -3
- package/lib/module/constants.js.map +1 -1
- package/lib/module/doNotSell.js +87 -0
- package/lib/module/doNotSell.js.map +1 -0
- package/lib/module/index.js +9 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/reducer.js +41 -41
- package/lib/module/reducer.js.map +1 -1
- package/lib/module/selectors.js +16 -1
- package/lib/module/selectors.js.map +1 -1
- package/lib/module/services/consent.js +34 -5
- 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 +164 -16
- package/lib/module/views/Drawer/Drawer.ConsentCategories.js.map +1 -1
- package/lib/typescript/src/ConsentManager.d.ts +7 -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/constants.d.ts.map +1 -1
- package/lib/typescript/src/doNotSell.d.ts +48 -0
- package/lib/typescript/src/doNotSell.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/reducer.d.ts.map +1 -1
- package/lib/typescript/src/selectors.d.ts +3 -0
- package/lib/typescript/src/selectors.d.ts.map +1 -1
- package/lib/typescript/src/services/consent.d.ts +2 -2
- 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 +28 -5
- package/src/components/PoweredByOsano.tsx +79 -0
- package/src/constants.ts +2 -3
- package/src/doNotSell.ts +122 -0
- package/src/index.tsx +10 -1
- package/src/reducer.ts +53 -56
- package/src/selectors.ts +25 -2
- package/src/services/consent.ts +49 -9
- package/src/types/consent-config.d.ts +12 -0
- package/src/views/Dialog/index.tsx +19 -1
- package/src/views/Drawer/Drawer.ConsentCategories.tsx +170 -12
|
@@ -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';
|
|
@@ -50,12 +54,13 @@ import {
|
|
|
50
54
|
getStack,
|
|
51
55
|
isCategoryAccepted,
|
|
52
56
|
isCategoryDisabled,
|
|
57
|
+
shouldRenderOptOut,
|
|
53
58
|
} from '../../selectors';
|
|
54
59
|
import type { Disclosure } from '../../types/store';
|
|
55
60
|
import DrawerHeader from './Drawer.Header';
|
|
56
61
|
|
|
57
62
|
type ConsentCategoryItem = {
|
|
58
|
-
type: 'info' | 'category' | 'specialFeature';
|
|
63
|
+
type: 'info' | 'category' | 'specialFeature' | 'uuid';
|
|
59
64
|
id: string;
|
|
60
65
|
headerHeight?: number;
|
|
61
66
|
};
|
|
@@ -511,21 +516,144 @@ const CategoryListHeader: React.FC<{ style: StyleProp<ViewStyle> }> = ({
|
|
|
511
516
|
);
|
|
512
517
|
};
|
|
513
518
|
|
|
519
|
+
const OSANO_URL =
|
|
520
|
+
'https://www.osano.com/?utm_campaign=cmp&utm_source=cmp-dialog&utm_medium=drawer';
|
|
521
|
+
|
|
514
522
|
const CategoryListFooter: React.FC = () => {
|
|
515
|
-
const { cmp, dispatch } = useContext(OsanoPrivateContext);
|
|
523
|
+
const { cmp, dispatch, translate } = useContext(OsanoPrivateContext);
|
|
516
524
|
|
|
517
525
|
const handleSave = useCallback(() => {
|
|
518
526
|
cmp && dispatch(saveConsent(cmp));
|
|
519
527
|
dispatch(close());
|
|
520
|
-
}, [cmp]);
|
|
528
|
+
}, [cmp, dispatch]);
|
|
521
529
|
|
|
530
|
+
// Always shown here; showBannerLogo only gates the logo on the consent banner.
|
|
522
531
|
return (
|
|
523
532
|
<View style={[styles.listItem, styles.listFooter]}>
|
|
524
533
|
<SaveButton onPress={handleSave} />
|
|
525
|
-
<
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
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}
|
|
529
657
|
</View>
|
|
530
658
|
</View>
|
|
531
659
|
);
|
|
@@ -550,6 +678,8 @@ const ListItemRenderer = ({
|
|
|
550
678
|
);
|
|
551
679
|
case 'specialFeature':
|
|
552
680
|
return <SpecialFeatureListItem style={style} specialFeatureId={id} />;
|
|
681
|
+
case 'uuid':
|
|
682
|
+
return <ConsentUuidRow style={style} />;
|
|
553
683
|
default:
|
|
554
684
|
return null;
|
|
555
685
|
}
|
|
@@ -598,7 +728,7 @@ export const ConsentCategoriesDrawer: React.FC = () => {
|
|
|
598
728
|
return 'none';
|
|
599
729
|
}, [previousStack, stack]);
|
|
600
730
|
const useTcf = config?.tcfIsApplicable;
|
|
601
|
-
const needsOptOut =
|
|
731
|
+
const needsOptOut = shouldRenderOptOut(state);
|
|
602
732
|
const categories: ConsentCategoryItem[] = useMemo(() => {
|
|
603
733
|
const newCategories: ConsentCategoryItem[] = (
|
|
604
734
|
useTcf ? config.iabCategories : config?.categories || []
|
|
@@ -626,6 +756,9 @@ export const ConsentCategoriesDrawer: React.FC = () => {
|
|
|
626
756
|
}),
|
|
627
757
|
);
|
|
628
758
|
}
|
|
759
|
+
if (config?.showConsentUuid) {
|
|
760
|
+
newCategories.push({ type: 'uuid', id: 'uuid', headerHeight });
|
|
761
|
+
}
|
|
629
762
|
newCategories.unshift({
|
|
630
763
|
type: 'info',
|
|
631
764
|
id: 'info',
|
|
@@ -674,9 +807,8 @@ const styles = StyleSheet.create({
|
|
|
674
807
|
poweredBy: {
|
|
675
808
|
alignItems: 'center',
|
|
676
809
|
justifyContent: 'center',
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
marginVertical: 16,
|
|
810
|
+
marginTop: 24,
|
|
811
|
+
marginBottom: 16,
|
|
680
812
|
},
|
|
681
813
|
switch: {
|
|
682
814
|
marginBottom: 8,
|
|
@@ -697,12 +829,38 @@ const styles = StyleSheet.create({
|
|
|
697
829
|
paddingVertical: 32,
|
|
698
830
|
},
|
|
699
831
|
listFooter: {
|
|
700
|
-
|
|
832
|
+
paddingTop: 16,
|
|
833
|
+
paddingBottom: 8,
|
|
701
834
|
},
|
|
702
835
|
listItem: {
|
|
703
836
|
fontSize: 12,
|
|
704
837
|
paddingHorizontal: 24,
|
|
705
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
|
+
},
|
|
706
864
|
paragraph: {
|
|
707
865
|
marginBottom: 24,
|
|
708
866
|
},
|