cordova-plugin-reteno 2.1.0 → 2.2.1

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 (42) hide show
  1. package/README.md +4 -0
  2. package/docs/Cordova/Action Buttons/README.md +62 -0
  3. package/docs/Cordova/AppInbox/README.md +55 -0
  4. package/docs/Cordova/AppLifeCycleEvents/README.md +80 -0
  5. package/docs/Cordova/Ecommerce/README.md +144 -0
  6. package/docs/Cordova/InAppMessages/README.md +72 -0
  7. package/docs/Cordova/Push notification/README.md +124 -0
  8. package/docs/Cordova/Recommendations/README.md +62 -0
  9. package/docs/Cordova/SetupGuide/Android.md +135 -0
  10. package/docs/Cordova/SetupGuide/IOS.md +325 -0
  11. package/docs/Cordova/SetupGuide/README.md +91 -0
  12. package/docs/Cordova/Tracking user behaviour/README.md +52 -0
  13. package/docs/Cordova/Tracking user information/README.md +112 -0
  14. package/docs/Ionic/Action Buttons/README.md +66 -0
  15. package/docs/Ionic/AppInbox/README.md +61 -0
  16. package/docs/Ionic/AppLifeCycleEvents/README.md +77 -0
  17. package/docs/Ionic/Ecommerce/README.md +139 -0
  18. package/docs/Ionic/InAppMessages/README.md +65 -0
  19. package/docs/Ionic/Push notification/README.md +128 -0
  20. package/docs/Ionic/Recommendations/README.md +46 -0
  21. package/docs/Ionic/SetupGuide/Android.md +173 -0
  22. package/docs/Ionic/SetupGuide/IOS.md +354 -0
  23. package/docs/Ionic/SetupGuide/README.md +167 -0
  24. package/docs/Ionic/Tracking user behaviour/README.md +31 -0
  25. package/docs/Ionic/Tracking user information/README.md +98 -0
  26. package/package.json +1 -1
  27. package/plugin.xml +30 -2
  28. package/scripts/ios-podfile-use-modular-headers.js +109 -0
  29. package/scripts/validate-install.js +37 -9
  30. package/src/android/app/build.gradle +1 -1
  31. package/src/android/app/src/main/AndroidManifest.xml +6 -1
  32. package/src/android/app/src/main/java/com/reteno/plugin/RetenoNotificationGroupingRuleProvider.java +117 -0
  33. package/src/android/app/src/main/java/com/reteno/plugin/RetenoNotificationSummaryManager.java +262 -0
  34. package/src/android/app/src/main/java/com/reteno/plugin/RetenoPlugin.java +99 -11
  35. package/src/android/reteno.gradle +19 -9
  36. package/src/ios/RetenoPlugin.swift +38 -6
  37. package/src/ios/RetenoPluginBootstrap.m +117 -0
  38. package/types/index.ts +11 -3
  39. package/www/cordova-plugin-reteno.js +39 -1
  40. package/docs/android.md +0 -247
  41. package/docs/api.md +0 -714
  42. package/docs/ios.md +0 -495
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Reteno SDK for Cordova and Capacitor apps.
4
4
 
5
+ Version 2.2.1 includes Reteno Android SDK 2.10.2 and Reteno iOS SDK 2.7.4.
6
+
5
7
  ## Installation
6
8
 
