Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add getters and observer for onesignal ID and external ID #959

Merged
merged 11 commits into from
Mar 4, 2024
13 changes: 13 additions & 0 deletions MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,21 @@ The User name space is accessible via `OneSignal.User` and provides access to us
| `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);` <br><br>***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 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

**Cordova/Ionic**
```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

Expand Down
28 changes: 28 additions & 0 deletions src/android/com/onesignal/cordova/OneSignalController.java
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,34 @@ public static boolean getTags(CallbackContext callbackContext) {
return true;
}

public static boolean getOnesignalId(CallbackContext callbackContext) {
String onesignalId = OneSignal.getUser().getOnesignalId();
try {
JSONObject onesignalIdObject = new JSONObject ();
if (!onesignalId.isEmpty()) {
onesignalIdObject.put("value", onesignalId);
}
CallbackHelper.callbackSuccess(callbackContext, onesignalIdObject);
} catch (JSONException e){
e.printStackTrace();
}
return true;
}

public static boolean getExternalId(CallbackContext callbackContext) {
String externalId = OneSignal.getUser().getExternalId();
try {
JSONObject externalIdObject = new JSONObject ();
if (!externalId.isEmpty()) {
externalIdObject.put("value", externalId);
}
CallbackHelper.callbackSuccess(callbackContext, externalIdObject);
} catch (JSONException e){
e.printStackTrace();
}
return true;
}

/**
* Notifications
*/
Expand Down
56 changes: 56 additions & 0 deletions src/android/com/onesignal/cordova/OneSignalObserverController.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,18 @@
import com.onesignal.user.subscriptions.PushSubscriptionChangedState;
import com.onesignal.user.subscriptions.PushSubscriptionState;
import com.onesignal.notifications.IPermissionObserver;
import com.onesignal.user.state.UserState;
import com.onesignal.user.state.UserChangedState;
import com.onesignal.user.state.IUserStateObserver;

public class OneSignalObserverController {
private static CallbackContext jsPermissionObserverCallBack;
private static CallbackContext jsSubscriptionObserverCallBack;
private static CallbackContext jsUserObserverCallBack;

private static IPermissionObserver permissionObserver;
private static IPushSubscriptionObserver pushSubscriptionObserver;
private static IUserStateObserver userStateObserver;

public static boolean addPermissionObserver(CallbackContext callbackContext) {
jsPermissionObserverCallBack = callbackContext;
Expand Down Expand Up @@ -65,6 +70,57 @@ public void onPushSubscriptionChange(PushSubscriptionChangedState state) {
return true;
}

public static boolean addUserStateObserver(CallbackContext callbackContext) {
jsUserObserverCallBack = callbackContext;
if (userStateObserver == null) {
userStateObserver = new IUserStateObserver() {
@Override
public void onUserStateChange(UserChangedState state) {
UserState current = state.getCurrent();

if (!(current instanceof UserState)) {
return;
}

try {
JSONObject hash = new JSONObject();
hash.put("current", createUserIds(current));

CallbackHelper.callbackSuccess(jsUserObserverCallBack, hash);

} catch (Exception e) {
e.printStackTrace();
}
}
};
OneSignal.getUser().addObserver(userStateObserver);
}
return true;
}

/** Helper method to return JSONObject.NULL if string is empty or nil **/
private static Object getStringOrJSONObjectNull(String str) {
if (str != null && !str.isEmpty()) {
return str;
} else {
return JSONObject.NULL;
}
}

private static JSONObject createUserIds(UserState user) {
JSONObject userIds = new JSONObject();
try {
String externalId = user.getExternalId();
String onesignalId = user.getOnesignalId();

userIds.put("externalId", getStringOrJSONObjectNull(externalId));
userIds.put("onesignalId", getStringOrJSONObjectNull(onesignalId));
} catch (JSONException e) {
e.printStackTrace();
}
return userIds;
}

private static JSONObject createPushSubscriptionProperties(PushSubscriptionState pushSubscription) {
JSONObject pushSubscriptionProperties = new JSONObject();
try {
Expand Down
16 changes: 16 additions & 0 deletions src/android/com/onesignal/cordova/OneSignalPush.java
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ public class OneSignalPush extends CordovaPlugin implements INotificationLifecyc

private static final String ADD_PERMISSION_OBSERVER = "addPermissionObserver";
private static final String ADD_PUSH_SUBSCRIPTION_OBSERVER = "addPushSubscriptionObserver";
private static final String ADD_USER_STATE_OBSERVER = "addUserStateObserver";

private static final String GET_ONESIGNAL_ID = "getOnesignalId";
private static final String GET_EXTERNAL_ID = "getExternalId";

private static final String OPT_IN = "optInPushSubscription";
private static final String OPT_OUT = "optOutPushSubscription";
Expand Down Expand Up @@ -432,6 +436,18 @@ public boolean execute(String action, JSONArray data, CallbackContext callbackCo
result = OneSignalObserverController.addPushSubscriptionObserver(callbackContext);
break;

case ADD_USER_STATE_OBSERVER:
result = OneSignalObserverController.addUserStateObserver(callbackContext);
break;

case GET_ONESIGNAL_ID:
result = OneSignalController.getOnesignalId(callbackContext);
break;

case GET_EXTERNAL_ID:
result = OneSignalController.getExternalId(callbackContext);
break;

case OPT_IN:
result = OneSignalController.optInPushSubscription();
break;
Expand Down
7 changes: 6 additions & 1 deletion src/ios/OneSignalPush.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

#import <OneSignalFramework/OneSignalFramework.h>

@interface OneSignalPush : CDVPlugin <OSNotificationPermissionObserver, OSNotificationLifecycleListener, OSNotificationClickListener, OSPushSubscriptionObserver, OSInAppMessageLifecycleListener, OSInAppMessageClickListener>
@interface OneSignalPush : CDVPlugin <OSNotificationPermissionObserver, OSNotificationLifecycleListener, OSNotificationClickListener, OSPushSubscriptionObserver, OSInAppMessageLifecycleListener, OSInAppMessageClickListener, OSUserStateObserver>

- (void)setProvidesNotificationSettingsView:(CDVInvokedUrlCommand* _Nonnull)command;
- (void)addForegroundLifecycleListener:(CDVInvokedUrlCommand* _Nonnull)command;
Expand All @@ -53,6 +53,10 @@
- (void)removeTags:(CDVInvokedUrlCommand* _Nonnull)command;
- (void)getTags:(CDVInvokedUrlCommand* _Nonnull)command;

- (void)addUserStateObserver:(CDVInvokedUrlCommand* _Nonnull)command;
- (void)getOnesignalId:(CDVInvokedUrlCommand* _Nonnull)command;
- (void)getExternalId:(CDVInvokedUrlCommand* _Nonnull)command;

// Push Subscription
- (void)addPushSubscriptionObserver:(CDVInvokedUrlCommand* _Nonnull)command;
- (void)getPushSubscriptionId:(CDVInvokedUrlCommand* _Nonnull)command;
Expand All @@ -69,6 +73,7 @@
- (void)canRequestPermission:(CDVInvokedUrlCommand* _Nonnull)command;
- (void)registerForProvisionalAuthorization:(CDVInvokedUrlCommand* _Nonnull)command;
- (void)clearAllNotifications:(CDVInvokedUrlCommand* _Nonnull)command;

// Android Only - Notifications
- (void)removeNotification:(CDVInvokedUrlCommand* _Nonnull)command;
- (void)removeGroupedNotifications:(CDVInvokedUrlCommand* _Nonnull)command;
Expand Down
54 changes: 54 additions & 0 deletions src/ios/OneSignalPush.m
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
NSString* inAppMessageWillDismissCallbackId;
NSString* inAppMessageDidDismissCallbackId;
NSString* inAppMessageClickedCallbackId;
NSString* userObserverCallbackId;

OSNotificationClickEvent *actionNotification;
OSNotification *notification;
Expand Down Expand Up @@ -104,6 +105,16 @@ void initOneSignalObject(NSDictionary* launchOptions) {
initialLaunchFired = true;
}

/** Helper method to return NSNull if string is empty or nil **/
NSString* getStringOrNSNull(NSString* string) {
// length method can be used on nil and strings
if (string.length > 0) {
return string;
} else {
return [NSNull null];
}
}

@implementation UIApplication(OneSignalCordovaPush)

static void injectSelectorCordova(Class newClass, SEL newSel, Class addToClass, SEL makeLikeSel) {
Expand Down Expand Up @@ -165,6 +176,41 @@ - (void)onPushSubscriptionDidChangeWithState:(OSPushSubscriptionChangedState *)s
successCallback(subscriptionObserverCallbackId, [state jsonRepresentation]);
}

- (void)onUserStateDidChangeWithState:(OSUserChangedState * _Nonnull)state {
NSString *onesignalId = state.current.onesignalId;
NSString *externalId = state.current.externalId;

NSMutableDictionary *result = [NSMutableDictionary new];

NSMutableDictionary *currentObject = [NSMutableDictionary new];

currentObject[@"onesignalId"] = getStringOrNSNull(onesignalId);
currentObject[@"externalId"] = getStringOrNSNull(externalId);
result[@"current"] = currentObject;

successCallback(userObserverCallbackId, result);
}

- (void)getOnesignalId:(CDVInvokedUrlCommand *)command {
NSString *onesignalId = OneSignal.User.onesignalId;

NSDictionary *result = @{
@"value" : (onesignalId ? onesignalId : [NSNull null])
};

successCallback(command.callbackId, result);
}

- (void)getExternalId:(CDVInvokedUrlCommand *)command {
NSString *externalId = OneSignal.User.externalId;

NSDictionary *result = @{
@"value" : (externalId ? externalId : [NSNull null])
};

successCallback(command.callbackId, result);
}

- (void)setProvidesNotificationSettingsView:(CDVInvokedUrlCommand *)command {
BOOL providesView = command.arguments[0];
[OneSignal setProvidesNotificationSettingsView:providesView];
Expand Down Expand Up @@ -272,6 +318,14 @@ - (void)addPushSubscriptionObserver:(CDVInvokedUrlCommand*)command {
[OneSignal.User.pushSubscription addObserver:self];
}

- (void)addUserStateObserver:(CDVInvokedUrlCommand*)command {
bool first = userObserverCallbackId == nil;
userObserverCallbackId = command.callbackId;
if (first) {
[OneSignal.User addObserver:self];
}
}

- (void)getPushSubscriptionId:(CDVInvokedUrlCommand*)command {
NSString *pushId = OneSignal.User.pushSubscription.id;
if (pushId) {
Expand Down
70 changes: 70 additions & 0 deletions www/UserNamespace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,28 @@ import PushSubscription from "./PushSubscriptionNamespace";
// Suppress TS warnings about window.cordova
declare let window: any; // turn off type checking

// Represents the current user state
export interface UserState {
onesignalId ?: string;
externalId ?: string;
}

export interface UserChangedState {
current: UserState;
}

export default class User {
// The push subscription associated to the current user.
pushSubscription: PushSubscription = new PushSubscription();

private _userStateObserverList: ((event:UserChangedState)=>void)[] = [];

private _processFunctionList(array: ((event:UserChangedState)=>void)[], param: UserChangedState): void {
for (let i = 0; i < array.length; i++) {
array[i](param);
}
}

/**
* Explicitly set a 2-character language code for the user.
* @param {string} language
Expand Down Expand Up @@ -159,4 +177,56 @@ export default class User {
window.cordova.exec(resolve, reject, "OneSignalPush", "getTags", []);
});
};

/**
* 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.
* @param {(event: UserChangedState)=>void} listener
* @returns void
*/
addEventListener(event: "change", listener: (event: UserChangedState) => void) {
this._userStateObserverList.push(listener as (event: UserChangedState) => void);
const userCallBackProcessor = (state: UserChangedState) => {
this._processFunctionList(this._userStateObserverList, state);
};
window.cordova.exec(userCallBackProcessor, function(){}, "OneSignalPush", "addUserStateObserver", []);
}

/**
* Remove a User State observer that has been previously added.
* @param {(event: UserChangedState)=>void} listener
* @returns void
*/
removeEventListener(event: "change", listener: (event: UserChangedState) => void) {
let index = this._userStateObserverList.indexOf(listener);
if (index !== -1) {
this._userStateObserverList.splice(index, 1);
}
}

/**
* Get the nullable OneSignal Id associated with the current user.
* @returns {Promise<string | null>}
*/
async getOnesignalId(): Promise<string | null> {
return new Promise<string | null>((resolve, reject) => {
const callback = (response: {value: string}) => {
resolve(response.value ? response.value : null)
};
window.cordova.exec(callback, reject, "OneSignalPush", "getOnesignalId", []);
});
}

/**
* Get the nullable External Id associated with the current user.
* @returns {Promise<string | null>}
*/
async getExternalId(): Promise<string | null> {
return new Promise<string | null>((resolve, reject) => {
const callback = (response: {value: string}) => {
resolve(response.value ? response.value : null)
};
window.cordova.exec(callback, reject, "OneSignalPush", "getExternalId", []);
});
}
}
5 changes: 5 additions & 0 deletions www/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,9 @@ export {
InAppMessageActionUrlType,
} from "./models/InAppMessage";

export {
UserState,
UserChangedState,
} from "./UserNamespace";

export default OneSignal;
Loading