cordova-plugin-reteno 2.2.0 → 2.2.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 +2 -4
- package/docs/Cordova/Action Buttons/README.md +62 -0
- package/docs/Cordova/AppInbox/README.md +55 -0
- package/docs/Cordova/AppLifeCycleEvents/README.md +80 -0
- package/docs/Cordova/Ecommerce/README.md +144 -0
- package/docs/Cordova/InAppMessages/README.md +72 -0
- package/docs/Cordova/Push notification/README.md +129 -0
- package/docs/Cordova/Recommendations/README.md +62 -0
- package/docs/Cordova/SetupGuide/Android.md +135 -0
- package/docs/Cordova/SetupGuide/IOS.md +325 -0
- package/docs/Cordova/SetupGuide/README.md +91 -0
- package/docs/Cordova/Tracking user behaviour/README.md +52 -0
- package/docs/Cordova/Tracking user information/README.md +112 -0
- package/docs/Ionic/Action Buttons/README.md +66 -0
- package/docs/Ionic/AppInbox/README.md +61 -0
- package/docs/Ionic/AppLifeCycleEvents/README.md +77 -0
- package/docs/Ionic/Ecommerce/README.md +139 -0
- package/docs/Ionic/InAppMessages/README.md +65 -0
- package/docs/Ionic/Push notification/README.md +133 -0
- package/docs/Ionic/Recommendations/README.md +46 -0
- package/docs/Ionic/SetupGuide/Android.md +173 -0
- package/docs/Ionic/SetupGuide/IOS.md +354 -0
- package/docs/Ionic/SetupGuide/README.md +167 -0
- package/docs/Ionic/Tracking user behaviour/README.md +31 -0
- package/docs/Ionic/Tracking user information/README.md +98 -0
- package/package.json +1 -1
- package/plugin.xml +20 -2
- package/scripts/ios-podfile-use-modular-headers.js +109 -0
- package/scripts/validate-install.js +37 -9
- package/src/android/app/build.gradle +1 -1
- package/src/android/app/src/main/java/com/reteno/plugin/RetenoPlugin.java +29 -9
- package/src/android/reteno.gradle +19 -9
- package/src/ios/RetenoPlugin.swift +37 -5
- package/src/ios/RetenoPluginBootstrap.m +117 -0
- package/types/index.ts +1 -1
- package/docs/android.md +0 -264
- package/docs/api.md +0 -734
- package/docs/ios.md +0 -495
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Tracking user information
|
|
2
|
+
|
|
3
|
+
## Set user attributes (identified user)
|
|
4
|
+
|
|
5
|
+
Use `setUserAttributes` with required `externalUserId`:
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
RetenoPlugin.setUserAttributes({
|
|
9
|
+
externalUserId: 'USER_ID',
|
|
10
|
+
user: {
|
|
11
|
+
userAttributes: {
|
|
12
|
+
email: 'john.doe@example.com',
|
|
13
|
+
phone: '+1234567890',
|
|
14
|
+
firstName: 'John',
|
|
15
|
+
lastName: 'Doe',
|
|
16
|
+
languageCode: 'en',
|
|
17
|
+
timeZone: 'Europe/Kyiv',
|
|
18
|
+
marketId: 'market_1',
|
|
19
|
+
address: {
|
|
20
|
+
region: 'Kyivska',
|
|
21
|
+
town: 'Kyiv',
|
|
22
|
+
address: 'Khreshchatyk St, 1',
|
|
23
|
+
postcode: '01001',
|
|
24
|
+
},
|
|
25
|
+
fields: [{ key: 'plan', value: 'premium' }],
|
|
26
|
+
},
|
|
27
|
+
subscriptionKeys: ['news', 'promotions'],
|
|
28
|
+
groupNamesInclude: ['beta-testers'],
|
|
29
|
+
groupNamesExclude: ['unsubscribed'],
|
|
30
|
+
},
|
|
31
|
+
})
|
|
32
|
+
.then(() => console.log('setUserAttributes: OK'))
|
|
33
|
+
.catch((err) => console.error('setUserAttributes: ERROR', err));
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Set anonymous user attributes
|
|
37
|
+
|
|
38
|
+
Use `setAnonymousUserAttributes` before user identification:
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
RetenoPlugin.setAnonymousUserAttributes({
|
|
42
|
+
firstName: 'John',
|
|
43
|
+
lastName: 'Doe',
|
|
44
|
+
languageCode: 'en',
|
|
45
|
+
timeZone: 'Europe/Kyiv',
|
|
46
|
+
marketId: 'market_1',
|
|
47
|
+
address: {
|
|
48
|
+
region: 'Kyivska',
|
|
49
|
+
town: 'Kyiv',
|
|
50
|
+
address: 'Khreshchatyk St, 1',
|
|
51
|
+
postcode: '01001',
|
|
52
|
+
},
|
|
53
|
+
fields: [{ key: 'utm_source', value: 'google' }],
|
|
54
|
+
})
|
|
55
|
+
.then(() => console.log('setAnonymousUserAttributes: OK'))
|
|
56
|
+
.catch((err) => console.error('setAnonymousUserAttributes: ERROR', err));
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Note: anonymous attributes cannot include `phone` or `email`.
|
|
60
|
+
|
|
61
|
+
## Multi-account mode
|
|
62
|
+
|
|
63
|
+
Use `setMultiAccountUserAttributes` when one device should receive push for multiple accounts:
|
|
64
|
+
|
|
65
|
+
```js
|
|
66
|
+
RetenoPlugin.setMultiAccountUserAttributes({
|
|
67
|
+
externalUserId: 'user-123',
|
|
68
|
+
user: {
|
|
69
|
+
userAttributes: {
|
|
70
|
+
email: 'john.doe@example.com',
|
|
71
|
+
phone: '+1234567890',
|
|
72
|
+
firstName: 'John',
|
|
73
|
+
lastName: 'Doe',
|
|
74
|
+
languageCode: 'en',
|
|
75
|
+
timeZone: 'Europe/Kyiv',
|
|
76
|
+
marketId: 'market_1',
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
})
|
|
80
|
+
.then(() => console.log('setMultiAccountUserAttributes: OK'))
|
|
81
|
+
.catch((err) => console.error('setMultiAccountUserAttributes: ERROR', err));
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
If you want default one-token-per-device behavior, switch back to `setUserAttributes`.
|
|
85
|
+
|
|
86
|
+
## Market ID
|
|
87
|
+
|
|
88
|
+
`marketId` is supported by `setUserAttributes`, `setAnonymousUserAttributes`, and
|
|
89
|
+
`setMultiAccountUserAttributes` starting with `cordova-plugin-reteno@2.1.0`.
|
|
90
|
+
|
|
91
|
+
- The value can contain up to 64 Latin letters, digits, hyphens (`-`), and underscores (`_`).
|
|
92
|
+
- Pass an empty string to clear the existing value:
|
|
93
|
+
|
|
94
|
+
```js
|
|
95
|
+
RetenoPlugin.setUserAttributes({
|
|
96
|
+
externalUserId: 'USER_ID',
|
|
97
|
+
user: {
|
|
98
|
+
userAttributes: {
|
|
99
|
+
marketId: '',
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
- Omit `marketId` to keep the existing value unchanged.
|
|
106
|
+
- The feature requires Reteno Android SDK 2.9.5+ or Reteno iOS SDK 2.7.1+. These versions are included in `cordova-plugin-reteno@2.1.0`.
|
|
107
|
+
|
|
108
|
+
## Notes
|
|
109
|
+
|
|
110
|
+
- `languageCode` should follow [RFC 5646](https://www.rfc-editor.org/rfc/rfc5646.html) (`de-AT`, `en`, etc).
|
|
111
|
+
- `timeZone` should be from [TZ database](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) (`Europe/Kyiv`, etc).
|
|
112
|
+
- API payload types are available in plugin [types](https://github.com/reteno-com/reteno-cordova/blob/main/types/index.ts).
|
|
@@ -0,0 +1,66 @@
|
|
|
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.
|
|
8
|
+
- Android supports up to 3 buttons.
|
|
9
|
+
|
|
10
|
+
<p align="center">
|
|
11
|
+
<img src="https://raw.githubusercontent.com/reteno-com/reteno-mobile-documentation/main/Ionic/Action%20Buttons/Resources/action_button_setup.png" width="100%"/>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
Use unique `Action ID` values for different buttons.
|
|
15
|
+
|
|
16
|
+
| Parameter | Notes |
|
|
17
|
+
| --- | --- |
|
|
18
|
+
| Action ID | Unique button action identifier |
|
|
19
|
+
| Text | Text shown on the button |
|
|
20
|
+
| iOS icon path | Available on iOS 15+. Name of image from app bundle |
|
|
21
|
+
| Launch URL | URL/deeplink to open |
|
|
22
|
+
| Custom data | Additional JSON payload |
|
|
23
|
+
|
|
24
|
+
## Handle action button click
|
|
25
|
+
|
|
26
|
+
### iOS
|
|
27
|
+
|
|
28
|
+
Enable handler:
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
await this.reteno.setNotificationActionHandler({ emitEvent: true });
|
|
32
|
+
// or equivalently:
|
|
33
|
+
await this.reteno.setNotificationActionHandler({ enabled: true, emitEvent: true });
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Any other value (`false`, `null`, `true`, `{ enabled: true }` without `emitEvent`) clears the handler.
|
|
37
|
+
|
|
38
|
+
Subscribe:
|
|
39
|
+
|
|
40
|
+
The plugin delivers the event through `document.addEventListener('reteno-push-button-clicked', ...)`, so the listener receives a DOM `CustomEvent` whose `detail` field matches `RetenoPushButtonClickedPayload`:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
const onPushButtonClicked = (event: any) => {
|
|
44
|
+
const detail = event?.detail ?? event;
|
|
45
|
+
console.log('reteno-push-button-clicked:', detail);
|
|
46
|
+
// detail.actionId — unique button action identifier
|
|
47
|
+
// detail.link — URL/deeplink associated with the button (may be null)
|
|
48
|
+
// detail.customData — additional JSON from the button (object, or raw string on Android)
|
|
49
|
+
// detail.userInfo — original notification payload
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
this.reteno.setOnRetenoPushButtonClickedListener(onPushButtonClicked);
|
|
53
|
+
|
|
54
|
+
// later
|
|
55
|
+
this.reteno.removeOnRetenoPushButtonClickedListener(onPushButtonClicked);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Disable:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
await this.reteno.setNotificationActionHandler(false);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Android
|
|
65
|
+
|
|
66
|
+
`setNotificationActionHandler(...)` is no-op on Android. Action button clicks are detected automatically.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# App Inbox
|
|
2
|
+
|
|
3
|
+
## Get App Inbox messages
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
const result = await this.reteno.getAppInboxMessages({
|
|
7
|
+
page: 1,
|
|
8
|
+
pageSize: 20,
|
|
9
|
+
status: 'UNOPENED', // optional: 'OPENED' | 'UNOPENED'
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
console.log(result.messages, result.totalPages);
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Returns `AppInboxMessages`: `{ messages: AppInboxMessage[], totalPages: number }`. Each `AppInboxMessage` contains:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
type AppInboxMessage = {
|
|
19
|
+
id: string;
|
|
20
|
+
title: string;
|
|
21
|
+
createdDate: string;
|
|
22
|
+
isNewMessage: boolean;
|
|
23
|
+
content?: string | null;
|
|
24
|
+
imageUrl?: string | null;
|
|
25
|
+
linkUrl?: string | null;
|
|
26
|
+
category?: string | null;
|
|
27
|
+
status?: 'OPENED' | 'UNOPENED' | null;
|
|
28
|
+
customData?: Record<string, string> | null;
|
|
29
|
+
};
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Get unread messages count
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
const count = await this.reteno.getAppInboxMessagesCount();
|
|
36
|
+
console.log('Unread messages:', count);
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Subscribe to count changes
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
this.reteno.subscribeOnMessagesCountChanged().subscribe({
|
|
43
|
+
next: (count) => console.log('App Inbox count changed:', count),
|
|
44
|
+
error: (err) => console.error('subscribeOnMessagesCountChanged:', err),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// later
|
|
48
|
+
await this.reteno.unsubscribeMessagesCountChanged();
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Mark message as opened
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
await this.reteno.markAsOpened('message-id');
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Mark all messages as opened
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
await this.reteno.markAllMessagesAsOpened();
|
|
61
|
+
```
|
|
@@ -0,0 +1,77 @@
|
|
|
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
|
+
During initialization:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
await this.reteno.init({
|
|
41
|
+
lifecycleTrackingOptions: {
|
|
42
|
+
appLifecycleEnabled: true,
|
|
43
|
+
foregroundLifecycleEnabled: false,
|
|
44
|
+
pushSubscriptionEnabled: true,
|
|
45
|
+
sessionStartEventsEnabled: true,
|
|
46
|
+
sessionEndEventsEnabled: false,
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Shortcuts:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
await this.reteno.init({ lifecycleTrackingOptions: 'ALL' });
|
|
55
|
+
await this.reteno.init({ lifecycleTrackingOptions: 'NONE' });
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Runtime update:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
await this.reteno.setLifecycleTrackingOptions({
|
|
62
|
+
sessionStartEventsEnabled: true,
|
|
63
|
+
sessionEndEventsEnabled: true,
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
iOS note: runtime method `setLifecycleTrackingOptions(...)` works only before `init()`.
|
|
68
|
+
Use `lifecycleTrackingOptions` in `init(...)` as the main configuration path for iOS.
|
|
69
|
+
Legacy note: `sessionEventsEnabled` is still supported as an alias that toggles both session start and end events.
|
|
70
|
+
|
|
71
|
+
## Screen tracking in Ionic/Cordova
|
|
72
|
+
|
|
73
|
+
Use manual tracking:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
await this.reteno.logScreenView('HomeScreen');
|
|
77
|
+
```
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# Ecommerce Activity Tracking
|
|
2
|
+
|
|
3
|
+
Use `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
|
+
`currencyCode` should use ISO 4217 format (for example: `USD`, `EUR`, `GBP`).
|
|
18
|
+
|
|
19
|
+
## Product viewed
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
await this.reteno.logEcommerceEvent({
|
|
23
|
+
eventType: 'productViewed',
|
|
24
|
+
product: {
|
|
25
|
+
productId: 'SKU-123',
|
|
26
|
+
price: 29.99,
|
|
27
|
+
isInStock: true,
|
|
28
|
+
attributes: [{ name: 'color', value: ['red', 'blue'] }],
|
|
29
|
+
},
|
|
30
|
+
currencyCode: 'USD',
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Product category viewed
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
await this.reteno.logEcommerceEvent({
|
|
38
|
+
eventType: 'productCategoryViewed',
|
|
39
|
+
category: {
|
|
40
|
+
productCategoryId: 'CATEGORY-01',
|
|
41
|
+
attributes: [{ name: 'gender', value: ['women'] }],
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Product added to wishlist
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
await this.reteno.logEcommerceEvent({
|
|
50
|
+
eventType: 'productAddedToWishlist',
|
|
51
|
+
product: {
|
|
52
|
+
productId: 'SKU-123',
|
|
53
|
+
price: 29.99,
|
|
54
|
+
isInStock: true,
|
|
55
|
+
attributes: [{ name: 'color', value: ['red'] }],
|
|
56
|
+
},
|
|
57
|
+
currencyCode: 'USD',
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Cart updated
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
await this.reteno.logEcommerceEvent({
|
|
65
|
+
eventType: 'cartUpdated',
|
|
66
|
+
cartId: 'cart-456',
|
|
67
|
+
products: [
|
|
68
|
+
{
|
|
69
|
+
productId: 'SKU-123',
|
|
70
|
+
quantity: 2,
|
|
71
|
+
price: 29.99,
|
|
72
|
+
name: 'T-Shirt',
|
|
73
|
+
category: 'Apparel',
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
currencyCode: 'USD',
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Order created
|
|
81
|
+
|
|
82
|
+
`status` accepts `'INITIALIZED' | 'IN_PROGRESS' | 'DELIVERED' | 'CANCELLED'` (plus any custom string).
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
await this.reteno.logEcommerceEvent({
|
|
86
|
+
eventType: 'orderCreated',
|
|
87
|
+
order: {
|
|
88
|
+
externalOrderId: 'ORD-789',
|
|
89
|
+
totalCost: 59.98,
|
|
90
|
+
status: 'INITIALIZED',
|
|
91
|
+
date: new Date().toISOString(),
|
|
92
|
+
items: [
|
|
93
|
+
{
|
|
94
|
+
externalItemId: 'SKU-123',
|
|
95
|
+
name: 'T-Shirt',
|
|
96
|
+
category: 'Apparel',
|
|
97
|
+
quantity: 2,
|
|
98
|
+
cost: 29.99,
|
|
99
|
+
url: 'https://example.com/products/SKU-123',
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
currencyCode: 'USD',
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Order updated
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
await this.reteno.logEcommerceEvent({
|
|
111
|
+
eventType: 'orderUpdated',
|
|
112
|
+
order: {
|
|
113
|
+
externalOrderId: 'ORD-789',
|
|
114
|
+
totalCost: 59.98,
|
|
115
|
+
status: 'IN_PROGRESS',
|
|
116
|
+
date: new Date().toISOString(),
|
|
117
|
+
},
|
|
118
|
+
currencyCode: 'USD',
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Order delivered / cancelled
|
|
123
|
+
|
|
124
|
+
For these event types use `externalOrderId` (not full `order` object):
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
await this.reteno.logEcommerceEvent({ eventType: 'orderDelivered', externalOrderId: 'ORD-789' });
|
|
128
|
+
await this.reteno.logEcommerceEvent({ eventType: 'orderCancelled', externalOrderId: 'ORD-789' });
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Search request
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
await this.reteno.logEcommerceEvent({
|
|
135
|
+
eventType: 'searchRequest',
|
|
136
|
+
search: 'red t-shirt',
|
|
137
|
+
isFound: true,
|
|
138
|
+
});
|
|
139
|
+
```
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# In-app messages
|
|
2
|
+
|
|
3
|
+
## Pause and resume in-app messages
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
await this.reteno.pauseInAppMessages(true); // pause
|
|
7
|
+
await this.reteno.pauseInAppMessages(false); // resume
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Configure pause behaviour
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
await this.reteno.setInAppMessagesPauseBehaviour('SKIP_IN_APPS');
|
|
14
|
+
await this.reteno.setInAppMessagesPauseBehaviour('POSTPONE_IN_APPS');
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
On iOS you can also preconfigure the pause behaviour during `init()` via `inAppMessagesPauseBehaviour` — see the options table in [Setup overview](../SetupGuide/README.md#options-reference).
|
|
18
|
+
|
|
19
|
+
## Subscribe to in-app lifecycle events
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
const onInAppStatus = (event: any) => {
|
|
23
|
+
const detail = event?.detail ?? event;
|
|
24
|
+
console.log('In-app status:', detail?.event, detail?.data);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
await this.reteno.setOnInAppLifecycleCallback(onInAppStatus);
|
|
28
|
+
|
|
29
|
+
// unsubscribe
|
|
30
|
+
await this.reteno.setOnInAppLifecycleCallback(null);
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Platform-specific payload (`detail.data`)
|
|
34
|
+
|
|
35
|
+
| Event | Android | iOS |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| `beforeDisplay` | `{ id }` | `{}` |
|
|
38
|
+
| `onDisplay` | `{ id }` | `{}` |
|
|
39
|
+
| `beforeClose` | `{ id, closeAction }` | `{ closeAction, action }` |
|
|
40
|
+
| `afterClose` | `{ id, closeAction }` | `{ closeAction, action }` |
|
|
41
|
+
| `onError` | `{ id, errorMessage }` | `{ errorMessage }` |
|
|
42
|
+
|
|
43
|
+
In short: Android payload usually includes in-app `id`, while iOS payload for display events is empty (`{}`) and does not include message id.
|
|
44
|
+
|
|
45
|
+
`closeAction` values:
|
|
46
|
+
- **Android**: string from native SDK (e.g. via `InAppCloseData.getCloseAction()`)
|
|
47
|
+
- **iOS**: `"closeButtonClicked"`, `"buttonClicked"`, `"openUrlClicked"`, or `"unknown"`
|
|
48
|
+
|
|
49
|
+
`action` (iOS only): `{ isCloseButtonClicked, isButtonClicked, isOpenUrlClicked }`
|
|
50
|
+
|
|
51
|
+
## Receive custom data from in-app (Android)
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
const onCustomData = (event: any) => {
|
|
55
|
+
const detail = event?.detail ?? event;
|
|
56
|
+
console.log('In-app custom data:', detail);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
this.reteno.setOnInAppMessageCustomDataReceivedListener(onCustomData);
|
|
60
|
+
|
|
61
|
+
// later
|
|
62
|
+
this.reteno.removeOnInAppMessageCustomDataReceivedListener(onCustomData);
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Important: when this listener is registered, automatic opening of in-app button links is disabled. Handle navigation yourself (for example, using `detail.url` / `payload.url`).
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Push notification
|
|
2
|
+
|
|
3
|
+
## Request notification permission
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
const granted = await this.reteno.requestNotificationPermission();
|
|
7
|
+
console.log('Permission result:', granted);
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Get initial notification
|
|
11
|
+
|
|
12
|
+
On Android, `getInitialNotification()` returns only a Reteno push payload
|
|
13
|
+
identified by `es_interaction_id`. Intent extras from Branch or other deeplink
|
|
14
|
+
SDKs are ignored. If the app was not opened from a Reteno push, the method
|
|
15
|
+
returns an empty object.
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
const initial = await this.reteno.getInitialNotification();
|
|
19
|
+
console.log('Initial push payload:', initial);
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Listen for received push
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
const onPushReceived = (event: any) => {
|
|
26
|
+
console.log('reteno-push-received', event);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
this.reteno.setOnRetenoPushReceivedListener(onPushReceived);
|
|
30
|
+
|
|
31
|
+
// later
|
|
32
|
+
this.reteno.removeOnRetenoPushReceivedListener(onPushReceived);
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Listen for notification click
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
const onClicked = (event: any) => {
|
|
39
|
+
console.log('reteno-notification-clicked', event);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
this.reteno.setOnRetenoNotificationClickedListener(onClicked);
|
|
43
|
+
|
|
44
|
+
// later
|
|
45
|
+
this.reteno.removeOnRetenoNotificationClickedListener(onClicked);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Android-only listeners
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
const onDismissed = (payload: any) => console.log('Push dismissed', payload);
|
|
52
|
+
const onCustomReceived = (payload: any) => console.log('Custom push received', payload);
|
|
53
|
+
|
|
54
|
+
this.reteno.setOnRetenoPushDismissedListener(onDismissed);
|
|
55
|
+
this.reteno.setOnRetenoCustomPushReceivedListener(onCustomReceived);
|
|
56
|
+
|
|
57
|
+
// later
|
|
58
|
+
this.reteno.removeOnRetenoPushDismissedListener(onDismissed);
|
|
59
|
+
this.reteno.removeOnRetenoCustomPushReceivedListener(onCustomReceived);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## iOS foreground/tap handlers (optional)
|
|
63
|
+
|
|
64
|
+
These methods are iOS-only. On Android they are not implemented and calls return an error (`Promise` reject / `error` callback).
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
await this.reteno.setWillPresentNotificationOptions({
|
|
68
|
+
options: ['badge', 'sound', 'banner'],
|
|
69
|
+
emitEvent: true,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
await this.reteno.setDidReceiveNotificationResponseHandler({
|
|
73
|
+
enabled: true,
|
|
74
|
+
emitEvent: true,
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`options` accepts any combination of `NotificationPresentationOption` values: `'badge' | 'sound' | 'alert' | 'banner' | 'list'`.
|
|
79
|
+
|
|
80
|
+
Shorthand forms accepted by `setWillPresentNotificationOptions`:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
// Pass only the options array
|
|
84
|
+
await this.reteno.setWillPresentNotificationOptions(['badge', 'sound', 'banner']);
|
|
85
|
+
|
|
86
|
+
// Clear previously set options
|
|
87
|
+
await this.reteno.setWillPresentNotificationOptions(null);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`setDidReceiveNotificationResponseHandler` also accepts `boolean | null` to disable or clear the handler:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
await this.reteno.setDidReceiveNotificationResponseHandler(false);
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
If `emitEvent: true`, plugin emits JS events:
|
|
97
|
+
|
|
98
|
+
- `reteno-push-received`
|
|
99
|
+
- `reteno-notification-clicked`
|
|
100
|
+
|
|
101
|
+
## Pass token manually
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
await this.reteno.setDeviceToken(token);
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Use this when token callbacks are handled outside `cordova-plugin-reteno` (for example, by another plugin/SDK that owns APNs/FCM token delivery).
|
|
108
|
+
|
|
109
|
+
## Android default channel update
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
await this.reteno.updateDefaultNotificationChannel({
|
|
113
|
+
name: 'New Channel Name',
|
|
114
|
+
description: 'New Channel Description',
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Android notification grouping
|
|
119
|
+
|
|
120
|
+
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.
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
// Group notifications by the `chatId` field in the push payload.
|
|
124
|
+
await this.reteno.setNotificationGroupingRule({ payloadKey: 'chatId', showSummary: true });
|
|
125
|
+
|
|
126
|
+
// Or assign every Reteno notification to one constant group.
|
|
127
|
+
await this.reteno.setNotificationGroupingRule({ groupId: 'messages', showSummary: true });
|
|
128
|
+
|
|
129
|
+
// Disable grouping.
|
|
130
|
+
await this.reteno.setNotificationGroupingRule(null);
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
`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,46 @@
|
|
|
1
|
+
# Recommendations
|
|
2
|
+
|
|
3
|
+
## Get recommendations
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
const result = await this.reteno.getRecommendations({
|
|
7
|
+
recomVariantId: 'variant-id',
|
|
8
|
+
productIds: ['product-1', 'product-2'],
|
|
9
|
+
fields: ['name', 'price', 'image', 'link'],
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
console.log(result.recoms);
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Category-based:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
await this.reteno.getRecommendations({
|
|
19
|
+
recomVariantId: 'variant-id',
|
|
20
|
+
categoryId: 'category-1',
|
|
21
|
+
fields: ['name', 'price'],
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
With filters (accepts a single filter object or an array):
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
await this.reteno.getRecommendations({
|
|
29
|
+
recomVariantId: 'variant-id',
|
|
30
|
+
productIds: ['product-1'],
|
|
31
|
+
fields: ['name', 'price'],
|
|
32
|
+
filters: [{ name: 'category', values: ['shoes', 'boots'] }],
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Log recommendation events
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
await this.reteno.logRecommendations({
|
|
40
|
+
recomVariantId: 'variant-id',
|
|
41
|
+
recomEvents: [
|
|
42
|
+
{ recomEventType: 'IMPRESSIONS', occurred: new Date().toISOString(), productId: 'product-1' },
|
|
43
|
+
{ recomEventType: 'CLICKS', occurred: new Date().toISOString(), productId: 'product-2' },
|
|
44
|
+
],
|
|
45
|
+
});
|
|
46
|
+
```
|