@digia-engage/core 2.3.2 → 2.5.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/DigiaEngageReactNative.podspec +1 -1
- package/android/.project +28 -0
- package/android/build.gradle +1 -1
- package/android/local.properties +1 -0
- package/android/src/main/java/com/digia/engage/rn/DigiaModule.kt +64 -6
- package/ios/DigiaEngageModule.m +8 -0
- package/ios/DigiaModule.swift +117 -8
- package/ios/RNEventBridgePlugin.swift +2 -0
- package/lib/commonjs/Digia.js +139 -100
- package/lib/commonjs/Digia.js.map +1 -1
- package/lib/commonjs/DigiaAnchorView.js +11 -1
- package/lib/commonjs/DigiaAnchorView.js.map +1 -1
- package/lib/commonjs/DigiaProvider.js +50 -1
- package/lib/commonjs/DigiaProvider.js.map +1 -1
- package/lib/commonjs/NativeDigiaEngage.js +3 -0
- package/lib/commonjs/NativeDigiaEngage.js.map +1 -1
- package/lib/commonjs/digiaAnchorRegistry.js +3 -1
- package/lib/commonjs/digiaAnchorRegistry.js.map +1 -1
- package/lib/commonjs/frequencyStore.js +3 -3
- package/lib/commonjs/frequencyStore.js.map +1 -1
- package/lib/commonjs/index.js +0 -7
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/Digia.js +139 -100
- package/lib/module/Digia.js.map +1 -1
- package/lib/module/DigiaAnchorView.js +11 -1
- package/lib/module/DigiaAnchorView.js.map +1 -1
- package/lib/module/DigiaProvider.js +50 -1
- package/lib/module/DigiaProvider.js.map +1 -1
- package/lib/module/NativeDigiaEngage.js +3 -0
- package/lib/module/NativeDigiaEngage.js.map +1 -1
- package/lib/module/digiaAnchorRegistry.js +3 -1
- package/lib/module/digiaAnchorRegistry.js.map +1 -1
- package/lib/module/frequencyStore.js +3 -3
- package/lib/module/frequencyStore.js.map +1 -1
- package/lib/module/index.js +4 -4
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/Digia.d.ts +17 -8
- package/lib/typescript/Digia.d.ts.map +1 -1
- package/lib/typescript/DigiaAnchorView.d.ts.map +1 -1
- package/lib/typescript/DigiaProvider.d.ts +3 -1
- package/lib/typescript/DigiaProvider.d.ts.map +1 -1
- package/lib/typescript/NativeDigiaEngage.d.ts +9 -0
- package/lib/typescript/NativeDigiaEngage.d.ts.map +1 -1
- package/lib/typescript/digiaAnchorRegistry.d.ts +1 -0
- package/lib/typescript/digiaAnchorRegistry.d.ts.map +1 -1
- package/lib/typescript/frequencyStore.d.ts +1 -1
- package/lib/typescript/frequencyStore.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +5 -5
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/templateTypes.d.ts +24 -1
- package/lib/typescript/templateTypes.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +17 -13
- package/lib/typescript/types.d.ts.map +1 -1
- package/package.json +8 -9
- package/src/Digia.ts +142 -114
- package/src/DigiaAnchorView.tsx +6 -1
- package/src/DigiaProvider.tsx +56 -1
- package/src/NativeDigiaEngage.ts +20 -0
- package/src/digiaAnchorRegistry.ts +3 -1
- package/src/frequencyStore.ts +4 -4
- package/src/index.ts +5 -5
- package/src/templateTypes.ts +31 -1
- package/src/types.ts +17 -13
package/src/DigiaProvider.tsx
CHANGED
|
@@ -3,17 +3,20 @@ import {
|
|
|
3
3
|
Animated,
|
|
4
4
|
Dimensions,
|
|
5
5
|
Modal,
|
|
6
|
+
Platform,
|
|
6
7
|
Pressable,
|
|
7
8
|
StyleSheet,
|
|
8
9
|
Text,
|
|
9
10
|
View,
|
|
10
11
|
useWindowDimensions,
|
|
11
12
|
} from 'react-native';
|
|
13
|
+
|
|
12
14
|
import { computePosition, flip, offset, shift } from '@floating-ui/core';
|
|
13
15
|
import Svg, { Path } from 'react-native-svg';
|
|
14
16
|
import { Digia } from './Digia';
|
|
15
17
|
import { digiaGuideController, type DigiaGuideRequest } from './DigiaGuideController';
|
|
16
18
|
import { digiaAnchorRegistry, type AnchorLayout } from './digiaAnchorRegistry';
|
|
19
|
+
import { digiaHealthReporter, HealthEventType } from './DigiaHealthReporter';
|
|
17
20
|
import { digiaActionHandler, type ActionCallbacks } from './actionHandler';
|
|
18
21
|
import type { DismissReason } from './types';
|
|
19
22
|
import type { Action, SpotlightConfig, SpotlightStep, TooltipConfig, TooltipStep } from './templateTypes';
|
|
@@ -247,9 +250,25 @@ function TooltipOverlay({
|
|
|
247
250
|
setLayout(null);
|
|
248
251
|
setFloatPos(null);
|
|
249
252
|
if (!ready) return;
|
|
253
|
+
if (!digiaAnchorRegistry.isRegistered(step.anchorKey)) {
|
|
254
|
+
// eslint-disable-next-line no-console
|
|
255
|
+
console.warn(`[Digia] campaign dropped — anchor_key "${step.anchorKey}" is not registered on this screen (campaign_key=${request.campaignKey}, step=${stepIndex})`);
|
|
256
|
+
digiaHealthReporter.report(HealthEventType.anchor_not_on_screen, {
|
|
257
|
+
campaign_key: request.campaignKey,
|
|
258
|
+
reason: 'anchor_key_not_registered',
|
|
259
|
+
anchor_key: step.anchorKey,
|
|
260
|
+
step_index: stepIndex,
|
|
261
|
+
});
|
|
262
|
+
digiaGuideController.cancel(request.payloadId);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
250
265
|
let skipCached = false;
|
|
251
266
|
const unsub = digiaAnchorRegistry.subscribe(step.anchorKey, (l) => {
|
|
252
267
|
if (!skipCached) return;
|
|
268
|
+
if (l.width === 0 || l.height === 0) {
|
|
269
|
+
digiaGuideController.cancel(request.payloadId);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
253
272
|
const { width: screenW, height: screenH } = Dimensions.get('window');
|
|
254
273
|
if (l.pageY + l.height <= 0 || l.pageY >= screenH || l.pageX + l.width <= 0 || l.pageX >= screenW) {
|
|
255
274
|
digiaGuideController.cancel(request.payloadId);
|
|
@@ -632,9 +651,25 @@ function SpotlightOverlay({
|
|
|
632
651
|
useEffect(() => {
|
|
633
652
|
setLayout(null);
|
|
634
653
|
if (!ready) return; // hold off measuring/showing until the delay has elapsed
|
|
654
|
+
if (!digiaAnchorRegistry.isRegistered(step.anchorKey)) {
|
|
655
|
+
// eslint-disable-next-line no-console
|
|
656
|
+
console.warn(`[Digia] campaign dropped — anchor_key "${step.anchorKey}" is not registered on this screen (campaign_key=${request.campaignKey}, step=${stepIndex})`);
|
|
657
|
+
digiaHealthReporter.report(HealthEventType.anchor_not_on_screen, {
|
|
658
|
+
campaign_key: request.campaignKey,
|
|
659
|
+
reason: 'anchor_key_not_registered',
|
|
660
|
+
anchor_key: step.anchorKey,
|
|
661
|
+
step_index: stepIndex,
|
|
662
|
+
});
|
|
663
|
+
digiaGuideController.cancel(request.payloadId);
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
635
666
|
let skipCached = false;
|
|
636
667
|
const unsub = digiaAnchorRegistry.subscribe(step.anchorKey, (l) => {
|
|
637
668
|
if (!skipCached) return;
|
|
669
|
+
if (l.width === 0 || l.height === 0) {
|
|
670
|
+
digiaGuideController.cancel(request.payloadId);
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
638
673
|
const { width: screenW, height: screenH } = Dimensions.get('window');
|
|
639
674
|
if (l.pageY + l.height <= 0 || l.pageY >= screenH || l.pageX + l.width <= 0 || l.pageX >= screenW) {
|
|
640
675
|
digiaGuideController.cancel(request.payloadId);
|
|
@@ -811,8 +846,28 @@ function DigiaGuideRuntime() {
|
|
|
811
846
|
}
|
|
812
847
|
|
|
813
848
|
// ─── DigiaHost ────────────────────────────────────────────────────────────────
|
|
849
|
+
//
|
|
850
|
+
// Place once at the app root. Accepts optional children (wrap mode) or can be
|
|
851
|
+
// used standalone (<DigiaHost />) as a sibling alongside other root elements.
|
|
852
|
+
//
|
|
853
|
+
// Renders ONLY the JS guide / tooltip / spotlight runtime (DigiaGuideRuntime).
|
|
854
|
+
// Native overlays (nudges, dialogs, bottom sheets, surveys) render through the
|
|
855
|
+
// single native overlay host that DigiaModule mounts imperatively after
|
|
856
|
+
// Digia.initialize() — that host owns its own touch handling and claims touches
|
|
857
|
+
// only while an overlay is active. Mounting a native host here as well would
|
|
858
|
+
// render every overlay twice (one stacked behind the other), and a
|
|
859
|
+
// React-tree host is not reliably touch-correct under the New Architecture.
|
|
860
|
+
|
|
861
|
+
export function DigiaHost({ children }: { children?: React.ReactNode }) {
|
|
862
|
+
if (children != null) {
|
|
863
|
+
return (
|
|
864
|
+
<>
|
|
865
|
+
{children}
|
|
866
|
+
<DigiaGuideRuntime />
|
|
867
|
+
</>
|
|
868
|
+
);
|
|
869
|
+
}
|
|
814
870
|
|
|
815
|
-
export function DigiaHost() {
|
|
816
871
|
return <DigiaGuideRuntime />;
|
|
817
872
|
}
|
|
818
873
|
|
package/src/NativeDigiaEngage.ts
CHANGED
|
@@ -55,6 +55,22 @@ export interface Spec extends TurboModule {
|
|
|
55
55
|
|
|
56
56
|
/** Return all registered components (anchors/slots) for health reporting. */
|
|
57
57
|
getRegisteredComponents(): Promise<Array<{ component_key: string; component_type: 'anchor' | 'slot'; screen_name: string | null }>>;
|
|
58
|
+
|
|
59
|
+
/** Set the authenticated user ID for analytics identity stitching. */
|
|
60
|
+
setUserId(userId: string): void;
|
|
61
|
+
/** Clear the user ID (e.g. on logout); rotates the analytics session. */
|
|
62
|
+
clearUserId(): void;
|
|
63
|
+
/**
|
|
64
|
+
* Record an analytics event from a JS-rendered campaign (guide/tooltip/spotlight).
|
|
65
|
+
* Native campaigns (nudge, inline, survey) are tracked automatically by the SDK.
|
|
66
|
+
*/
|
|
67
|
+
trackEvent(
|
|
68
|
+
eventType: string,
|
|
69
|
+
campaignId: string,
|
|
70
|
+
campaignKey: string,
|
|
71
|
+
campaignType: string,
|
|
72
|
+
elementId?: string | null,
|
|
73
|
+
): void;
|
|
58
74
|
}
|
|
59
75
|
|
|
60
76
|
let _resolved: Spec | null = null;
|
|
@@ -89,5 +105,9 @@ export const nativeDigiaModule: Spec = {
|
|
|
89
105
|
registerAnchor: (key, x, y, width, height) => getModule()?.registerAnchor(key, x, y, width, height),
|
|
90
106
|
unregisterAnchor: (key) => getModule()?.unregisterAnchor(key),
|
|
91
107
|
getRegisteredComponents: () => getModule()?.getRegisteredComponents() ?? Promise.resolve([]),
|
|
108
|
+
setUserId: (userId) => getModule()?.setUserId(userId),
|
|
109
|
+
clearUserId: () => getModule()?.clearUserId(),
|
|
110
|
+
trackEvent: (eventType, campaignId, campaignKey, campaignType, elementId) =>
|
|
111
|
+
getModule()?.trackEvent(eventType, campaignId, campaignKey, campaignType, elementId),
|
|
92
112
|
getConstants: () => getModule()?.getConstants?.() ?? {},
|
|
93
113
|
};
|
|
@@ -38,5 +38,7 @@ const remeasure = (key: string) => {
|
|
|
38
38
|
_measureCallbacks.get(key)?.()
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
const isRegistered = (key: string): boolean => _measureCallbacks.has(key)
|
|
42
|
+
|
|
41
43
|
export type { AnchorLayout }
|
|
42
|
-
export const digiaAnchorRegistry = { setLayout, getLayout, subscribe, remove, registerMeasure, unregisterMeasure, remeasure }
|
|
44
|
+
export const digiaAnchorRegistry = { setLayout, getLayout, subscribe, remove, registerMeasure, unregisterMeasure, remeasure, isRegistered }
|
package/src/frequencyStore.ts
CHANGED
|
@@ -36,18 +36,18 @@ const _sessionStore = new Map<string, FrequencyState>();
|
|
|
36
36
|
const storeKey = (campaignKey: string) => `digia:freq:${campaignKey}`;
|
|
37
37
|
|
|
38
38
|
export const frequencyStore = {
|
|
39
|
-
async
|
|
39
|
+
async checkApiKey(apiKey: string): Promise<void> {
|
|
40
40
|
const storage = getStorage();
|
|
41
41
|
if (!storage) return;
|
|
42
42
|
try {
|
|
43
43
|
const stored = await storage.getItem(STORE_META_KEY);
|
|
44
|
-
const meta = stored ? (JSON.parse(stored) as {
|
|
45
|
-
if (meta && meta.
|
|
44
|
+
const meta = stored ? (JSON.parse(stored) as { apiKey: string }) : null;
|
|
45
|
+
if (meta && meta.apiKey !== apiKey) {
|
|
46
46
|
const keys = await storage.getAllKeys();
|
|
47
47
|
const digiaKeys = keys.filter((k) => k.startsWith('digia:freq:'));
|
|
48
48
|
if (digiaKeys.length > 0) await storage.multiRemove([...digiaKeys]);
|
|
49
49
|
}
|
|
50
|
-
await storage.setItem(STORE_META_KEY, JSON.stringify({
|
|
50
|
+
await storage.setItem(STORE_META_KEY, JSON.stringify({ apiKey }));
|
|
51
51
|
} catch {
|
|
52
52
|
// non-fatal
|
|
53
53
|
}
|
package/src/index.ts
CHANGED
|
@@ -4,18 +4,18 @@
|
|
|
4
4
|
* React Native bridge for the Digia Engage SDK.
|
|
5
5
|
*
|
|
6
6
|
* The SDK surfaces Digia Compose UI inside React Native via:
|
|
7
|
-
* • `Digia`
|
|
8
|
-
* • `
|
|
9
|
-
*
|
|
7
|
+
* • `Digia` – SDK lifecycle (initialize, setCurrentScreen)
|
|
8
|
+
* • `DigiaHost` – Place once at the app root. Hosts JS guide/tooltip overlays
|
|
9
|
+
* and the native Compose overlay for dialogs/bottom-sheets.
|
|
10
|
+
* Use as <DigiaHost /> standalone or <DigiaHost>{children}</DigiaHost>.
|
|
10
11
|
*/
|
|
11
12
|
|
|
12
13
|
export { Digia } from './Digia';
|
|
13
|
-
export { DigiaHostView } from './DigiaHostView';
|
|
14
14
|
export { DigiaHost } from './DigiaProvider';
|
|
15
15
|
export { DigiaSlotView } from './DigiaSlotView';
|
|
16
16
|
export { DigiaAnchorView } from './DigiaAnchorView';
|
|
17
17
|
export type { DigiaAnchorViewRef } from './DigiaAnchorView';
|
|
18
|
-
export type { ActionContext, ActionResult, CampaignType, DigiaAction, DigiaConfig, DigiaDelegate, DigiaExperienceEvent, DigiaPlugin, InAppBrowserAdapter,
|
|
18
|
+
export type { ActionContext, ActionResult, CEPTriggerPayload, CampaignType, DigiaAction, DigiaConfig, DigiaDelegate, DigiaExperienceEvent, DigiaPlugin, InAppBrowserAdapter, OnAction } from './types';
|
|
19
19
|
export { defaultInAppBrowser } from './defaultInAppBrowser';
|
|
20
20
|
export { DigiaHealthReporter, HealthEventType, digiaHealthReporter } from './DigiaHealthReporter';
|
|
21
21
|
export type {
|
package/src/templateTypes.ts
CHANGED
|
@@ -120,4 +120,34 @@ export type SurveyTemplateConfig = {
|
|
|
120
120
|
rootNodeId: string
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
export type
|
|
123
|
+
export type NudgeContainerConfig = {
|
|
124
|
+
bgColor?: string
|
|
125
|
+
cornerRadius?: number
|
|
126
|
+
padding?: number
|
|
127
|
+
dismissOnOutsideTap?: boolean
|
|
128
|
+
scrimColor?: string
|
|
129
|
+
/** Bottom-sheet only: max height as a fraction of screen height. */
|
|
130
|
+
maxHeightRatio?: number
|
|
131
|
+
/** Bottom-sheet only: show the drag handle + enable drag-to-dismiss. */
|
|
132
|
+
dragHandle?: boolean
|
|
133
|
+
/** Dialog only: width in dp. */
|
|
134
|
+
width?: number
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* BottomSheet / dialog nudge. `layout` is the native DUI VWData tree (root
|
|
139
|
+
* `digia/column`), parsed and rendered entirely by the native SDK — JS does not
|
|
140
|
+
* render nudges; they are forwarded to the native bridge via triggerCampaign.
|
|
141
|
+
*/
|
|
142
|
+
export type NudgeConfig = {
|
|
143
|
+
templateType: 'bottomSheet' | 'dialog'
|
|
144
|
+
container: NudgeContainerConfig
|
|
145
|
+
layout: Record<string, unknown>
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export type TemplateConfig =
|
|
149
|
+
| TooltipConfig
|
|
150
|
+
| SpotlightConfig
|
|
151
|
+
| CarouselConfig
|
|
152
|
+
| SurveyTemplateConfig
|
|
153
|
+
| NudgeConfig
|
package/src/types.ts
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The translation contract between a CEP plugin and Digia's rendering engine.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Plugin authors map their CEP's native callback into this struct.
|
|
5
|
+
* Mirrors CEPTriggerPayload on Android / Flutter — Digia core never imports
|
|
6
|
+
* CleverTap, MoEngage, or WebEngage types directly.
|
|
5
7
|
*/
|
|
6
|
-
export interface
|
|
7
|
-
/**
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
/** CEP
|
|
12
|
-
|
|
8
|
+
export interface CEPTriggerPayload {
|
|
9
|
+
/** The CEP's own identifier for this campaign instance. Opaque to Digia — passed through for analytics correlation. */
|
|
10
|
+
cepCampaignId: string;
|
|
11
|
+
/** Additional metadata the CEP passes through (UTM params, user segment, CEP-specific tracking fields). Forwarded as-is in ExperienceEvents. */
|
|
12
|
+
cepMetadata: Record<string, unknown>;
|
|
13
|
+
/** The coupling key linking this CEP campaign to a Digia campaign. Used to look up the matching campaign in the store. */
|
|
14
|
+
campaignKey: string;
|
|
15
|
+
/** Optional runtime variables to interpolate into the campaign config. Keys must match variable placeholders in the Digia dashboard. */
|
|
16
|
+
variables?: Record<string, string>;
|
|
13
17
|
}
|
|
14
18
|
|
|
15
19
|
export type CampaignType = 'nudge' | 'guide' | 'inline' | 'survey';
|
|
@@ -70,7 +74,7 @@ export type GuideLifecycleEvent =
|
|
|
70
74
|
*/
|
|
71
75
|
export interface DigiaDelegate {
|
|
72
76
|
/** Deliver a campaign payload into the Digia rendering engine. */
|
|
73
|
-
onCampaignTriggered(payload:
|
|
77
|
+
onCampaignTriggered(payload: CEPTriggerPayload): void | Promise<void>;
|
|
74
78
|
/** Invalidate / dismiss a campaign by its ID. */
|
|
75
79
|
onCampaignInvalidated(campaignId: string): void;
|
|
76
80
|
}
|
|
@@ -90,7 +94,7 @@ export interface DigiaPlugin {
|
|
|
90
94
|
* (impressed / clicked / dismissed). Plugins use this to report
|
|
91
95
|
* analytics back to their CEP platform.
|
|
92
96
|
*/
|
|
93
|
-
notifyEvent(event: DigiaExperienceEvent, payload:
|
|
97
|
+
notifyEvent(event: DigiaExperienceEvent, payload: CEPTriggerPayload): void;
|
|
94
98
|
/**
|
|
95
99
|
* Called by the Digia SDK to record a named analytics event with properties.
|
|
96
100
|
* Implement this to forward Digia lifecycle events (e.g. "Digia Experience Viewed")
|
|
@@ -170,8 +174,8 @@ export interface FrequencyEvalResult {
|
|
|
170
174
|
* Configuration for initialising the Digia Engage SDK.
|
|
171
175
|
*/
|
|
172
176
|
export interface DigiaConfig {
|
|
173
|
-
/** The Engage
|
|
174
|
-
|
|
177
|
+
/** The Engage API key — sent as x-digia-project-id on all SDK requests. */
|
|
178
|
+
apiKey: string;
|
|
175
179
|
/**
|
|
176
180
|
* Base URL for the Digia API.
|
|
177
181
|
* Defaults to the production API root, or the Engage sandbox root when
|