@digia-engage/core 2.0.0-rc.1 → 2.0.0-rc.2
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/android/build.gradle +1 -1
- package/lib/commonjs/Digia.js +90 -2
- package/lib/commonjs/Digia.js.map +1 -1
- package/lib/commonjs/DigiaAnchorView.js +35 -3
- package/lib/commonjs/DigiaAnchorView.js.map +1 -1
- package/lib/commonjs/DigiaProvider.js +2 -0
- package/lib/commonjs/DigiaProvider.js.map +1 -1
- package/lib/commonjs/frequencyEvaluator.js +70 -0
- package/lib/commonjs/frequencyEvaluator.js.map +1 -0
- package/lib/commonjs/frequencyStore.js +70 -0
- package/lib/commonjs/frequencyStore.js.map +1 -0
- package/lib/module/Digia.js +89 -2
- package/lib/module/Digia.js.map +1 -1
- package/lib/module/DigiaAnchorView.js +33 -1
- package/lib/module/DigiaAnchorView.js.map +1 -1
- package/lib/module/DigiaProvider.js +2 -0
- package/lib/module/DigiaProvider.js.map +1 -1
- package/lib/module/frequencyEvaluator.js +61 -0
- package/lib/module/frequencyEvaluator.js.map +1 -0
- package/lib/module/frequencyStore.js +64 -0
- package/lib/module/frequencyStore.js.map +1 -0
- package/lib/typescript/Digia.d.ts +6 -1
- package/lib/typescript/Digia.d.ts.map +1 -1
- package/lib/typescript/DigiaAnchorView.d.ts +5 -1
- package/lib/typescript/DigiaAnchorView.d.ts.map +1 -1
- package/lib/typescript/DigiaProvider.d.ts.map +1 -1
- package/lib/typescript/frequencyEvaluator.d.ts +14 -0
- package/lib/typescript/frequencyEvaluator.d.ts.map +1 -0
- package/lib/typescript/frequencyStore.d.ts +7 -0
- package/lib/typescript/frequencyStore.d.ts.map +1 -0
- package/lib/typescript/types.d.ts +23 -1
- package/lib/typescript/types.d.ts.map +1 -1
- package/package.json +5 -1
- package/src/Digia.ts +99 -1
- package/src/DigiaAnchorView.tsx +30 -2
- package/src/DigiaProvider.tsx +2 -0
- package/src/frequencyEvaluator.ts +57 -0
- package/src/frequencyStore.ts +79 -0
- package/src/types.ts +30 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const WINDOW_MS = {
|
|
2
|
+
day: 86_400_000,
|
|
3
|
+
week: 7 * 86_400_000,
|
|
4
|
+
month: 30 * 86_400_000
|
|
5
|
+
};
|
|
6
|
+
export const isSessionPolicy = policy => policy.max_per_window?.window === 'session';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Pure eligibility function. No side effects.
|
|
10
|
+
*
|
|
11
|
+
* Semantics for max_per_window { count, window }:
|
|
12
|
+
* - "count" shows are allowed, measured from first_shown_at.
|
|
13
|
+
* - Once the window duration has elapsed since first_shown_at, permanently blocked (reason: 'window').
|
|
14
|
+
* - Once shown_count >= count, permanently blocked (reason: 'max_total').
|
|
15
|
+
* - 'session' window is checked by the caller via in-memory state — same logic applies.
|
|
16
|
+
*/
|
|
17
|
+
export const evaluate = (policy, state, now) => {
|
|
18
|
+
if (!state) return {
|
|
19
|
+
allow: true,
|
|
20
|
+
reason: null
|
|
21
|
+
};
|
|
22
|
+
if (state.stopped_at !== null) {
|
|
23
|
+
return {
|
|
24
|
+
allow: false,
|
|
25
|
+
reason: 'stopped'
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
if (policy.max_total !== null && state.shown_count >= policy.max_total) {
|
|
29
|
+
return {
|
|
30
|
+
allow: false,
|
|
31
|
+
reason: 'max_total'
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
if (policy.max_per_window !== null) {
|
|
35
|
+
const {
|
|
36
|
+
count,
|
|
37
|
+
window: win
|
|
38
|
+
} = policy.max_per_window;
|
|
39
|
+
const windowMs = WINDOW_MS[win];
|
|
40
|
+
if (windowMs !== undefined && state.first_shown_at !== null) {
|
|
41
|
+
if (now - state.first_shown_at > windowMs) {
|
|
42
|
+
return {
|
|
43
|
+
allow: false,
|
|
44
|
+
reason: 'window'
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (state.shown_count >= count) {
|
|
49
|
+
return {
|
|
50
|
+
allow: false,
|
|
51
|
+
reason: 'max_total'
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
allow: true,
|
|
57
|
+
reason: null
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
export const hasPolicy = policy => policy !== null && policy !== undefined && (policy.max_total !== null || policy.max_per_window !== null || policy.stop_on !== null);
|
|
61
|
+
//# sourceMappingURL=frequencyEvaluator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["WINDOW_MS","day","week","month","isSessionPolicy","policy","max_per_window","window","evaluate","state","now","allow","reason","stopped_at","max_total","shown_count","count","win","windowMs","undefined","first_shown_at","hasPolicy","stop_on"],"sourceRoot":"../../src","sources":["frequencyEvaluator.ts"],"mappings":"AAEA,MAAMA,SAAiC,GAAG;EACtCC,GAAG,EAAI,UAAU;EACjBC,IAAI,EAAG,CAAC,GAAG,UAAU;EACrBC,KAAK,EAAE,EAAE,GAAG;AAChB,CAAC;AAED,OAAO,MAAMC,eAAe,GAAIC,MAAuB,IACnDA,MAAM,CAACC,cAAc,EAAEC,MAAM,KAAK,SAAS;;AAE/C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,QAAQ,GAAGA,CACpBH,MAAuB,EACvBI,KAA4B,EAC5BC,GAAW,KACW;EACtB,IAAI,CAACD,KAAK,EAAE,OAAO;IAAEE,KAAK,EAAE,IAAI;IAAEC,MAAM,EAAE;EAAK,CAAC;EAEhD,IAAIH,KAAK,CAACI,UAAU,KAAK,IAAI,EAAE;IAC3B,OAAO;MAAEF,KAAK,EAAE,KAAK;MAAEC,MAAM,EAAE;IAAU,CAAC;EAC9C;EAEA,IAAIP,MAAM,CAACS,SAAS,KAAK,IAAI,IAAIL,KAAK,CAACM,WAAW,IAAIV,MAAM,CAACS,SAAS,EAAE;IACpE,OAAO;MAAEH,KAAK,EAAE,KAAK;MAAEC,MAAM,EAAE;IAAY,CAAC;EAChD;EAEA,IAAIP,MAAM,CAACC,cAAc,KAAK,IAAI,EAAE;IAChC,MAAM;MAAEU,KAAK;MAAET,MAAM,EAAEU;IAAI,CAAC,GAAGZ,MAAM,CAACC,cAAc;IACpD,MAAMY,QAAQ,GAAGlB,SAAS,CAACiB,GAAG,CAAC;IAE/B,IAAIC,QAAQ,KAAKC,SAAS,IAAIV,KAAK,CAACW,cAAc,KAAK,IAAI,EAAE;MACzD,IAAIV,GAAG,GAAGD,KAAK,CAACW,cAAc,GAAGF,QAAQ,EAAE;QACvC,OAAO;UAAEP,KAAK,EAAE,KAAK;UAAEC,MAAM,EAAE;QAAS,CAAC;MAC7C;IACJ;IAEA,IAAIH,KAAK,CAACM,WAAW,IAAIC,KAAK,EAAE;MAC5B,OAAO;QAAEL,KAAK,EAAE,KAAK;QAAEC,MAAM,EAAE;MAAY,CAAC;IAChD;EACJ;EAEA,OAAO;IAAED,KAAK,EAAE,IAAI;IAAEC,MAAM,EAAE;EAAK,CAAC;AACxC,CAAC;AAED,OAAO,MAAMS,SAAS,GAAIhB,MAA0C,IAChEA,MAAM,KAAK,IAAI,IACfA,MAAM,KAAKc,SAAS,KACnBd,MAAM,CAACS,SAAS,KAAK,IAAI,IAAIT,MAAM,CAACC,cAAc,KAAK,IAAI,IAAID,MAAM,CAACiB,OAAO,KAAK,IAAI,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
2
|
+
const STORE_META_KEY = 'digia:freq:__meta__';
|
|
3
|
+
let _storage = null;
|
|
4
|
+
const _loadStorage = () => {
|
|
5
|
+
try {
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
7
|
+
const mod = require('@react-native-async-storage/async-storage');
|
|
8
|
+
return mod.default ?? mod;
|
|
9
|
+
} catch {
|
|
10
|
+
console.warn('[Digia] AsyncStorage unavailable — frequency state is in-memory only (resets on app restart)');
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
const getStorage = () => {
|
|
15
|
+
if (_storage === undefined) {
|
|
16
|
+
_storage = _loadStorage();
|
|
17
|
+
}
|
|
18
|
+
return _storage;
|
|
19
|
+
};
|
|
20
|
+
const _sessionStore = new Map();
|
|
21
|
+
const storeKey = campaignKey => `digia:freq:${campaignKey}`;
|
|
22
|
+
export const frequencyStore = {
|
|
23
|
+
async checkProjectId(projectId) {
|
|
24
|
+
const storage = getStorage();
|
|
25
|
+
if (!storage) return;
|
|
26
|
+
try {
|
|
27
|
+
const stored = await storage.getItem(STORE_META_KEY);
|
|
28
|
+
const meta = stored ? JSON.parse(stored) : null;
|
|
29
|
+
if (meta && meta.projectId !== projectId) {
|
|
30
|
+
const keys = await storage.getAllKeys();
|
|
31
|
+
const digiaKeys = keys.filter(k => k.startsWith('digia:freq:'));
|
|
32
|
+
if (digiaKeys.length > 0) await storage.multiRemove([...digiaKeys]);
|
|
33
|
+
}
|
|
34
|
+
await storage.setItem(STORE_META_KEY, JSON.stringify({
|
|
35
|
+
projectId
|
|
36
|
+
}));
|
|
37
|
+
} catch {
|
|
38
|
+
// non-fatal
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
async get(campaignKey, isSession) {
|
|
42
|
+
if (isSession) return _sessionStore.get(campaignKey) ?? null;
|
|
43
|
+
const storage = getStorage();
|
|
44
|
+
if (!storage) return _sessionStore.get(campaignKey) ?? null;
|
|
45
|
+
try {
|
|
46
|
+
const raw = await storage.getItem(storeKey(campaignKey));
|
|
47
|
+
return raw ? JSON.parse(raw) : null;
|
|
48
|
+
} catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
async set(campaignKey, state, isSession) {
|
|
53
|
+
_sessionStore.set(campaignKey, state);
|
|
54
|
+
if (isSession) return;
|
|
55
|
+
const storage = getStorage();
|
|
56
|
+
if (!storage) return;
|
|
57
|
+
try {
|
|
58
|
+
await storage.setItem(storeKey(campaignKey), JSON.stringify(state));
|
|
59
|
+
} catch {
|
|
60
|
+
// non-fatal: state already updated in-memory above
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
//# sourceMappingURL=frequencyStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["STORE_META_KEY","_storage","_loadStorage","mod","require","default","console","warn","getStorage","undefined","_sessionStore","Map","storeKey","campaignKey","frequencyStore","checkProjectId","projectId","storage","stored","getItem","meta","JSON","parse","keys","getAllKeys","digiaKeys","filter","k","startsWith","length","multiRemove","setItem","stringify","get","isSession","raw","set","state"],"sourceRoot":"../../src","sources":["frequencyStore.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -21,6 +21,7 @@ declare class DigiaClass implements DigiaDelegate {
|
|
|
21
21
|
private readonly _activePayloads;
|
|
22
22
|
private _engageSubscription;
|
|
23
23
|
private _projectId;
|
|
24
|
+
private _deviceId;
|
|
24
25
|
private _apiBaseUrl;
|
|
25
26
|
private _logLevel;
|
|
26
27
|
private _fontFamily;
|
|
@@ -71,7 +72,7 @@ declare class DigiaClass implements DigiaDelegate {
|
|
|
71
72
|
* so their text matches native-rendered campaigns.
|
|
72
73
|
*/
|
|
73
74
|
get fontFamily(): string | undefined;
|
|
74
|
-
onCampaignTriggered(payload: InAppPayload): void
|
|
75
|
+
onCampaignTriggered(payload: InAppPayload): Promise<void>;
|
|
75
76
|
onCampaignInvalidated(campaignId: string): void;
|
|
76
77
|
/**
|
|
77
78
|
* Subscribes to `digiaOverlayEvent` emitted by the native
|
|
@@ -94,6 +95,10 @@ declare class DigiaClass implements DigiaDelegate {
|
|
|
94
95
|
private _extractCampaignKey;
|
|
95
96
|
private _extractString;
|
|
96
97
|
private _parseTemplateConfig;
|
|
98
|
+
private _loadOrCreateDeviceId;
|
|
99
|
+
private _getFrequencyState;
|
|
100
|
+
_bumpFrequencyImpression(campaignKey: string): Promise<void>;
|
|
101
|
+
_applyStopOn(campaignKey: string, interactionType: 'click' | 'dismiss'): Promise<void>;
|
|
97
102
|
private _log;
|
|
98
103
|
}
|
|
99
104
|
export declare const Digia: DigiaClass;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Digia.d.ts","sourceRoot":"","sources":["../../src/Digia.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;
|
|
1
|
+
{"version":3,"file":"Digia.d.ts","sourceRoot":"","sources":["../../src/Digia.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAUH,OAAO,KAAK,EAER,WAAW,EACX,aAAa,EAEb,WAAW,EAIX,YAAY,EACf,MAAM,SAAS,CAAC;AAgBjB,cAAM,UAAW,YAAW,aAAa;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkC;IAG3D,OAAO,CAAC,kBAAkB,CAAS;IAGnC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAmC;IACnE,OAAO,CAAC,mBAAmB,CAAmC;IAC9D,OAAO,CAAC,UAAU,CAAM;IACxB,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,WAAW,CAAM;IACzB,OAAO,CAAC,SAAS,CAAoC;IACrD,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkC;IAClE,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAqB;IAE3D;;;;;OAKG;IACG,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IA4BpD;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAgBnC;;;OAGG;IACH,UAAU,CAAC,UAAU,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI;IAMlD;;;;;;OAMG;IACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAMpC,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAMpE,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIzC;;;;OAIG;IACH,IAAI,UAAU,IAAI,MAAM,GAAG,SAAS,CAEnC;IAOK,mBAAmB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAiF/D,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAQ/C;;;;;;;OAOG;IACH,OAAO,CAAC,oBAAoB;IAS5B,OAAO,CAAC,uBAAuB;IA8B/B,OAAO,CAAC,sBAAsB;IA8B9B,OAAO,CAAC,eAAe;IAYvB,OAAO,CAAC,qBAAqB;IAqC7B,OAAO,CAAC,kBAAkB;YAOZ,qBAAqB;YAgCrB,QAAQ;IAmBtB,OAAO,CAAC,mBAAmB;IAmB3B,OAAO,CAAC,cAAc;IAiBtB,OAAO,CAAC,mBAAmB;IAc3B,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,oBAAoB;YAkBd,qBAAqB;YAiBrB,kBAAkB;IAI1B,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB5D,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,eAAe,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAsB5F,OAAO,CAAC,IAAI;CAMf;AAED,eAAO,MAAM,KAAK,YAAmB,CAAC"}
|
|
@@ -5,17 +5,21 @@
|
|
|
5
5
|
* When a SHOW_TOOLTIP or SHOW_SPOTLIGHT campaign fires, the native SDK looks up this view
|
|
6
6
|
* via AnchorRegistry and uses getLocationOnScreen() for accurate pixel-perfect coordinates.
|
|
7
7
|
*
|
|
8
|
+
* Also reports layout into the JS digiaAnchorRegistry so JS-rendered guides (tooltip/spotlight)
|
|
9
|
+
* can position themselves relative to this anchor.
|
|
10
|
+
*
|
|
8
11
|
* Usage:
|
|
9
12
|
* <DigiaAnchorView anchorKey="pdp_add_to_cart" style={{ alignSelf: 'flex-start' }}>
|
|
10
13
|
* <TouchableOpacity ...>Add to Cart</TouchableOpacity>
|
|
11
14
|
* </DigiaAnchorView>
|
|
12
15
|
*/
|
|
16
|
+
import React from 'react';
|
|
13
17
|
import { type ViewProps } from 'react-native';
|
|
14
18
|
interface DigiaAnchorViewProps extends ViewProps {
|
|
15
19
|
anchorKey: string;
|
|
16
20
|
/** Corner radius in dp — used to round the spotlight cutout to match the wrapped button. */
|
|
17
21
|
cornerRadius?: number;
|
|
18
22
|
}
|
|
19
|
-
export declare const DigiaAnchorView:
|
|
23
|
+
export declare const DigiaAnchorView: ({ anchorKey, onLayout, ...rest }: DigiaAnchorViewProps) => React.JSX.Element;
|
|
20
24
|
export {};
|
|
21
25
|
//# sourceMappingURL=DigiaAnchorView.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DigiaAnchorView.d.ts","sourceRoot":"","sources":["../../src/DigiaAnchorView.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"DigiaAnchorView.d.ts","sourceRoot":"","sources":["../../src/DigiaAnchorView.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAA8B,MAAM,OAAO,CAAC;AACnD,OAAO,EAAgC,KAAK,SAAS,EAA0B,MAAM,cAAc,CAAC;AAGpG,UAAU,oBAAqB,SAAQ,SAAS;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,4FAA4F;IAC5F,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB;AAID,eAAO,MAAM,eAAe,GAAI,kCAAkC,oBAAoB,sBAqBrF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DigiaProvider.d.ts","sourceRoot":"","sources":["../../src/DigiaProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmD,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"DigiaProvider.d.ts","sourceRoot":"","sources":["../../src/DigiaProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmD,MAAM,OAAO,CAAC;AA0uBxE,wBAAgB,SAAS,sBAExB"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { FrequencyPolicy, FrequencyState, FrequencyEvalResult } from './types';
|
|
2
|
+
export declare const isSessionPolicy: (policy: FrequencyPolicy) => boolean;
|
|
3
|
+
/**
|
|
4
|
+
* Pure eligibility function. No side effects.
|
|
5
|
+
*
|
|
6
|
+
* Semantics for max_per_window { count, window }:
|
|
7
|
+
* - "count" shows are allowed, measured from first_shown_at.
|
|
8
|
+
* - Once the window duration has elapsed since first_shown_at, permanently blocked (reason: 'window').
|
|
9
|
+
* - Once shown_count >= count, permanently blocked (reason: 'max_total').
|
|
10
|
+
* - 'session' window is checked by the caller via in-memory state — same logic applies.
|
|
11
|
+
*/
|
|
12
|
+
export declare const evaluate: (policy: FrequencyPolicy, state: FrequencyState | null, now: number) => FrequencyEvalResult;
|
|
13
|
+
export declare const hasPolicy: (policy: FrequencyPolicy | null | undefined) => policy is FrequencyPolicy;
|
|
14
|
+
//# sourceMappingURL=frequencyEvaluator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frequencyEvaluator.d.ts","sourceRoot":"","sources":["../../src/frequencyEvaluator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAQpF,eAAO,MAAM,eAAe,GAAI,QAAQ,eAAe,KAAG,OACX,CAAC;AAEhD;;;;;;;;GAQG;AACH,eAAO,MAAM,QAAQ,GACjB,QAAQ,eAAe,EACvB,OAAO,cAAc,GAAG,IAAI,EAC5B,KAAK,MAAM,KACZ,mBA2BF,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,QAAQ,eAAe,GAAG,IAAI,GAAG,SAAS,KAAG,MAAM,IAAI,eAGW,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { FrequencyState } from './types';
|
|
2
|
+
export declare const frequencyStore: {
|
|
3
|
+
checkProjectId(projectId: string): Promise<void>;
|
|
4
|
+
get(campaignKey: string, isSession: boolean): Promise<FrequencyState | null>;
|
|
5
|
+
set(campaignKey: string, state: FrequencyState, isSession: boolean): Promise<void>;
|
|
6
|
+
};
|
|
7
|
+
//# sourceMappingURL=frequencyStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frequencyStore.d.ts","sourceRoot":"","sources":["../../src/frequencyStore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAqC9C,eAAO,MAAM,cAAc;8BACS,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;qBAiB/B,MAAM,aAAa,OAAO,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;qBAY3D,MAAM,SAAS,cAAc,aAAa,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;CAW3F,CAAC"}
|
|
@@ -98,7 +98,7 @@ export type GuideLifecycleEvent = {
|
|
|
98
98
|
*/
|
|
99
99
|
export interface DigiaDelegate {
|
|
100
100
|
/** Deliver a campaign payload into the Digia rendering engine. */
|
|
101
|
-
onCampaignTriggered(payload: InAppPayload): void
|
|
101
|
+
onCampaignTriggered(payload: InAppPayload): void | Promise<void>;
|
|
102
102
|
/** Invalidate / dismiss a campaign by its ID. */
|
|
103
103
|
onCampaignInvalidated(campaignId: string): void;
|
|
104
104
|
}
|
|
@@ -163,6 +163,28 @@ export type OnAction = (action: DigiaAction, context: ActionContext) => ActionRe
|
|
|
163
163
|
export type InAppBrowserAdapter = {
|
|
164
164
|
open: (url: string) => Promise<void>;
|
|
165
165
|
};
|
|
166
|
+
export interface FrequencyWindow {
|
|
167
|
+
count: number;
|
|
168
|
+
window: 'session' | 'day' | 'week' | 'month';
|
|
169
|
+
}
|
|
170
|
+
export interface FrequencyPolicy {
|
|
171
|
+
max_total: number | null;
|
|
172
|
+
max_per_window: FrequencyWindow | null;
|
|
173
|
+
stop_on: 'click' | 'dismiss' | 'any_action' | null;
|
|
174
|
+
min_gap_ms?: number | null;
|
|
175
|
+
}
|
|
176
|
+
export interface FrequencyState {
|
|
177
|
+
shown_count: number;
|
|
178
|
+
first_shown_at: number | null;
|
|
179
|
+
last_shown_at: number | null;
|
|
180
|
+
stopped_at: number | null;
|
|
181
|
+
stopped_reason: string | null;
|
|
182
|
+
}
|
|
183
|
+
export type FrequencySkipReason = 'max_total' | 'window' | 'stopped';
|
|
184
|
+
export interface FrequencyEvalResult {
|
|
185
|
+
allow: boolean;
|
|
186
|
+
reason: FrequencySkipReason | null;
|
|
187
|
+
}
|
|
166
188
|
/**
|
|
167
189
|
* Configuration for initialising the Digia Engage SDK.
|
|
168
190
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@digia-engage/core",
|
|
3
|
-
"version": "2.0.0-rc.
|
|
3
|
+
"version": "2.0.0-rc.2",
|
|
4
4
|
"description": "React Native bridge for Digia Engage – renders native Android Compose UI inside React Native apps",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"module": "src/index.ts",
|
|
@@ -44,6 +44,10 @@
|
|
|
44
44
|
},
|
|
45
45
|
"author": "Digia Technology Private Limited",
|
|
46
46
|
"license": "MIT",
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@react-native-async-storage/async-storage": "^2.1.2",
|
|
49
|
+
"react-native-uuid": "^2.0.3"
|
|
50
|
+
},
|
|
47
51
|
"peerDependencies": {
|
|
48
52
|
"@floating-ui/core": "^1.0.0",
|
|
49
53
|
"react-native-reanimated": ">=3.0.0",
|
package/src/Digia.ts
CHANGED
|
@@ -20,12 +20,17 @@ import { nativeDigiaModule } from './NativeDigiaEngage';
|
|
|
20
20
|
import { digiaHealthReporter, HealthEventType } from './DigiaHealthReporter';
|
|
21
21
|
import { digiaGuideController } from './DigiaGuideController';
|
|
22
22
|
import { digiaActionHandler } from './actionHandler';
|
|
23
|
+
import uuid from 'react-native-uuid';
|
|
24
|
+
import { frequencyStore } from './frequencyStore';
|
|
25
|
+
import { evaluate, hasPolicy, isSessionPolicy } from './frequencyEvaluator';
|
|
23
26
|
import type {
|
|
24
27
|
CampaignType,
|
|
25
28
|
DigiaConfig,
|
|
26
29
|
DigiaDelegate,
|
|
27
30
|
DigiaExperienceEvent,
|
|
28
31
|
DigiaPlugin,
|
|
32
|
+
FrequencyPolicy,
|
|
33
|
+
FrequencyState,
|
|
29
34
|
GuideLifecycleEvent,
|
|
30
35
|
InAppPayload,
|
|
31
36
|
} from './types';
|
|
@@ -41,6 +46,7 @@ interface SdkCampaign {
|
|
|
41
46
|
campaign_key: string;
|
|
42
47
|
campaign_type: CampaignType;
|
|
43
48
|
templateConfig?: Record<string, unknown>;
|
|
49
|
+
frequency?: FrequencyPolicy | null;
|
|
44
50
|
}
|
|
45
51
|
|
|
46
52
|
class DigiaClass implements DigiaDelegate {
|
|
@@ -53,6 +59,7 @@ class DigiaClass implements DigiaDelegate {
|
|
|
53
59
|
private readonly _activePayloads = new Map<string, InAppPayload>();
|
|
54
60
|
private _engageSubscription: { remove(): void } | null = null;
|
|
55
61
|
private _projectId = '';
|
|
62
|
+
private _deviceId = '';
|
|
56
63
|
private _apiBaseUrl = '';
|
|
57
64
|
private _logLevel: DigiaConfig['logLevel'] = 'error';
|
|
58
65
|
private _fontFamily: string | undefined;
|
|
@@ -89,6 +96,8 @@ class DigiaClass implements DigiaDelegate {
|
|
|
89
96
|
throw e;
|
|
90
97
|
}
|
|
91
98
|
|
|
99
|
+
this._deviceId = await this._loadOrCreateDeviceId();
|
|
100
|
+
await frequencyStore.checkProjectId(config.projectId);
|
|
92
101
|
await this._refreshCampaignStore();
|
|
93
102
|
}
|
|
94
103
|
|
|
@@ -170,7 +179,7 @@ class DigiaClass implements DigiaDelegate {
|
|
|
170
179
|
// Mirrors DigiaCEPDelegate on Android.
|
|
171
180
|
// Forwards to the native DigiaCEPDelegate via the bridge.
|
|
172
181
|
|
|
173
|
-
onCampaignTriggered(payload: InAppPayload): void {
|
|
182
|
+
async onCampaignTriggered(payload: InAppPayload): Promise<void> {
|
|
174
183
|
if (!this._nativeBridgeWired) {
|
|
175
184
|
digiaHealthReporter.report(HealthEventType.plugin_not_registered, { campaign_key: payload.id });
|
|
176
185
|
}
|
|
@@ -180,6 +189,18 @@ class DigiaClass implements DigiaDelegate {
|
|
|
180
189
|
|
|
181
190
|
if (campaignKey) {
|
|
182
191
|
const campaign = this._campaignsByKey.get(campaignKey);
|
|
192
|
+
|
|
193
|
+
if (campaign && hasPolicy(campaign.frequency)) {
|
|
194
|
+
const policy = campaign.frequency!;
|
|
195
|
+
const isSession = isSessionPolicy(policy);
|
|
196
|
+
const state = await this._getFrequencyState(campaignKey, isSession);
|
|
197
|
+
const result = evaluate(policy, state, Date.now());
|
|
198
|
+
if (!result.allow) {
|
|
199
|
+
this._log(`frequency_capped campaign_key=${campaignKey} reason=${result.reason}`);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
183
204
|
if (campaign?.campaign_type === 'inline' || campaign?.campaign_type === 'survey') {
|
|
184
205
|
this._log(`${campaign.campaign_type} campaign triggered campaign_key=${campaignKey}, forwarding to native`);
|
|
185
206
|
this._activePayloads.set(payload.id, payload);
|
|
@@ -270,16 +291,21 @@ class DigiaClass implements DigiaDelegate {
|
|
|
270
291
|
const payload = this._activePayloads.get(data.campaignId);
|
|
271
292
|
if (!payload) return;
|
|
272
293
|
|
|
294
|
+
const campaignKey = this._extractCampaignKey(payload);
|
|
295
|
+
|
|
273
296
|
let event: DigiaExperienceEvent;
|
|
274
297
|
switch (data.type) {
|
|
275
298
|
case 'impressed':
|
|
276
299
|
event = { type: 'impressed' };
|
|
300
|
+
if (campaignKey) void this._bumpFrequencyImpression(campaignKey);
|
|
277
301
|
break;
|
|
278
302
|
case 'clicked':
|
|
279
303
|
event = { type: 'clicked', elementId: data.elementId };
|
|
304
|
+
if (campaignKey) void this._applyStopOn(campaignKey, 'click');
|
|
280
305
|
break;
|
|
281
306
|
case 'dismissed':
|
|
282
307
|
event = { type: 'dismissed' };
|
|
308
|
+
if (campaignKey) void this._applyStopOn(campaignKey, 'dismiss');
|
|
283
309
|
this._activePayloads.delete(data.campaignId);
|
|
284
310
|
break;
|
|
285
311
|
default:
|
|
@@ -299,6 +325,16 @@ class DigiaClass implements DigiaDelegate {
|
|
|
299
325
|
const properties = this._buildGuideProperties(event, campaignId, campaignKey);
|
|
300
326
|
this._plugins.forEach((p) => p.track?.(eventName, properties));
|
|
301
327
|
|
|
328
|
+
if (event.type === 'viewed') {
|
|
329
|
+
void this._bumpFrequencyImpression(campaignKey);
|
|
330
|
+
}
|
|
331
|
+
if (event.type === 'clicked' || event.type === 'completed') {
|
|
332
|
+
void this._applyStopOn(campaignKey, 'click');
|
|
333
|
+
}
|
|
334
|
+
if (event.type === 'dismissed') {
|
|
335
|
+
void this._applyStopOn(campaignKey, 'dismiss');
|
|
336
|
+
}
|
|
337
|
+
|
|
302
338
|
// Notify plugins of CEP lifecycle termination (template cleanup) on exit events.
|
|
303
339
|
if (event.type === 'dismissed' || event.type === 'completed') {
|
|
304
340
|
const storedPayload = this._activePayloads.get(payloadId);
|
|
@@ -403,6 +439,7 @@ class DigiaClass implements DigiaDelegate {
|
|
|
403
439
|
headers: {
|
|
404
440
|
'Content-Type': 'application/json',
|
|
405
441
|
'x-digia-project-id': this._projectId,
|
|
442
|
+
'x-digia-device-id': this._deviceId,
|
|
406
443
|
},
|
|
407
444
|
body: JSON.stringify(body),
|
|
408
445
|
});
|
|
@@ -489,6 +526,67 @@ class DigiaClass implements DigiaDelegate {
|
|
|
489
526
|
return { ...raw, templateType: type, steps } as TemplateConfig;
|
|
490
527
|
}
|
|
491
528
|
|
|
529
|
+
// ── Device ID ────────────────────────────────────────────────────────────
|
|
530
|
+
|
|
531
|
+
private async _loadOrCreateDeviceId(): Promise<string> {
|
|
532
|
+
const DEVICE_ID_KEY = 'digia:device_id';
|
|
533
|
+
try {
|
|
534
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
535
|
+
const AsyncStorage = require('@react-native-async-storage/async-storage').default;
|
|
536
|
+
const stored = await AsyncStorage.getItem(DEVICE_ID_KEY);
|
|
537
|
+
if (stored) return stored;
|
|
538
|
+
const id = uuid.v4() as string;
|
|
539
|
+
await AsyncStorage.setItem(DEVICE_ID_KEY, id);
|
|
540
|
+
return id;
|
|
541
|
+
} catch {
|
|
542
|
+
return uuid.v4() as string;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// ── Frequency capping ────────────────────────────────────────────────────
|
|
547
|
+
|
|
548
|
+
private async _getFrequencyState(campaignKey: string, isSession: boolean): Promise<FrequencyState | null> {
|
|
549
|
+
return frequencyStore.get(campaignKey, isSession);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
async _bumpFrequencyImpression(campaignKey: string): Promise<void> {
|
|
553
|
+
const campaign = this._campaignsByKey.get(campaignKey);
|
|
554
|
+
if (!campaign || !hasPolicy(campaign.frequency)) return;
|
|
555
|
+
const isSession = isSessionPolicy(campaign.frequency!);
|
|
556
|
+
const now = Date.now();
|
|
557
|
+
const prev = await frequencyStore.get(campaignKey, isSession);
|
|
558
|
+
const next: FrequencyState = {
|
|
559
|
+
shown_count: (prev?.shown_count ?? 0) + 1,
|
|
560
|
+
first_shown_at: prev?.first_shown_at ?? now,
|
|
561
|
+
last_shown_at: now,
|
|
562
|
+
stopped_at: prev?.stopped_at ?? null,
|
|
563
|
+
stopped_reason: prev?.stopped_reason ?? null,
|
|
564
|
+
};
|
|
565
|
+
await frequencyStore.set(campaignKey, next, isSession);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
async _applyStopOn(campaignKey: string, interactionType: 'click' | 'dismiss'): Promise<void> {
|
|
569
|
+
const campaign = this._campaignsByKey.get(campaignKey);
|
|
570
|
+
const stopOn = campaign?.frequency?.stop_on;
|
|
571
|
+
if (!stopOn) return;
|
|
572
|
+
const matches =
|
|
573
|
+
stopOn === 'any_action' ||
|
|
574
|
+
stopOn === interactionType;
|
|
575
|
+
if (!matches) return;
|
|
576
|
+
const isSession = isSessionPolicy(campaign!.frequency!);
|
|
577
|
+
const prev = await frequencyStore.get(campaignKey, isSession);
|
|
578
|
+
if (prev?.stopped_at) return;
|
|
579
|
+
const now = Date.now();
|
|
580
|
+
const next: FrequencyState = {
|
|
581
|
+
shown_count: prev?.shown_count ?? 0,
|
|
582
|
+
first_shown_at: prev?.first_shown_at ?? null,
|
|
583
|
+
last_shown_at: prev?.last_shown_at ?? null,
|
|
584
|
+
stopped_at: now,
|
|
585
|
+
stopped_reason: interactionType,
|
|
586
|
+
};
|
|
587
|
+
await frequencyStore.set(campaignKey, next, isSession);
|
|
588
|
+
}
|
|
589
|
+
|
|
492
590
|
private _log(message: string): void {
|
|
493
591
|
if (this._logLevel !== 'verbose') return;
|
|
494
592
|
// eslint-disable-next-line no-console
|
package/src/DigiaAnchorView.tsx
CHANGED
|
@@ -5,13 +5,18 @@
|
|
|
5
5
|
* When a SHOW_TOOLTIP or SHOW_SPOTLIGHT campaign fires, the native SDK looks up this view
|
|
6
6
|
* via AnchorRegistry and uses getLocationOnScreen() for accurate pixel-perfect coordinates.
|
|
7
7
|
*
|
|
8
|
+
* Also reports layout into the JS digiaAnchorRegistry so JS-rendered guides (tooltip/spotlight)
|
|
9
|
+
* can position themselves relative to this anchor.
|
|
10
|
+
*
|
|
8
11
|
* Usage:
|
|
9
12
|
* <DigiaAnchorView anchorKey="pdp_add_to_cart" style={{ alignSelf: 'flex-start' }}>
|
|
10
13
|
* <TouchableOpacity ...>Add to Cart</TouchableOpacity>
|
|
11
14
|
* </DigiaAnchorView>
|
|
12
15
|
*/
|
|
13
16
|
|
|
14
|
-
import {
|
|
17
|
+
import React, { useCallback, useRef } from 'react';
|
|
18
|
+
import { requireNativeComponent, View, type ViewProps, type LayoutChangeEvent } from 'react-native';
|
|
19
|
+
import { digiaAnchorRegistry } from './digiaAnchorRegistry';
|
|
15
20
|
|
|
16
21
|
interface DigiaAnchorViewProps extends ViewProps {
|
|
17
22
|
anchorKey: string;
|
|
@@ -19,4 +24,27 @@ interface DigiaAnchorViewProps extends ViewProps {
|
|
|
19
24
|
cornerRadius?: number;
|
|
20
25
|
}
|
|
21
26
|
|
|
22
|
-
|
|
27
|
+
const NativeDigiaAnchorView = requireNativeComponent<DigiaAnchorViewProps>('DigiaAnchorView');
|
|
28
|
+
|
|
29
|
+
export const DigiaAnchorView = ({ anchorKey, onLayout, ...rest }: DigiaAnchorViewProps) => {
|
|
30
|
+
const viewRef = useRef<View>(null);
|
|
31
|
+
|
|
32
|
+
const handleLayout = useCallback((e: LayoutChangeEvent) => {
|
|
33
|
+
onLayout?.(e);
|
|
34
|
+
// Use measure() for absolute screen coordinates (onLayout gives relative coords)
|
|
35
|
+
viewRef.current?.measure((_x, _y, width, height, pageX, pageY) => {
|
|
36
|
+
if (width > 0 && height > 0) {
|
|
37
|
+
digiaAnchorRegistry.setLayout(anchorKey, { pageX, pageY, width, height });
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}, [anchorKey, onLayout]);
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<NativeDigiaAnchorView
|
|
44
|
+
ref={viewRef as any}
|
|
45
|
+
anchorKey={anchorKey}
|
|
46
|
+
onLayout={handleLayout}
|
|
47
|
+
{...rest}
|
|
48
|
+
/>
|
|
49
|
+
);
|
|
50
|
+
};
|
package/src/DigiaProvider.tsx
CHANGED
|
@@ -221,7 +221,9 @@ function TooltipOverlay({
|
|
|
221
221
|
useEffect(() => {
|
|
222
222
|
setLayout(null);
|
|
223
223
|
setFloatPos(null);
|
|
224
|
+
if (__DEV__) console.log(`[Digia] guide waiting for anchor key="${step.anchorKey}"`);
|
|
224
225
|
return digiaAnchorRegistry.subscribe(step.anchorKey, (l) => {
|
|
226
|
+
if (__DEV__) console.log(`[Digia] anchor resolved key="${step.anchorKey}"`, l);
|
|
225
227
|
setLayout(l);
|
|
226
228
|
});
|
|
227
229
|
}, [step.anchorKey]);
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { FrequencyPolicy, FrequencyState, FrequencyEvalResult } from './types';
|
|
2
|
+
|
|
3
|
+
const WINDOW_MS: Record<string, number> = {
|
|
4
|
+
day: 86_400_000,
|
|
5
|
+
week: 7 * 86_400_000,
|
|
6
|
+
month: 30 * 86_400_000,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const isSessionPolicy = (policy: FrequencyPolicy): boolean =>
|
|
10
|
+
policy.max_per_window?.window === 'session';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Pure eligibility function. No side effects.
|
|
14
|
+
*
|
|
15
|
+
* Semantics for max_per_window { count, window }:
|
|
16
|
+
* - "count" shows are allowed, measured from first_shown_at.
|
|
17
|
+
* - Once the window duration has elapsed since first_shown_at, permanently blocked (reason: 'window').
|
|
18
|
+
* - Once shown_count >= count, permanently blocked (reason: 'max_total').
|
|
19
|
+
* - 'session' window is checked by the caller via in-memory state — same logic applies.
|
|
20
|
+
*/
|
|
21
|
+
export const evaluate = (
|
|
22
|
+
policy: FrequencyPolicy,
|
|
23
|
+
state: FrequencyState | null,
|
|
24
|
+
now: number,
|
|
25
|
+
): FrequencyEvalResult => {
|
|
26
|
+
if (!state) return { allow: true, reason: null };
|
|
27
|
+
|
|
28
|
+
if (state.stopped_at !== null) {
|
|
29
|
+
return { allow: false, reason: 'stopped' };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (policy.max_total !== null && state.shown_count >= policy.max_total) {
|
|
33
|
+
return { allow: false, reason: 'max_total' };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (policy.max_per_window !== null) {
|
|
37
|
+
const { count, window: win } = policy.max_per_window;
|
|
38
|
+
const windowMs = WINDOW_MS[win];
|
|
39
|
+
|
|
40
|
+
if (windowMs !== undefined && state.first_shown_at !== null) {
|
|
41
|
+
if (now - state.first_shown_at > windowMs) {
|
|
42
|
+
return { allow: false, reason: 'window' };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (state.shown_count >= count) {
|
|
47
|
+
return { allow: false, reason: 'max_total' };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return { allow: true, reason: null };
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const hasPolicy = (policy: FrequencyPolicy | null | undefined): policy is FrequencyPolicy =>
|
|
55
|
+
policy !== null &&
|
|
56
|
+
policy !== undefined &&
|
|
57
|
+
(policy.max_total !== null || policy.max_per_window !== null || policy.stop_on !== null);
|