@osano/osano-cmp-react-native 0.19.2 → 0.19.3
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 +18 -5
- package/lib/commonjs/ConsentManager.js.map +1 -1
- package/lib/commonjs/ConsentUiBuilder.js +8 -4
- package/lib/commonjs/ConsentUiBuilder.js.map +1 -1
- package/lib/commonjs/doNotSell.js +4 -6
- package/lib/commonjs/doNotSell.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 +11 -5
- package/lib/commonjs/services/consent.js.map +1 -1
- package/lib/commonjs/utils/safeArea.js +38 -0
- package/lib/commonjs/utils/safeArea.js.map +1 -0
- package/lib/commonjs/views/Dialog/index.js +19 -5
- package/lib/commonjs/views/Dialog/index.js.map +1 -1
- package/lib/module/ConsentManager.js +18 -5
- package/lib/module/ConsentManager.js.map +1 -1
- package/lib/module/ConsentUiBuilder.js +8 -4
- package/lib/module/ConsentUiBuilder.js.map +1 -1
- package/lib/module/doNotSell.js +4 -6
- package/lib/module/doNotSell.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 +11 -5
- package/lib/module/services/consent.js.map +1 -1
- package/lib/module/utils/safeArea.js +33 -0
- package/lib/module/utils/safeArea.js.map +1 -0
- package/lib/module/views/Dialog/index.js +20 -6
- package/lib/module/views/Dialog/index.js.map +1 -1
- package/lib/typescript/src/ConsentManager.d.ts +13 -4
- package/lib/typescript/src/ConsentManager.d.ts.map +1 -1
- package/lib/typescript/src/ConsentUiBuilder.d.ts.map +1 -1
- package/lib/typescript/src/doNotSell.d.ts +4 -1
- package/lib/typescript/src/doNotSell.d.ts.map +1 -1
- package/lib/typescript/src/reducer.d.ts.map +1 -1
- package/lib/typescript/src/services/consent.d.ts.map +1 -1
- package/lib/typescript/src/utils/safeArea.d.ts +12 -0
- package/lib/typescript/src/utils/safeArea.d.ts.map +1 -0
- package/lib/typescript/src/views/Dialog/index.d.ts.map +1 -1
- package/package.json +11 -5
- package/src/ConsentManager.ts +19 -5
- package/src/ConsentUiBuilder.tsx +8 -3
- package/src/doNotSell.ts +4 -6
- package/src/reducer.ts +5 -1
- package/src/services/consent.ts +11 -5
- package/src/utils/safeArea.ts +39 -0
- package/src/views/Dialog/index.tsx +23 -6
package/src/services/consent.ts
CHANGED
|
@@ -152,7 +152,11 @@ export const storeConsent = async (
|
|
|
152
152
|
): Promise<ConsentCategoryType[]> => {
|
|
153
153
|
cmp.consent = consent ?? cmp.consent;
|
|
154
154
|
return new Promise<ConsentCategoryType[]>((resolve, reject) => {
|
|
155
|
+
// Only once both writes land: `consentSaved` mirrors web's isSaved (a
|
|
156
|
+
// stored record exists), and a failed save must leave the implied-consent
|
|
157
|
+
// close path able to record a choice later.
|
|
155
158
|
const resConsent = () => {
|
|
159
|
+
cmp.consentSaved = true;
|
|
156
160
|
resolve(cmp.consent ?? []);
|
|
157
161
|
return cmp.consent;
|
|
158
162
|
};
|
|
@@ -280,11 +284,11 @@ export const migrateConsentStorage = async (
|
|
|
280
284
|
}
|
|
281
285
|
};
|
|
282
286
|
|
|
283
|
-
// The consent to fall back to when a visitor has no stored consent yet
|
|
284
|
-
//
|
|
285
|
-
// The Do-Not-Sell default is then applied, but only
|
|
286
|
-
// actually be visible (tenant flag + CCPA/US), so we
|
|
287
|
-
// user cannot see or control.
|
|
287
|
+
// The consent to fall back to when a visitor has no stored consent yet:
|
|
288
|
+
// essential-only, plus analytics for analytics-always flavors, matching the web
|
|
289
|
+
// CMP's pre-choice state. The Do-Not-Sell default is then applied, but only
|
|
290
|
+
// when the opt-out would actually be visible (tenant flag + CCPA/US), so we
|
|
291
|
+
// never seed an OPT_OUT the user cannot see or control.
|
|
288
292
|
const firstLoadConsent = (cmp: ConsentManager): ConsentCategoryType[] => {
|
|
289
293
|
let categories: ConsentCategoryType[] = [ConsentCategory.ESSENTIAL];
|
|
290
294
|
if (cmp.config?.analyticsDefaultsOnAtFirstLoad) {
|
|
@@ -309,6 +313,7 @@ const clearToFirstLoadDefault = async (
|
|
|
309
313
|
await clearConsent(cmp);
|
|
310
314
|
const seed = firstLoadConsent(cmp);
|
|
311
315
|
cmp.consent = seed;
|
|
316
|
+
cmp.consentSaved = false;
|
|
312
317
|
return seed;
|
|
313
318
|
};
|
|
314
319
|
|
|
@@ -388,6 +393,7 @@ const retrieveConsent = async (cmp: ConsentManager) => {
|
|
|
388
393
|
const consent = JSON.parse(consentJson) as ConsentCategoryType[];
|
|
389
394
|
if (consent.length > 0) {
|
|
390
395
|
cmp.consent = consent;
|
|
396
|
+
cmp.consentSaved = true;
|
|
391
397
|
}
|
|
392
398
|
}
|
|
393
399
|
if (cmp.consent && cmp.consent.length > 0) {
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export type SafeAreaInsets = {
|
|
2
|
+
top: number;
|
|
3
|
+
right: number;
|
|
4
|
+
bottom: number;
|
|
5
|
+
left: number;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const FALLBACK_INSETS: SafeAreaInsets = {
|
|
9
|
+
top: 0,
|
|
10
|
+
right: 0,
|
|
11
|
+
bottom: 0,
|
|
12
|
+
left: 0,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// react-native-safe-area-context is an optional peer: it is a native module,
|
|
16
|
+
// so a hard dependency would force every host app to relink on upgrade.
|
|
17
|
+
// Resolved once at module load, so the hook below branches on a constant.
|
|
18
|
+
let useLibInsets: (() => SafeAreaInsets) | undefined;
|
|
19
|
+
try {
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
21
|
+
const lib = require('react-native-safe-area-context') as {
|
|
22
|
+
useSafeAreaInsets: () => SafeAreaInsets;
|
|
23
|
+
};
|
|
24
|
+
useLibInsets = lib.useSafeAreaInsets;
|
|
25
|
+
} catch {
|
|
26
|
+
useLibInsets = undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Device safe-area insets, or zeros when react-native-safe-area-context is not
|
|
31
|
+
* installed or no SafeAreaProvider is mounted (its hook throws in that case).
|
|
32
|
+
*/
|
|
33
|
+
export const useSafeAreaInsetsSafe = (): SafeAreaInsets => {
|
|
34
|
+
try {
|
|
35
|
+
return useLibInsets ? useLibInsets() : FALLBACK_INSETS;
|
|
36
|
+
} catch {
|
|
37
|
+
return FALLBACK_INSETS;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
Linking,
|
|
11
11
|
Modal,
|
|
12
12
|
Pressable,
|
|
13
|
+
ScrollView,
|
|
13
14
|
StyleSheet,
|
|
14
15
|
Text,
|
|
15
16
|
useWindowDimensions,
|
|
@@ -21,6 +22,7 @@ import {
|
|
|
21
22
|
} from 'react-native';
|
|
22
23
|
import { acceptAll, close, toggleCategory } from '../../actions';
|
|
23
24
|
import { saveConsent } from '../../commands';
|
|
25
|
+
import { useSafeAreaInsetsSafe } from '../../utils/safeArea';
|
|
24
26
|
import { getConsent, isCategoryDisabled, isDialogOpen } from '../../selectors';
|
|
25
27
|
|
|
26
28
|
import { CloseButton } from '../../components/Button';
|
|
@@ -60,7 +62,10 @@ export const Dialog = (): ReactElement<Modal> => {
|
|
|
60
62
|
const categories = useTcf
|
|
61
63
|
? config.iabCategories
|
|
62
64
|
: config?.nonEssentialCategories || [];
|
|
63
|
-
const { width: windowWidth } = useWindowDimensions();
|
|
65
|
+
const { width: windowWidth, height: windowHeight } = useWindowDimensions();
|
|
66
|
+
const insets = useSafeAreaInsetsSafe();
|
|
67
|
+
const safePaddingTop = Math.max(insets.top, 20);
|
|
68
|
+
const safePaddingBottom = Math.max(insets.bottom, 20);
|
|
64
69
|
|
|
65
70
|
const clearImpliedConsentTimeout = useCallback(() => {
|
|
66
71
|
if (!impliedConsentTimeout.current) return;
|
|
@@ -70,9 +75,10 @@ export const Dialog = (): ReactElement<Modal> => {
|
|
|
70
75
|
|
|
71
76
|
const handleClose = useCallback(() => {
|
|
72
77
|
clearImpliedConsentTimeout();
|
|
73
|
-
|
|
78
|
+
// `consentSaved`, not `consent` — the first-load seed populates `consent`
|
|
79
|
+
// and would otherwise suppress the implied accept-all forever.
|
|
80
|
+
if (config?.isConsentImplied && !cmp?.consentSaved) {
|
|
74
81
|
dispatch(acceptAll());
|
|
75
|
-
// Only save here if consent is implied and no consent has been given yet
|
|
76
82
|
cmp && dispatch(saveConsent(cmp));
|
|
77
83
|
}
|
|
78
84
|
dispatch(close());
|
|
@@ -142,6 +148,9 @@ export const Dialog = (): ReactElement<Modal> => {
|
|
|
142
148
|
{
|
|
143
149
|
backgroundColor: dialogBackgroundColor,
|
|
144
150
|
maxWidth: 1024,
|
|
151
|
+
// Content-heavy flavors overflow small screens; without a cap the
|
|
152
|
+
// buttons are pushed off-screen and the banner can't be acted on.
|
|
153
|
+
maxHeight: Math.max(0, windowHeight - safePaddingTop - safePaddingBottom),
|
|
145
154
|
width: '100%',
|
|
146
155
|
borderRadius: dialogType === 'box' ? 8 : 0,
|
|
147
156
|
padding: 28,
|
|
@@ -184,9 +193,16 @@ export const Dialog = (): ReactElement<Modal> => {
|
|
|
184
193
|
presentationStyle="overFullScreen"
|
|
185
194
|
supportedOrientations={['portrait', 'landscape']}
|
|
186
195
|
>
|
|
187
|
-
<View
|
|
196
|
+
<View
|
|
197
|
+
style={[
|
|
198
|
+
styles.modalContainer,
|
|
199
|
+
dialogOverlay,
|
|
200
|
+
{ paddingTop: safePaddingTop, paddingBottom: safePaddingBottom },
|
|
201
|
+
]}
|
|
202
|
+
>
|
|
188
203
|
<View style={dialogStyles}>
|
|
189
|
-
|
|
204
|
+
{/* Buttons stay outside the scroll area so they are always reachable. */}
|
|
205
|
+
<ScrollView style={styles.dialogContent} nestedScrollEnabled>
|
|
190
206
|
<View style={[styles.row, styles.fitContent]}>
|
|
191
207
|
<Text style={dialogForeground} testID="dialog-message">
|
|
192
208
|
<Message />
|
|
@@ -256,7 +272,7 @@ export const Dialog = (): ReactElement<Modal> => {
|
|
|
256
272
|
))}
|
|
257
273
|
</View>
|
|
258
274
|
) : null}
|
|
259
|
-
</
|
|
275
|
+
</ScrollView>
|
|
260
276
|
<Buttons
|
|
261
277
|
style={windowWidth >= 768 ? styles.dialogRow : styles.dialogCol}
|
|
262
278
|
buttonStyle={
|
|
@@ -325,6 +341,7 @@ const styles = StyleSheet.create({
|
|
|
325
341
|
justifyContent: 'center',
|
|
326
342
|
},
|
|
327
343
|
dialogContent: {
|
|
344
|
+
flexShrink: 1,
|
|
328
345
|
marginBottom: 16,
|
|
329
346
|
},
|
|
330
347
|
bannerLogo: {
|