@attentive-mobile/attentive-react-native-sdk 2.0.0-beta.8 → 2.0.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
CHANGED
|
@@ -9,6 +9,18 @@ This project uses **npm** as the preferred package manager for consistency and a
|
|
|
9
9
|
|
|
10
10
|
**Note on package managers:** Modern npm (v7+) has significantly improved performance and features, making it the recommended choice for React Native projects. Both npm and yarn work well with React Native, but this project standardizes on npm for development workflows.
|
|
11
11
|
|
|
12
|
+
## Requirements
|
|
13
|
+
|
|
14
|
+
| Tool | Version |
|
|
15
|
+
|------|---------|
|
|
16
|
+
| Node.js | >= 18.0.0 |
|
|
17
|
+
| React Native | >= 0.74 |
|
|
18
|
+
| Ruby | >= 3.3 |
|
|
19
|
+
| CocoaPods | ~> 1.16 |
|
|
20
|
+
| Xcode | >= 15 |
|
|
21
|
+
| Android SDK | API 24+ |
|
|
22
|
+
| JDK | 17 |
|
|
23
|
+
|
|
12
24
|
## Installation
|
|
13
25
|
|
|
14
26
|
Run `npm install @attentive-mobile/attentive-react-native-sdk` from your app's root directory.
|
|
@@ -22,23 +34,23 @@ __*** NOTE: Please refrain from using any private or undocumented classes or met
|
|
|
22
34
|
### Import the SDK
|
|
23
35
|
|
|
24
36
|
```typescript
|
|
25
|
-
import {
|
|
37
|
+
import { initialize, identify, triggerCreative, recordPurchaseEvent, /* ... */ } from '@attentive-mobile/attentive-react-native-sdk';
|
|
26
38
|
```
|
|
27
39
|
|
|
28
40
|
### Create the AttentiveConfig
|
|
29
41
|
|
|
30
42
|
```typescript
|
|
31
|
-
// Create an
|
|
32
|
-
const config
|
|
43
|
+
// Create an AttentiveSdkConfiguration with your attentive domain, in production mode
|
|
44
|
+
const config: AttentiveSdkConfiguration = {
|
|
33
45
|
attentiveDomain: 'YOUR_ATTENTIVE_DOMAIN',
|
|
34
|
-
mode:
|
|
46
|
+
mode: 'production',
|
|
35
47
|
}
|
|
36
48
|
```
|
|
37
49
|
```typescript
|
|
38
50
|
// Alternatively, use "debug" mode. When in debug mode, the Creative will not be shown, but instead a popup will show with debug information about your creative and any reason the Creative wouldn't show.
|
|
39
|
-
const config
|
|
51
|
+
const config: AttentiveSdkConfiguration = {
|
|
40
52
|
attentiveDomain: 'YOUR_ATTENTIVE_DOMAIN',
|
|
41
|
-
mode:
|
|
53
|
+
mode: 'debug',
|
|
42
54
|
}
|
|
43
55
|
```
|
|
44
56
|
|
|
@@ -47,9 +59,9 @@ const config : AttentiveConfiguration = {
|
|
|
47
59
|
The SDK includes debugging helpers to show what data is being sent and received. Enable debugging by setting `enableDebugger: true`:
|
|
48
60
|
|
|
49
61
|
```typescript
|
|
50
|
-
const config
|
|
62
|
+
const config: AttentiveSdkConfiguration = {
|
|
51
63
|
attentiveDomain: 'YOUR_ATTENTIVE_DOMAIN',
|
|
52
|
-
mode:
|
|
64
|
+
mode: 'debug',
|
|
53
65
|
enableDebugger: true, // Shows debug overlays for events and creatives
|
|
54
66
|
}
|
|
55
67
|
```
|
|
@@ -61,7 +73,7 @@ When enabled, debug overlays will automatically appear when:
|
|
|
61
73
|
You can also manually invoke the debug helper:
|
|
62
74
|
|
|
63
75
|
```typescript
|
|
64
|
-
|
|
76
|
+
invokeAttentiveDebugHelper();
|
|
65
77
|
```
|
|
66
78
|
|
|
67
79
|
See [DEBUGGING.md](./DEBUGGING.md) for detailed information about debugging features.
|
|
@@ -76,7 +88,7 @@ On iOS, call `initialize` from TypeScript as early as possible (e.g. the root `A
|
|
|
76
88
|
|
|
77
89
|
```typescript
|
|
78
90
|
// Called once per app session, before any other SDK operations.
|
|
79
|
-
|
|
91
|
+
initialize(config);
|
|
80
92
|
```
|
|
81
93
|
|
|
82
94
|
#### Android — Initialize from Native Code
|
|
@@ -109,6 +121,7 @@ class MainApplication : Application(), ReactApplication {
|
|
|
109
121
|
.applicationContext(this)
|
|
110
122
|
.domain("YOUR_ATTENTIVE_DOMAIN")
|
|
111
123
|
.mode(AttentiveConfig.Mode.PRODUCTION) // or Mode.DEBUG for testing
|
|
124
|
+
.notificationIconId(R.drawable.ic_stat_notification)
|
|
112
125
|
.skipFatigueOnCreatives(false)
|
|
113
126
|
.logLevel(AttentiveLogLevel.VERBOSE)
|
|
114
127
|
.build()
|
|
@@ -120,6 +133,30 @@ class MainApplication : Application(), ReactApplication {
|
|
|
120
133
|
}
|
|
121
134
|
```
|
|
122
135
|
|
|
136
|
+
##### Android notification small icon
|
|
137
|
+
|
|
138
|
+
Android requires push notifications to use a small status-bar icon. If `notificationIconId` is not set, the Attentive Android SDK uses its default fallback icon. To use your app's branding, add a notification small icon resource to your host app and pass its resource ID during native initialization.
|
|
139
|
+
|
|
140
|
+
Add the icon to your Android app's drawable resources:
|
|
141
|
+
|
|
142
|
+
```text
|
|
143
|
+
android/app/src/main/res/drawable/ic_stat_notification.png
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Use a white-only transparent PNG designed for Android notification status bars. Do not use the full-color launcher icon from `mipmap-*`, because Android masks small notification icons and it can render poorly.
|
|
147
|
+
|
|
148
|
+
Then add the icon to your existing `AttentiveConfig.Builder()` chain in `MainApplication.kt` before `build()`:
|
|
149
|
+
|
|
150
|
+
```text
|
|
151
|
+
android/app/src/main/java/<your-package>/MainApplication.kt
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
```kotlin
|
|
155
|
+
.notificationIconId(R.drawable.ic_stat_notification)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
`MainApplication.kt` is host-app code, not generated by this SDK at runtime. If `R.drawable.ic_stat_notification` does not resolve, confirm the drawable file name matches exactly and that you are referencing your app module's `R` class.
|
|
159
|
+
|
|
123
160
|
After the native initialization, all other SDK operations (`identify`, `recordAddToCartEvent`, `recordPurchaseEvent`, etc.) are called from TypeScript as normal on both platforms.
|
|
124
161
|
|
|
125
162
|
> **Tip:** If you see `[AttentiveSDK] recordAddToCartEvent failed — SDK may not be initialized` in your Android logcat, it means `AttentiveSdk.initialize()` was not called from native code before the event was recorded. Check your `Application.onCreate()` setup.
|
|
@@ -128,22 +165,22 @@ After the native initialization, all other SDK operations (`identify`, `recordAd
|
|
|
128
165
|
|
|
129
166
|
```typescript
|
|
130
167
|
// This will remove the creative along with its web view
|
|
131
|
-
|
|
168
|
+
destroyCreative();
|
|
132
169
|
```
|
|
133
170
|
|
|
134
171
|
|
|
135
172
|
### Identify the current user
|
|
136
173
|
```typescript
|
|
137
174
|
// Before loading the creative or sending events, if you have any user identifiers, they will need to be registered. Each identifier is optional. It is okay to skip this step if you have no identifiers about the user yet.
|
|
138
|
-
const identifiers
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
175
|
+
const identifiers: UserIdentifiers = {
|
|
176
|
+
phone: '+15556667777',
|
|
177
|
+
email: 'some_email@gmailfake.com',
|
|
178
|
+
klaviyoId: 'userKlaviyoId',
|
|
179
|
+
shopifyId: 'userShopifyId',
|
|
180
|
+
clientUserId: 'userClientUserId',
|
|
181
|
+
customIdentifiers: { customIdKey: 'customIdValue' }
|
|
145
182
|
};
|
|
146
|
-
|
|
183
|
+
identify(identifiers);
|
|
147
184
|
```
|
|
148
185
|
|
|
149
186
|
The more identifiers that are passed to `identify`, the better the SDK will function. Here is the list of possible identifiers:
|
|
@@ -160,46 +197,34 @@ The more identifiers that are passed to `identify`, the better the SDK will func
|
|
|
160
197
|
|
|
161
198
|
```typescript
|
|
162
199
|
// Trigger the Creative. This will show the Creative as a pop-up over the rest of the app.
|
|
163
|
-
|
|
200
|
+
triggerCreative();
|
|
164
201
|
```
|
|
165
202
|
|
|
166
203
|
### Record user events
|
|
167
204
|
|
|
168
|
-
The SDK currently supports `
|
|
205
|
+
The SDK currently supports `Purchase`, `AddToCart`, `ProductView`, and `CustomEvent`.
|
|
169
206
|
|
|
170
207
|
```typescript
|
|
171
208
|
// Construct one or more "Item"s, which represents the product(s) purchased
|
|
172
|
-
const items
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
// Construct an "Order", which represents the order for the purchase
|
|
184
|
-
const order : Order = {
|
|
185
|
-
orderId: '88888'
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// (Optional) Construct a "Cart", which represents the cart this Purchase was made from
|
|
189
|
-
const cart : Cart = {
|
|
190
|
-
cartId: '555555',
|
|
191
|
-
cartCoupon: 'SOME-DISCOUNT'
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// Construct a PurchaseEvent, which ties together the preceding objects
|
|
195
|
-
const purchaseEvent : PurchaseEvent = {
|
|
209
|
+
const items: Item[] = [
|
|
210
|
+
{
|
|
211
|
+
productId: '555',
|
|
212
|
+
productVariantId: '777',
|
|
213
|
+
price: '14.99',
|
|
214
|
+
currency: 'USD',
|
|
215
|
+
},
|
|
216
|
+
];
|
|
217
|
+
|
|
218
|
+
// Construct a Purchase event
|
|
219
|
+
const purchase: Purchase = {
|
|
196
220
|
items: items,
|
|
197
|
-
|
|
198
|
-
|
|
221
|
+
orderId: '88888',
|
|
222
|
+
cartId: '555555', // optional
|
|
223
|
+
cartCoupon: 'SOME-DISCOUNT', // optional
|
|
199
224
|
}
|
|
200
225
|
|
|
201
226
|
// Record the PurchaseEvent
|
|
202
|
-
|
|
227
|
+
recordPurchaseEvent(purchase);
|
|
203
228
|
```
|
|
204
229
|
|
|
205
230
|
The process is similar for the other events. See [eventTypes.tsx](https://github.com/attentive-mobile/attentive-react-native-sdk/blob/main/src/eventTypes.tsx) for all events.
|
|
@@ -208,12 +233,12 @@ The process is similar for the other events. See [eventTypes.tsx](https://github
|
|
|
208
233
|
|
|
209
234
|
```typescript
|
|
210
235
|
// If new identifiers are available for the user, register them
|
|
211
|
-
|
|
236
|
+
identify({email: 'theusersemail@gmail.com'});
|
|
212
237
|
```
|
|
213
238
|
|
|
214
239
|
```typescript
|
|
215
|
-
|
|
216
|
-
|
|
240
|
+
identify({email: 'theusersemail@gmail.com'});
|
|
241
|
+
identify({phone: '+15556667777'});
|
|
217
242
|
// The SDK will have these two identifiers:
|
|
218
243
|
// email: 'theusersemail@gmail.com'
|
|
219
244
|
// phone: '+15556667777'
|
|
@@ -281,57 +306,45 @@ identify({ email: 'user@example.com', clientUserId: 'id-123' });
|
|
|
281
306
|
|
|
282
307
|
#### Push notifications on Android (FCM)
|
|
283
308
|
|
|
284
|
-
|
|
309
|
+
The Attentive Android SDK registers its own `FirebaseMessagingService` automatically — **you do not need to create a subclass**. As long as your app is registered with Firebase and includes a valid `google-services.json`, the SDK handles FCM token registration and foreground push delivery for you. Follow the [Firebase Android setup guide](https://firebase.google.com/docs/cloud-messaging/android/client) to add FCM to your project.
|
|
285
310
|
|
|
286
|
-
|
|
311
|
+
##### If you already have a FirebaseMessagingService subclass
|
|
312
|
+
|
|
313
|
+
If your app has an existing `FirebaseMessagingService` subclass for other purposes, route Attentive messages through to the SDK:
|
|
287
314
|
|
|
288
315
|
```kotlin
|
|
289
316
|
import com.attentive.androidsdk.AttentiveSdk
|
|
290
317
|
import com.google.firebase.messaging.FirebaseMessagingService
|
|
291
318
|
import com.google.firebase.messaging.RemoteMessage
|
|
292
319
|
|
|
293
|
-
class
|
|
294
|
-
|
|
295
|
-
override fun onNewToken(token: String) {
|
|
296
|
-
super.onNewToken(token)
|
|
297
|
-
// Register the FCM token with the Attentive SDK
|
|
298
|
-
AttentiveSdk.registerDeviceToken(token)
|
|
299
|
-
}
|
|
320
|
+
class YourFirebaseMessagingService : FirebaseMessagingService() {
|
|
300
321
|
|
|
301
322
|
override fun onMessageReceived(remoteMessage: RemoteMessage) {
|
|
302
323
|
super.onMessageReceived(remoteMessage)
|
|
303
|
-
|
|
304
|
-
|
|
324
|
+
if (AttentiveSdk.isAttentiveFirebaseMessage(remoteMessage)) {
|
|
325
|
+
AttentiveSdk.sendNotification(remoteMessage)
|
|
326
|
+
}
|
|
327
|
+
// Handle your own messages below...
|
|
305
328
|
}
|
|
306
329
|
}
|
|
307
330
|
```
|
|
308
331
|
|
|
309
|
-
|
|
332
|
+
##### Notification opens (singleTask apps)
|
|
333
|
+
|
|
334
|
+
React Native apps use `singleTask` launch mode by default. When a push notification is tapped while the app is in the background, Android delivers the intent via `onNewIntent()` rather than recreating the activity. Override `onNewIntent` in your `MainActivity` so the SDK can detect the notification tap:
|
|
310
335
|
|
|
311
336
|
```kotlin
|
|
312
|
-
import
|
|
337
|
+
import android.content.Intent
|
|
313
338
|
|
|
314
339
|
class MainActivity : ReactActivity() {
|
|
315
340
|
|
|
316
|
-
override fun
|
|
317
|
-
super.
|
|
318
|
-
intent?.let {
|
|
341
|
+
override fun onNewIntent(intent: Intent?) {
|
|
342
|
+
super.onNewIntent(intent)
|
|
343
|
+
intent?.let { setIntent(it) }
|
|
319
344
|
}
|
|
320
345
|
}
|
|
321
346
|
```
|
|
322
347
|
|
|
323
|
-
Declare the service in your `AndroidManifest.xml`:
|
|
324
|
-
|
|
325
|
-
```xml
|
|
326
|
-
<service
|
|
327
|
-
android:name=".AttentiveMessagingService"
|
|
328
|
-
android:exported="false">
|
|
329
|
-
<intent-filter>
|
|
330
|
-
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
|
331
|
-
</intent-filter>
|
|
332
|
-
</service>
|
|
333
|
-
```
|
|
334
|
-
|
|
335
348
|
Refer to the [Attentive Android SDK documentation](https://github.com/attentive-mobile/attentive-android-sdk) for the full list of native APIs available for push notification integration.
|
|
336
349
|
|
|
337
350
|
---
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
require "json"
|
|
2
2
|
|
|
3
3
|
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
|
4
|
-
folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'
|
|
5
4
|
|
|
6
5
|
Pod::Spec.new do |s|
|
|
7
6
|
s.name = "attentive-react-native-sdk"
|
|
@@ -15,23 +14,10 @@ Pod::Spec.new do |s|
|
|
|
15
14
|
s.source = { :git => "https://github.com/attentive-mobile/attentive-react-native-sdk.git", :tag => "#{s.version}" }
|
|
16
15
|
|
|
17
16
|
s.source_files = "ios/**/*.{h,m,mm,swift}"
|
|
17
|
+
s.public_header_files = "ios/AttentiveReactNativeSdk.h"
|
|
18
18
|
|
|
19
19
|
s.dependency 'attentive-ios-sdk', '2.0.13'
|
|
20
20
|
s.swift_versions = ['5']
|
|
21
|
-
s.dependency "React-Core"
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then
|
|
25
|
-
s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
|
|
26
|
-
s.pod_target_xcconfig = {
|
|
27
|
-
"HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/Headers/Public/React-NativeModulesApple\" \"$(PODS_ROOT)/Headers/Private/React-NativeModulesApple\" \"$(PODS_ROOT)/Headers/Private/React-Codegen/react/renderer/components\" \"${PODS_CONFIGURATION_BUILD_DIR}/React-Codegen/React_Codegen.framework/Headers\"",
|
|
28
|
-
"OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
|
|
29
|
-
"CLANG_CXX_LANGUAGE_STANDARD" => "c++20"
|
|
30
|
-
}
|
|
31
|
-
s.dependency "React-Codegen"
|
|
32
|
-
s.dependency "RCT-Folly"
|
|
33
|
-
s.dependency "RCTRequired"
|
|
34
|
-
s.dependency "RCTTypeSafety"
|
|
35
|
-
s.dependency "ReactCommon/turbomodule/core"
|
|
36
|
-
end
|
|
22
|
+
install_modules_dependencies(s)
|
|
37
23
|
end
|
|
@@ -5,14 +5,7 @@
|
|
|
5
5
|
// Created by Wyatt Davis on 2/13/23.
|
|
6
6
|
//
|
|
7
7
|
|
|
8
|
-
#ifdef RCT_NEW_ARCH_ENABLED
|
|
9
|
-
#import "AttentiveReactNativeSdkSpec.h"
|
|
10
|
-
|
|
11
|
-
@interface AttentiveReactNativeSdk : NSObject <NativeAttentiveReactNativeSdkSpec>
|
|
12
|
-
#else
|
|
13
8
|
#import <React/RCTBridgeModule.h>
|
|
14
9
|
|
|
15
10
|
@interface AttentiveReactNativeSdk : NSObject <RCTBridgeModule>
|
|
16
|
-
#endif
|
|
17
|
-
|
|
18
11
|
@end
|
|
@@ -4,17 +4,31 @@
|
|
|
4
4
|
//
|
|
5
5
|
// Created by Wyatt Davis on 2/13/23.
|
|
6
6
|
//
|
|
7
|
+
// NOTE: This file contains both new arch and old arch implementations. Only the new arch path
|
|
8
|
+
// (RCT_NEW_ARCH_ENABLED) is functional. The old arch #else branch does not compile and is
|
|
9
|
+
// retained as scaffolding for future old arch support work (MSDK-350).
|
|
10
|
+
//
|
|
7
11
|
|
|
8
12
|
#import "AttentiveReactNativeSdk.h"
|
|
9
13
|
#import <UserNotifications/UserNotifications.h>
|
|
10
14
|
|
|
11
|
-
#
|
|
15
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
16
|
+
#import "AttentiveReactNativeSdkSpec.h"
|
|
17
|
+
#endif
|
|
18
|
+
|
|
19
|
+
#if __has_include(<attentive_react_native_sdk/attentive_react_native_sdk-Swift.h>)
|
|
20
|
+
#import <attentive_react_native_sdk/attentive_react_native_sdk-Swift.h>
|
|
21
|
+
#elif __has_include(<AttentiveReactNativeSdk-Swift.h>)
|
|
12
22
|
#import "AttentiveReactNativeSdk-Swift.h"
|
|
13
23
|
#else
|
|
14
|
-
// Load the headers from the attentive-ios-sdk Pod
|
|
15
24
|
#import "attentive_react_native_sdk-Swift.h"
|
|
16
25
|
#endif
|
|
17
26
|
|
|
27
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
28
|
+
@interface AttentiveReactNativeSdk () <NativeAttentiveReactNativeSdkSpec>
|
|
29
|
+
@end
|
|
30
|
+
#endif
|
|
31
|
+
|
|
18
32
|
@implementation AttentiveReactNativeSdk {
|
|
19
33
|
ATTNNativeSDK* _sdk;
|
|
20
34
|
}
|
|
@@ -190,8 +204,9 @@ customIdentifiers:(NSDictionary *)customIdentifiers {
|
|
|
190
204
|
}
|
|
191
205
|
|
|
192
206
|
#else
|
|
193
|
-
// Old Architecture implementation
|
|
194
|
-
//
|
|
207
|
+
// Old Architecture implementation — currently does not compile (missing RCT_EXPORT_METHOD macros,
|
|
208
|
+
// bridge module registration, etc.). Kept here as a starting point for restoring old arch support
|
|
209
|
+
// in a future ticket (MSDK-350).
|
|
195
210
|
- (void)initialize:(NSDictionary*)configuration {
|
|
196
211
|
_sdk = [[ATTNNativeSDK alloc] initWithDomain:configuration[@"attentiveDomain"]
|
|
197
212
|
mode:configuration[@"mode"]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@attentive-mobile/attentive-react-native-sdk",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "React Native Module for the Attentive SDK",
|
|
5
5
|
"main": "lib/commonjs/index",
|
|
6
6
|
"module": "lib/module/index",
|
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
"*.podspec",
|
|
17
17
|
"!lib/typescript/example",
|
|
18
18
|
"!ios/build",
|
|
19
|
+
"!ios/Pods",
|
|
20
|
+
"!ios/Podfile.lock",
|
|
21
|
+
"!ios/*.xcworkspace",
|
|
22
|
+
"!**/xcuserdata",
|
|
19
23
|
"!android/build",
|
|
20
24
|
"!android/gradle",
|
|
21
25
|
"!android/gradlew",
|
|
@@ -111,7 +115,7 @@
|
|
|
111
115
|
"release-it": {
|
|
112
116
|
"git": {
|
|
113
117
|
"commitMessage": "chore: release ${version}",
|
|
114
|
-
"tagName": "
|
|
118
|
+
"tagName": "${version}"
|
|
115
119
|
},
|
|
116
120
|
"npm": {
|
|
117
121
|
"publish": true
|