@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.
Files changed (39) hide show
  1. package/android/build.gradle +1 -1
  2. package/lib/commonjs/Digia.js +90 -2
  3. package/lib/commonjs/Digia.js.map +1 -1
  4. package/lib/commonjs/DigiaAnchorView.js +35 -3
  5. package/lib/commonjs/DigiaAnchorView.js.map +1 -1
  6. package/lib/commonjs/DigiaProvider.js +2 -0
  7. package/lib/commonjs/DigiaProvider.js.map +1 -1
  8. package/lib/commonjs/frequencyEvaluator.js +70 -0
  9. package/lib/commonjs/frequencyEvaluator.js.map +1 -0
  10. package/lib/commonjs/frequencyStore.js +70 -0
  11. package/lib/commonjs/frequencyStore.js.map +1 -0
  12. package/lib/module/Digia.js +89 -2
  13. package/lib/module/Digia.js.map +1 -1
  14. package/lib/module/DigiaAnchorView.js +33 -1
  15. package/lib/module/DigiaAnchorView.js.map +1 -1
  16. package/lib/module/DigiaProvider.js +2 -0
  17. package/lib/module/DigiaProvider.js.map +1 -1
  18. package/lib/module/frequencyEvaluator.js +61 -0
  19. package/lib/module/frequencyEvaluator.js.map +1 -0
  20. package/lib/module/frequencyStore.js +64 -0
  21. package/lib/module/frequencyStore.js.map +1 -0
  22. package/lib/typescript/Digia.d.ts +6 -1
  23. package/lib/typescript/Digia.d.ts.map +1 -1
  24. package/lib/typescript/DigiaAnchorView.d.ts +5 -1
  25. package/lib/typescript/DigiaAnchorView.d.ts.map +1 -1
  26. package/lib/typescript/DigiaProvider.d.ts.map +1 -1
  27. package/lib/typescript/frequencyEvaluator.d.ts +14 -0
  28. package/lib/typescript/frequencyEvaluator.d.ts.map +1 -0
  29. package/lib/typescript/frequencyStore.d.ts +7 -0
  30. package/lib/typescript/frequencyStore.d.ts.map +1 -0
  31. package/lib/typescript/types.d.ts +23 -1
  32. package/lib/typescript/types.d.ts.map +1 -1
  33. package/package.json +5 -1
  34. package/src/Digia.ts +99 -1
  35. package/src/DigiaAnchorView.tsx +30 -2
  36. package/src/DigiaProvider.tsx +2 -0
  37. package/src/frequencyEvaluator.ts +57 -0
  38. package/src/frequencyStore.ts +79 -0
  39. 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;AAOH,OAAO,KAAK,EAER,WAAW,EACX,aAAa,EAEb,WAAW,EAEX,YAAY,EACf,MAAM,SAAS,CAAC;AAejB,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,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;IA0BpD;;;;;;;;;;;;;;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;IAOD,mBAAmB,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI;IAqEhD,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAQ/C;;;;;;;OAOG;IACH,OAAO,CAAC,oBAAoB;IAS5B,OAAO,CAAC,uBAAuB;IAyB/B,OAAO,CAAC,sBAAsB;IAoB9B,OAAO,CAAC,eAAe;IAYvB,OAAO,CAAC,qBAAqB;IAqC7B,OAAO,CAAC,kBAAkB;YAOZ,qBAAqB;YAgCrB,QAAQ;IAkBtB,OAAO,CAAC,mBAAmB;IAmB3B,OAAO,CAAC,cAAc;IAiBtB,OAAO,CAAC,mBAAmB;IAc3B,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,oBAAoB;IAgB5B,OAAO,CAAC,IAAI;CAMf;AAED,eAAO,MAAM,KAAK,YAAmB,CAAC"}
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: import("react-native").HostComponent<DigiaAnchorViewProps>;
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;;;;;;;;;;;GAWG;AAEH,OAAO,EAA0B,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAEtE,UAAU,oBAAqB,SAAQ,SAAS;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,4FAA4F;IAC5F,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,eAAe,4DAAkE,CAAC"}
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;AAwuBxE,wBAAgB,SAAS,sBAExB"}
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":"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,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAInE,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,wEAAwE;AACxE,MAAM,WAAW,mBAAmB;IAChC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;CAC9B;AAED,yDAAyD;AACzD,MAAM,MAAM,oBAAoB,GAC1B,mBAAmB,GACnB,iBAAiB,GACjB,mBAAmB,GACnB,mBAAmB,CAAC;AAI1B,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,WAAW,GAAG,cAAc,GAAG,cAAc,CAAC;AAEzF;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GACzB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,SAAS,GAAG,WAAW,CAAA;CAAE,GAClH;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,SAAS,GAAG,WAAW,CAAA;CAAE,GACvH;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,SAAS,GAAG,WAAW,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACjM;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,SAAS,GAAG,WAAW,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACtM;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,SAAS,GAAG,WAAW,CAAC;IAAC,aAAa,EAAE,aAAa,CAAA;CAAE,GACnJ;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,SAAS,GAAG,WAAW,CAAC;IAAC,aAAa,EAAE,aAAa,CAAA;CAAE,GACxJ;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,SAAS,GAAG,WAAW,CAAA;CAAE,CAAC;AAE5H;;;;;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;;;;OAIG;IACH,KAAK,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACrE,iEAAiE;IACjE,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,iEAAiE;IACjE,QAAQ,IAAI,IAAI,CAAC;CACpB;AAID,MAAM,MAAM,WAAW,GACjB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GACzD;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,UAAU,GAAG,QAAQ,CAAA;CAAE,GACtE;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvB,MAAM,MAAM,aAAa,GAAG;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACvD,MAAM,EAAE;QACJ,IAAI,EAAE,QAAQ,GAAG,UAAU,GAAG,gBAAgB,GAAG,cAAc,CAAC;QAChE,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,YAAY,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;AAE7D,MAAM,MAAM,QAAQ,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,aAAa,KAAK,YAAY,CAAC;AAErF,MAAM,MAAM,mBAAmB,GAAG;IAC9B,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC,CAAC;AAIF;;GAEG;AACH,MAAM,WAAW,WAAW;IACxB,8EAA8E;IAC9E,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,WAAW,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;IACvC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;IACxC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB;;OAEG;IACH,OAAO,CAAC,EAAE;QACN;;;WAGG;QACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;QAChC;;;WAGG;QACH,YAAY,CAAC,EAAE,mBAAmB,CAAC;KACtC,CAAC;CACL"}
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.1",
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
@@ -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 { requireNativeComponent, type ViewProps } from 'react-native';
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
- export const DigiaAnchorView = requireNativeComponent<DigiaAnchorViewProps>('DigiaAnchorView');
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
+ };
@@ -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);