@digia-engage/core 1.0.0-beta.4 → 1.0.0-beta.6

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.
Files changed (44) hide show
  1. package/DigiaEngageReactNative.podspec +24 -8
  2. package/README.md +8 -17
  3. package/android/.project +28 -0
  4. package/android/build.gradle +1 -1
  5. package/android/settings.gradle +1 -3
  6. package/android/src/main/java/com/digia/engage/rn/DigiaModule.kt +1 -1
  7. package/android/src/main/java/com/digia/engage/rn/DigiaSlotViewManager.kt +146 -31
  8. package/ios/DigiaEngageModule.m +25 -44
  9. package/ios/DigiaHostViewManager.swift +128 -0
  10. package/ios/DigiaModule.swift +241 -0
  11. package/ios/DigiaSlotViewManager.swift +275 -0
  12. package/ios/RNEventBridgePlugin.swift +71 -0
  13. package/lib/commonjs/Digia.js +50 -0
  14. package/lib/commonjs/Digia.js.map +1 -1
  15. package/lib/commonjs/DigiaHostView.js +6 -50
  16. package/lib/commonjs/DigiaHostView.js.map +1 -1
  17. package/lib/commonjs/DigiaSlotView.js +37 -54
  18. package/lib/commonjs/DigiaSlotView.js.map +1 -1
  19. package/lib/commonjs/NativeDigiaEngage.js.map +1 -1
  20. package/lib/module/Digia.js +50 -0
  21. package/lib/module/Digia.js.map +1 -1
  22. package/lib/module/DigiaHostView.js +6 -51
  23. package/lib/module/DigiaHostView.js.map +1 -1
  24. package/lib/module/DigiaSlotView.js +37 -52
  25. package/lib/module/DigiaSlotView.js.map +1 -1
  26. package/lib/module/NativeDigiaEngage.js.map +1 -1
  27. package/lib/typescript/Digia.d.ts +12 -0
  28. package/lib/typescript/Digia.d.ts.map +1 -1
  29. package/lib/typescript/DigiaHostView.d.ts +2 -28
  30. package/lib/typescript/DigiaHostView.d.ts.map +1 -1
  31. package/lib/typescript/DigiaSlotView.d.ts +3 -39
  32. package/lib/typescript/DigiaSlotView.d.ts.map +1 -1
  33. package/lib/typescript/NativeDigiaEngage.d.ts.map +1 -1
  34. package/lib/typescript/index.d.ts +1 -1
  35. package/lib/typescript/index.d.ts.map +1 -1
  36. package/lib/typescript/types.d.ts +21 -0
  37. package/lib/typescript/types.d.ts.map +1 -1
  38. package/package.json +8 -18
  39. package/src/Digia.ts +60 -1
  40. package/src/DigiaHostView.tsx +7 -48
  41. package/src/DigiaSlotView.tsx +42 -49
  42. package/src/NativeDigiaEngage.ts +1 -0
  43. package/src/index.ts +1 -1
  44. package/src/types.ts +30 -0
@@ -1,49 +1,13 @@
1
1
  /**
2
2
  * DigiaSlotView
3
3
  *
4
- * A React Native view that renders inline campaign content (banners, cards,
5
- * widgets) at a specific placement position inside your screen layout.
6
- *
7
- * On Android this mounts a Jetpack Compose `DigiaSlot` composable that
8
- * observes the slot payload for the given placement key and renders the
9
- * matching campaign component. On iOS it is a transparent no-op.
10
- *
11
- * ─── Usage ───────────────────────────────────────────────────────────────────
12
- * ```tsx
13
- * import { DigiaSlotView } from '@digia/engage-react-native';
14
- *
15
- * // Fixed height (you control the space)
16
- * <DigiaSlotView
17
- * placementKey="hero_banner"
18
- * style={{ width: '100%', height: 200 }}
19
- * />
20
- *
21
- * // Inside a scroll view
22
- * <ScrollView>
23
- * <ProductList />
24
- * <DigiaSlotView
25
- * placementKey="pdp_mid_banner"
26
- * style={{ width: '100%', height: 120 }}
27
- * />
28
- * <RelatedProducts />
29
- * </ScrollView>
30
- * ```
31
- *
32
- * The `placementKey` must match the key the marketer selects when creating
33
- * inline content on the Digia dashboard. The view collapses to nothing when
34
- * no campaign is active for that key.
35
- *
36
- * ─── Sizing ──────────────────────────────────────────────────────────────────
37
- * React Native's layout system controls the dimensions of this view.
38
- * Provide an explicit `height` (or flex) via the `style` prop so that the
39
- * native Compose layer has space to render. When no campaign is active the
40
- * Compose `DigiaSlot` renders nothing inside that space.
41
- * ─────────────────────────────────────────────────────────────────────────────
4
+ * Renders inline campaign content at a placement position.
5
+ * Auto-sizes to match native content height via `onContentSizeChange`;
6
+ * pass an explicit `height` in `style` to fix the size instead.
42
7
  */
