From 0ad50bc2f8d59f08b44a6b01e9f98c038371bb2d Mon Sep 17 00:00:00 2001 From: Jenna Antilla <46546946+jennantilla@users.noreply.github.com> Date: Tue, 9 Jan 2024 16:12:08 -0800 Subject: [PATCH 01/14] Update User namespace - add getters for onesignal ID/external ID - add user state observer --- src/events/EventManager.ts | 2 ++ src/events/events.ts | 1 + src/index.ts | 50 ++++++++++++++++++++++++++++++++++++++ src/models/User.ts | 9 +++++++ 4 files changed, 62 insertions(+) create mode 100644 src/models/User.ts diff --git a/src/events/EventManager.ts b/src/events/EventManager.ts index 2c4eeaa1..fd81128a 100644 --- a/src/events/EventManager.ts +++ b/src/events/EventManager.ts @@ -7,6 +7,7 @@ import NotificationWillDisplayEvent from './NotificationWillDisplayEvent'; import { PERMISSION_CHANGED, SUBSCRIPTION_CHANGED, + USER_STATE_CHANGED, NOTIFICATION_WILL_DISPLAY, NOTIFICATION_CLICKED, IN_APP_MESSAGE_CLICKED, @@ -20,6 +21,7 @@ import OSNotification from '../OSNotification'; const eventList = [ PERMISSION_CHANGED, SUBSCRIPTION_CHANGED, + USER_STATE_CHANGED, NOTIFICATION_WILL_DISPLAY, NOTIFICATION_CLICKED, IN_APP_MESSAGE_CLICKED, diff --git a/src/events/events.ts b/src/events/events.ts index 3ac0c57d..d2e6541e 100644 --- a/src/events/events.ts +++ b/src/events/events.ts @@ -8,3 +8,4 @@ export const IN_APP_MESSAGE_WILL_DISMISS = 'OneSignal-inAppMessageWillDismiss'; export const IN_APP_MESSAGE_DID_DISMISS = 'OneSignal-inAppMessageDidDismiss'; export const PERMISSION_CHANGED = 'OneSignal-permissionChanged'; export const SUBSCRIPTION_CHANGED = 'OneSignal-subscriptionChanged'; +export const USER_STATE_CHANGED = 'OneSignal-userStateChanged'; diff --git a/src/index.ts b/src/index.ts index ed6d2e67..1bbc76f5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,6 +12,7 @@ import { NOTIFICATION_WILL_DISPLAY, PERMISSION_CHANGED, SUBSCRIPTION_CHANGED, + USER_STATE_CHANGED, } from './events/events'; import { NotificationEventName, @@ -23,6 +24,10 @@ import { OSNotificationPermission, PushSubscriptionChangedState, } from './models/Subscription'; +import { + UserState, + UserChangedState, +} from './models/User'; import NotificationWillDisplayEvent from './events/NotificationWillDisplayEvent'; import { InAppMessage, @@ -264,6 +269,49 @@ export namespace OneSignal { } } + /** Add a callback that fires when the OneSignal user state changes. */ + export function addEventListener( + event: 'change', + listener: (event: UserChangedState) => void, + ) { + if (!isNativeModuleLoaded(RNOneSignal)) return; + + isValidCallback(listener); + RNOneSignal.addUserStateObserver(); + eventManager.addEventListener( + USER_STATE_CHANGED, + listener, + ); + } + + /** Clears current user state observers. */ + export function removeEventListener( + event: 'change', + listener: (event: UserChangedState) => void, + ) { + if (!isNativeModuleLoaded(RNOneSignal)) return; + + eventManager.removeEventListener(USER_STATE_CHANGED, listener); + } + + /** Get the OneSignal Id associated with the user. */ + export function getOnesignalId(): Promise { + if (!isNativeModuleLoaded(RNOneSignal)) { + return Promise.reject(new Error('OneSignal native module not loaded')); + } + + return RNOneSignal.getOnesignalId(); + } + + /** Get the External Id associated with the user. */ + export function getExternalId(): Promise { + if (!isNativeModuleLoaded(RNOneSignal)) { + return Promise.reject(new Error('OneSignal native module not loaded')); + } + + return RNOneSignal.getExternalId(); + } + /** Explicitly set a 2-character language code for the user. */ export function setLanguage(language: string) { if (!isNativeModuleLoaded(RNOneSignal)) return; @@ -805,6 +853,8 @@ export { InAppMessageDidDismissEvent, PushSubscriptionState, PushSubscriptionChangedState, + UserState, + UserChangedState, OSNotificationPermission, }; diff --git a/src/models/User.ts b/src/models/User.ts new file mode 100644 index 00000000..3805d6f8 --- /dev/null +++ b/src/models/User.ts @@ -0,0 +1,9 @@ +export interface UserState { + externalId: string; + onesignalId: string; +} + +export interface UserChangedState { + previous: UserState; + current: UserState; +} \ No newline at end of file From c65edce0fbc14d00a54a4b143aa050966300e422 Mon Sep 17 00:00:00 2001 From: Jenna Antilla <46546946+jennantilla@users.noreply.github.com> Date: Tue, 9 Jan 2024 16:14:53 -0800 Subject: [PATCH 02/14] Update example app --- examples/RNOneSignalTS/src/OSButtons.tsx | 18 ++++++++++++++++++ examples/RNOneSignalTS/src/OSDemo.tsx | 4 ++++ 2 files changed, 22 insertions(+) diff --git a/examples/RNOneSignalTS/src/OSButtons.tsx b/examples/RNOneSignalTS/src/OSButtons.tsx index d9a4914f..14926a65 100644 --- a/examples/RNOneSignalTS/src/OSButtons.tsx +++ b/examples/RNOneSignalTS/src/OSButtons.tsx @@ -342,6 +342,22 @@ class OSButtons extends React.Component { }, ); + const getOnesignalIdButton = renderButtonView( + 'Get OneSignal Id', + async () => { + const onesignalId = await OneSignal.User.getOnesignalId(); + loggingFunction('OneSignal Id: ', onesignalId); + }, + ); + + const getExternalIdButton = renderButtonView( + 'Get External Id', + async () => { + const externalId = await OneSignal.User.getExternalId(); + loggingFunction('External Id:', externalId); + }, + ); + return [ loginButton, logoutButton, @@ -359,6 +375,8 @@ class OSButtons extends React.Component { removeAliasButton, addAliasesButton, removeAliasesButton, + getOnesignalIdButton, + getExternalIdButton ]; } diff --git a/examples/RNOneSignalTS/src/OSDemo.tsx b/examples/RNOneSignalTS/src/OSDemo.tsx index 825e3ab0..1cf82f0c 100644 --- a/examples/RNOneSignalTS/src/OSDemo.tsx +++ b/examples/RNOneSignalTS/src/OSDemo.tsx @@ -99,6 +99,10 @@ class OSDemo extends React.Component { OneSignal.Notifications.addEventListener('permissionChange', (granted) => { this.OSLog('OneSignal: permission changed:', granted); }); + + OneSignal.User.addEventListener('change', (event) => { + this.OSLog('OneSignal: user changed: ', event); + }) } OSLog = (message: string, optionalArg: any = null) => { From 2a831c38458a435690c8f51d822703aa7e68fe33 Mon Sep 17 00:00:00 2001 From: Jenna Antilla <46546946+jennantilla@users.noreply.github.com> Date: Tue, 9 Jan 2024 16:17:31 -0800 Subject: [PATCH 03/14] [Android] Add getters for onesignal ID/external ID, add user state observer --- .../rnonesignalandroid/RNOneSignal.java | 46 +++++++++++++++++++ .../onesignal/rnonesignalandroid/RNUtils.java | 22 +++++++++ 2 files changed, 68 insertions(+) diff --git a/android/src/main/java/com/onesignal/rnonesignalandroid/RNOneSignal.java b/android/src/main/java/com/onesignal/rnonesignalandroid/RNOneSignal.java index b856594e..f30033f7 100644 --- a/android/src/main/java/com/onesignal/rnonesignalandroid/RNOneSignal.java +++ b/android/src/main/java/com/onesignal/rnonesignalandroid/RNOneSignal.java @@ -74,6 +74,9 @@ of this software and associated documentation files (the "Software"), to deal import com.onesignal.user.subscriptions.IPushSubscriptionObserver; import com.onesignal.user.subscriptions.PushSubscriptionState; import com.onesignal.user.subscriptions.PushSubscriptionChangedState; +import com.onesignal.user.state.UserState; +import com.onesignal.user.state.UserChangedState; +import com.onesignal.user.state.IUserStateObserver; import org.json.JSONException; import java.util.HashMap; @@ -82,6 +85,7 @@ of this software and associated documentation files (the "Software"), to deal public class RNOneSignal extends ReactContextBaseJavaModule implements IPushSubscriptionObserver, IPermissionObserver, + IUserStateObserver, LifecycleEventListener, INotificationLifecycleListener{ private ReactApplicationContext mReactApplicationContext; @@ -90,6 +94,7 @@ public class RNOneSignal extends ReactContextBaseJavaModule implements private boolean oneSignalInitDone; private boolean hasSetPermissionObserver = false; private boolean hasSetPushSubscriptionObserver = false; + private boolean hasSetUserStateObserver = false; private HashMap notificationWillDisplayCache; private HashMap preventDefaultCache; @@ -162,6 +167,7 @@ public void onClick(INotificationClickEvent event) { private void removeObservers() { this.removePermissionObserver(); this.removePushSubscriptionObserver(); + this.removeUserStateObserver(); } private void removeHandlers() { @@ -650,6 +656,46 @@ public void removeAliases(ReadableArray aliasLabels) { OneSignal.getUser().removeAliases(RNUtils.convertReadableArrayIntoStringCollection(aliasLabels)); } + @ReactMethod + public void getOnesignalId(Promise promise) { + String onesignalId = OneSignal.getUser().getOnesignalId(); + promise.resolve(onesignalId); + } + + @ReactMethod + public void getExternalId(Promise promise) { + String externalId = OneSignal.getUser().getExternalId(); + promise.resolve(externalId); + } + + @ReactMethod + public void addUserStateObserver() { + if (!hasSetUserStateObserver) { + OneSignal.getUser().addObserver(this); + hasSetUserStateObserver = true; + } + } + + @Override + public void onUserStateChange(UserChangedState UserChangedState) { + try { + sendEvent("OneSignal-userStateChanged", + RNUtils.convertHashMapToWritableMap( + RNUtils.convertUserChangedStateToMap(UserChangedState))); + Log.i("OneSignal", "sending user state change event"); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + @ReactMethod + public void removeUserStateObserver() { + if (hasSetUserStateObserver) { + OneSignal.getUser().removeObserver(this); + hasSetUserStateObserver = false; + } + } + /** Added for NativeEventEmitter */ @ReactMethod public void addListener(String eventName) { diff --git a/android/src/main/java/com/onesignal/rnonesignalandroid/RNUtils.java b/android/src/main/java/com/onesignal/rnonesignalandroid/RNUtils.java index ee7c60f6..5f4256a6 100644 --- a/android/src/main/java/com/onesignal/rnonesignalandroid/RNUtils.java +++ b/android/src/main/java/com/onesignal/rnonesignalandroid/RNUtils.java @@ -27,6 +27,8 @@ import com.onesignal.user.subscriptions.IPushSubscription; import com.onesignal.user.subscriptions.PushSubscriptionState; import com.onesignal.user.subscriptions.PushSubscriptionChangedState; +import com.onesignal.user.state.UserState; +import com.onesignal.user.state.UserChangedState; import org.json.JSONArray; import org.json.JSONException; @@ -190,6 +192,19 @@ public static HashMap convertPushSubscriptionStateToMap(PushSubs return hash; } + public static HashMap convertUserStateToMap(UserState user) { + HashMap hash = new HashMap<>(); + + if (!user.getExternalId().isEmpty()) { + hash.put("externalId", user.getExternalId()); + } + if (!user.getOnesignalId().isEmpty()) { + hash.put("onesignalId", user.getOnesignalId()); + } + + return hash; + } + public static HashMap convertPushSubscriptionChangedStateToMap(PushSubscriptionChangedState state) { HashMap hash = new HashMap<>(); hash.put("current", convertPushSubscriptionStateToMap(state.getCurrent())); @@ -198,6 +213,13 @@ public static HashMap convertPushSubscriptionChangedStateToMap(P return hash; } + public static HashMap convertUserChangedStateToMap(UserChangedState state) { + HashMap hash = new HashMap<>(); + hash.put("current", convertUserStateToMap(state.getCurrent())); + + return hash; + } + public static HashMap convertJSONObjectToHashMap(JSONObject object) throws JSONException { HashMap hash = new HashMap<>(); From 94c16e6605e17c4b68c14124d7fda3b0e4e13a9c Mon Sep 17 00:00:00 2001 From: Jenna Antilla <46546946+jennantilla@users.noreply.github.com> Date: Tue, 9 Jan 2024 16:18:32 -0800 Subject: [PATCH 04/14] [iOS] Add getters for onesignal ID/external ID, add user state observer --- ios/RCTOneSignal/RCTOneSignal.h | 3 +-- ios/RCTOneSignal/RCTOneSignal.m | 19 +++++++++++++++ ios/RCTOneSignal/RCTOneSignalEventEmitter.h | 3 ++- ios/RCTOneSignal/RCTOneSignalEventEmitter.m | 27 +++++++++++++++++++++ 4 files changed, 49 insertions(+), 3 deletions(-) diff --git a/ios/RCTOneSignal/RCTOneSignal.h b/ios/RCTOneSignal/RCTOneSignal.h index ccf3336e..be93f668 100644 --- a/ios/RCTOneSignal/RCTOneSignal.h +++ b/ios/RCTOneSignal/RCTOneSignal.h @@ -5,8 +5,7 @@ #import "../OneSignalFramework.h" #endif -@interface RCTOneSignal : NSObject - +@interface RCTOneSignal : NSObject + (RCTOneSignal *) sharedInstance; @end diff --git a/ios/RCTOneSignal/RCTOneSignal.m b/ios/RCTOneSignal/RCTOneSignal.m index 1e4486cf..47e1355d 100644 --- a/ios/RCTOneSignal/RCTOneSignal.m +++ b/ios/RCTOneSignal/RCTOneSignal.m @@ -77,6 +77,25 @@ - (void)sendEvent:(NSString *)eventName withBody:(NSDictionary *)body { [RCTOneSignalEventEmitter sendEventWithName:eventName withBody:body]; } +- (void)onUserStateDidChangeWithState:(OSUserChangedState * _Nonnull)state { + NSString *onesignalId = state.current.onesignalId; + NSString *externalId = state.current.externalId; + + NSMutableDictionary *currentDictionary = [NSMutableDictionary dictionary]; + + if (onesignalId.length > 0) { + [currentDictionary setObject:onesignalId forKey:@"onesignalId"]; + } + + if (externalId.length > 0) { + [currentDictionary setObject:externalId forKey:@"externalId"]; + } + + NSDictionary *result = @{@"current": currentDictionary}; + + [self sendEvent:OSEventString(UserStateChanged) withBody:result]; +} + - (void)onPushSubscriptionDidChangeWithState:(OSPushSubscriptionChangedState * _Nonnull)state { [self sendEvent:OSEventString(SubscriptionChanged) withBody:[state jsonRepresentation]]; } diff --git a/ios/RCTOneSignal/RCTOneSignalEventEmitter.h b/ios/RCTOneSignal/RCTOneSignalEventEmitter.h index 458446e8..ca153320 100644 --- a/ios/RCTOneSignal/RCTOneSignalEventEmitter.h +++ b/ios/RCTOneSignal/RCTOneSignalEventEmitter.h @@ -15,6 +15,7 @@ typedef NS_ENUM(NSInteger, OSNotificationEventTypes) { PermissionChanged, SubscriptionChanged, + UserStateChanged, NotificationWillDisplayInForeground, NotificationClicked, InAppMessageClicked, @@ -24,7 +25,7 @@ typedef NS_ENUM(NSInteger, OSNotificationEventTypes) { InAppMessageDidDismiss, }; -#define OSNotificationEventTypesArray @[@"OneSignal-permissionChanged",@"OneSignal-subscriptionChanged",@"OneSignal-notificationWillDisplayInForeground",@"OneSignal-notificationClicked",@"OneSignal-inAppMessageClicked", @"OneSignal-inAppMessageWillDisplay", @"OneSignal-inAppMessageDidDisplay", @"OneSignal-inAppMessageWillDismiss", @"OneSignal-inAppMessageDidDismiss"] +#define OSNotificationEventTypesArray @[@"OneSignal-permissionChanged",@"OneSignal-subscriptionChanged",@"OneSignal-userStateChanged",@"OneSignal-notificationWillDisplayInForeground",@"OneSignal-notificationClicked",@"OneSignal-inAppMessageClicked", @"OneSignal-inAppMessageWillDisplay", @"OneSignal-inAppMessageDidDisplay", @"OneSignal-inAppMessageWillDismiss", @"OneSignal-inAppMessageDidDismiss"] #define OSEventString(enum) [OSNotificationEventTypesArray objectAtIndex:enum] diff --git a/ios/RCTOneSignal/RCTOneSignalEventEmitter.m b/ios/RCTOneSignal/RCTOneSignalEventEmitter.m index 5861b208..bd9ecf1e 100644 --- a/ios/RCTOneSignal/RCTOneSignalEventEmitter.m +++ b/ios/RCTOneSignal/RCTOneSignalEventEmitter.m @@ -9,6 +9,7 @@ @implementation RCTOneSignalEventEmitter { BOOL _hasListeners; BOOL _hasSetSubscriptionObserver; BOOL _hasSetPermissionObserver; + BOOL _hasSetUserStateObserver; BOOL _hasAddedNotificationClickListener; BOOL _hasAddedNotificationForegroundLifecycleListener; BOOL _hasAddedInAppMessageClickListener; @@ -310,6 +311,20 @@ + (void)sendEventWithName:(NSString *)name withBody:(NSDictionary *)body { } // OneSignal.User namespace methods +RCT_EXPORT_METHOD(addUserStateObserver) { + if (!_hasSetUserStateObserver) { + [OneSignal.User addObserver:[RCTOneSignal sharedInstance]]; + _hasSetUserStateObserver = true; + } +} + +RCT_EXPORT_METHOD(removeUserStateObserver) { + if (_hasSetUserStateObserver) { + [OneSignal.User removeObserver:[RCTOneSignal sharedInstance]]; + _hasSetUserStateObserver = false; + } +} + RCT_EXPORT_METHOD(addPushSubscriptionObserver) { if (!_hasSetSubscriptionObserver) { [OneSignal.User.pushSubscription addObserver:[RCTOneSignal sharedInstance]]; @@ -365,6 +380,18 @@ + (void)sendEventWithName:(NSString *)name withBody:(NSDictionary *)body { resolve(tags); } +RCT_REMAP_METHOD(getOnesignalId, + getOnesignalIdResolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) { + resolve(OneSignal.User.onesignalId); +} + +RCT_REMAP_METHOD(getExternalId, + getExternalIdResolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) { + resolve(OneSignal.User.externalId); +} + RCT_EXPORT_METHOD(addAlias:(NSString *)label :(NSString *)id) { [OneSignal.User addAliasWithLabel:label id:id]; } From 5a024572b72a7c9c2a3f42e4bf8077ee49efb6fd Mon Sep 17 00:00:00 2001 From: Jenna Antilla <46546946+jennantilla@users.noreply.github.com> Date: Tue, 9 Jan 2024 16:32:04 -0800 Subject: [PATCH 05/14] Update Migration Guide --- MIGRATION_GUIDE.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 54d15b49..ed3c7e15 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -212,6 +212,22 @@ The User namespace is accessible via `OneSignal.User` and provides access to use | `OneSignal.User.removeTag("KEY")` | _Remove the data tag with the provided key from the current user._ | | `OneSignal.User.removeTags(["KEY_01", "KEY_02"])` | _Remove multiple tags with the provided keys from the current user._ | | `OneSignal.User.getTags()` | _Returns the local tags for the current user._| +| `OneSignal.User.addEventListener("change", (event: UserChangedState) => void)`

**_See below for usage_** | _Add a User State callback which contains the nullable onesignalId and externalId. The listener will be fired when these values change._| +| `await OneSignal.User.getOnesignalId()` | _Returns the nullable OneSignal ID for the current user._| +| `await OneSignal.User.getExternalId()` | _Returns the nullable external ID for the current user._| + +### User State Listener + +```typescript + const listener = (event: UserChangedState) => { + console.log("User changed: " + (event)); + }; + + OneSignal.User.addEventListener("change", listener); + // Remove the listener + OneSignal.User.removeEventListener("change", listener); +``` + ## Push Subscription Namespace The Push Subscription namespace is accessible via `OneSignal.User.pushSubscription` and provides access to push subscription-scoped functionality. From e30afd950e5236453a6b943782c716e0c5ef5e66 Mon Sep 17 00:00:00 2001 From: Jenna Antilla <46546946+jennantilla@users.noreply.github.com> Date: Tue, 9 Jan 2024 17:18:40 -0800 Subject: [PATCH 06/14] Run prettier to fix linting errors --- examples/RNOneSignalTS/src/OSButtons.tsx | 8 ++++---- examples/RNOneSignalTS/src/OSDemo.tsx | 2 +- src/index.ts | 5 +---- src/models/User.ts | 12 ++++++------ 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/examples/RNOneSignalTS/src/OSButtons.tsx b/examples/RNOneSignalTS/src/OSButtons.tsx index 14926a65..112c257c 100644 --- a/examples/RNOneSignalTS/src/OSButtons.tsx +++ b/examples/RNOneSignalTS/src/OSButtons.tsx @@ -277,8 +277,8 @@ class OSButtons extends React.Component { }); const getTagsButton = renderButtonView('Get tags', async () => { - const tags = await OneSignal.User.getTags(); - loggingFunction('Tags:', tags); + const tags = await OneSignal.User.getTags(); + loggingFunction('Tags:', tags); }); const setLanguageButton = renderButtonView('Set Language', () => { @@ -357,7 +357,7 @@ class OSButtons extends React.Component { loggingFunction('External Id:', externalId); }, ); - + return [ loginButton, logoutButton, @@ -376,7 +376,7 @@ class OSButtons extends React.Component { addAliasesButton, removeAliasesButton, getOnesignalIdButton, - getExternalIdButton + getExternalIdButton, ]; } diff --git a/examples/RNOneSignalTS/src/OSDemo.tsx b/examples/RNOneSignalTS/src/OSDemo.tsx index 1cf82f0c..94f2dbf1 100644 --- a/examples/RNOneSignalTS/src/OSDemo.tsx +++ b/examples/RNOneSignalTS/src/OSDemo.tsx @@ -102,7 +102,7 @@ class OSDemo extends React.Component { OneSignal.User.addEventListener('change', (event) => { this.OSLog('OneSignal: user changed: ', event); - }) + }); } OSLog = (message: string, optionalArg: any = null) => { diff --git a/src/index.ts b/src/index.ts index 1bbc76f5..8ffc61ba 100644 --- a/src/index.ts +++ b/src/index.ts @@ -24,10 +24,7 @@ import { OSNotificationPermission, PushSubscriptionChangedState, } from './models/Subscription'; -import { - UserState, - UserChangedState, -} from './models/User'; +import { UserState, UserChangedState } from './models/User'; import NotificationWillDisplayEvent from './events/NotificationWillDisplayEvent'; import { InAppMessage, diff --git a/src/models/User.ts b/src/models/User.ts index 3805d6f8..0bc33203 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -1,9 +1,9 @@ export interface UserState { - externalId: string; - onesignalId: string; + externalId: string; + onesignalId: string; } - + export interface UserChangedState { - previous: UserState; - current: UserState; -} \ No newline at end of file + previous: UserState; + current: UserState; +} From 1cc33139110083fba906c03a41fc8efe799ce6ca Mon Sep 17 00:00:00 2001 From: Jenna Antilla <46546946+jennantilla@users.noreply.github.com> Date: Thu, 25 Jan 2024 16:37:08 -0800 Subject: [PATCH 07/14] Update UserState properties to optional --- src/models/User.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/models/User.ts b/src/models/User.ts index 0bc33203..e4c32f71 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -1,6 +1,6 @@ export interface UserState { - externalId: string; - onesignalId: string; + externalId?: string; + onesignalId?: string; } export interface UserChangedState { From 986353962cee635d55a7855f638d4cf7257a393f Mon Sep 17 00:00:00 2001 From: Jenna Antilla <46546946+jennantilla@users.noreply.github.com> Date: Thu, 25 Jan 2024 16:41:10 -0800 Subject: [PATCH 08/14] Update User Id getters to async --- src/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/index.ts b/src/index.ts index 8ffc61ba..e6507a44 100644 --- a/src/index.ts +++ b/src/index.ts @@ -292,21 +292,21 @@ export namespace OneSignal { } /** Get the OneSignal Id associated with the user. */ - export function getOnesignalId(): Promise { + export async function getOnesignalId(): Promise { if (!isNativeModuleLoaded(RNOneSignal)) { return Promise.reject(new Error('OneSignal native module not loaded')); } - return RNOneSignal.getOnesignalId(); + return await RNOneSignal.getOnesignalId(); } /** Get the External Id associated with the user. */ - export function getExternalId(): Promise { + export async function getExternalId(): Promise { if (!isNativeModuleLoaded(RNOneSignal)) { return Promise.reject(new Error('OneSignal native module not loaded')); } - return RNOneSignal.getExternalId(); + return await RNOneSignal.getExternalId(); } /** Explicitly set a 2-character language code for the user. */ From 4e252cc82110db189b25a6dcdff88d49b0f79732 Mon Sep 17 00:00:00 2001 From: Jenna Antilla <46546946+jennantilla@users.noreply.github.com> Date: Thu, 25 Jan 2024 16:48:32 -0800 Subject: [PATCH 09/14] Update Migration Guide --- MIGRATION_GUIDE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index ed3c7e15..9b5d6e41 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -213,8 +213,8 @@ The User namespace is accessible via `OneSignal.User` and provides access to use | `OneSignal.User.removeTags(["KEY_01", "KEY_02"])` | _Remove multiple tags with the provided keys from the current user._ | | `OneSignal.User.getTags()` | _Returns the local tags for the current user._| | `OneSignal.User.addEventListener("change", (event: UserChangedState) => void)`

**_See below for usage_** | _Add a User State callback which contains the nullable onesignalId and externalId. The listener will be fired when these values change._| -| `await OneSignal.User.getOnesignalId()` | _Returns the nullable OneSignal ID for the current user._| -| `await OneSignal.User.getExternalId()` | _Returns the nullable external ID for the current user._| +| `await OneSignal.User.getOnesignalId()` | _Returns the OneSignal ID for the current user, which can be the empty string if it is not yet available._| +| `await OneSignal.User.getExternalId()` | _Returns the External ID for the current user, which can be the empty string if not set._| ### User State Listener From 0db0d7c18b070374895fbf833af89a605bc73dc7 Mon Sep 17 00:00:00 2001 From: Jenna Antilla <46546946+jennantilla@users.noreply.github.com> Date: Wed, 31 Jan 2024 15:56:21 -0800 Subject: [PATCH 10/14] Update user observer method description with instructions to check externalId --- src/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index e6507a44..318f9960 100644 --- a/src/index.ts +++ b/src/index.ts @@ -266,7 +266,10 @@ export namespace OneSignal { } } - /** Add a callback that fires when the OneSignal user state changes. */ + /** + * Add a callback that fires when the OneSignal user state changes. + * Important: When using the observer to retrieve the onesignalId, check the externalId as well to confirm the values are associated with the expected user. + */ export function addEventListener( event: 'change', listener: (event: UserChangedState) => void, From ed9e67926e6a9bd26f66f91718e03fb9e4aba97d Mon Sep 17 00:00:00 2001 From: Jenna Antilla <46546946+jennantilla@users.noreply.github.com> Date: Wed, 31 Jan 2024 16:18:52 -0800 Subject: [PATCH 11/14] Fix linting --- src/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/index.ts b/src/index.ts index 318f9960..7bdf950f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -266,10 +266,10 @@ export namespace OneSignal { } } - /** - * Add a callback that fires when the OneSignal user state changes. + /** + * Add a callback that fires when the OneSignal user state changes. * Important: When using the observer to retrieve the onesignalId, check the externalId as well to confirm the values are associated with the expected user. - */ + */ export function addEventListener( event: 'change', listener: (event: UserChangedState) => void, From 3a6a3e09d02dbc89a6cbbbc82705bb30ef709115 Mon Sep 17 00:00:00 2001 From: Jenna Antilla <46546946+jennantilla@users.noreply.github.com> Date: Thu, 15 Feb 2024 17:37:53 -0800 Subject: [PATCH 12/14] Ensure Ids are nullable --- MIGRATION_GUIDE.md | 4 ++-- .../rnonesignalandroid/RNOneSignal.java | 11 +++++++++-- ios/RCTOneSignal/RCTOneSignalEventEmitter.m | 16 ++++++++++++++-- src/index.ts | 10 ++++------ 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 9b5d6e41..bbb9ed7a 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -213,8 +213,8 @@ The User namespace is accessible via `OneSignal.User` and provides access to use | `OneSignal.User.removeTags(["KEY_01", "KEY_02"])` | _Remove multiple tags with the provided keys from the current user._ | | `OneSignal.User.getTags()` | _Returns the local tags for the current user._| | `OneSignal.User.addEventListener("change", (event: UserChangedState) => void)`

**_See below for usage_** | _Add a User State callback which contains the nullable onesignalId and externalId. The listener will be fired when these values change._| -| `await OneSignal.User.getOnesignalId()` | _Returns the OneSignal ID for the current user, which can be the empty string if it is not yet available._| -| `await OneSignal.User.getExternalId()` | _Returns the External ID for the current user, which can be the empty string if not set._| +| `await OneSignal.User.getOnesignalId()` | _Returns the OneSignal ID for the current user, which can be null if it is not yet available._| +| `await OneSignal.User.getExternalId()` | _Returns the External ID for the current user, which can be null if not set._| ### User State Listener diff --git a/android/src/main/java/com/onesignal/rnonesignalandroid/RNOneSignal.java b/android/src/main/java/com/onesignal/rnonesignalandroid/RNOneSignal.java index f30033f7..f0e45500 100644 --- a/android/src/main/java/com/onesignal/rnonesignalandroid/RNOneSignal.java +++ b/android/src/main/java/com/onesignal/rnonesignalandroid/RNOneSignal.java @@ -659,12 +659,19 @@ public void removeAliases(ReadableArray aliasLabels) { @ReactMethod public void getOnesignalId(Promise promise) { String onesignalId = OneSignal.getUser().getOnesignalId(); + if (onesignalId.isEmpty()) { + onesignalId = null; + } promise.resolve(onesignalId); + } @ReactMethod public void getExternalId(Promise promise) { String externalId = OneSignal.getUser().getExternalId(); + if (externalId.isEmpty()) { + externalId = null; + } promise.resolve(externalId); } @@ -677,11 +684,11 @@ public void addUserStateObserver() { } @Override - public void onUserStateChange(UserChangedState UserChangedState) { + public void onUserStateChange(UserChangedState state) { try { sendEvent("OneSignal-userStateChanged", RNUtils.convertHashMapToWritableMap( - RNUtils.convertUserChangedStateToMap(UserChangedState))); + RNUtils.convertUserChangedStateToMap(state))); Log.i("OneSignal", "sending user state change event"); } catch (JSONException e) { e.printStackTrace(); diff --git a/ios/RCTOneSignal/RCTOneSignalEventEmitter.m b/ios/RCTOneSignal/RCTOneSignalEventEmitter.m index bd9ecf1e..208c9977 100644 --- a/ios/RCTOneSignal/RCTOneSignalEventEmitter.m +++ b/ios/RCTOneSignal/RCTOneSignalEventEmitter.m @@ -383,13 +383,25 @@ + (void)sendEventWithName:(NSString *)name withBody:(NSDictionary *)body { RCT_REMAP_METHOD(getOnesignalId, getOnesignalIdResolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { - resolve(OneSignal.User.onesignalId); + NSString *onesignalId = OneSignal.User.onesignalId; + + if (onesignalId == nil || [onesignalId length] == 0) { + resolve([NSNull null]); // Resolve with null if nil or empty + } else { + resolve(onesignalId); + } } RCT_REMAP_METHOD(getExternalId, getExternalIdResolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { - resolve(OneSignal.User.externalId); + NSString *externalId = OneSignal.User.externalId; + + if (externalId == nil || [externalId length] == 0) { + resolve([NSNull null]); // Resolve with null if nil or empty + } else { + resolve(externalId); + } } RCT_EXPORT_METHOD(addAlias:(NSString *)label :(NSString *)id) { diff --git a/src/index.ts b/src/index.ts index 7bdf950f..17a39361 100644 --- a/src/index.ts +++ b/src/index.ts @@ -294,22 +294,20 @@ export namespace OneSignal { eventManager.removeEventListener(USER_STATE_CHANGED, listener); } - /** Get the OneSignal Id associated with the user. */ + /** Get the nullable OneSignal Id associated with the user. */ export async function getOnesignalId(): Promise { if (!isNativeModuleLoaded(RNOneSignal)) { return Promise.reject(new Error('OneSignal native module not loaded')); } - - return await RNOneSignal.getOnesignalId(); + return RNOneSignal.getOnesignalId(); } - /** Get the External Id associated with the user. */ + /** Get the nullable External Id associated with the user. */ export async function getExternalId(): Promise { if (!isNativeModuleLoaded(RNOneSignal)) { return Promise.reject(new Error('OneSignal native module not loaded')); } - - return await RNOneSignal.getExternalId(); + return RNOneSignal.getExternalId(); } /** Explicitly set a 2-character language code for the user. */ From 4b80bf85254f04d380bdb19fbe9d649da8e93c7e Mon Sep 17 00:00:00 2001 From: Jenna Antilla <46546946+jennantilla@users.noreply.github.com> Date: Thu, 15 Feb 2024 17:39:03 -0800 Subject: [PATCH 13/14] Remove unused method and nonexistent interface property --- ios/RCTOneSignal/RCTOneSignalEventEmitter.m | 7 ------- src/models/User.ts | 1 - 2 files changed, 8 deletions(-) diff --git a/ios/RCTOneSignal/RCTOneSignalEventEmitter.m b/ios/RCTOneSignal/RCTOneSignalEventEmitter.m index 208c9977..9cc3dc4b 100644 --- a/ios/RCTOneSignal/RCTOneSignalEventEmitter.m +++ b/ios/RCTOneSignal/RCTOneSignalEventEmitter.m @@ -318,13 +318,6 @@ + (void)sendEventWithName:(NSString *)name withBody:(NSDictionary *)body { } } -RCT_EXPORT_METHOD(removeUserStateObserver) { - if (_hasSetUserStateObserver) { - [OneSignal.User removeObserver:[RCTOneSignal sharedInstance]]; - _hasSetUserStateObserver = false; - } -} - RCT_EXPORT_METHOD(addPushSubscriptionObserver) { if (!_hasSetSubscriptionObserver) { [OneSignal.User.pushSubscription addObserver:[RCTOneSignal sharedInstance]]; diff --git a/src/models/User.ts b/src/models/User.ts index e4c32f71..465121f2 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -4,6 +4,5 @@ export interface UserState { } export interface UserChangedState { - previous: UserState; current: UserState; } From 4997f2017e399823d5f30a3bf3e5acad262bbdb4 Mon Sep 17 00:00:00 2001 From: Jenna Antilla <46546946+jennantilla@users.noreply.github.com> Date: Fri, 8 Mar 2024 15:38:51 -0800 Subject: [PATCH 14/14] Update UserState observer properties to return null if empty --- .../java/com/onesignal/rnonesignalandroid/RNUtils.java | 10 ++++++++-- ios/RCTOneSignal/RCTOneSignal.m | 6 ++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/com/onesignal/rnonesignalandroid/RNUtils.java b/android/src/main/java/com/onesignal/rnonesignalandroid/RNUtils.java index 5f4256a6..ee3be544 100644 --- a/android/src/main/java/com/onesignal/rnonesignalandroid/RNUtils.java +++ b/android/src/main/java/com/onesignal/rnonesignalandroid/RNUtils.java @@ -195,12 +195,18 @@ public static HashMap convertPushSubscriptionStateToMap(PushSubs public static HashMap convertUserStateToMap(UserState user) { HashMap hash = new HashMap<>(); - if (!user.getExternalId().isEmpty()) { + if (user.getExternalId() != null && !user.getExternalId().isEmpty()) { hash.put("externalId", user.getExternalId()); } - if (!user.getOnesignalId().isEmpty()) { + else { + hash.put("externalId", JSONObject.NULL); + } + if (user.getOnesignalId() != null && !user.getOnesignalId().isEmpty()) { hash.put("onesignalId", user.getOnesignalId()); } + else { + hash.put("onesignalId", JSONObject.NULL); + } return hash; } diff --git a/ios/RCTOneSignal/RCTOneSignal.m b/ios/RCTOneSignal/RCTOneSignal.m index 47e1355d..33e3ab54 100644 --- a/ios/RCTOneSignal/RCTOneSignal.m +++ b/ios/RCTOneSignal/RCTOneSignal.m @@ -86,10 +86,16 @@ - (void)onUserStateDidChangeWithState:(OSUserChangedState * _Nonnull)state { if (onesignalId.length > 0) { [currentDictionary setObject:onesignalId forKey:@"onesignalId"]; } + else { + [currentDictionary setObject:[NSNull null] forKey:@"onesignalId"]; + } if (externalId.length > 0) { [currentDictionary setObject:externalId forKey:@"externalId"]; } + else { + [currentDictionary setObject:[NSNull null] forKey:@"externalId"]; + } NSDictionary *result = @{@"current": currentDictionary};