7
9
  ```bash
@@ -10,6 +12,8 @@ npm install cordova-plugin-reteno
10
12
 
11
13
  ## Documentation
12
14
 
15
+ - [Bundled Cordova documentation](docs/Cordova/SetupGuide/README.md)
16
+ - [Bundled Ionic documentation](docs/Ionic/SetupGuide/README.md)
13
17
  - [Cordova SDK Overview](https://docs.reteno.com/reference/cordova-sdk-setup)
14
18
  - [Ionic SDK Overview](https://docs.reteno.com/reference/ionic-sdk)
15
19
 
@@ -0,0 +1,62 @@
1
+ # Action Buttons
2
+
3
+ ## Configuring Action Buttons
4
+
5
+ When creating mobile push notifications, you can add action buttons.
6
+
7
+ - iOS supports up to 4 buttons in one notification.
8
+ - Android supports up to 3 buttons in one notification.
9
+
10
+ ### Adding Action Buttons
11
+
12
+ 1. In Reteno, open `Messages -> Mobile Push`.
13
+ 2. Create a push or edit existing one.
14
+ 3. Configure the `Buttons` section.
15
+
16
+ <p align="center">
17
+ <img src="https://raw.githubusercontent.com/reteno-com/reteno-mobile-documentation/main/Cordova/Action%20Buttons/Resources/action_button_setup.png" width="100%"/>
18
+ </p>
19
+
20
+ Use unique `Action ID` values for different buttons.
21
+
22
+ | Parameter | Notes |
23
+ | --- | --- |
24
+ | Action ID | Unique button action identifier |
25
+ | Text | Text shown on the button |
26
+ | iOS icon path | Available on iOS 15+. Name of image from app bundle |
27
+ | Launch URL | URL/deeplink to open |
28
+ | Custom data | Additional JSON payload |
29
+
30
+ ## Handle Action Button click
31
+
32
+ Enable handler for iOS event emission:
33
+
34
+ ```js
35
+ await RetenoPlugin.setNotificationActionHandler({ emitEvent: true });
36
+ ```
37
+
38
+ Subscribe:
39
+
40
+ ```js
41
+ function onPushButtonClicked(event) {
42
+ const detail = event && event.detail !== undefined ? event.detail : event;
43
+ console.log('reteno-push-button-clicked:', detail);
44
+ // detail.actionId
45
+ // detail.link
46
+ // detail.customData
47
+ // detail.userInfo
48
+ }
49
+
50
+ RetenoPlugin.setOnRetenoPushButtonClickedListener(onPushButtonClicked);
51
+
52
+ // later
53
+ RetenoPlugin.removeOnRetenoPushButtonClickedListener(onPushButtonClicked);
54
+ ```
55
+
56
+ Disable handler:
57
+
58
+ ```js
59
+ await RetenoPlugin.setNotificationActionHandler(false);
60
+ ```
61
+
62
+ Android note: `setNotificationActionHandler(...)` is no-op on Android. Action button clicks are detected automatically.
@@ -0,0 +1,55 @@
1
+ # App inbox
2
+
3
+ ## Get App Inbox messages
4
+
5
+ ```js
6
+ RetenoPlugin.getAppInboxMessages({
7
+ page: 1,
8
+ pageSize: 20,
9
+ status: 'UNOPENED', // optional: OPENED | UNOPENED
10
+ })
11
+ .then((result) => {
12
+ console.log('messages:', result.messages);
13
+ console.log('totalPages:', result.totalPages);
14
+ })
15
+ .catch((err) => console.error('getAppInboxMessages: ERROR', err));
16
+ ```
17
+
18
+ ## Get unread messages count
19
+
20
+ ```js
21
+ RetenoPlugin.getAppInboxMessagesCount()
22
+ .then((count) => console.log('Unread messages:', count))
23
+ .catch((err) => console.error('getAppInboxMessagesCount: ERROR', err));
24
+ ```
25
+
26
+ ## Subscribe to count changes
27
+
28
+ ```js
29
+ function onInboxCountChanged(count) {
30
+ console.log('App Inbox count changed:', count);
31
+ }
32
+
33
+ RetenoPlugin.subscribeOnMessagesCountChanged(onInboxCountChanged, (err) => {
34
+ console.error('subscribeOnMessagesCountChanged: ERROR', err);
35
+ });
36
+
37
+ // unsubscribe
38
+ RetenoPlugin.unsubscribeMessagesCountChanged();
39
+ ```
40
+
41
+ ## Mark message as opened
42
+
43
+ ```js
44
+ RetenoPlugin.markAsOpened('message-id')
45
+ .then(() => console.log('markAsOpened: OK'))
46
+ .catch((err) => console.error('markAsOpened: ERROR', err));
47
+ ```
48
+
49
+ ## Mark all messages as opened
50
+
51
+ ```js
52
+ RetenoPlugin.markAllMessagesAsOpened()
53
+ .then(() => console.log('markAllMessagesAsOpened: OK'))
54
+ .catch((err) => console.error('markAllMessagesAsOpened: ERROR', err));
55
+ ```
@@ -0,0 +1,80 @@
1
+ # App Lifecycle Events
2
+
3
+ Reteno can automatically track app lifecycle, foreground lifecycle, push subscription, and session events.
4
+
5
+ ## Tracked app lifecycle events
6
+
7
+ When `appLifecycleEnabled` is enabled:
8
+
9
+ - `ApplicationInstalled`
10
+ - `ApplicationUpdated`
11
+ - `ApplicationOpened`
12
+ - `ApplicationBackgrounded`
13
+
14
+ ## Tracked foreground lifecycle events
15
+
16
+ When `foregroundLifecycleEnabled` is enabled:
17
+
18
+ - `ApplicationOpened`
19
+ - `ApplicationBackgrounded`
20
+
21
+ ## Tracked push subscription events
22
+
23
+ When `pushSubscriptionEnabled` is enabled:
24
+
25
+ - `PushNotificationsSubscribed`
26
+ - `PushNotificationsUnsubscribed`
27
+
28
+ ## Tracked session events
29
+
30
+ When `sessionStartEventsEnabled` / `sessionEndEventsEnabled` are enabled:
31
+
32
+ - `SessionStarted`
33
+ - `SessionEnded`
34
+
35
+ ## Configure lifecycle tracking
36
+
37
+ Configure during initialization:
38
+
39
+ ```js
40
+ RetenoPlugin.init({
41
+ lifecycleTrackingOptions: {
42
+ appLifecycleEnabled: true,
43
+ foregroundLifecycleEnabled: false,
44
+ pushSubscriptionEnabled: true,
45
+ sessionStartEventsEnabled: true,
46
+ sessionEndEventsEnabled: false,
47
+ },
48
+ });
49
+ ```
50
+
51
+ Or with shortcuts:
52
+
53
+ ```js
54
+ RetenoPlugin.init({ lifecycleTrackingOptions: 'ALL' });
55
+ RetenoPlugin.init({ lifecycleTrackingOptions: 'NONE' });
56
+ ```
57
+
58
+ You can also call:
59
+
60
+ ```js
61
+ RetenoPlugin.setLifecycleTrackingOptions({
62
+ sessionStartEventsEnabled: true,
63
+ sessionEndEventsEnabled: true,
64
+ });
65
+ RetenoPlugin.setLifecycleTrackingOptions('ALL');
66
+ RetenoPlugin.setLifecycleTrackingOptions('NONE');
67
+ ```
68
+
69
+ iOS note: `setLifecycleTrackingOptions(...)` must be called before `init()`.
70
+ Legacy note: `sessionEventsEnabled` is still supported as an alias that toggles both session start and end events.
71
+
72
+ ## Screen tracking in Cordova
73
+
74
+ For Cordova apps, manual screen tracking is recommended:
75
+
76
+ ```js
77
+ RetenoPlugin.logScreenView('HomeScreen');
78
+ ```
79
+
80
+ Automatic native screen tracking in hybrid WebView apps does not reflect JS route changes.
@@ -0,0 +1,144 @@
1
+ # Ecommerce Activity Tracking
2
+
3
+ Reteno tracks ecommerce activity using `logEcommerceEvent(...)`.
4
+
5
+ Supported `eventType` values:
6
+
7
+ - `productViewed`
8
+ - `productCategoryViewed`
9
+ - `productAddedToWishlist`
10
+ - `cartUpdated`
11
+ - `orderCreated`
12
+ - `orderUpdated`
13
+ - `orderDelivered`
14
+ - `orderCancelled`
15
+ - `searchRequest`
16
+
17
+ ## Product viewed
18
+
19
+ ```js
20
+ RetenoPlugin.logEcommerceEvent({
21
+ eventType: 'productViewed',
22
+ product: {
23
+ productId: 'SKU-123',
24
+ price: 29.99,
25
+ isInStock: true,
26
+ attributes: [{ name: 'color', value: ['red', 'blue'] }],
27
+ },
28
+ currencyCode: 'USD',
29
+ });
30
+ ```
31
+
32
+ ## Product category viewed
33
+
34
+ ```js
35
+ RetenoPlugin.logEcommerceEvent({
36
+ eventType: 'productCategoryViewed',
37
+ category: {
38
+ productCategoryId: 'CATEGORY-01',
39
+ attributes: [{ name: 'gender', value: ['women'] }],
40
+ },
41
+ });
42
+ ```
43
+
44
+ ## Product added to wishlist
45
+
46
+ ```js
47
+ RetenoPlugin.logEcommerceEvent({
48
+ eventType: 'productAddedToWishlist',
49
+ product: {
50
+ productId: 'SKU-123',
51
+ price: 29.99,
52
+ isInStock: true,
53
+ attributes: [{ name: 'color', value: ['red'] }],
54
+ },
55
+ currencyCode: 'USD',
56
+ });
57
+ ```
58
+
59
+ ## Cart updated
60
+
61
+ ```js
62
+ RetenoPlugin.logEcommerceEvent({
63
+ eventType: 'cartUpdated',
64
+ cartId: 'cart-456',
65
+ products: [
66
+ {
67
+ productId: 'SKU-123',
68
+ quantity: 2,
69
+ price: 29.99,
70
+ name: 'T-Shirt',
71
+ category: 'Apparel',
72
+ },
73
+ ],
74
+ currencyCode: 'USD',
75
+ });
76
+ ```
77
+
78
+ ## Order created
79
+
80
+ ```js
81
+ RetenoPlugin.logEcommerceEvent({
82
+ eventType: 'orderCreated',
83
+ order: {
84
+ externalOrderId: 'ORD-789',
85
+ totalCost: 59.98,
86
+ status: 'INITIALIZED',
87
+ date: new Date().toISOString(),
88
+ items: [
89
+ {
90
+ externalItemId: 'SKU-123',
91
+ name: 'T-Shirt',
92
+ category: 'Apparel',
93
+ quantity: 2,
94
+ cost: 29.99,
95
+ url: 'https://example.com/products/SKU-123',
96
+ },
97
+ ],
98
+ },
99
+ currencyCode: 'USD',
100
+ });
101
+ ```
102
+
103
+ ## Order updated
104
+
105
+ ```js
106
+ RetenoPlugin.logEcommerceEvent({
107
+ eventType: 'orderUpdated',
108
+ order: {
109
+ externalOrderId: 'ORD-789',
110
+ totalCost: 59.98,
111
+ status: 'IN_PROGRESS',
112
+ date: new Date().toISOString(),
113
+ },
114
+ currencyCode: 'USD',
115
+ });
116
+ ```
117
+
118
+ ## Order delivered / cancelled
119
+
120
+ For these event types the payload requires `externalOrderId` (not full `order` object):
121
+
122
+ ```js
123
+ RetenoPlugin.logEcommerceEvent({
124
+ eventType: 'orderDelivered',
125
+ externalOrderId: 'ORD-789',
126
+ });
127
+
128
+ RetenoPlugin.logEcommerceEvent({
129
+ eventType: 'orderCancelled',
130
+ externalOrderId: 'ORD-789',
131
+ });
132
+ ```
133
+
134
+ ## Search request
135
+
136
+ ```js
137
+ RetenoPlugin.logEcommerceEvent({
138
+ eventType: 'searchRequest',
139
+ search: 'red t-shirt',
140
+ isFound: true,
141
+ });
142
+ ```
143
+
144
+ Use valid [ISO-4217](https://en.wikipedia.org/wiki/ISO_4217) currency codes.
@@ -0,0 +1,72 @@
1
+ # In app messages
2
+
3
+ ## Pause and resume In-App messages
4
+
5
+ ```js
6
+ // pause
7
+ RetenoPlugin.pauseInAppMessages(true)
8
+ .then(() => console.log('In-apps paused'))
9
+ .catch((err) => console.error(err));
10
+
11
+ // resume
12
+ RetenoPlugin.pauseInAppMessages(false)
13
+ .then(() => console.log('In-apps resumed'))
14
+ .catch((err) => console.error(err));
15
+ ```
16
+
17
+ ## Configure pause behaviour
18
+
19
+ ```js
20
+ // Drop in-apps received while paused
21
+ RetenoPlugin.setInAppMessagesPauseBehaviour('SKIP_IN_APPS');
22
+
23
+ // Queue one and show later after resume
24
+ RetenoPlugin.setInAppMessagesPauseBehaviour('POSTPONE_IN_APPS');
25
+ ```
26
+
27
+ ## Subscribe to In-App lifecycle events
28
+
29
+ ```js
30
+ function onInAppStatus(event) {
31
+ const detail = event.detail || event;
32
+ console.log('In-app status:', detail.event, detail.data);
33
+ // detail.event:
34
+ // beforeDisplay, onDisplay, beforeClose, afterClose, onError
35
+ }
36
+
37
+ RetenoPlugin.setOnInAppLifecycleCallback(onInAppStatus);
38
+
39
+ // unsubscribe
40
+ RetenoPlugin.setOnInAppLifecycleCallback(null);
41
+ ```
42
+
43
+ ### Platform-specific payload (`detail.data`)
44
+
45
+ | Event | Android | iOS |
46
+ |---|---|---|
47
+ | `beforeDisplay` | `{ id }` | `{}` (empty) |
48
+ | `onDisplay` | `{ id }` | `{}` (empty) |
49
+ | `beforeClose` | `{ id, closeAction }` | `{ closeAction, action }` |
50
+ | `afterClose` | `{ id, closeAction }` | `{ closeAction, action }` |
51
+ | `onError` | `{ id, errorMessage }` | `{ errorMessage }` |
52
+
53
+ `closeAction` values:
54
+ - **Android**: string from native SDK (e.g. via `InAppCloseData.getCloseAction()`)
55
+ - **iOS**: `"closeButtonClicked"`, `"buttonClicked"`, `"openUrlClicked"`, or `"unknown"`
56
+
57
+ `action` (iOS only): `{ isCloseButtonClicked, isButtonClicked, isOpenUrlClicked }`
58
+
59
+ ## Receive custom data from In-App (Android)
60
+
61
+ ```js
62
+ function onInAppCustomData(event) {
63
+ console.log('In-app custom data:', event.detail);
64
+ }
65
+
66
+ RetenoPlugin.setOnInAppMessageCustomDataReceivedListener(onInAppCustomData);
67
+
68
+ // later
69
+ RetenoPlugin.removeOnInAppMessageCustomDataReceivedListener(onInAppCustomData);
70
+ ```
71
+
72
+ Important: when this listener is registered, automatic opening of In-App button links is disabled. Handle navigation yourself (for example, using `event.detail.url`).
@@ -0,0 +1,124 @@
1
+ # Push notification
2
+
3
+ ## Request notification permission
4
+
5
+ ```js
6
+ RetenoPlugin.requestNotificationPermission()
7
+ .then((result) => {
8
+ // Android: 1 granted / 0 declined
9
+ console.log('Permission result:', result);
10
+ })
11
+ .catch((err) => console.error(err));
12
+ ```
13
+
14
+ ## Get initial notification
15
+
16
+ When app starts from push tap, get payload with `getInitialNotification()`:
17
+
18
+ ```js
19
+ RetenoPlugin.getInitialNotification()
20
+ .then((data) => console.log('getInitialNotification', data))
21
+ .catch((err) => console.error('getInitialNotification: ERROR', err));
22
+ ```
23
+
24
+ ## Listen for received push while app is active
25
+
26
+ ```js
27
+ function onPushReceived(event) {
28
+ console.log('reteno-push-received:', event);
29
+ }
30
+
31
+ RetenoPlugin.setOnRetenoPushReceivedListener(onPushReceived);
32
+
33
+ // later
34
+ RetenoPlugin.removeOnRetenoPushReceivedListener(onPushReceived);
35
+ ```
36
+
37
+ ## Listen for push click events
38
+
39
+ ```js
40
+ function onNotificationClicked(event) {
41
+ console.log('reteno-notification-clicked:', event);
42
+ }
43
+
44
+ RetenoPlugin.setOnRetenoNotificationClickedListener(onNotificationClicked);
45
+
46
+ // later
47
+ RetenoPlugin.removeOnRetenoNotificationClickedListener(onNotificationClicked);
48
+ ```
49
+
50
+ ## Android-only listeners
51
+
52
+ ```js
53
+ function onDismissed(payload) {
54
+ console.log('Push dismissed', payload);
55
+ }
56
+
57
+ function onCustomReceived(payload) {
58
+ console.log('Custom push received', payload);
59
+ }
60
+
61
+ RetenoPlugin.setOnRetenoPushDismissedListener(onDismissed);
62
+ RetenoPlugin.setOnRetenoCustomPushReceivedListener(onCustomReceived);
63
+
64
+ // later
65
+ RetenoPlugin.removeOnRetenoPushDismissedListener(onDismissed);
66
+ RetenoPlugin.removeOnRetenoCustomPushReceivedListener(onCustomReceived);
67
+ ```
68
+
69
+ ## iOS foreground/tap handlers (optional)
70
+
71
+ These methods are **iOS-only**. On Android they are not implemented and calls will fail.
72
+
73
+ ```js
74
+ await RetenoPlugin.setWillPresentNotificationOptions({
75
+ options: ['badge', 'sound', 'banner'],
76
+ emitEvent: true,
77
+ });
78
+
79
+ await RetenoPlugin.setDidReceiveNotificationResponseHandler({
80
+ enabled: true,
81
+ emitEvent: true,
82
+ });
83
+ ```
84
+
85
+ If `emitEvent: true`, plugin emits JS events:
86
+
87
+ - `reteno-push-received`
88
+ - `reteno-notification-clicked`
89
+
90
+ ## Pass token manually (if another plugin gets it)
91
+
92
+ ```js
93
+ await RetenoPlugin.setDeviceToken(token);
94
+ ```
95
+
96
+ Use this when token callbacks are handled outside `cordova-plugin-reteno`.
97
+
98
+ ## Android default notification channel update
99
+
100
+ ```js
101
+ RetenoPlugin.updateDefaultNotificationChannel({
102
+ name: 'New Channel Name',
103
+ description: 'New Channel Description',
104
+ })
105
+ .then(() => console.log('updateDefaultNotificationChannel: OK'))
106
+ .catch((err) => console.error('updateDefaultNotificationChannel: ERROR', err));
107
+ ```
108
+
109
+ ## Android notification grouping
110
+
111
+ Groups Android notifications by a value from the push payload or by a constant group ID. The rule is persisted natively and restored when the Android process starts, so it also applies to a push handled before the WebView is active.
112
+
113
+ ```js
114
+ // Group notifications by the `chatId` field in the push payload.
115
+ await RetenoPlugin.setNotificationGroupingRule({ payloadKey: 'chatId', showSummary: true });
116
+
117
+ // Or assign every Reteno notification to one constant group.
118
+ await RetenoPlugin.setNotificationGroupingRule({ groupId: 'messages', showSummary: true });
119
+
120
+ // Disable grouping.
121
+ await RetenoPlugin.setNotificationGroupingRule(null);
122
+ ```
123
+
124
+ `showSummary` is optional and defaults to `false`. When enabled, the plugin creates and maintains an Android group-summary notification for the group ("You have N new notifications") once at least two notifications share it, and removes the summary once fewer than two remain. Without `showSummary`, grouping only sets the group key on each notification — no collapsed summary row appears unless the app posts its own summary notification.
@@ -0,0 +1,62 @@
1
+ # Recommendations
2
+
3
+ ## Get recommendations
4
+
5
+ ```js
6
+ RetenoPlugin.getRecommendations({
7
+ recomVariantId: 'variant-id',
8
+ productIds: ['product-1', 'product-2'],
9
+ fields: ['name', 'price', 'image', 'link'],
10
+ })
11
+ .then((result) => {
12
+ result.recoms.forEach((item) => {
13
+ console.log(item.productId, item.name);
14
+ });
15
+ })
16
+ .catch((err) => console.error('getRecommendations: ERROR', err));
17
+ ```
18
+
19
+ Category-based example:
20
+
21
+ ```js
22
+ RetenoPlugin.getRecommendations({
23
+ recomVariantId: 'variant-id',
24
+ categoryId: 'category-1',
25
+ fields: ['name', 'price'],
26
+ });
27
+ ```
28
+
29
+ With filters:
30
+
31
+ ```js
32
+ RetenoPlugin.getRecommendations({
33
+ recomVariantId: 'variant-id',
34
+ productIds: ['product-1'],
35
+ fields: ['name', 'price'],
36
+ filters: [{ name: 'category', values: ['shoes', 'boots'] }],
37
+ })
38
+ .then((result) => console.log('getRecommendations: OK', result))
39
+ .catch((err) => console.error('getRecommendations: ERROR', err));
40
+ ```
41
+
42
+ ## Log recommendation events
43
+
44
+ ```js
45
+ RetenoPlugin.logRecommendations({
46
+ recomVariantId: 'variant-id',
47
+ recomEvents: [
48
+ {
49
+ recomEventType: 'IMPRESSIONS',
50
+ occurred: new Date().toISOString(),
51
+ productId: 'product-1',
52
+ },
53
+ {
54
+ recomEventType: 'CLICKS',
55
+ occurred: new Date().toISOString(),
56
+ productId: 'product-2',
57
+ },
58
+ ],
59
+ })
60
+ .then(() => console.log('logRecommendations: OK'))
61
+ .catch((err) => console.error('logRecommendations: ERROR', err));
62
+ ```