43
8
  import React from 'react';
44
9
  import { type StyleProp, type ViewStyle } from 'react-native';
45
10
  interface DigiaSlotViewProps {
46
- /** Placement key that links this view to a Digia dashboard slot. */
47
11
  placementKey: string;
48
12
  style?: StyleProp<ViewStyle>;
49
13
  }
@@ -1 +1 @@
1
- {"version":3,"file":"DigiaSlotView.d.ts","sourceRoot":"","sources":["../../src/DigiaSlotView.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAGH,KAAK,SAAS,EACd,KAAK,SAAS,EACjB,MAAM,cAAc,CAAC;AAEtB,UAAU,kBAAkB;IACxB,oEAAoE;IACpE,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAChC;AAWD,wBAAgB,aAAa,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,EAAE,kBAAkB,4BAYxE"}
1
+ {"version":3,"file":"DigiaSlotView.d.ts","sourceRoot":"","sources":["../../src/DigiaSlotView.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAgC,MAAM,OAAO,CAAC;AACrD,OAAO,EAIH,KAAK,SAAS,EACd,KAAK,SAAS,EACjB,MAAM,cAAc,CAAC;AAEtB,UAAU,kBAAkB;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAChC;AAYD,wBAAgB,aAAa,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,EAAE,kBAAkB,4BAuCxE"}
@@ -1 +1 @@
1
- {"version":3,"file":"NativeDigiaEngage.d.ts","sourceRoot":"","sources":["../../src/NativeDigiaEngage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD;;;;;;;GAOG;AACH,MAAM,WAAW,IAAK,SAAQ,WAAW;IACrC,0DAA0D;IAC1D,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjF;;;;OAIG;IACH,cAAc,IAAI,IAAI,CAAC;IAEvB,sDAAsD;IACtD,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAErC;;;OAGG;IACH,eAAe,CACX,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,GACnB,IAAI,CAAC;IAER,iDAAiD;IACjD,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAChD;AAwBD,eAAO,MAAM,iBAAiB,EAAE,IAS/B,CAAC"}
1
+ {"version":3,"file":"NativeDigiaEngage.d.ts","sourceRoot":"","sources":["../../src/NativeDigiaEngage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD;;;;;;;GAOG;AACH,MAAM,WAAW,IAAK,SAAQ,WAAW;IACrC,0DAA0D;IAC1D,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjF;;;;OAIG;IACH,cAAc,IAAI,IAAI,CAAC;IAEvB,sDAAsD;IACtD,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAErC;;;OAGG;IACH,eAAe,CACX,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,GACnB,IAAI,CAAC;IAER,iDAAiD;IACjD,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAEhD;AAwBD,eAAO,MAAM,iBAAiB,EAAE,IAS/B,CAAC"}
@@ -11,5 +11,5 @@
11
11
  export { Digia } from './Digia';
12
12
  export { DigiaHostView } from './DigiaHostView';
13
13
  export { DigiaSlotView } from './DigiaSlotView';
14
- export type { DigiaConfig, DigiaDelegate, DigiaPlugin, InAppPayload } from './types';
14
+ export type { DigiaConfig, DigiaDelegate, DigiaExperienceEvent, DigiaPlugin, InAppPayload } from './types';
15
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,oBAAoB,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC"}
@@ -11,6 +11,21 @@ export interface InAppPayload {
11
11
  /** CEP-platform metadata, e.g. { campaignId, campaignName }. */
12
12
  cepContext: Record<string, unknown>;
13
13
  }
14
+ /** The experience became visible to the user. */
15
+ export interface ExperienceImpressed {
16
+ readonly type: 'impressed';
17
+ }
18
+ /** The user interacted with an actionable element. */
19
+ export interface ExperienceClicked {
20
+ readonly type: 'clicked';
21
+ readonly elementId?: string;
22
+ }
23
+ /** The experience was dismissed — by the user or programmatically. */
24
+ export interface ExperienceDismissed {
25
+ readonly type: 'dismissed';
26
+ }
27
+ /** Discriminated union of all experience event types. */
28
+ export type DigiaExperienceEvent = ExperienceImpressed | ExperienceClicked | ExperienceDismissed;
14
29
  /**
15
30
  * Delegate passed by the Digia SDK to each registered plugin via setup().
16
31
  *
@@ -33,6 +48,12 @@ export interface DigiaPlugin {
33
48
  readonly identifier: string;
34
49
  /** Called by Digia.register() — do not call manually. */
35
50
  setup(delegate: DigiaDelegate): void;
51
+ /**
52
+ * Called by the Digia SDK when the overlay fires a lifecycle event
53
+ * (impressed / clicked / dismissed). Plugins use this to report
54
+ * analytics back to their CEP platform.
55
+ */
56
+ notifyEvent(event: DigiaExperienceEvent, payload: InAppPayload): void;
36
57
  /** Called by Digia.setCurrentScreen() — do not call manually. */
37
58
  forwardScreen(name: string): void;
38
59
  /** Called by Digia.unregister() or when tearing down the app. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,YAAY;IACzB,gDAAgD;IAChD,EAAE,EAAE,MAAM,CAAC;IACX,yDAAyD;IACzD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC1B,kEAAkE;IAClE,mBAAmB,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;IACjD,iDAAiD;IACjD,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CACnD;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,yDAAyD;IACzD,KAAK,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IACrC,iEAAiE;IACjE,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,iEAAiE;IACjE,QAAQ,IAAI,IAAI,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IACxB,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,WAAW,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;IACvC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;CAC3C"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,YAAY;IACzB,gDAAgD;IAChD,EAAE,EAAE,MAAM,CAAC;IACX,yDAAyD;IACzD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAID,iDAAiD;AACjD,MAAM,WAAW,mBAAmB;IAChC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;CAC9B;AAED,sDAAsD;AACtD,MAAM,WAAW,iBAAiB;IAC9B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,sEAAsE;AACtE,MAAM,WAAW,mBAAmB;IAChC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;CAC9B;AAED,yDAAyD;AACzD,MAAM,MAAM,oBAAoB,GAC1B,mBAAmB,GACnB,iBAAiB,GACjB,mBAAmB,CAAC;AAE1B;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC1B,kEAAkE;IAClE,mBAAmB,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;IACjD,iDAAiD;IACjD,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CACnD;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,yDAAyD;IACzD,KAAK,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IACrC;;;;OAIG;IACH,WAAW,CAAC,KAAK,EAAE,oBAAoB,EAAE,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;IACtE,iEAAiE;IACjE,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,iEAAiE;IACjE,QAAQ,IAAI,IAAI,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IACxB,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,WAAW,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;IACvC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;CAC3C"}
package/package.json CHANGED
@@ -1,19 +1,12 @@
1
1
  {
2
2
  "name": "@digia-engage/core",
3
- "version": "1.0.0-beta.4",
3
+ "version": "1.0.0-beta.6",
4
4
  "description": "React Native bridge for Digia Engage – renders native Android Compose UI inside React Native apps",
5
- "main": "lib/commonjs/index",
6
- "module": "lib/module/index",
7
- "types": "lib/typescript/index.d.ts",
8
- "react-native": "src/index",
9
- "source": "src/index",
10
- "exports": {
11
- ".": {
12
- "import": "./lib/module/index.js",
13
- "require": "./lib/commonjs/index.js",
14
- "types": "./lib/typescript/index.d.ts"
15
- }
16
- },
5
+ "main": "src/index.ts",
6
+ "module": "src/index.ts",
7
+ "types": "src/index.ts",
8
+ "react-native": "src/index.ts",
9
+ "source": "src/index.ts",
17
10
  "files": [
18
11
  "src",
19
12
  "lib",
@@ -31,7 +24,7 @@
31
24
  ],
32
25
  "scripts": {
33
26
  "typecheck": "tsc --noEmit",
34
- "lint": "eslint \"**/*.{js,ts,tsx}\"",
27
+ "lint": "eslint \"src/**/*.{js,ts,tsx}\"",
35
28
  "build": "bob build",
36
29
  "prepare": "bob build"
37
30
  },
@@ -50,10 +43,7 @@
50
43
  },
51
44
  "author": "Digia Technology Private Limited",
52
45
  "license": "MIT",
53
- "peerDependencies": {
54
- "react": "*",
55
- "react-native": "*"
56
- },
46
+ "peerDependencies": {},
57
47
  "devDependencies": {
58
48
  "@react-native/eslint-config": "^0.73.0",
59
49
  "@types/react": "^18.2.0",
package/src/Digia.ts CHANGED
@@ -15,14 +15,25 @@
15
15
  * ```
16
16
  */
17
17
 
18
+ import { DeviceEventEmitter } from 'react-native';
18
19
  import { nativeDigiaModule } from './NativeDigiaEngage';
19
- import type { DigiaConfig, DigiaDelegate, DigiaPlugin, InAppPayload } from './types';
20
+ import type {
21
+ DigiaConfig,
22
+ DigiaDelegate,
23
+ DigiaExperienceEvent,
24
+ DigiaPlugin,
25
+ InAppPayload,
26
+ } from './types';
20
27
 
21
28
  class DigiaClass implements DigiaDelegate {
22
29
  private readonly _plugins = new Map<string, DigiaPlugin>();
23
30
  // Tracks whether the native bridge plugin (RNEventBridgePlugin) has been
24
31
  // wired to the native SDK. Done once on the first Digia.register() call.
25
32
  private _nativeBridgeWired = false;
33
+ // Cache of triggered payloads keyed by campaign ID, used to reconstruct
34
+ // the full InAppPayload when overlay lifecycle events arrive from native.
35
+ private readonly _activePayloads = new Map<string, InAppPayload>();
36
+ private _engageSubscription: { remove(): void } | null = null;
26
37
 
27
38
  /**
28
39
  * Initialise the Digia Engage SDK.
@@ -59,6 +70,7 @@ class DigiaClass implements DigiaDelegate {
59
70
  // so the delegate is ready when JS campaigns start flowing.
60
71
  if (!this._nativeBridgeWired) {
61
72
  nativeDigiaModule.registerBridge();
73
+ this._startEngageListener();
62
74
  this._nativeBridgeWired = true;
63
75
  }
64
76
  plugin.setup(this);
@@ -87,18 +99,65 @@ class DigiaClass implements DigiaDelegate {
87
99
  this._plugins.forEach((plugin) => plugin.forwardScreen(name));
88
100
  }
89
101
 
102
+
90
103
  // ── DigiaDelegate ────────────────────────────────────────────────────────
91
104
  // Mirrors DigiaCEPDelegate on Android.
92
105
  // Forwards to the native DigiaCEPDelegate via the bridge.
93
106
 
94
107
  onCampaignTriggered(payload: InAppPayload): void {
108
+ this._activePayloads.set(payload.id, payload);
95
109
  nativeDigiaModule.triggerCampaign(payload.id, payload.content, payload.cepContext);
96
110
  }
97
111
 
98
112
  onCampaignInvalidated(campaignId: string): void {
113
+ this._activePayloads.delete(campaignId);
99
114
  nativeDigiaModule.invalidateCampaign(campaignId);
100
115
  }
101
116
 
117
+ // ── Overlay event forwarding ─────────────────────────────────────────────
118
+
119
+ /**
120
+ * Subscribes to `digiaOverlayEvent` emitted by the native
121
+ * RNEventBridgePlugin when the Compose overlay fires a lifecycle event
122
+ * (impressed / clicked / dismissed).
123
+ *
124
+ * Each event is forwarded to every registered plugin's notifyEvent() so
125
+ * that CEP plugins (e.g. WebEngagePlugin) can report analytics.
126
+ */
127
+ private _startEngageListener(): void {
128
+ if (this._engageSubscription) return;
129
+ this._engageSubscription = DeviceEventEmitter.addListener(
130
+ 'digiaEngageEvent',
131
+ (data: { campaignId: string; type: string; elementId?: string }) =>
132
+ this._forwardExperienceEvent(data),
133
+ );
134
+ }
135
+
136
+ private _forwardExperienceEvent(
137
+ data: { campaignId: string; type: string; elementId?: string },
138
+ ): void {
139
+ const payload = this._activePayloads.get(data.campaignId);
140
+ if (!payload) return;
141
+
142
+ let event: DigiaExperienceEvent;
143
+ switch (data.type) {
144
+ case 'impressed':
145
+ event = { type: 'impressed' };
146
+ break;
147
+ case 'clicked':
148
+ event = { type: 'clicked', elementId: data.elementId };
149
+ break;
150
+ case 'dismissed':
151
+ event = { type: 'dismissed' };
152
+ this._activePayloads.delete(data.campaignId);
153
+ break;
154
+ default:
155
+ return;
156
+ }
157
+
158
+ this._plugins.forEach((plugin) => plugin.notifyEvent(event, payload));
159
+ }
160
+
102
161
  }
103
162
 
104
163
  export const Digia = new DigiaClass();
@@ -1,34 +1,8 @@
1
1
  /**
2
2
  * DigiaHostView
3
3
  *
4
- * A transparent React Native view that attaches the Digia Android Compose
5
- * overlay layer (dialogs, bottom sheets) on top of your app's content.
6
- *
7
- * ─── Usage ───────────────────────────────────────────────────────────────────
8
- * Place `<DigiaHostView>` at the **root** of your component tree so that
9
- * Digia dialogs and bottom sheets can stack on top of all your content.
10
- *
11
- * ```tsx
12
- * import React from 'react';
13
- * import { StyleSheet, View } from 'react-native';
14
- * import { DigiaHostView } from '@digia/engage-react-native';
15
- *
16
- * export default function App() {
17
- * return (
18
- * <View style={styles.root}>
19
- * <DigiaHostView style={StyleSheet.absoluteFill} />
20
- * {/ * your navigation / app content here * /}
21
- * </View>
22
- * );
23
- * }
24
- *
25
- * const styles = StyleSheet.create({ root: { flex: 1 } });
26
- * ```
27
- *
28
- * On Android this mounts a Jetpack Compose `DigiaHost` composable that
29
- * manages dialog + bottom-sheet presentation triggered by CEP plugins.
30
- * On iOS the view is a transparent no-op until iOS support is implemented.
31
- * ─────────────────────────────────────────────────────────────────────────────
4
+ * Transparent full-screen overlay that hosts Digia dialogs and bottom sheets.
5
+ * Place at the root of your component tree so overlays stack above all content.
32
6
  */
33
7
 
34
8
  import React from 'react';
@@ -44,40 +18,25 @@ interface DigiaHostViewProps {
44
18
  style?: StyleProp<ViewStyle>;
45
19
  }
46
20
 
47
- // requireNativeComponent expects a plain ViewStyle, not StyleProp.
48
21
  interface NativeDigiaHostViewProps {
49
22
  style?: ViewStyle;
23
+ pointerEvents?: 'auto' | 'none' | 'box-none' | 'box-only';
50
24
  }
51
25
 
52
- // Fabric (New Architecture) resolves view configs lazily — no UIManager
53
- // guard needed. requireNativeComponent is called unconditionally on Android.
54
26
  const NativeDigiaHostView =
55
- Platform.OS === 'android'
27
+ Platform.OS === 'android' || Platform.OS === 'ios'
56
28
  ? requireNativeComponent<NativeDigiaHostViewProps>('DigiaHostView')
57
29
  : null;
58
30
 
59
- // ── DigiaHostView ─────────────────────────────────────────────────────────────
60
-
61
31
  export function DigiaHostView({ style }: DigiaHostViewProps) {
62
- if (Platform.OS === 'android' && NativeDigiaHostView) {
63
- // The native Compose DigiaHost renders dialogs / bottom sheets that
64
- // float above the view hierarchy on their own — the host view only
65
- // needs to be mounted in the tree, not take up any screen space.
32
+ if ((Platform.OS === 'android' || Platform.OS === 'ios') && NativeDigiaHostView) {
66
33
  return (
67
34
  <NativeDigiaHostView
68
- style={StyleSheet.flatten([styles.host, style])}
35
+ pointerEvents="none"
36
+ style={StyleSheet.flatten([StyleSheet.absoluteFillObject, style])}
69
37
  />
70
38
  );
71
39
  }
72
40
 
73
- // iOS / other platforms: no-op, nothing to mount.
74
41
  return null;
75
42
  }
76
-
77
- const styles = StyleSheet.create({
78
- host: {
79
- width: 0,
80
- height: 0,
81
- overflow: 'hidden',
82
- },
83
- });
@@ -1,79 +1,72 @@
1
1
  /**
2
2
  * DigiaSlotView
3
3
  *
4
- * A React Native view that renders inline campaign content (banners, cards,
5
- * widgets) at a specific placement position inside your screen layout.
6
- *
7
- * On Android this mounts a Jetpack Compose `DigiaSlot` composable that
8
- * observes the slot payload for the given placement key and renders the
9
- * matching campaign component. On iOS it is a transparent no-op.
10
- *
11
- * ─── Usage ───────────────────────────────────────────────────────────────────
12
- * ```tsx
13
- * import { DigiaSlotView } from '@digia/engage-react-native';
14
- *
15
- * // Fixed height (you control the space)
16
- * <DigiaSlotView
17
- * placementKey="hero_banner"
18
- * style={{ width: '100%', height: 200 }}
19
- * />
20
- *
21
- * // Inside a scroll view
22
- * <ScrollView>
23
- * <ProductList />
24
- * <DigiaSlotView
25
- * placementKey="pdp_mid_banner"
26
- * style={{ width: '100%', height: 120 }}
27
- * />
28
- * <RelatedProducts />
29
- * </ScrollView>
30
- * ```
31
- *
32
- * The `placementKey` must match the key the marketer selects when creating
33
- * inline content on the Digia dashboard. The view collapses to nothing when
34
- * no campaign is active for that key.
35
- *
36
- * ─── Sizing ──────────────────────────────────────────────────────────────────
37
- * React Native's layout system controls the dimensions of this view.
38
- * Provide an explicit `height` (or flex) via the `style` prop so that the
39
- * native Compose layer has space to render. When no campaign is active the
40
- * Compose `DigiaSlot` renders nothing inside that space.
41
- * ─────────────────────────────────────────────────────────────────────────────
4
+ * Renders inline campaign content at a placement position.
5
+ * Auto-sizes to match native content height via `onContentSizeChange`;
6
+ * pass an explicit `height` in `style` to fix the size instead.
42
7
  */
43
8
 
44
- import React from 'react';
9
+ import React, { useCallback, useState } from 'react';
45
10
  import {
46
11
  Platform,
12
+ StyleSheet,
47
13
  requireNativeComponent,
48
14
  type StyleProp,
49
15
  type ViewStyle,
50
16
  } from 'react-native';
51
17
 
52
18
  interface DigiaSlotViewProps {
53
- /** Placement key that links this view to a Digia dashboard slot. */
54
19
  placementKey: string;
55
20
  style?: StyleProp<ViewStyle>;
56
21
  }
57
22
 
58
- // Fabric (New Architecture) resolves view configs lazily — no UIManager
59
- // guard needed. requireNativeComponent is called unconditionally on Android.
23
+ interface NativeDigiaSlotViewProps extends DigiaSlotViewProps {
24
+ onContentSizeChange?: (event: { nativeEvent: { height: number } }) => void;
25
+ collapsable?: boolean;
26
+ }
27
+
60
28
  const NativeDigiaSlotView =
61
- Platform.OS === 'android'
62
- ? requireNativeComponent<DigiaSlotViewProps>('DigiaSlotView')
29
+ Platform.OS === 'android' || Platform.OS === 'ios'
30
+ ? requireNativeComponent<NativeDigiaSlotViewProps>('DigiaSlotView')
63
31
  : null;
64
32
 
65
- // ── DigiaSlotView ─────────────────────────────────────────────────────────────
66
-
67
33
  export function DigiaSlotView({ placementKey, style }: DigiaSlotViewProps) {
68
- if (Platform.OS === 'android' && NativeDigiaSlotView) {
34
+ const [contentHeight, setContentHeight] = useState(0);
35
+
36
+ const onContentSizeChange = useCallback(
37
+ (event: { nativeEvent: { height: number } }) => {
38
+ const h = event.nativeEvent.height ?? 0;
39
+ setContentHeight(Math.max(0, h));
40
+ },
41
+ [],
42
+ );
43
+
44
+ if ((Platform.OS === 'android' || Platform.OS === 'ios') && NativeDigiaSlotView) {
45
+ const flatStyle = StyleSheet.flatten(style) || {};
46
+ const hasExplicitHeight = flatStyle.height !== undefined;
47
+
48
+ if (hasExplicitHeight) {
49
+ return (
50
+ <NativeDigiaSlotView
51
+ placementKey={placementKey}
52
+ style={[{ width: '100%' }, style]}
53
+ {...(Platform.OS === 'android' ? { collapsable: false } : {})}
54
+ />
55
+ );
56
+ }
57
+
58
+ // 1dp bootstrap ensures a real layout pass before any campaign arrives.
59
+ const bootstrapHeight = Math.max(contentHeight, 1);
60
+
69
61
  return (
70
62
  <NativeDigiaSlotView
71
63
  placementKey={placementKey}
72
- style={style}
64
+ style={[{ width: '100%', height: bootstrapHeight }, style]}
65
+ onContentSizeChange={onContentSizeChange}
66
+ {...(Platform.OS === 'android' ? { collapsable: false } : {})}
73
67
  />
74
68
  );
75
69
  }
76
70
 
77
- // iOS / other platforms: not yet implemented — render nothing.
78
71
  return null;
79
72
  }
@@ -50,6 +50,7 @@ export interface Spec extends TurboModule {
50
50
 
51
51
  /** Invalidate / dismiss a campaign by its ID. */
52
52
  invalidateCampaign(campaignId: string): void;
53
+
53
54
  }
54
55
 
55
56
  // Try TurboModuleRegistry first (New Architecture / JSI).
package/src/index.ts CHANGED
@@ -12,4 +12,4 @@
12
12
  export { Digia } from './Digia';
13
13
  export { DigiaHostView } from './DigiaHostView';
14
14
  export { DigiaSlotView } from './DigiaSlotView';
15
- export type { DigiaConfig, DigiaDelegate, DigiaPlugin, InAppPayload } from './types';
15
+ export type { DigiaConfig, DigiaDelegate, DigiaExperienceEvent, DigiaPlugin, InAppPayload } from './types';
package/src/types.ts CHANGED
@@ -12,6 +12,30 @@ export interface InAppPayload {
12
12
  cepContext: Record<string, unknown>;
13
13
  }
14
14
 
15
+ // ─── Experience events ────────────────────────────────────────────────────────
16
+
17
+ /** The experience became visible to the user. */
18
+ export interface ExperienceImpressed {
19
+ readonly type: 'impressed';
20
+ }
21
+
22
+ /** The user interacted with an actionable element. */
23
+ export interface ExperienceClicked {
24
+ readonly type: 'clicked';
25
+ readonly elementId?: string;
26
+ }
27
+
28
+ /** The experience was dismissed — by the user or programmatically. */
29
+ export interface ExperienceDismissed {
30
+ readonly type: 'dismissed';
31
+ }
32
+
33
+ /** Discriminated union of all experience event types. */
34
+ export type DigiaExperienceEvent =
35
+ | ExperienceImpressed
36
+ | ExperienceClicked
37
+ | ExperienceDismissed;
38
+
15
39
  /**
16
40
  * Delegate passed by the Digia SDK to each registered plugin via setup().
17
41
  *
@@ -35,6 +59,12 @@ export interface DigiaPlugin {
35
59
  readonly identifier: string;
36
60
  /** Called by Digia.register() — do not call manually. */
37
61
  setup(delegate: DigiaDelegate): void;
62
+ /**
63
+ * Called by the Digia SDK when the overlay fires a lifecycle event
64
+ * (impressed / clicked / dismissed). Plugins use this to report
65
+ * analytics back to their CEP platform.
66
+ */
67
+ notifyEvent(event: DigiaExperienceEvent, payload: InAppPayload): void;
38
68
  /** Called by Digia.setCurrentScreen() — do not call manually. */
39
69
  forwardScreen(name: string): void;
40
70
  /** Called by Digia.unregister() or when tearing down the app. */