From a533eaadffa083847f8694cf2d244676956a95b8 Mon Sep 17 00:00:00 2001 From: Ulrich GIBERNE Date: Fri, 1 Sep 2023 16:09:18 -0400 Subject: [PATCH] add editTags support --- DEV_README.md | 2 +- android/gradle.properties | 4 +- .../urbanairship/reactnative/AirshipModule.kt | 9 ++- .../reactnative/ReactAutopilot.kt | 2 +- example/README.md | 39 ++++++++--- example/ios/Podfile.lock | 53 ++++++++------- example/src/App.tsx | 9 ++- ios/AirshipReactNative.swift | 7 +- ios/RTNAirship.mm | 11 +++ react-native-airship.podspec | 2 +- src/AirshipChannel.ts | 13 ++++ src/NativeRTNAirship.ts | 1 + src/TagEditor.ts | 68 +++++++++++++++++++ 13 files changed, 175 insertions(+), 45 deletions(-) create mode 100644 src/TagEditor.ts diff --git a/DEV_README.md b/DEV_README.md index b3226bbc..3741f259 100644 --- a/DEV_README.md +++ b/DEV_README.md @@ -24,7 +24,7 @@ yarn install ## iOS -1) Run `pod install` in `example/ios` +1) Run `pod install --repo-update` in `example/ios` 2) Open the `example/ios/AirshipSample.xcworkspace` Project diff --git a/android/gradle.properties b/android/gradle.properties index 6497bdac..339aa2e1 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -4,8 +4,8 @@ Airship_targetSdkVersion=31 Airship_compileSdkVersion=31 Airship_ndkversion=21.4.7075529 -Airship_airshipProxyVersion=3.0.2 +Airship_airshipProxyVersion=4.1.0 # workaround for now, used for HMS -Airship_airshipVersion=17.0.3 +Airship_airshipVersion=17.2.0 diff --git a/android/src/main/java/com/urbanairship/reactnative/AirshipModule.kt b/android/src/main/java/com/urbanairship/reactnative/AirshipModule.kt index 9919deee..beeccb41 100644 --- a/android/src/main/java/com/urbanairship/reactnative/AirshipModule.kt +++ b/android/src/main/java/com/urbanairship/reactnative/AirshipModule.kt @@ -57,7 +57,7 @@ class AirshipModule internal constructor(val context: ReactApplicationContext) : // Background events will create a headless JS task in ReactAutopilot since // initialized wont be called until we have a JS task. EventEmitter.shared().pendingEventListener - .filter { it.isForeground() } + .filter { it.type.isForeground() } .collect { notifyPending() } @@ -155,6 +155,13 @@ class AirshipModule internal constructor(val context: ReactApplicationContext) : } } + @ReactMethod + override fun channelEditTags(operations: ReadableArray, promise: Promise) { + promise.resolveResult { + proxy.channel.editTags(Utils.convertArray(operations).toJsonValue()) + } + } + @ReactMethod override fun channelGetTags(promise: Promise) { promise.resolveResult { diff --git a/android/src/main/java/com/urbanairship/reactnative/ReactAutopilot.kt b/android/src/main/java/com/urbanairship/reactnative/ReactAutopilot.kt index 9770a9bc..e73c29f4 100644 --- a/android/src/main/java/com/urbanairship/reactnative/ReactAutopilot.kt +++ b/android/src/main/java/com/urbanairship/reactnative/ReactAutopilot.kt @@ -29,7 +29,7 @@ class ReactAutopilot : BaseAutopilot() { MainScope().launch { EventEmitter.shared().pendingEventListener - .filter { !it.isForeground() } + .filter { !it.type.isForeground() } .collect { AirshipHeadlessEventService.startService(context) } diff --git a/example/README.md b/example/README.md index e51dea36..12515445 100644 --- a/example/README.md +++ b/example/README.md @@ -6,23 +6,42 @@ A basic sample application that integrates the Airship React Native module. 1) Install modules: Run `yarn` in repository root -### iOS +## Call TakeOff + +`takeOff` should be called at the beginning of the lifecycle. + +```javascript +import Airship from '@ua/react-native-airship'; + +Airship.takeOff({ + default: { + appSecret: "REPLACE_WITH_YOUR_APP_SECRET", + appKey: "REPLACE_WITH_YOUR_APP_KEY" + }, + site: "us", // use "eu" for EU cloud projects + urlAllowList: ["*"], + android: { + notificationConfig: { + icon: "ic_notification", + accentColor: "#00ff00" + } + } +}); +``` -1) Run `pod install` in `example/ios` +### iOS -2) Create the `AirshipConfig.plist` file in `example/ios` +1) Run `pod install --repo-update` in `example/ios` -3) Start the webserver in the top-level directory by running `yarn start` +2) Start the webserver in the top-level directory by running `yarn start` -4) Build and run the sample in the `example` directory: `yarn run:ios` +3) Build and run the sample in the `example` directory: `yarn ios` ### Android -1) Create the `airshipconfig.properties` file in `example/android/app/src/main/assets` - -2) If using FCM, add your `google-services.json` file in `example/android/app` +1) If using FCM, add your `google-services.json` file in `example/android/app` -3) Start the webserver in the top-level directory by running `yarn start` +2) Start the webserver in the top-level directory by running `yarn start` -4) Build and run the sample in the `example` directory: `yarn run:android` +3) Build and run the sample in the `example` directory: `yarn android` diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index f33b2aec..77e5bf65 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,21 +1,24 @@ PODS: - - Airship (17.0.3): - - Airship/Automation (= 17.0.3) - - Airship/Basement (= 17.0.3) - - Airship/Core (= 17.0.3) - - Airship/MessageCenter (= 17.0.3) - - Airship/PreferenceCenter (= 17.0.3) - - Airship/Automation (17.0.3): + - Airship (17.2.2): + - Airship/Automation (= 17.2.2) + - Airship/Basement (= 17.2.2) + - Airship/Core (= 17.2.2) + - Airship/FeatureFlags (= 17.2.2) + - Airship/MessageCenter (= 17.2.2) + - Airship/PreferenceCenter (= 17.2.2) + - Airship/Automation (17.2.2): - Airship/Core - - Airship/Basement (17.0.3) - - Airship/Core (17.0.3): + - Airship/Basement (17.2.2) + - Airship/Core (17.2.2): - Airship/Basement - - Airship/MessageCenter (17.0.3): + - Airship/FeatureFlags (17.2.2): - Airship/Core - - Airship/PreferenceCenter (17.0.3): + - Airship/MessageCenter (17.2.2): - Airship/Core - - AirshipFrameworkProxy (3.0.2): - - Airship (= 17.0.3) + - Airship/PreferenceCenter (17.2.2): + - Airship/Core + - AirshipFrameworkProxy (4.1.0): + - Airship (= 17.2.2) - boost (1.76.0) - CocoaAsyncSocket (7.6.5) - DoubleConversion (1.1.6) @@ -315,10 +318,14 @@ PODS: - React-logger (0.71.1): - glog - react-native-airship (16.0.1): - - AirshipFrameworkProxy (= 3.0.2) + - AirshipFrameworkProxy (= 4.1.0) - React-Core - - react-native-safe-area-context (4.7.1): + - react-native-safe-area-context (4.5.0): + - RCT-Folly + - RCTRequired + - RCTTypeSafety - React-Core + - ReactCommon/turbomodule/core - React-perflogger (0.71.1) - React-RCTActionSheet (0.71.1): - React-Core/RCTActionSheetHeaders (= 0.71.1) @@ -400,9 +407,9 @@ PODS: - React-jsi (= 0.71.1) - React-logger (= 0.71.1) - React-perflogger (= 0.71.1) - - RNGestureHandler (2.12.0): + - RNGestureHandler (2.9.0): - React-Core - - RNScreens (3.22.1): + - RNScreens (3.19.0): - React-Core - React-RCTImage - SocketRocket (0.6.1) @@ -574,8 +581,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/yoga" SPEC CHECKSUMS: - Airship: 9fdd1ccc3a78c42544f34b7de043d39491951023 - AirshipFrameworkProxy: 9a0a26b1b117fe3ccbe1b2d4a9053875e2c0d6d2 + Airship: f5a106a6daa01ba46ae2f519b1b3d4595e7e0972 + AirshipFrameworkProxy: b9822473fef17241cf54508770fdf198f1af9405 boost: 57d2868c099736d80fcd648bf211b4431e51a558 CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 @@ -609,8 +616,8 @@ SPEC CHECKSUMS: React-jsiexecutor: 60cf272aababc5212410e4249d17cea14fc36caa React-jsinspector: ff56004b0c974b688a6548c156d5830ad751ae07 React-logger: 60a0b5f8bed667ecf9e24fecca1f30d125de6d75 - react-native-airship: 68a842b1002cabb9e8f12a13cbf7c38260eae868 - react-native-safe-area-context: 9697629f7b2cda43cf52169bb7e0767d330648c2 + react-native-airship: cb708f7d12b3fcea474119f2eb694d686aac54f6 + react-native-safe-area-context: 39c2d8be3328df5d437ac1700f4f3a4f75716acc React-perflogger: ec8eef2a8f03ecfa6361c2c5fb9197ef4a29cc85 React-RCTActionSheet: a0c023b86cf4c862fa9c4eb0f6f91fbe878fb2de React-RCTAnimation: 168d53718c74153947c0109f55900faa64d79439 @@ -624,8 +631,8 @@ SPEC CHECKSUMS: React-RCTVibration: 49d531ec8498e0afa2c9b22c2205784372e3d4f3 React-runtimeexecutor: 311feb67600774723fe10eb8801d3138cae9ad67 ReactCommon: 03be76588338a27a88d103b35c3c44a3fd43d136 - RNGestureHandler: dec4645026e7401a0899f2846d864403478ff6a5 - RNScreens: 50ffe2fa2342eabb2d0afbe19f7c1af286bc7fb3 + RNGestureHandler: 071d7a9ad81e8b83fe7663b303d132406a7d8f39 + RNScreens: ea4cd3a853063cda19a4e3c28d2e52180c80f4eb SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 Yoga: 921eb014669cf9c718ada68b08d362517d564e0c YogaKit: f782866e155069a2cca2517aafea43200b01fd5a diff --git a/example/src/App.tsx b/example/src/App.tsx index 136bbdc0..addf5a65 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -13,7 +13,6 @@ import Airship, { EventType } from '@ua/react-native-airship'; const Tab = createBottomTabNavigator(); const MessageCenterStack = createStackNavigator(); - Airship.addListener(EventType.NotificationResponse, (event) => { console.log('NotificationResponse:', JSON.stringify(event)); }); @@ -26,12 +25,12 @@ Airship.addListener(EventType.ChannelCreated, (event) => { console.log('ChannelCreated:', JSON.stringify(event)); }); -Airship.addListener(EventType.NotificationStatusChangedStatus, (event) => { - console.log('NotificationStatusChangedStatus:', JSON.stringify(event)); +Airship.addListener(EventType.PushNotificationStatusChangedStatus, (event) => { + console.log('PushNotificationStatusChangedStatus:', JSON.stringify(event)); }); -Airship.addListener(EventType.iOSAuthorizedNotificationSettingsChanged, (event) => { - console.log('iOSAuthorizedNotificationSettingsChanged:', JSON.stringify(event)); +Airship.addListener(EventType.IOSAuthorizedNotificationSettingsChanged, (event) => { + console.log('IOSAuthorizedNotificationSettingsChanged:', JSON.stringify(event)); }); diff --git a/ios/AirshipReactNative.swift b/ios/AirshipReactNative.swift index 864b4967..a6b292c3 100644 --- a/ios/AirshipReactNative.swift +++ b/ios/AirshipReactNative.swift @@ -106,7 +106,7 @@ public class AirshipReactNative: NSObject { try? AirshipProxy.shared.attemptTakeOff(launchOptions: launchOptions) Task { - let stream = await AirshipProxyEventEmitter.shared.pendingEventTypeAdded + let stream = await AirshipProxyEventEmitter.shared.pendingEventAdded for await _ in stream { await self.eventNotifier.notifyPendingEvents() } @@ -183,6 +183,11 @@ public extension AirshipReactNative { func channelRemoveTag(_ tag: String) throws { return try AirshipProxy.shared.channel.removeTags([tag]) } + + @objc + func channelEditTags(json: Any) throws { + try AirshipProxy.shared.channel.editTags(json: json) + } @objc func channelEnableChannelCreation() throws -> Void { diff --git a/ios/RTNAirship.mm b/ios/RTNAirship.mm index 924e62f8..6cb9e059 100644 --- a/ios/RTNAirship.mm +++ b/ios/RTNAirship.mm @@ -106,6 +106,17 @@ + (BOOL)requiresMainQueueSetup { [self handleResult:nil error:error resolve:resolve reject:reject]; } +RCT_REMAP_METHOD(channelEditTags, + channelEditTags:(NSArray *)operations + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { + NSError *error; + [AirshipReactNative.shared channelEditTagsWithJson:operations + error:&error]; + + [self handleResult:nil error:error resolve:resolve reject:reject]; +} + RCT_REMAP_METHOD(pushGetActiveNotifications, pushGetActiveNotifications:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { diff --git a/react-native-airship.podspec b/react-native-airship.podspec index 6c5040cb..c8056084 100644 --- a/react-native-airship.podspec +++ b/react-native-airship.podspec @@ -36,6 +36,6 @@ Pod::Spec.new do |s| - s.dependency "AirshipFrameworkProxy", "3.0.2" + s.dependency "AirshipFrameworkProxy", "4.1.0" end diff --git a/src/AirshipChannel.ts b/src/AirshipChannel.ts index 93ba1daa..b0554f8a 100644 --- a/src/AirshipChannel.ts +++ b/src/AirshipChannel.ts @@ -3,6 +3,7 @@ import { SubscriptionListEditor, SubscriptionListOperation, } from './SubscriptionListEditor'; import { TagGroupEditor, TagGroupOperation } from './TagGroupEditor'; +import { TagEditor, TagOperation } from './TagEditor'; /** * Airship channel. @@ -12,6 +13,7 @@ export class AirshipChannel { /** * Adds a device tag. + * Deprecated. Use editTags() instead. * @param tag The tag. * @returns A promise. */ @@ -21,6 +23,7 @@ export class AirshipChannel { /** * Removes a device tag. + * Deprecated. Use editTags() instead. * @param tag The tag. * @returns A promise. */ @@ -28,6 +31,16 @@ export class AirshipChannel { return this.module.channelRemoveTag(tag); } + /** + * Edits device tags. + * @returns A tag editor. + */ + public editTags(): TagEditor { + return new TagEditor((operations: TagOperation[]) => { + return this.module.channelEditTags(operations); + }); + } + /** * Gets the device tags. * @returns A promise with the result. diff --git a/src/NativeRTNAirship.ts b/src/NativeRTNAirship.ts index 1549e17f..adaa759c 100644 --- a/src/NativeRTNAirship.ts +++ b/src/NativeRTNAirship.ts @@ -16,6 +16,7 @@ export interface Spec extends TurboModule { // Channel channelAddTag(tag: string): Promise; channelRemoveTag(tag: string): Promise; + channelEditTags(operations: Object[]): Promise; channelGetTags(): Promise; channelGetChannelId(): Promise; channelGetSubscriptionLists(): Promise; diff --git a/src/TagEditor.ts b/src/TagEditor.ts new file mode 100644 index 00000000..ce498810 --- /dev/null +++ b/src/TagEditor.ts @@ -0,0 +1,68 @@ +/* Copyright Airship and Contributors */ + +'use strict'; + +/** + * Tag operation. + * @hidden + */ +export interface TagOperation { + /** + * The operation name + */ + operationType: string; + /** + * An array of tags. + */ + tags: string[]; +} + +/** + * Editor for device tags. + */ +export class TagEditor { + onApply: (operations: TagOperation[]) => Promise; + operations: TagOperation[]; + + /** + * TagEditor constructor + * + * @hidden + * @param onApply The apply function + */ + constructor(onApply: (operations: TagOperation[]) => Promise) { + this.onApply = onApply; + this.operations = []; + } + + /** + * Adds tags to a channel. + * + * @param tags Tags to add. + * @return The tag editor instance. + */ + addTags(tags: string[]): TagEditor { + const operation = { operationType: 'add', tags: tags }; + this.operations.push(operation); + return this; + } + + /** + * Removes tags from the channel. + * + * @param tags Tags to remove. + * @return The tag editor instance. + */ + removeTags(tags: string[]): TagEditor { + const operation = { operationType: 'remove', tags: tags }; + this.operations.push(operation); + return this; + } + + /** + * Applies the tag changes. + */ + apply(): Promise { + return this.onApply(this.operations); + } +}