@ezoic/react-native-sdk 1.11.0 → 1.13.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.
@@ -0,0 +1,69 @@
1
+ /** Which button the user closed the consent dialog with. */
2
+ export type EzoicConsentDecision = 'acceptAll' | 'rejectAll' | 'custom';
3
+
4
+ /**
5
+ * Result of `EzoicAds.presentConsentIfRequired` / `presentConsentSettings`.
6
+ *
7
+ * - `notRequired`: GDPR doesn't apply, the built-in CMP is disabled, another
8
+ * CMP owns consent, or consent is managed by the app (`setGDPRConsent`, or
9
+ * `autoReadConsent: false`).
10
+ * - `alreadyDecided`: a still-valid decision is stored; no dialog was shown.
11
+ * - `decided`: the user made a choice, which has been saved.
12
+ * - `dismissed`: the dialog closed without a choice; ads stay gated for this
13
+ * session.
14
+ * - `alreadyPresenting`: a consent dialog is already on screen or being
15
+ * prepared.
16
+ * - `failed`: the dialog could not be shown. `code` is the native
17
+ * `EzoicError` code, or `-1` (`'No foreground Activity'`) when the wrapper
18
+ * had no foreground Activity / view controller: native was not called, ads
19
+ * stay gated, and you should call again once a screen is showing.
20
+ */
21
+ export type EzoicConsentOutcome =
22
+ | { type: 'notRequired' }
23
+ | { type: 'alreadyDecided' }
24
+ | { type: 'dismissed' }
25
+ | { type: 'alreadyPresenting' }
26
+ | { type: 'decided'; decision: EzoicConsentDecision }
27
+ | { type: 'failed'; code: number; message: string };
28
+
29
+ const SIMPLE_TYPES = [
30
+ 'notRequired',
31
+ 'alreadyDecided',
32
+ 'dismissed',
33
+ 'alreadyPresenting',
34
+ ] as const;
35
+
36
+ const DECISIONS: readonly string[] = ['acceptAll', 'rejectAll', 'custom'];
37
+
38
+ export function consentFailure(
39
+ code: number,
40
+ message: string
41
+ ): EzoicConsentOutcome {
42
+ return { type: 'failed', code, message };
43
+ }
44
+
45
+ /**
46
+ * Maps the native glue's outcome map to `EzoicConsentOutcome`. Anything that
47
+ * doesn't match the wire format becomes `failed(-1, 'Unrecognized outcome')`.
48
+ */
49
+ export function parseConsentOutcome(raw: unknown): EzoicConsentOutcome {
50
+ if (raw && typeof raw === 'object' && !Array.isArray(raw)) {
51
+ const { type, decision, code, message } = raw as Record<string, unknown>;
52
+ const simple = SIMPLE_TYPES.find((t) => t === type);
53
+ if (simple) return { type: simple };
54
+ if (
55
+ type === 'decided' &&
56
+ typeof decision === 'string' &&
57
+ DECISIONS.includes(decision)
58
+ ) {
59
+ return { type: 'decided', decision: decision as EzoicConsentDecision };
60
+ }
61
+ if (type === 'failed') {
62
+ return consentFailure(
63
+ typeof code === 'number' ? code : -1,
64
+ typeof message === 'string' ? message : 'Unknown error'
65
+ );
66
+ }
67
+ }
68
+ return consentFailure(-1, 'Unrecognized outcome');
69
+ }
@@ -8,6 +8,12 @@ export interface EzoicConfig {
8
8
  requestATTBeforeAds?: boolean;
9
9
  debugEnabled?: boolean;
10
10
  testMode?: boolean;
11
+ /** Record a pageview automatically on native screen changes. Default `true`. */
12
+ autoTrackPageviews?: boolean;
13
+ /** Enable the built-in TCF CMP for GDPR regions. Default `true`; set `false` if you run your own CMP. */
14
+ cmpEnabled?: boolean;
15
+ /** Present the consent dialog (if required) right after `initialize` succeeds. Default `true`. */
16
+ autoPresentConsent?: boolean;
11
17
  }
12
18
 
