@digia-engage/core 1.1.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/README.md +134 -51
- package/android/build.gradle +3 -3
- package/android/src/main/java/com/digia/engage/rn/DigiaModule.kt +52 -8
- package/android/src/main/java/com/digia/engage/rn/DigiaSlotViewManager.kt +6 -2
- package/android/src/main/java/com/digia/engage/rn/DigiaViewManager.kt +1 -0
- package/ios/DigiaEngageModule.m +7 -1
- package/ios/DigiaHostViewManager.swift +20 -20
- package/ios/DigiaModule.swift +8 -4
- package/lib/commonjs/Digia.js +390 -4
- 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/DigiaGuideController.js +59 -0
- package/lib/commonjs/DigiaGuideController.js.map +1 -0
- package/lib/commonjs/DigiaHealthReporter.js +45 -0
- package/lib/commonjs/DigiaHealthReporter.js.map +1 -0
- package/lib/commonjs/DigiaProvider.js +1081 -0
- package/lib/commonjs/DigiaProvider.js.map +1 -0
- package/lib/commonjs/DigiaSlotView.js +18 -3
- package/lib/commonjs/DigiaSlotView.js.map +1 -1
- package/lib/commonjs/NativeDigiaEngage.js +14 -8
- package/lib/commonjs/NativeDigiaEngage.js.map +1 -1
- package/lib/commonjs/actionHandler.js +316 -0
- package/lib/commonjs/actionHandler.js.map +1 -0
- package/lib/commonjs/defaultInAppBrowser.js +31 -0
- package/lib/commonjs/defaultInAppBrowser.js.map +1 -0
- package/lib/commonjs/digiaAnchorRegistry.js +32 -0
- package/lib/commonjs/digiaAnchorRegistry.js.map +1 -0
- 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/commonjs/index.js +7 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/templateTypes.js +2 -0
- package/lib/commonjs/templateTypes.js.map +1 -0
- package/lib/module/Digia.js +389 -4
- 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/DigiaGuideController.js +53 -0
- package/lib/module/DigiaGuideController.js.map +1 -0
- package/lib/module/DigiaHealthReporter.js +38 -0
- package/lib/module/DigiaHealthReporter.js.map +1 -0
- package/lib/module/DigiaProvider.js +1074 -0
- package/lib/module/DigiaProvider.js.map +1 -0
- package/lib/module/DigiaSlotView.js +20 -5
- package/lib/module/DigiaSlotView.js.map +1 -1
- package/lib/module/NativeDigiaEngage.js +14 -8
- package/lib/module/NativeDigiaEngage.js.map +1 -1
- package/lib/module/actionHandler.js +311 -0
- package/lib/module/actionHandler.js.map +1 -0
- package/lib/module/defaultInAppBrowser.js +25 -0
- package/lib/module/defaultInAppBrowser.js.map +1 -0
- package/lib/module/digiaAnchorRegistry.js +26 -0
- package/lib/module/digiaAnchorRegistry.js.map +1 -0
- 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/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/templateTypes.js +2 -0
- package/lib/module/templateTypes.js.map +1 -0
- package/lib/typescript/Digia.d.ts +35 -3
- 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/DigiaGuideController.d.ts +30 -0
- package/lib/typescript/DigiaGuideController.d.ts.map +1 -0
- package/lib/typescript/DigiaHealthReporter.d.ts +24 -0
- package/lib/typescript/DigiaHealthReporter.d.ts.map +1 -0
- package/lib/typescript/DigiaProvider.d.ts +3 -0
- package/lib/typescript/DigiaProvider.d.ts.map +1 -0
- package/lib/typescript/DigiaSlotView.d.ts.map +1 -1
- package/lib/typescript/NativeDigiaEngage.d.ts +10 -6
- package/lib/typescript/NativeDigiaEngage.d.ts.map +1 -1
- package/lib/typescript/actionHandler.d.ts +20 -0
- package/lib/typescript/actionHandler.d.ts.map +1 -0
- package/lib/typescript/defaultInAppBrowser.d.ts +3 -0
- package/lib/typescript/defaultInAppBrowser.d.ts.map +1 -0
- package/lib/typescript/digiaAnchorRegistry.d.ts +15 -0
- package/lib/typescript/digiaAnchorRegistry.d.ts.map +1 -0
- 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/index.d.ts +1 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/templateTypes.d.ts +140 -0
- package/lib/typescript/templateTypes.d.ts.map +1 -0
- package/lib/typescript/types.d.ts +163 -4
- package/lib/typescript/types.d.ts.map +1 -1
- package/package.json +15 -3
- package/src/Digia.ts +439 -4
- package/src/DigiaAnchorView.tsx +30 -2
- package/src/DigiaGuideController.ts +61 -0
- package/src/DigiaHealthReporter.ts +43 -0
- package/src/DigiaProvider.tsx +778 -0
- package/src/DigiaSlotView.tsx +26 -6
- package/src/NativeDigiaEngage.ts +28 -13
- package/src/actionHandler.ts +311 -0
- package/src/defaultInAppBrowser.ts +31 -0
- package/src/digiaAnchorRegistry.ts +27 -0
- package/src/frequencyEvaluator.ts +57 -0
- package/src/frequencyStore.ts +79 -0
- package/src/index.ts +1 -0
- package/src/templateTypes.ts +121 -0
- package/src/types.ts +132 -6
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.isSessionPolicy = exports.hasPolicy = exports.evaluate = void 0;
|
|
7
|
+
const WINDOW_MS = {
|
|
8
|
+
day: 86_400_000,
|
|
9
|
+
week: 7 * 86_400_000,
|
|
10
|
+
month: 30 * 86_400_000
|
|
11
|
+
};
|
|
12
|
+
const isSessionPolicy = policy => policy.max_per_window?.window === 'session';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Pure eligibility function. No side effects.
|
|
16
|
+
*
|
|
17
|
+
* Semantics for max_per_window { count, window }:
|
|
18
|
+
* - "count" shows are allowed, measured from first_shown_at.
|
|
19
|
+
* - Once the window duration has elapsed since first_shown_at, permanently blocked (reason: 'window').
|
|
20
|
+
* - Once shown_count >= count, permanently blocked (reason: 'max_total').
|
|
21
|
+
* - 'session' window is checked by the caller via in-memory state — same logic applies.
|
|
22
|
+
*/
|
|
23
|
+
exports.isSessionPolicy = isSessionPolicy;
|
|
24
|
+
const evaluate = (policy, state, now) => {
|
|
25
|
+
if (!state) return {
|
|
26
|
+
allow: true,
|
|
27
|
+
reason: null
|
|
28
|
+
};
|
|
29
|
+
if (state.stopped_at !== null) {
|
|
30
|
+
return {
|
|
31
|
+
allow: false,
|
|
32
|
+
reason: 'stopped'
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
if (policy.max_total !== null && state.shown_count >= policy.max_total) {
|
|
36
|
+
return {
|
|
37
|
+
allow: false,
|
|
38
|
+
reason: 'max_total'
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
if (policy.max_per_window !== null) {
|
|
42
|
+
const {
|
|
43
|
+
count,
|
|
44
|
+
window: win
|
|
45
|
+
} = policy.max_per_window;
|
|
46
|
+
const windowMs = WINDOW_MS[win];
|
|
47
|
+
if (windowMs !== undefined && state.first_shown_at !== null) {
|
|
48
|
+
if (now - state.first_shown_at > windowMs) {
|
|
49
|
+
return {
|
|
50
|
+
allow: false,
|
|
51
|
+
reason: 'window'
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (state.shown_count >= count) {
|
|
56
|
+
return {
|
|
57
|
+
allow: false,
|
|
58
|
+
reason: 'max_total'
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
allow: true,
|
|
64
|
+
reason: null
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
exports.evaluate = evaluate;
|
|
68
|
+
const hasPolicy = policy => policy !== null && policy !== undefined && (policy.max_total !== null || policy.max_per_window !== null || policy.stop_on !== null);
|
|
69
|
+
exports.hasPolicy = hasPolicy;
|
|
70
|
+
//# sourceMappingURL=frequencyEvaluator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["WINDOW_MS","day","week","month","isSessionPolicy","policy","max_per_window","window","exports","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;AAEM,MAAMC,eAAe,GAAIC,MAAuB,IACnDA,MAAM,CAACC,cAAc,EAAEC,MAAM,KAAK,SAAS;;AAE/C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AARAC,OAAA,CAAAJ,eAAA,GAAAA,eAAA;AASO,MAAMK,QAAQ,GAAGA,CACpBJ,MAAuB,EACvBK,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,IAAIR,MAAM,CAACU,SAAS,KAAK,IAAI,IAAIL,KAAK,CAACM,WAAW,IAAIX,MAAM,CAACU,SAAS,EAAE;IACpE,OAAO;MAAEH,KAAK,EAAE,KAAK;MAAEC,MAAM,EAAE;IAAY,CAAC;EAChD;EAEA,IAAIR,MAAM,CAACC,cAAc,KAAK,IAAI,EAAE;IAChC,MAAM;MAAEW,KAAK;MAAEV,MAAM,EAAEW;IAAI,CAAC,GAAGb,MAAM,CAACC,cAAc;IACpD,MAAMa,QAAQ,GAAGnB,SAAS,CAACkB,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;AAACL,OAAA,CAAAC,QAAA,GAAAA,QAAA;AAEK,MAAMa,SAAS,GAAIjB,MAA0C,IAChEA,MAAM,KAAK,IAAI,IACfA,MAAM,KAAKe,SAAS,KACnBf,MAAM,CAACU,SAAS,KAAK,IAAI,IAAIV,MAAM,CAACC,cAAc,KAAK,IAAI,IAAID,MAAM,CAACkB,OAAO,KAAK,IAAI,CAAC;AAACf,OAAA,CAAAc,SAAA,GAAAA,SAAA","ignoreList":[]}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.frequencyStore = void 0;
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
8
|
+
const STORE_META_KEY = 'digia:freq:__meta__';
|
|
9
|
+
let _storage = null;
|
|
10
|
+
const _loadStorage = () => {
|
|
11
|
+
try {
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
13
|
+
const mod = require('@react-native-async-storage/async-storage');
|
|
14
|
+
return mod.default ?? mod;
|
|
15
|
+
} catch {
|
|
16
|
+
console.warn('[Digia] AsyncStorage unavailable — frequency state is in-memory only (resets on app restart)');
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
const getStorage = () => {
|
|
21
|
+
if (_storage === undefined) {
|
|
22
|
+
_storage = _loadStorage();
|
|
23
|
+
}
|
|
24
|
+
return _storage;
|
|
25
|
+
};
|
|
26
|
+
const _sessionStore = new Map();
|
|
27
|
+
const storeKey = campaignKey => `digia:freq:${campaignKey}`;
|
|
28
|
+
const frequencyStore = exports.frequencyStore = {
|
|
29
|
+
async checkProjectId(projectId) {
|
|
30
|
+
const storage = getStorage();
|
|
31
|
+
if (!storage) return;
|
|
32
|
+
try {
|
|
33
|
+
const stored = await storage.getItem(STORE_META_KEY);
|
|
34
|
+
const meta = stored ? JSON.parse(stored) : null;
|
|
35
|
+
if (meta && meta.projectId !== projectId) {
|
|
36
|
+
const keys = await storage.getAllKeys();
|
|
37
|
+
const digiaKeys = keys.filter(k => k.startsWith('digia:freq:'));
|
|
38
|
+
if (digiaKeys.length > 0) await storage.multiRemove([...digiaKeys]);
|
|
39
|
+
}
|
|
40
|
+
await storage.setItem(STORE_META_KEY, JSON.stringify({
|
|
41
|
+
projectId
|
|
42
|
+
}));
|
|
43
|
+
} catch {
|
|
44
|
+
// non-fatal
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
async get(campaignKey, isSession) {
|
|
48
|
+
if (isSession) return _sessionStore.get(campaignKey) ?? null;
|
|
49
|
+
const storage = getStorage();
|
|
50
|
+
if (!storage) return _sessionStore.get(campaignKey) ?? null;
|
|
51
|
+
try {
|
|
52
|
+
const raw = await storage.getItem(storeKey(campaignKey));
|
|
53
|
+
return raw ? JSON.parse(raw) : null;
|
|
54
|
+
} catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
async set(campaignKey, state, isSession) {
|
|
59
|
+
_sessionStore.set(campaignKey, state);
|
|
60
|
+
if (isSession) return;
|
|
61
|
+
const storage = getStorage();
|
|
62
|
+
if (!storage) return;
|
|
63
|
+
try {
|
|
64
|
+
await storage.setItem(storeKey(campaignKey), JSON.stringify(state));
|
|
65
|
+
} catch {
|
|
66
|
+
// non-fatal: state already updated in-memory above
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
//# 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","exports","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":[]}
|
package/lib/commonjs/index.js
CHANGED
|
@@ -15,6 +15,12 @@ Object.defineProperty(exports, "DigiaAnchorView", {
|
|
|
15
15
|
return _DigiaAnchorView.DigiaAnchorView;
|
|
16
16
|
}
|
|
17
17
|
});
|
|
18
|
+
Object.defineProperty(exports, "DigiaHost", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () {
|
|
21
|
+
return _DigiaProvider.DigiaHost;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
18
24
|
Object.defineProperty(exports, "DigiaHostView", {
|
|
19
25
|
enumerable: true,
|
|
20
26
|
get: function () {
|
|
@@ -29,6 +35,7 @@ Object.defineProperty(exports, "DigiaSlotView", {
|
|
|
29
35
|
});
|
|
30
36
|
var _Digia = require("./Digia");
|
|
31
37
|
var _DigiaHostView = require("./DigiaHostView");
|
|
38
|
+
var _DigiaProvider = require("./DigiaProvider");
|
|
32
39
|
var _DigiaSlotView = require("./DigiaSlotView");
|
|
33
40
|
var _DigiaAnchorView = require("./DigiaAnchorView");
|
|
34
41
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_Digia","require","_DigiaHostView","_DigiaSlotView","_DigiaAnchorView"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":"
|
|
1
|
+
{"version":3,"names":["_Digia","require","_DigiaHostView","_DigiaProvider","_DigiaSlotView","_DigiaAnchorView"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAWA,IAAAA,MAAA,GAAAC,OAAA;AACA,IAAAC,cAAA,GAAAD,OAAA;AACA,IAAAE,cAAA,GAAAF,OAAA;AACA,IAAAG,cAAA,GAAAH,OAAA;AACA,IAAAI,gBAAA,GAAAJ,OAAA","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sourceRoot":"../../src","sources":["templateTypes.ts"],"mappings":"","ignoreList":[]}
|
package/lib/module/Digia.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* import { Digia } from '@digia/engage-react-native';
|
|
8
8
|
*
|
|
9
9
|
* // In your App entry point (e.g. App.tsx):
|
|
10
|
-
* await Digia.initialize({
|
|
10
|
+
* await Digia.initialize({ projectId: 'YOUR_PROJECT_ID' });
|
|
11
11
|
*
|
|
12
12
|
* // Whenever your navigation screen changes:
|
|
13
13
|
* Digia.setCurrentScreen('Home');
|
|
@@ -17,6 +17,15 @@
|
|
|
17
17
|
|
|
18
18
|
import { DeviceEventEmitter } from 'react-native';
|
|
19
19
|
import { nativeDigiaModule } from './NativeDigiaEngage';
|
|
20
|
+
import { digiaHealthReporter, HealthEventType } from './DigiaHealthReporter';
|
|
21
|
+
import { digiaGuideController } from './DigiaGuideController';
|
|
22
|
+
import { digiaActionHandler } from './actionHandler';
|
|
23
|
+
import uuid from 'react-native-uuid';
|
|
24
|
+
import { frequencyStore } from './frequencyStore';
|
|
25
|
+
import { evaluate, hasPolicy, isSessionPolicy } from './frequencyEvaluator';
|
|
26
|
+
const PRODUCTION_API_ROOT = 'https://app.digia.tech';
|
|
27
|
+
const SANDBOX_API_ROOT = 'https://dev.digia.tech';
|
|
28
|
+
const DIGIA_SDK_VERSION = '1.0.0';
|
|
20
29
|
class DigiaClass {
|
|
21
30
|
_plugins = new Map();
|
|
22
31
|
// Tracks whether the native bridge plugin (RNEventBridgePlugin) has been
|
|
@@ -26,6 +35,13 @@ class DigiaClass {
|
|
|
26
35
|
// the full InAppPayload when overlay lifecycle events arrive from native.
|
|
27
36
|
_activePayloads = new Map();
|
|
28
37
|
_engageSubscription = null;
|
|
38
|
+
_projectId = '';
|
|
39
|
+
_deviceId = '';
|
|
40
|
+
_apiBaseUrl = '';
|
|
41
|
+
_logLevel = 'error';
|
|
42
|
+
_currentScreen = null;
|
|
43
|
+
_campaignsByKey = new Map();
|
|
44
|
+
_registeredAnchorKeys = new Set();
|
|
29
45
|
|
|
30
46
|
/**
|
|
31
47
|
* Initialise the Digia Engage SDK.
|
|
@@ -36,7 +52,26 @@ class DigiaClass {
|
|
|
36
52
|
async initialize(config) {
|
|
37
53
|
const environment = config.environment ?? 'production';
|
|
38
54
|
const logLevel = config.logLevel ?? 'error';
|
|
39
|
-
|
|
55
|
+
this._projectId = config.projectId;
|
|
56
|
+
this._apiBaseUrl = this._resolveApiBaseUrl(config);
|
|
57
|
+
this._logLevel = logLevel;
|
|
58
|
+
this._fontFamily = config.fontFamily?.trim() || undefined;
|
|
59
|
+
digiaHealthReporter.init(config.projectId, this._apiBaseUrl);
|
|
60
|
+
digiaActionHandler.configure({
|
|
61
|
+
onAction: config.onAction,
|
|
62
|
+
routeViaSystemLinking: config.linking?.routeViaSystemLinking ?? true,
|
|
63
|
+
inAppBrowser: config.linking?.inAppBrowser
|
|
64
|
+
});
|
|
65
|
+
try {
|
|
66
|
+
await nativeDigiaModule.initialize(config.projectId, environment, logLevel, config.baseUrl, config.fontFamily);
|
|
67
|
+
} catch (e) {
|
|
68
|
+
// TODO: TO BE PICKED LATER @aditya-digia — health event reporting being removed
|
|
69
|
+
// digiaHealthReporter.report(HealthEventType.fetch_failed, { error_code: 0, platform: 'react_native' });
|
|
70
|
+
throw e;
|
|
71
|
+
}
|
|
72
|
+
this._deviceId = await this._loadOrCreateDeviceId();
|
|
73
|
+
await frequencyStore.checkProjectId(config.projectId);
|
|
74
|
+
await this._refreshCampaignStore();
|
|
40
75
|
}
|
|
41
76
|
|
|
42
77
|
/**
|
|
@@ -50,7 +85,7 @@ class DigiaClass {
|
|
|
50
85
|
* ```ts
|
|
51
86
|
* import { DigiaMoEngagePlugin } from '@digia/moengage-plugin';
|
|
52
87
|
*
|
|
53
|
-
* await Digia.initialize({
|
|
88
|
+
* await Digia.initialize({ projectId: 'YOUR_PROJECT_ID' });
|
|
54
89
|
* Digia.register(new DigiaMoEngagePlugin({ moEngage: MoEngage }));
|
|
55
90
|
* ```
|
|
56
91
|
*/
|
|
@@ -66,6 +101,7 @@ class DigiaClass {
|
|
|
66
101
|
this._nativeBridgeWired = true;
|
|
67
102
|
}
|
|
68
103
|
plugin.setup(this);
|
|
104
|
+
plugin.reportHealth?.(digiaHealthReporter);
|
|
69
105
|
this._plugins.set(plugin.identifier, plugin);
|
|
70
106
|
}
|
|
71
107
|
|
|
@@ -87,20 +123,104 @@ class DigiaClass {
|
|
|
87
123
|
* All registered plugins will have forwardScreen() called automatically.
|
|
88
124
|
*/
|
|
89
125
|
setCurrentScreen(name) {
|
|
126
|
+
this._currentScreen = name;
|
|
90
127
|
nativeDigiaModule.setCurrentScreen(name);
|
|
91
128
|
this._plugins.forEach(plugin => plugin.forwardScreen(name));
|
|
92
129
|
}
|
|
130
|
+
registerAnchor(anchorKey, _screenName) {
|
|
131
|
+
const cleanAnchorKey = anchorKey.trim();
|
|
132
|
+
if (!cleanAnchorKey) return;
|
|
133
|
+
this._registeredAnchorKeys.add(cleanAnchorKey);
|
|
134
|
+
}
|
|
135
|
+
unregisterAnchor(anchorKey) {
|
|
136
|
+
this._registeredAnchorKeys.delete(anchorKey);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Global font family configured via {@link initialize}, or `undefined` when
|
|
141
|
+
* none was set. Used by the JS-rendered guide overlays (tooltip/spotlight)
|
|
142
|
+
* so their text matches native-rendered campaigns.
|
|
143
|
+
*/
|
|
144
|
+
get fontFamily() {
|
|
145
|
+
return this._fontFamily;
|
|
146
|
+
}
|
|
93
147
|
|
|
94
148
|
// ── DigiaDelegate ────────────────────────────────────────────────────────
|
|
95
149
|
// Mirrors DigiaCEPDelegate on Android.
|
|
96
150
|
// Forwards to the native DigiaCEPDelegate via the bridge.
|
|
97
151
|
|
|
98
|
-
onCampaignTriggered(payload) {
|
|
152
|
+
async onCampaignTriggered(payload) {
|
|
153
|
+
if (!this._nativeBridgeWired) {
|
|
154
|
+
digiaHealthReporter.report(HealthEventType.plugin_not_registered, {
|
|
155
|
+
campaign_key: payload.id
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
const campaignKey = this._extractCampaignKey(payload);
|
|
159
|
+
this._log(`onCampaignTriggered payloadId=${payload.id} extractedKey=${campaignKey} knownKeys=[${[...this._campaignsByKey.keys()].join(', ')}]`);
|
|
160
|
+
if (campaignKey) {
|
|
161
|
+
const campaign = this._campaignsByKey.get(campaignKey);
|
|
162
|
+
if (campaign && hasPolicy(campaign.frequency)) {
|
|
163
|
+
const policy = campaign.frequency;
|
|
164
|
+
const isSession = isSessionPolicy(policy);
|
|
165
|
+
const state = await this._getFrequencyState(campaignKey, isSession);
|
|
166
|
+
const result = evaluate(policy, state, Date.now());
|
|
167
|
+
if (!result.allow) {
|
|
168
|
+
this._log(`frequency_capped campaign_key=${campaignKey} reason=${result.reason}`);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (campaign?.campaign_type === 'inline' || campaign?.campaign_type === 'survey') {
|
|
173
|
+
this._log(`${campaign.campaign_type} campaign triggered campaign_key=${campaignKey}, forwarding to native`);
|
|
174
|
+
this._activePayloads.set(payload.id, payload);
|
|
175
|
+
if (campaign.campaign_type === 'inline') {
|
|
176
|
+
this._emitSlotWidth(campaign);
|
|
177
|
+
}
|
|
178
|
+
nativeDigiaModule.triggerCampaign(payload.id, payload.content, payload.cepContext);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (campaign?.campaign_type === 'guide') {
|
|
182
|
+
const config = this._parseTemplateConfig(campaign);
|
|
183
|
+
if (!config || config.templateType !== 'tooltip' && config.templateType !== 'spotlight' || config.steps.length === 0) {
|
|
184
|
+
digiaHealthReporter.report(HealthEventType.anchor_not_on_screen, {
|
|
185
|
+
campaign_key: campaignKey,
|
|
186
|
+
reason: 'guide_campaign_has_no_steps'
|
|
187
|
+
});
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
this._activePayloads.set(payload.id, payload);
|
|
191
|
+
const digiaId = campaign._id ?? campaign.id ?? campaignKey;
|
|
192
|
+
const mounted = digiaGuideController.start({
|
|
193
|
+
payloadId: payload.id,
|
|
194
|
+
campaignKey,
|
|
195
|
+
campaignId: digiaId,
|
|
196
|
+
config,
|
|
197
|
+
onExperienceEvent: event => this._onGuideLifecycleEvent(event, payload.id, campaignKey, digiaId)
|
|
198
|
+
});
|
|
199
|
+
this._log(`guide trigger campaign_key=${campaignKey} mounted=${mounted}`);
|
|
200
|
+
if (!mounted) {
|
|
201
|
+
digiaHealthReporter.report(HealthEventType.host_not_mounted, {
|
|
202
|
+
campaign_key: campaignKey,
|
|
203
|
+
payload_id: payload.id
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if (!campaign) {
|
|
209
|
+
this._log(`campaign_key_mismatch: no campaign found for key="${campaignKey}"`);
|
|
210
|
+
digiaHealthReporter.report(HealthEventType.campaign_key_mismatch, {
|
|
211
|
+
campaign_key: campaignKey,
|
|
212
|
+
payload_id: payload.id,
|
|
213
|
+
available_campaign_keys: [...this._campaignsByKey.keys()]
|
|
214
|
+
});
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
99
218
|
this._activePayloads.set(payload.id, payload);
|
|
100
219
|
nativeDigiaModule.triggerCampaign(payload.id, payload.content, payload.cepContext);
|
|
101
220
|
}
|
|
102
221
|
onCampaignInvalidated(campaignId) {
|
|
103
222
|
this._activePayloads.delete(campaignId);
|
|
223
|
+
digiaGuideController.cancel(campaignId);
|
|
104
224
|
nativeDigiaModule.invalidateCampaign(campaignId);
|
|
105
225
|
}
|
|
106
226
|
|
|
@@ -121,23 +241,27 @@ class DigiaClass {
|
|
|
121
241
|
_forwardExperienceEvent(data) {
|
|
122
242
|
const payload = this._activePayloads.get(data.campaignId);
|
|
123
243
|
if (!payload) return;
|
|
244
|
+
const campaignKey = this._extractCampaignKey(payload);
|
|
124
245
|
let event;
|
|
125
246
|
switch (data.type) {
|
|
126
247
|
case 'impressed':
|
|
127
248
|
event = {
|
|
128
249
|
type: 'impressed'
|
|
129
250
|
};
|
|
251
|
+
if (campaignKey) void this._bumpFrequencyImpression(campaignKey);
|
|
130
252
|
break;
|
|
131
253
|
case 'clicked':
|
|
132
254
|
event = {
|
|
133
255
|
type: 'clicked',
|
|
134
256
|
elementId: data.elementId
|
|
135
257
|
};
|
|
258
|
+
if (campaignKey) void this._applyStopOn(campaignKey, 'click');
|
|
136
259
|
break;
|
|
137
260
|
case 'dismissed':
|
|
138
261
|
event = {
|
|
139
262
|
type: 'dismissed'
|
|
140
263
|
};
|
|
264
|
+
if (campaignKey) void this._applyStopOn(campaignKey, 'dismiss');
|
|
141
265
|
this._activePayloads.delete(data.campaignId);
|
|
142
266
|
break;
|
|
143
267
|
default:
|
|
@@ -145,6 +269,267 @@ class DigiaClass {
|
|
|
145
269
|
}
|
|
146
270
|
this._plugins.forEach(plugin => plugin.notifyEvent(event, payload));
|
|
147
271
|
}
|
|
272
|
+
_onGuideLifecycleEvent(event, payloadId, campaignKey, campaignId) {
|
|
273
|
+
const eventName = this._guideEventName(event.type);
|
|
274
|
+
const properties = this._buildGuideProperties(event, campaignId, campaignKey);
|
|
275
|
+
this._plugins.forEach(p => p.track?.(eventName, properties));
|
|
276
|
+
if (event.type === 'viewed') {
|
|
277
|
+
void this._bumpFrequencyImpression(campaignKey);
|
|
278
|
+
}
|
|
279
|
+
if (event.type === 'clicked' || event.type === 'completed') {
|
|
280
|
+
void this._applyStopOn(campaignKey, 'click');
|
|
281
|
+
}
|
|
282
|
+
if (event.type === 'dismissed') {
|
|
283
|
+
void this._applyStopOn(campaignKey, 'dismiss');
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Notify plugins of CEP lifecycle termination (template cleanup) on exit events.
|
|
287
|
+
if (event.type === 'dismissed' || event.type === 'completed') {
|
|
288
|
+
const storedPayload = this._activePayloads.get(payloadId);
|
|
289
|
+
if (storedPayload) {
|
|
290
|
+
this._plugins.forEach(p => p.notifyEvent({
|
|
291
|
+
type: 'dismissed'
|
|
292
|
+
}, storedPayload));
|
|
293
|
+
this._activePayloads.delete(payloadId);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
_guideEventName(type) {
|
|
298
|
+
switch (type) {
|
|
299
|
+
case 'viewed':
|
|
300
|
+
return 'Digia Experience Viewed';
|
|
301
|
+
case 'step_viewed':
|
|
302
|
+
return 'Digia Step Viewed';
|
|
303
|
+
case 'clicked':
|
|
304
|
+
return 'Digia Experience Clicked';
|
|
305
|
+
case 'step_clicked':
|
|
306
|
+
return 'Digia Step Clicked';
|
|
307
|
+
case 'dismissed':
|
|
308
|
+
return 'Digia Experience Dismissed';
|
|
309
|
+
case 'step_dismissed':
|
|
310
|
+
return 'Digia Step Dismissed';
|
|
311
|
+
case 'completed':
|
|
312
|
+
return 'Digia Experience Completed';
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
_buildGuideProperties(event, campaignId, campaignKey) {
|
|
316
|
+
const base = {
|
|
317
|
+
campaign_id: campaignId,
|
|
318
|
+
campaign_key: campaignKey,
|
|
319
|
+
campaign_type: 'guide',
|
|
320
|
+
display_style: event.displayStyle,
|
|
321
|
+
step_index: event.stepIndex + 1,
|
|
322
|
+
step_total: event.stepTotal,
|
|
323
|
+
anchor_key: event.anchorKey,
|
|
324
|
+
slot_key: null,
|
|
325
|
+
element_id: null,
|
|
326
|
+
cta_label: null,
|
|
327
|
+
action_type: null,
|
|
328
|
+
action_url: null,
|
|
329
|
+
dismiss_reason: null,
|
|
330
|
+
abandoned_at_step: null,
|
|
331
|
+
digia_sdk_version: DIGIA_SDK_VERSION,
|
|
332
|
+
digia_platform: 'react_native'
|
|
333
|
+
};
|
|
334
|
+
if (event.type === 'clicked' || event.type === 'step_clicked') {
|
|
335
|
+
base.element_id = event.elementId ?? null;
|
|
336
|
+
base.cta_label = event.ctaLabel;
|
|
337
|
+
base.action_type = event.actionType;
|
|
338
|
+
base.action_url = event.actionUrl ?? null;
|
|
339
|
+
} else if (event.type === 'dismissed' || event.type === 'step_dismissed') {
|
|
340
|
+
base.dismiss_reason = event.dismissReason;
|
|
341
|
+
base.abandoned_at_step = event.stepIndex + 1;
|
|
342
|
+
}
|
|
343
|
+
return base;
|
|
344
|
+
}
|
|
345
|
+
_resolveApiBaseUrl(config) {
|
|
346
|
+
const root = (config.baseUrl ?? (config.environment === 'sandbox' ? SANDBOX_API_ROOT : PRODUCTION_API_ROOT)).trim();
|
|
347
|
+
const cleanRoot = root.replace(/\/+$/, '');
|
|
348
|
+
return cleanRoot.endsWith('/api/v1') ? cleanRoot : `${cleanRoot}/api/v1`;
|
|
349
|
+
}
|
|
350
|
+
async _refreshCampaignStore() {
|
|
351
|
+
try {
|
|
352
|
+
this._log(`fetching campaigns from ${this._apiBaseUrl}/engage/sdk/getCampaigns`);
|
|
353
|
+
const campaigns = await this._sdkPost('getCampaigns');
|
|
354
|
+
this._campaignsByKey.clear();
|
|
355
|
+
if (campaigns.length > 0) {
|
|
356
|
+
this._log(`campaign[0] raw keys: ${JSON.stringify(Object.keys(campaigns[0]))}`);
|
|
357
|
+
}
|
|
358
|
+
campaigns.forEach(campaign => {
|
|
359
|
+
const raw = campaign;
|
|
360
|
+
const key = typeof raw.campaign_key === 'string' && raw.campaign_key || typeof raw.campaignKey === 'string' && raw.campaignKey || null;
|
|
361
|
+
const type = typeof raw.campaign_type === 'string' && raw.campaign_type || typeof raw.campaignType === 'string' && raw.campaignType || '';
|
|
362
|
+
if (key) {
|
|
363
|
+
this._campaignsByKey.set(key, {
|
|
364
|
+
...campaign,
|
|
365
|
+
campaign_key: key,
|
|
366
|
+
campaign_type: type
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
this._log(`loaded ${campaigns.length} campaign(s): [${[...this._campaignsByKey.keys()].join(', ')}]`);
|
|
371
|
+
} catch (e) {
|
|
372
|
+
const reason = e instanceof Error ? e.message : String(e);
|
|
373
|
+
this._log(`getCampaigns FAILED: ${reason}`);
|
|
374
|
+
digiaHealthReporter.report(HealthEventType.fetch_failed, {
|
|
375
|
+
error_code: 0,
|
|
376
|
+
platform: 'react_native',
|
|
377
|
+
reason
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
async _sdkPost(path, body = {}) {
|
|
382
|
+
const res = await fetch(`${this._apiBaseUrl}/engage/sdk/${path}`, {
|
|
383
|
+
method: 'POST',
|
|
384
|
+
headers: {
|
|
385
|
+
'Content-Type': 'application/json',
|
|
386
|
+
'x-digia-project-id': this._projectId,
|
|
387
|
+
'x-digia-device-id': this._deviceId
|
|
388
|
+
},
|
|
389
|
+
body: JSON.stringify(body)
|
|
390
|
+
});
|
|
391
|
+
if (!res.ok) {
|
|
392
|
+
throw new Error(`${path} failed: HTTP ${res.status}`);
|
|
393
|
+
}
|
|
394
|
+
const json = await res.json();
|
|
395
|
+
return this._extractApiResponse(json);
|
|
396
|
+
}
|
|
397
|
+
_extractApiResponse(json) {
|
|
398
|
+
if (Array.isArray(json)) return json;
|
|
399
|
+
if (json && typeof json === 'object') {
|
|
400
|
+
const obj = json;
|
|
401
|
+
const data = obj.data;
|
|
402
|
+
if (data && typeof data === 'object' && 'response' in data) {
|
|
403
|
+
const value = data.response;
|
|
404
|
+
if (value == null) throw new Error('SDK response.data.response is null');
|
|
405
|
+
return value;
|
|
406
|
+
}
|
|
407
|
+
if ('response' in obj) {
|
|
408
|
+
const value = obj.response;
|
|
409
|
+
if (value == null) throw new Error('SDK response.response is null');
|
|
410
|
+
return value;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
throw new Error('SDK response missing data.response');
|
|
414
|
+
}
|
|
415
|
+
_emitSlotWidth(campaign) {
|
|
416
|
+
const raw = campaign;
|
|
417
|
+
const config = raw.templateConfig;
|
|
418
|
+
if (!config) {
|
|
419
|
+
this._log(`_emitSlotWidth: no templateConfig for campaign ${campaign.campaign_key}`);
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
const slotKey = typeof config.slotKey === 'string' ? config.slotKey : null;
|
|
423
|
+
const width = typeof config.width === 'number' && config.width > 0 ? config.width : null;
|
|
424
|
+
this._log(`_emitSlotWidth slotKey=${slotKey} width=${width} campaign=${campaign.campaign_key}`);
|
|
425
|
+
if (slotKey) {
|
|
426
|
+
DeviceEventEmitter.emit('digiaSlotWidth', {
|
|
427
|
+
slotKey,
|
|
428
|
+
width
|
|
429
|
+
});
|
|
430
|
+
} else {
|
|
431
|
+
this._log(`_emitSlotWidth: no slotKey in templateConfig ${JSON.stringify(config)}`);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
_extractCampaignKey(payload) {
|
|
435
|
+
const fromContent = this._extractString(payload.content, 'digiaKey', 'campaign_key', 'campaignKey');
|
|
436
|
+
if (fromContent) return fromContent;
|
|
437
|
+
const args = payload.content.args;
|
|
438
|
+
if (args && typeof args === 'object' && !Array.isArray(args)) {
|
|
439
|
+
const fromArgs = this._extractString(args, 'digiaKey', 'campaign_key', 'campaignKey');
|
|
440
|
+
if (fromArgs) return fromArgs;
|
|
441
|
+
}
|
|
442
|
+
if (this._campaignsByKey.has(payload.id)) return payload.id;
|
|
443
|
+
return null;
|
|
444
|
+
}
|
|
445
|
+
_extractString(data, ...keys) {
|
|
446
|
+
for (const key of keys) {
|
|
447
|
+
const value = data[key];
|
|
448
|
+
if (typeof value === 'string' && value.trim()) return value.trim();
|
|
449
|
+
}
|
|
450
|
+
return null;
|
|
451
|
+
}
|
|
452
|
+
_parseTemplateConfig(campaign) {
|
|
453
|
+
const c = campaign;
|
|
454
|
+
const raw = c.templateConfig;
|
|
455
|
+
if (!raw || typeof raw !== 'object') return null;
|
|
456
|
+
const type = raw.templateType;
|
|
457
|
+
if (type !== 'tooltip' && type !== 'spotlight' && type !== 'carousel') {
|
|
458
|
+
this._log(`unknown templateType="${type}" for campaign_key=${campaign.campaign_key}`);
|
|
459
|
+
return null;
|
|
460
|
+
}
|
|
461
|
+
if (type === 'carousel') {
|
|
462
|
+
return raw;
|
|
463
|
+
}
|
|
464
|
+
const steps = Array.isArray(raw.steps) ? raw.steps : [];
|
|
465
|
+
return {
|
|
466
|
+
...raw,
|
|
467
|
+
templateType: type,
|
|
468
|
+
steps
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// ── Device ID ────────────────────────────────────────────────────────────
|
|
473
|
+
|
|
474
|
+
async _loadOrCreateDeviceId() {
|
|
475
|
+
const DEVICE_ID_KEY = 'digia:device_id';
|
|
476
|
+
try {
|
|
477
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
478
|
+
const AsyncStorage = require('@react-native-async-storage/async-storage').default;
|
|
479
|
+
const stored = await AsyncStorage.getItem(DEVICE_ID_KEY);
|
|
480
|
+
if (stored) return stored;
|
|
481
|
+
const id = uuid.v4();
|
|
482
|
+
await AsyncStorage.setItem(DEVICE_ID_KEY, id);
|
|
483
|
+
return id;
|
|
484
|
+
} catch {
|
|
485
|
+
return uuid.v4();
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// ── Frequency capping ────────────────────────────────────────────────────
|
|
490
|
+
|
|
491
|
+
async _getFrequencyState(campaignKey, isSession) {
|
|
492
|
+
return frequencyStore.get(campaignKey, isSession);
|
|
493
|
+
}
|
|
494
|
+
async _bumpFrequencyImpression(campaignKey) {
|
|
495
|
+
const campaign = this._campaignsByKey.get(campaignKey);
|
|
496
|
+
if (!campaign || !hasPolicy(campaign.frequency)) return;
|
|
497
|
+
const isSession = isSessionPolicy(campaign.frequency);
|
|
498
|
+
const now = Date.now();
|
|
499
|
+
const prev = await frequencyStore.get(campaignKey, isSession);
|
|
500
|
+
const next = {
|
|
501
|
+
shown_count: (prev?.shown_count ?? 0) + 1,
|
|
502
|
+
first_shown_at: prev?.first_shown_at ?? now,
|
|
503
|
+
last_shown_at: now,
|
|
504
|
+
stopped_at: prev?.stopped_at ?? null,
|
|
505
|
+
stopped_reason: prev?.stopped_reason ?? null
|
|
506
|
+
};
|
|
507
|
+
await frequencyStore.set(campaignKey, next, isSession);
|
|
508
|
+
}
|
|
509
|
+
async _applyStopOn(campaignKey, interactionType) {
|
|
510
|
+
const campaign = this._campaignsByKey.get(campaignKey);
|
|
511
|
+
const stopOn = campaign?.frequency?.stop_on;
|
|
512
|
+
if (!stopOn) return;
|
|
513
|
+
const matches = stopOn === 'any_action' || stopOn === interactionType;
|
|
514
|
+
if (!matches) return;
|
|
515
|
+
const isSession = isSessionPolicy(campaign.frequency);
|
|
516
|
+
const prev = await frequencyStore.get(campaignKey, isSession);
|
|
517
|
+
if (prev?.stopped_at) return;
|
|
518
|
+
const now = Date.now();
|
|
519
|
+
const next = {
|
|
520
|
+
shown_count: prev?.shown_count ?? 0,
|
|
521
|
+
first_shown_at: prev?.first_shown_at ?? null,
|
|
522
|
+
last_shown_at: prev?.last_shown_at ?? null,
|
|
523
|
+
stopped_at: now,
|
|
524
|
+
stopped_reason: interactionType
|
|
525
|
+
};
|
|
526
|
+
await frequencyStore.set(campaignKey, next, isSession);
|
|
527
|
+
}
|
|
528
|
+
_log(message) {
|
|
529
|
+
if (this._logLevel !== 'verbose') return;
|
|
530
|
+
// eslint-disable-next-line no-console
|
|
531
|
+
console.log(`[Digia] ${message}`);
|
|
532
|
+
}
|
|
148
533
|
}
|
|
149
534
|
export const Digia = new DigiaClass();
|
|
150
535
|
//# sourceMappingURL=Digia.js.map
|