diff --git a/.npmignore b/.npmignore
index c012b6d..c078174 100644
--- a/.npmignore
+++ b/.npmignore
@@ -1,3 +1,2 @@
#sample apps
AppboyProject
-HelloReact
diff --git a/AppboyProject/.babelrc b/AppboyProject/.babelrc
deleted file mode 100644
index a9ce136..0000000
--- a/AppboyProject/.babelrc
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "presets": ["react-native"]
-}
diff --git a/AppboyProject/.gitignore b/AppboyProject/.gitignore
deleted file mode 100644
index 10be197..0000000
--- a/AppboyProject/.gitignore
+++ /dev/null
@@ -1,53 +0,0 @@
-# OSX
-#
-.DS_Store
-
-# Xcode
-#
-build/
-*.pbxuser
-!default.pbxuser
-*.mode1v3
-!default.mode1v3
-*.mode2v3
-!default.mode2v3
-*.perspectivev3
-!default.perspectivev3
-xcuserdata
-*.xccheckout
-*.moved-aside
-DerivedData
-*.hmap
-*.ipa
-*.xcuserstate
-project.xcworkspace
-
-# Android/IntelliJ
-#
-build/
-.idea
-.gradle
-local.properties
-*.iml
-
-# node.js
-#
-node_modules/
-npm-debug.log
-yarn-error.log
-
-# BUCK
-buck-out/
-\.buckd/
-*.keystore
-
-# fastlane
-#
-# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
-# screenshots whenever they are needed.
-# For more information about the recommended setup visit:
-# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
-
-fastlane/report.xml
-fastlane/Preview.html
-fastlane/screenshots
diff --git a/AppboyProject/AppboyProject.js b/AppboyProject/AppboyProject.js
index 257439e..2246050 100644
--- a/AppboyProject/AppboyProject.js
+++ b/AppboyProject/AppboyProject.js
@@ -47,6 +47,7 @@ class AppboyProject extends Component {
this._disableSDK = this._disableSDK.bind(this);
this._enableSDK = this._enableSDK.bind(this);
this._requestLocationInitialization = this._requestLocationInitialization.bind(this);
+ this._requestGeofences = this._requestGeofences.bind(this);
this._setLocationCustomAttribute = this._setLocationCustomAttribute.bind(this);
this._setGenderPress = this._setGenderPress.bind(this);
this._requestContentCardsRefresh = this._requestContentCardsRefresh.bind(this);
@@ -275,6 +276,10 @@ class AppboyProject extends Component {
Request Location Initialization
: false }
+
+ Request Geofences
+
Set Custom Location Attribute
@@ -508,6 +513,13 @@ class AppboyProject extends Component {
this._showToast('Init Requested');
}
+ // Note that this should normally be called only once per session
+ // Demo location is Baltimore
+ _requestGeofences(event) {
+ ReactAppboy.requestGeofences(39.29, -76.61);
+ this._showToast('Geofences Requested');
+ }
+
_setLocationCustomAttribute(event) {
ReactAppboy.setLocationCustomAttribute("work", 40.7128, 74.0060);
this._showToast('Location Set');
diff --git a/AppboyProject/android/app/src/main/AndroidManifest.xml b/AppboyProject/android/app/src/main/AndroidManifest.xml
index ff53f61..b64e7ad 100644
--- a/AppboyProject/android/app/src/main/AndroidManifest.xml
+++ b/AppboyProject/android/app/src/main/AndroidManifest.xml
@@ -31,11 +31,11 @@
android:windowSoftInputMode="adjustResize">
-
+
-
-
-
+
+
+
diff --git a/AppboyProject/ios/AppboyProject.xcodeproj/xcshareddata/xcschemes/AppboyProject.xcscheme b/AppboyProject/ios/AppboyProject.xcodeproj/xcshareddata/xcschemes/AppboyProject.xcscheme
deleted file mode 100644
index f464326..0000000
--- a/AppboyProject/ios/AppboyProject.xcodeproj/xcshareddata/xcschemes/AppboyProject.xcscheme
+++ /dev/null
@@ -1,131 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/AppboyProject/ios/Podfile.lock b/AppboyProject/ios/Podfile.lock
index ac1ca31..217a386 100644
--- a/AppboyProject/ios/Podfile.lock
+++ b/AppboyProject/ios/Podfile.lock
@@ -199,7 +199,7 @@ PODS:
- React-cxxreact (= 0.61.5)
- React-jsi (= 0.61.5)
- React-jsinspector (0.61.5)
- - react-native-appboy-sdk (1.19.0):
+ - react-native-appboy-sdk (1.20.0):
- Appboy-iOS-SDK (~> 3.21.3)
- React
- React-RCTActionSheet (0.61.5):
@@ -243,6 +243,7 @@ PODS:
- Yoga (1.14.0)
DEPENDENCIES:
+ - Appboy-iOS-SDK (= 3.21.3)
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
- FBReactNativeSpec (from `../node_modules/react-native/Libraries/FBReactNativeSpec`)
@@ -274,7 +275,7 @@ DEPENDENCIES:
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
SPEC REPOS:
- https://github.com/cocoapods/specs.git:
+ https://github.com/CocoaPods/Specs.git:
- Appboy-iOS-SDK
- boost-for-react-native
- SDWebImage
@@ -350,7 +351,7 @@ SPEC CHECKSUMS:
React-jsi: cb2cd74d7ccf4cffb071a46833613edc79cdf8f7
React-jsiexecutor: d5525f9ed5f782fdbacb64b9b01a43a9323d2386
React-jsinspector: fa0ecc501688c3c4c34f28834a76302233e29dc0
- react-native-appboy-sdk: 72d1f86efd6734e68b49a90ec06aff6c1421b0c0
+ react-native-appboy-sdk: c07a192dcc08f3e3c19203a2e0253ab32d36655a
React-RCTActionSheet: 600b4d10e3aea0913b5a92256d2719c0cdd26d76
React-RCTAnimation: 791a87558389c80908ed06cc5dfc5e7920dfa360
React-RCTBlob: d89293cc0236d9cb0933d85e430b0bbe81ad1d72
@@ -364,6 +365,6 @@ SPEC CHECKSUMS:
SDWebImage: 4d5c027c935438f341ed33dbac53ff9f479922ca
Yoga: f2a7cd4280bfe2cca5a7aed98ba0eb3d1310f18b
-PODFILE CHECKSUM: be9f31f36175d3d762ad3f72521bc6356f05a9b1
+PODFILE CHECKSUM: ac245fcadc6d9d7a2f662945e62fccd1d450c5ba
-COCOAPODS: 1.6.1
+COCOAPODS: 1.9.1
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e79ac4d..b2a3178 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,11 @@
+## 1.20.0
+
+##### ⚠ Breaking
+- Updated the native Android bridge to [Braze Android SDK 7.0.0](https://github.com/Appboy/appboy-android-sdk/blob/master/CHANGELOG.md#700).
+
+##### Added
+- Added `ReactAppboy.requestGeofences()` to request a Braze Geofences update for a manually provided GPS coordinate. Automatic Braze Geofence requests must be disabled to properly use this method.
+
## 1.19.0
##### Breaking
diff --git a/README.md b/README.md
index 22e1274..d813d47 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,7 @@ The following commands apply to both sample projects and use the `AppboyProject`
```
cd AppboyProject/
-npm install
+yarn install
```
### iOS
@@ -42,3 +42,11 @@ From the `AppboyProject` directory:
```
react-native run-android
```
+
+## Style
+- Generally we try to mimic the Braze Web SDK's Javascript interface where appropriate.
+- We use [eslint](http://eslint.org/) as our linter. From the root directory, run `npm run lint` to list errors or `npm run lint-fix` to automatically fix errors. To override the rules in the [`standard-react`](https://github.com/feross/eslint-config-standard-react) config, add `"rules"` in `.eslintrc.json`.
+
+## Testing
+- We use [jest](https://facebook.github.io/jest/) for testing the React SDK.
+- Run the tests and code coverage report using `npm test`
diff --git a/__tests__/index.test.js b/__tests__/index.test.js
new file mode 100644
index 0000000..798a92c
--- /dev/null
+++ b/__tests__/index.test.js
@@ -0,0 +1,465 @@
+const ReactAppboy = require('../index');
+const NativeModules = require('react-native').NativeModules;
+const EventEmitter = require('EventEmitter');
+const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
+
+/**
+ * Mock the NativeEventEmitter as a normal JS EventEmitter.
+ */
+class NativeEventEmitter extends EventEmitter {
+ constructor() {
+ super(RCTDeviceEventEmitter.sharedSubscriber);
+ }
+}
+
+jest.mock('NativeEventEmitter');
+
+jest.mock('NativeModules', () => {
+ return {
+ AppboyReactBridge: {
+ registerAndroidPushToken: jest.fn(),
+ setFirstName: jest.fn(),
+ setLastName: jest.fn(),
+ setLanguage: jest.fn(),
+ setEmail: jest.fn(),
+ setPhoneNumber: jest.fn(),
+ changeUser: jest.fn(),
+ setSDKFlavor: jest.fn(),
+ logCustomEvent: jest.fn(),
+ logPurchase: jest.fn(),
+ setCountry: jest.fn(),
+ setHomeCity: jest.fn(),
+ setAvatarImageUrl: jest.fn(),
+ setDateOfBirth: jest.fn(),
+ setTwitterData: jest.fn(),
+ setFacebookData: jest.fn(),
+ setAttributionData: jest.fn(),
+ launchNewsFeed: jest.fn(),
+ launchContentCards: jest.fn(),
+ getContentCards: jest.fn(),
+ logContentCardClicked: jest.fn(),
+ logContentCardDismissed: jest.fn(),
+ logContentCardImpression: jest.fn(),
+ logContentCardsDisplayed: jest.fn(),
+ requestFeedRefresh: jest.fn(),
+ requestImmediateDataFlush: jest.fn(),
+ enableSDK: jest.fn(),
+ disableSDK: jest.fn(),
+ wipeData: jest.fn(),
+ setDateCustomUserAttribute: jest.fn(),
+ setCustomUserAttributeArray: jest.fn(),
+ setBoolCustomUserAttribute: jest.fn(),
+ setStringCustomUserAttribute: jest.fn(),
+ setIntCustomUserAttribute: jest.fn(),
+ setDoubleCustomUserAttribute: jest.fn(),
+ incrementCustomUserAttribute: jest.fn(),
+ setGender: jest.fn(),
+ setPushNotificationSubscriptionType: jest.fn(),
+ setEmailNotificationSubscriptionType: jest.fn(),
+ addToCustomAttributeArray: jest.fn(),
+ removeFromCustomAttributeArray: jest.fn(),
+ unsetCustomUserAttribute: jest.fn(),
+ getCardCountForCategories: jest.fn(),
+ getUnreadCardCountForCategories: jest.fn(),
+ getInitialUrl: jest.fn(),
+ getInstallTrackingId: jest.fn(),
+ requestLocationInitialization: jest.fn(),
+ requestGeofences: jest.fn(),
+ setLocationCustomAttribute: jest.fn(),
+ requestContentCardsRefresh: jest.fn(),
+ hideCurrentInAppMessage: jest.fn()
+ }
+ };
+});
+
+console.log = jest.fn();
+testCallback = jest.fn();
+
+afterEach(() => {
+ jest.clearAllMocks();
+});
+
+test('it calls AppboyReactBridge.registerAndroidPushToken', () => {
+ const token = "some_token";
+ ReactAppboy.registerAndroidPushToken(token);
+ expect(NativeModules.AppboyReactBridge.registerAndroidPushToken).toBeCalledWith(token);
+});
+
+test('it calls AppboyReactBridge.setFirstName', () => {
+ const first_name = "some_name";
+ ReactAppboy.setFirstName(first_name);
+ expect(NativeModules.AppboyReactBridge.setFirstName).toBeCalledWith(first_name);
+});
+
+test('it calls AppboyReactBridge.setLastName', () => {
+ const last_name = "some_name";
+ ReactAppboy.setLastName(last_name);
+ expect(NativeModules.AppboyReactBridge.setLastName).toBeCalledWith(last_name);
+});
+
+test('it calls AppboyReactBridge.setLanguage', () => {
+ const language = "to";
+ ReactAppboy.setLanguage(language);
+ expect(NativeModules.AppboyReactBridge.setLanguage).toBeCalledWith(language);
+});
+
+test('it calls AppboyReactBridge.setEmail', () => {
+ const email = "some_email";
+ ReactAppboy.setEmail(email);
+ expect(NativeModules.AppboyReactBridge.setEmail).toBeCalledWith(email);
+});
+
+test('it calls AppboyReactBridge.setCountry', () => {
+ const country = "some_country";
+ ReactAppboy.setCountry(country);
+ expect(NativeModules.AppboyReactBridge.setCountry).toBeCalledWith(country);
+});
+
+test('it calls AppboyReactBridge.setHomeCity', () => {
+ const city = "some_city";
+ ReactAppboy.setHomeCity(city);
+ expect(NativeModules.AppboyReactBridge.setHomeCity).toBeCalledWith(city);
+});
+
+test('it calls AppboyReactBridge.setPhoneNumber', () => {
+ const number = "555-867-5309";
+ ReactAppboy.setPhoneNumber(number);
+ expect(NativeModules.AppboyReactBridge.setPhoneNumber).toBeCalledWith(number);
+});
+
+test('it calls AppboyReactBridge.launchNewsFeed', () => {
+ ReactAppboy.launchNewsFeed();
+ expect(NativeModules.AppboyReactBridge.launchNewsFeed).toBeCalled();
+});
+
+test('it calls AppboyReactBridge.launchContentCards', () => {
+ ReactAppboy.launchContentCards();
+ expect(NativeModules.AppboyReactBridge.launchContentCards).toBeCalled();
+});
+
+test('it calls AppboyReactBridge.getContentCards', () => {
+ ReactAppboy.getContentCards();
+ expect(NativeModules.AppboyReactBridge.getContentCards).toBeCalled();
+});
+
+test('it calls AppboyReactBridge.logContentCardClicked', () => {
+ const id = "1234";
+ ReactAppboy.logContentCardClicked(id);
+ expect(NativeModules.AppboyReactBridge.logContentCardClicked).toBeCalledWith(id);
+});
+
+test('it calls AppboyReactBridge.logContentCardDismissed', () => {
+ const id = "1234";
+ ReactAppboy.logContentCardDismissed(id);
+ expect(NativeModules.AppboyReactBridge.logContentCardDismissed).toBeCalledWith(id);
+});
+
+test('it calls AppboyReactBridge.logContentCardImpression', () => {
+ const id = "1234";
+ ReactAppboy.logContentCardImpression(id);
+ expect(NativeModules.AppboyReactBridge.logContentCardImpression).toBeCalledWith(id);
+});
+
+test('it calls AppboyReactBridge.logContentCardsDisplayed', () => {
+ ReactAppboy.logContentCardsDisplayed();
+ expect(NativeModules.AppboyReactBridge.logContentCardsDisplayed).toBeCalled();
+});
+
+test('it calls AppboyReactBridge.requestFeedRefresh', () => {
+ ReactAppboy.requestFeedRefresh();
+ expect(NativeModules.AppboyReactBridge.requestFeedRefresh).toBeCalled();
+});
+
+test('it calls AppboyReactBridge.requestImmediateDataFlush', () => {
+ ReactAppboy.requestImmediateDataFlush();
+ expect(NativeModules.AppboyReactBridge.requestImmediateDataFlush).toBeCalled();
+});
+
+test('it calls AppboyReactBridge.wipeData', () => {
+ ReactAppboy.wipeData();
+ expect(NativeModules.AppboyReactBridge.wipeData).toBeCalled();
+});
+
+test('it calls AppboyReactBridge.disableSDK', () => {
+ ReactAppboy.disableSDK();
+ expect(NativeModules.AppboyReactBridge.disableSDK).toBeCalled();
+});
+
+test('it calls AppboyReactBridge.enableSDK', () => {
+ ReactAppboy.enableSDK();
+ expect(NativeModules.AppboyReactBridge.enableSDK).toBeCalled();
+});
+
+test('it calls AppboyReactBridge.requestLocationInitialization', () => {
+ ReactAppboy.requestLocationInitialization();
+ expect(NativeModules.AppboyReactBridge.requestLocationInitialization).toBeCalled();
+});
+
+test('it calls AppboyReactBridge.requestGeofences', () => {
+ const latitude = 40.7128;
+ const longitude = 74.0060;
+ ReactAppboy.requestGeofences(latitude, longitude);
+ expect(NativeModules.AppboyReactBridge.requestGeofences).toBeCalledWith(latitude, longitude);
+});
+
+test('it calls AppboyReactBridge.setLocationCustomAttribute', () => {
+ const key = "some_key";
+ const latitude = 40.7128;
+ const longitude = 74.0060;
+ ReactAppboy.setLocationCustomAttribute(key, latitude, longitude, testCallback);
+ expect(NativeModules.AppboyReactBridge.setLocationCustomAttribute).toBeCalledWith(key, latitude, longitude, testCallback);
+});
+
+test('it calls AppboyReactBridge.requestContentCardsRefresh', () => {
+ ReactAppboy.requestContentCardsRefresh();
+ expect(NativeModules.AppboyReactBridge.requestContentCardsRefresh).toBeCalled();
+});
+
+test('it calls AppboyReactBridge.setAvatarImageUrl', () => {
+ const url = "braze.com";
+ ReactAppboy.setAvatarImageUrl(url);
+ expect(NativeModules.AppboyReactBridge.setAvatarImageUrl).toBeCalledWith(url);
+});
+
+test('it calls AppboyReactBridge.setDateOfBirth', () => {
+ const year = 2011;
+ const month = 11;
+ const day = 23;
+ ReactAppboy.setDateOfBirth(year, month, day);
+ expect(NativeModules.AppboyReactBridge.setDateOfBirth).toBeCalledWith(year, month, day);
+});
+
+test('it calls AppboyReactBridge.changeUser', () => {
+ const user_id = "some_id";
+ ReactAppboy.changeUser(user_id);
+ expect(NativeModules.AppboyReactBridge.setSDKFlavor).toBeCalled();
+ expect(NativeModules.AppboyReactBridge.changeUser).toBeCalledWith(user_id);
+});
+
+test('it calls AppboyReactBridge.logCustomEvent', () => {
+ const event_name = "event_name";
+ const event_properties = "event_properties";
+ ReactAppboy.logCustomEvent(event_name, event_properties);
+ expect(NativeModules.AppboyReactBridge.setSDKFlavor).toBeCalled();
+ expect(NativeModules.AppboyReactBridge.logCustomEvent).toBeCalledWith(event_name, event_properties);
+});
+
+test('it calls AppboyReactBridge.logPurchase', () => {
+ const product_id = "product_id";
+ const price = "price";
+ const currency_code = "currency_code";
+ const quantity = "quantity";
+ const purchase_properties = "purchase_properties";
+ ReactAppboy.logPurchase(product_id, price, currency_code, quantity, purchase_properties);
+ expect(NativeModules.AppboyReactBridge.logPurchase).toBeCalledWith(product_id, price, currency_code, quantity, purchase_properties);
+});
+
+test('it calls AppboyReactBridge.setTwitterData', () => {
+ const id = "some_id";
+ const screen_name = "some_screen_name";
+ const name = "some_name";
+ const description = "some_description";
+ const followers_count = 22;
+ const friends_count = 33;
+ const statuses_count = 44;
+ const profile_image_url = "braze.com"
+ ReactAppboy.setTwitterData(id, screen_name, name, description, followers_count, friends_count, statuses_count, profile_image_url);
+ expect(NativeModules.AppboyReactBridge.setTwitterData).toBeCalledWith(id, screen_name, name, description, followers_count, friends_count, statuses_count, profile_image_url);
+});
+
+test('it does not call AppboyReactBridge.setTwitterData when required arguments are missing, and logs to the console', () => {
+ let id = null;
+ const screen_name = "some_screen_name";
+ const name = "some_name";
+ const description = "some_description";
+ let followers_count = 22;
+ let friends_count = 33;
+ let statuses_count = 44;
+ const profile_image_url = "braze.com"
+ ReactAppboy.setTwitterData(id, screen_name, name, description, followers_count, friends_count, statuses_count, profile_image_url);
+ id = "some_id";
+ followers_count = null;
+ ReactAppboy.setTwitterData(id, screen_name, name, description, followers_count, friends_count, statuses_count, profile_image_url);
+ followers_count = 22;
+ friends_count = null;
+ ReactAppboy.setTwitterData(id, screen_name, name, description, followers_count, friends_count, statuses_count, profile_image_url);
+ friends_count = 33;
+ statuses_count = null;
+ ReactAppboy.setTwitterData(id, screen_name, name, description, followers_count, friends_count, statuses_count, profile_image_url);
+ expect(console.log).toHaveBeenCalledTimes(4);
+ expect(NativeModules.AppboyReactBridge.setTwitterData).not.toHaveBeenCalled();
+});
+
+test('it calls AppboyReactBridge.setFacebookData', () => {
+ const facebook_user_dictionary = "some_facebook_user_dictionary";
+ const number_of_friends = 55;
+ const likes = 600;
+ ReactAppboy.setFacebookData(facebook_user_dictionary, number_of_friends, likes);
+ expect(NativeModules.AppboyReactBridge.setFacebookData).toBeCalledWith(facebook_user_dictionary, number_of_friends, likes);
+});
+
+test('it does not call AppboyReactBridge.setFacebookData when required arguments are missing, and logs to the console', () => {
+ const facebook_user_dictionary = "some_facebook_user_dictionary";
+ const number_of_friends = null;
+ const likes = 600;
+ ReactAppboy.setFacebookData(facebook_user_dictionary, number_of_friends, likes);
+ expect(console.log).toHaveBeenCalled();
+ expect(NativeModules.AppboyReactBridge.setFacebookData).not.toHaveBeenCalled();
+});
+
+test('it calls AppboyReactBridge.setAttributionData', () => {
+ const network = "some_network";
+ const campaign = "some_campaign";
+ const adGroup = "some_adGroup";
+ const creative = "some_creative";
+ ReactAppboy.setAttributionData(network, campaign, adGroup, creative);
+ expect(NativeModules.AppboyReactBridge.setAttributionData).toBeCalledWith(network, campaign, adGroup, creative);
+});
+
+test('it calls AppboyReactBridge.setDateCustomUserAttribute', () => {
+ const key = "some_key";
+ const date = new Date('December 17, 1995 03:24:00');
+ ReactAppboy.setCustomUserAttribute(key, date, testCallback);
+ expect(NativeModules.AppboyReactBridge.setDateCustomUserAttribute).toBeCalledWith(key, Math.floor(date.getTime() / 1000), testCallback);
+});
+
+test('it calls AppboyReactBridge.setCustomUserAttributeArray', () => {
+ const key = "some_key";
+ const array = ['a','b'];
+ ReactAppboy.setCustomUserAttribute(key, array, testCallback);
+ expect(NativeModules.AppboyReactBridge.setCustomUserAttributeArray).toBeCalledWith(key, array, testCallback);
+});
+
+test('it calls AppboyReactBridge.setBoolCustomUserAttribute', () => {
+ const key = "some_key";
+ const bool_value = true;
+ ReactAppboy.setCustomUserAttribute(key, bool_value, testCallback);
+ expect(NativeModules.AppboyReactBridge.setBoolCustomUserAttribute).toBeCalledWith(key, bool_value, testCallback);
+});
+
+test('it calls AppboyReactBridge.setStringCustomUserAttribute', () => {
+ const key = "some_key";
+ const string_value = "some string";
+ ReactAppboy.setCustomUserAttribute(key, string_value, testCallback);
+ expect(NativeModules.AppboyReactBridge.setStringCustomUserAttribute).toBeCalledWith(key, string_value, testCallback);
+});
+
+test('it calls AppboyReactBridge.setIntCustomUserAttribute', () => {
+ const key = "some_key";
+ const int_value = 55;
+ ReactAppboy.setCustomUserAttribute(key, int_value, testCallback);
+ expect(NativeModules.AppboyReactBridge.setIntCustomUserAttribute).toBeCalledWith(key, int_value, testCallback);
+});
+
+test('it calls AppboyReactBridge.setDoubleCustomUserAttribute', () => {
+ const key = "some_key";
+ const double_value = 3.14;
+ ReactAppboy.setCustomUserAttribute(key, double_value, testCallback);
+ expect(NativeModules.AppboyReactBridge.setDoubleCustomUserAttribute).toBeCalledWith(key, double_value, testCallback);
+});
+
+test('it calls AppboyReactBridge.incrementCustomUserAttribute', () => {
+ const key = "some_key";
+ const value = 5;
+ ReactAppboy.incrementCustomUserAttribute(key, value, testCallback);
+ expect(NativeModules.AppboyReactBridge.incrementCustomUserAttribute).toBeCalledWith(key, value, testCallback);
+});
+
+test('it calls AppboyReactBridge.setGender', () => {
+ const gender = "male";
+ ReactAppboy.setGender(gender, testCallback);
+ expect(NativeModules.AppboyReactBridge.setGender).toBeCalledWith(gender, testCallback);
+});
+
+test('it calls AppboyReactBridge.setPushNotificationSubscriptionType', () => {
+ const sub_type = "some_sub_type";
+ ReactAppboy.setPushNotificationSubscriptionType(sub_type, testCallback);
+ expect(NativeModules.AppboyReactBridge.setPushNotificationSubscriptionType).toBeCalledWith(sub_type, testCallback);
+});
+
+test('it calls AppboyReactBridge.setEmailNotificationSubscriptionType', () => {
+ const sub_type = "some_sub_type";
+ ReactAppboy.setEmailNotificationSubscriptionType(sub_type, testCallback);
+ expect(NativeModules.AppboyReactBridge.setEmailNotificationSubscriptionType).toBeCalledWith(sub_type, testCallback);
+});
+
+test('it calls AppboyReactBridge.addToCustomAttributeArray', () => {
+ const key = "some_key";
+ const value = "some_value"
+ ReactAppboy.addToCustomUserAttributeArray(key, value, testCallback);
+ expect(NativeModules.AppboyReactBridge.addToCustomAttributeArray).toBeCalledWith(key, value, testCallback);
+});
+
+test('it calls AppboyReactBridge.removeFromCustomAttributeArray', () => {
+ const key = "some_key";
+ const value = "some_value"
+ ReactAppboy.removeFromCustomUserAttributeArray(key, value, testCallback);
+ expect(NativeModules.AppboyReactBridge.removeFromCustomAttributeArray).toBeCalledWith(key, value, testCallback);
+});
+
+test('it calls AppboyReactBridge.unsetCustomUserAttribute', () => {
+ const key = "some_key";
+ ReactAppboy.unsetCustomUserAttribute(key, testCallback);
+ expect(NativeModules.AppboyReactBridge.unsetCustomUserAttribute).toBeCalledWith(key, testCallback);
+});
+
+test('it calls AppboyReactBridge.getCardCountForCategories', () => {
+ const category = "some_category";
+ ReactAppboy.getCardCountForCategories(category, testCallback);
+ expect(NativeModules.AppboyReactBridge.getCardCountForCategories).toBeCalledWith(category, testCallback);
+});
+
+test('it calls AppboyReactBridge.getUnreadCardCountForCategories', () => {
+ const category = "some_category";
+ ReactAppboy.getUnreadCardCountForCategories(category, testCallback);
+ expect(NativeModules.AppboyReactBridge.getUnreadCardCountForCategories).toBeCalledWith(category, testCallback);
+});
+
+test('it calls AppboyReactBridge.getInitialUrl if defined', () => {
+ NativeModules.AppboyReactBridge.getInitialUrl.mockImplementation((callback) => {
+ callback(null, "some_data");
+ });
+ ReactAppboy.getInitialURL(testCallback);
+ expect(NativeModules.AppboyReactBridge.getInitialUrl).toBeCalled();
+ expect(testCallback).toBeCalledWith("some_data");
+});
+
+test('it calls AppboyReactBridge.getInstallTrackingId', () => {
+ NativeModules.AppboyReactBridge.getInstallTrackingId.mockImplementation((callback) => {
+ callback(null, "some_tracking_id");
+ });
+ ReactAppboy.getInstallTrackingId(testCallback);
+ expect(NativeModules.AppboyReactBridge.getInstallTrackingId).toBeCalled();
+ expect(testCallback).toBeCalledWith(null, "some_tracking_id");
+});
+
+test('it calls the callback with null and logs the error if AppboyReactBridge.getInitialUrl returns an error', () => {
+ NativeModules.AppboyReactBridge.getInitialUrl.mockImplementation((callback) => {
+ callback("error", null);
+ });
+ ReactAppboy.getInitialURL(testCallback);
+ expect(NativeModules.AppboyReactBridge.getInitialUrl).toBeCalled();
+ expect(testCallback).toBeCalledWith(null);
+ expect(console.log).toBeCalledWith("error");
+});
+
+test('it calls the callback with null if AppboyReactBridge.getInitialUrl is not defined', () => {
+ NativeModules.AppboyReactBridge.getInitialUrl = null;
+ ReactAppboy.getInitialURL(testCallback);
+ expect(testCallback).toBeCalledWith(null);
+});
+
+test('it calls AppboyReactBridge.hideCurrentInAppMessage', () => {
+ ReactAppboy.hideCurrentInAppMessage();
+ expect(NativeModules.AppboyReactBridge.hideCurrentInAppMessage).toBeCalled();
+});
+
+test('it adds a listener', () => {
+ let counter = 0;
+ let testFunction = () => {counter += 1};
+ let testEvent = ReactAppboy.Events.CONTENT_CARDS_UPDATED;
+ const nativeEmitter = new NativeEventEmitter();
+ ReactAppboy.addListener(testEvent, testFunction);
+ nativeEmitter.emit(testEvent);
+ expect(counter).toBe(1);
+});
diff --git a/android/build.gradle b/android/build.gradle
index 93509d6..8d3f34b 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -17,6 +17,6 @@ android {
}
dependencies {
- api 'com.appboy:android-sdk-ui:5.0.0'
+ api 'com.appboy:android-sdk-ui:7.0.0'
api 'com.facebook.react:react-native:+'
}
diff --git a/android/src/main/java/com/appboy/reactbridge/AppboyReactBridge.java b/android/src/main/java/com/appboy/reactbridge/AppboyReactBridge.java
index 3dcf6b1..6e331f5 100644
--- a/android/src/main/java/com/appboy/reactbridge/AppboyReactBridge.java
+++ b/android/src/main/java/com/appboy/reactbridge/AppboyReactBridge.java
@@ -707,6 +707,11 @@ public void requestLocationInitialization() {
AppboyLocationService.requestInitialization(getReactApplicationContext());
}
+ @ReactMethod
+ public void requestGeofences(Double latitude, Double longitude) {
+ Appboy.getInstance(getReactApplicationContext()).requestGeofences(latitude, longitude);
+ }
+
@ReactMethod
public void setLocationCustomAttribute(String key, Double latitude, Double longitude, Callback callback) {
Appboy.getInstance(getReactApplicationContext()).getCurrentUser().setLocationCustomAttribute(key, latitude, longitude);
diff --git a/iOS/AppboyReactBridge/AppboyReactBridge/AppboyReactBridge.m b/iOS/AppboyReactBridge/AppboyReactBridge/AppboyReactBridge.m
index 4e46cd4..56bfb6b 100644
--- a/iOS/AppboyReactBridge/AppboyReactBridge/AppboyReactBridge.m
+++ b/iOS/AppboyReactBridge/AppboyReactBridge/AppboyReactBridge.m
@@ -456,6 +456,11 @@ - (ABKCardCategory)getCardCategoryForString:(NSString *)category {
RCTLogInfo(@"Warning: This is an Android only feature.");
}
+RCT_EXPORT_METHOD(requestGeofences:(double)latitude longitude:(double)longitude) {
+ RCTLogInfo(@"[Appboy sharedInstance] requestGeofencesWithLongitude:latitude: with latitude %g and longitude %g", latitude, longitude);
+ [[Appboy sharedInstance] requestGeofencesWithLongitude:longitude latitude:latitude];
+}
+
RCT_EXPORT_METHOD(getCardCountForCategories:(NSString *)category callback:(RCTResponseSenderBlock)callback) {
ABKCardCategory cardCategory = [self getCardCategoryForString:category];
if (cardCategory == 0) {
diff --git a/index.d.ts b/index.d.ts
index aed304a..7372678 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -482,6 +482,18 @@ export function enableSDK(): void;
*/
export function requestLocationInitialization(): void;
+/**
+ * Call this method to request a Braze Geofences update for a manually provided
+ * GPS coordinate. Automatic Braze Geofence requests must be disabled to properly
+ * use this method. Calling this method is a no-op on iOS.
+ * @param {double} latitude - Location latitude.
+ * @param {double} longitude - Location longitude.
+ */
+export function requestGeofences(
+ latitude: number,
+ longitude: number
+): void;
+
/**
* Sets a custom location attribute for the user.
* @param {string} key - The identifier of the custom attribute. Limited to 255 characters in length, cannot begin with
diff --git a/index.js b/index.js
index 7c8e889..db7f24f 100644
--- a/index.js
+++ b/index.js
@@ -564,6 +564,17 @@ var ReactAppboy = {
AppboyReactBridge.requestLocationInitialization();
},
+ /**
+ * Call this method to request a Braze Geofences update for a manually provided
+ * GPS coordinate. Automatic Braze Geofence requests must be disabled to properly
+ * use this method. Calling this method is a no-op on iOS.
+ * @param {double} latitude - Location latitude.
+ * @param {double} longitude - Location longitude.
+ */
+ requestGeofences: function(latitude, longitude) {
+ AppboyReactBridge.requestGeofences(latitude, longitude);
+ },
+
/**
* Sets a custom location attribute for the user.
* @param {string} key - The identifier of the custom attribute. Limited to 255 characters in length, cannot begin with
diff --git a/package.json b/package.json
index bcdac94..9abba56 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "react-native-appboy-sdk",
- "version": "1.19.0",
+ "version": "1.20.0",
"description": "Braze SDK for React Native.",
"main": "index.js",
"types": "index.d.ts",