13
19
  /**
@@ -22,12 +28,31 @@ export interface EzoicRewardResult {
22
28
  amount: number;
23
29
  }
24
30
 
31
+ /**
32
+ * Wire format of a consent outcome from the native glue (see
33
+ * `parseConsentOutcome`): `type` is one of `notRequired`, `alreadyDecided`,
34
+ * `dismissed`, `alreadyPresenting`, `decided` (with `decision`) or `failed`
35
+ * (with `code` and `message`).
36
+ */
37
+ export interface EzoicConsentOutcomeRaw {
38
+ type: string;
39
+ decision?: string;
40
+ code?: number;
41
+ message?: string;
42
+ }
43
+
25
44
  export interface Spec extends TurboModule {
26
45
  initialize(config: EzoicConfig): Promise<void>;
27
46
  setGDPRConsent(applies: boolean, consentString?: string): void;
28
47
  setGPPConsent(gppString?: string, sectionIds?: string): void;
29
48
  setSubjectToCOPPA(value: boolean): void;
30
- trackPageview(): Promise<boolean>;
49
+ trackPageview(screen: string | null): Promise<boolean>;
50
+ // Consent: the present* promises always resolve (never reject); a missing
51
+ // host Activity / view controller resolves as failed(-1).
52
+ presentConsentIfRequired(): Promise<EzoicConsentOutcomeRaw>;
53
+ presentConsentSettings(): Promise<EzoicConsentOutcomeRaw>;
54
+ isConsentRequired(): Promise<boolean | null>;
55
+ resetConsent(): void;
31
56
  loadRewardedAd(adUnitIdentifier: string): Promise<void>;
32
57
  showRewardedAd(
33
58
  adUnitIdentifier: string,
package/src/helpers.ts CHANGED
@@ -13,6 +13,11 @@ export function normalizeConfig(config: EzoicConfig): EzoicConfig {
13
13
  out.requestATTBeforeAds = config.requestATTBeforeAds;
14
14
  if (config.debugEnabled !== undefined) out.debugEnabled = config.debugEnabled;
15
15
  if (config.testMode !== undefined) out.testMode = config.testMode;
16
+ if (config.autoTrackPageviews !== undefined)
17
+ out.autoTrackPageviews = config.autoTrackPageviews;
18
+ if (config.cmpEnabled !== undefined) out.cmpEnabled = config.cmpEnabled;
19
+ if (config.autoPresentConsent !== undefined)
20
+ out.autoPresentConsent = config.autoPresentConsent;
16
21
  return out;
17
22
  }
18
23
 
package/src/index.tsx CHANGED
@@ -5,8 +5,14 @@ import EzoicBannerNative from './EzoicBannerViewNativeComponent';
5
5
  import EzoicNativeAdNative from './EzoicNativeAdViewNativeComponent';
6
6
  import EzoicOutstreamNative from './EzoicOutstreamAdViewNativeComponent';
7
7
  import { coerceAdUnitId, normalizeConfig, normalizeSize } from './helpers';
8
+ import {
9
+ consentFailure,
10
+ parseConsentOutcome,
11
+ type EzoicConsentOutcome,
12
+ } from './EzoicConsent';
8
13
 
9
14
  export type { EzoicConfig };
15
+ export type { EzoicConsentDecision, EzoicConsentOutcome } from './EzoicConsent';
10
16
  export {
11
17
  EzoicRewardedAd,
12
18
  type EzoicReward,
@@ -22,6 +28,27 @@ export {
22
28
  type EzoicInstreamImpressionOptions,
23
29
  } from './EzoicInstreamAd';
24
30
 
31
+ /**
32
+ * Numeric native error codes: `code` on ad-view `onError` events, and
33
+ * `error.userInfo.code` on rejected rewarded / interstitial / instream
34
+ * `load()` promises (whose own `code` is the string `'EzoicAds'`).
35
+ */
36
+ export const EzoicErrorCode = {
37
+ /**
38
+ * GDPR applies and the user hasn't decided: the ad load waited for the
39
+ * consent dialog and timed out. Call `EzoicAds.presentConsentIfRequired()`.
40
+ */
41
+ consentRequired: 5001,
42
+ } as const;
43
+
44
+ function presentConsent(
45
+ call: () => Promise<unknown>
46
+ ): Promise<EzoicConsentOutcome> {
47
+ return call().then(parseConsentOutcome, (e: unknown) =>
48
+ consentFailure(-1, e instanceof Error ? e.message : String(e))
49
+ );
50
+ }
51
+
25
52
  export const EzoicAds = {
26
53
  initialize(config: EzoicConfig): Promise<void> {
27
54
  return NativeEzoicAds.initialize(normalizeConfig(config));
@@ -35,8 +62,41 @@ export const EzoicAds = {
35
62
  setSubjectToCOPPA(value: boolean): void {
36
63
  NativeEzoicAds.setSubjectToCOPPA(value);
37
64
  },
38
- trackPageview(): Promise<boolean> {
39
- return NativeEzoicAds.trackPageview();
65
+ /**
66
+ * Records a pageview. Pass a `screen` label (e.g. `'Home'`,
67
+ * `'members/profile'`) to name the screen in Ezoic reporting; without one
68
+ * the pageview lands on a single app-wide bucket.
69
+ */
70
+ trackPageview(screen?: string): Promise<boolean> {
71
+ return NativeEzoicAds.trackPageview(screen ?? null);
72
+ },
73
+ /**
74
+ * Shows the built-in consent dialog if this user must decide (GDPR applies,
75
+ * no valid stored decision). Runs automatically after `initialize` unless
76
+ * `autoPresentConsent: false`; repeat calls are harmless. Always resolves.
77
+ */
78
+ presentConsentIfRequired(): Promise<EzoicConsentOutcome> {
79
+ return presentConsent(() => NativeEzoicAds.presentConsentIfRequired());
80
+ },
81
+ /**
82
+ * Re-opens the consent dialog with the user's stored choices. TCF requires
83
+ * a persistent "Privacy settings" entry point that calls this. Always
84
+ * resolves.
85
+ */
86
+ presentConsentSettings(): Promise<EzoicConsentOutcome> {
87
+ return presentConsent(() => NativeEzoicAds.presentConsentSettings());
88
+ },
89
+ /**
90
+ * `true` when GDPR applies and the built-in CMP handles consent, `false`
91
+ * otherwise, `null` until the init request completes or when the server
92
+ * sent no consent information.
93
+ */
94
+ isConsentRequired(): Promise<boolean | null> {
95
+ return NativeEzoicAds.isConsentRequired().then((v) => v ?? null);
96
+ },
97
+ /** Deletes the decision stored by the built-in CMP so the dialog shows again. */
98
+ resetConsent(): void {
99
+ NativeEzoicAds.resetConsent();
40
100
  },
41
101
  };
42
102