From 7379e2d905d75378e7d0550cd6a723253814d913 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20R=C4=B1za=20Kat?= Date: Tue, 5 Nov 2024 19:32:22 +0300 Subject: [PATCH 01/13] Reworking User Profile --- Countly.d.ts | 14 ++++++++++ Countly.js | 2 ++ UserProfile.js | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++ Validators.js | 15 ++++++++++- 4 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 UserProfile.js diff --git a/Countly.d.ts b/Countly.d.ts index de3ad085..0b29143f 100644 --- a/Countly.d.ts +++ b/Countly.d.ts @@ -579,6 +579,20 @@ declare module "countly-sdk-react-native-bridge" { */ export function setUserData(userData: CountlyUserData): string | Promise; + namespace userProfile { + export function setProperty(keyName: string, keyValue: any): Promise; + export function setProperties(userData: CountlyUserData): Promise; + export function increment(keyName: string): Promise; + export function incrementBy(keyName: string, keyValue: any): Promise; + export function multiply(keyName: string, keyValue: any): Promise; + export function saveMax(keyName: string, keyValue: any): Promise; + export function saveMin(keyName: string, keyValue: any): Promise; + export function setOnce(keyName: string, keyValue: any): Promise; + export function pushUniqueValue(keyName: string, keyValue: any): Promise; + export function pushValue(keyName: string, keyValue: any): Promise; + export function pullValue(keyName: string, keyValue: any): Promise; + } + namespace userData { /** * diff --git a/Countly.js b/Countly.js index a3072885..2ae30f0b 100644 --- a/Countly.js +++ b/Countly.js @@ -11,6 +11,7 @@ import CountlyState from "./CountlyState.js"; import Feedback from "./Feedback.js"; import Event from "./Event.js"; import DeviceId from "./DeviceId.js"; +import UserProfile from "./UserProfile.js"; import * as L from "./Logger.js"; import * as Utils from "./Utils.js"; import * as Validate from "./Validators.js"; @@ -28,6 +29,7 @@ CountlyState.eventEmitter = eventEmitter; Countly.feedback = new Feedback(CountlyState); Countly.events = new Event(CountlyState); Countly.deviceId = new DeviceId(CountlyState); +Countly.userProfile = new UserProfile(CountlyState); let _isCrashReportingEnabled = false; diff --git a/UserProfile.js b/UserProfile.js new file mode 100644 index 00000000..14c814a9 --- /dev/null +++ b/UserProfile.js @@ -0,0 +1,73 @@ +import * as L from "./Logger.js"; +import * as Validate from "./Validators.js"; + +class UserProfile { + #state; + + constructor(state) { + this.#state = state; + } + + setProperty = async function (keyName, keyValue) { + if (!this.#state.isInitialized) { + L.w("setProperty, 'init' must be called before 'setProperty'"); + return; + } + // Validate keyName + if (typeof keyName !== 'string' || keyName.trim() === '') { + L.w("setProperty, 'keyName' must be a valid non-empty string"); + return; + } + // Validate keyValue + if (keyValue === null || !Validate.isValidPrimitiveOrArray(keyValue)) { + L.w("setProperty, 'keyValue' must be a valid primitive type or an array of primitives"); + return; + } + + let formattedKeyValue = keyValue.toString(); + L.d(`setProperty, Setting user property: [${keyName}, ${formattedKeyValue}]`); + await this.#state.CountlyReactNative.userData_setProperty([keyName, formattedKeyValue]); + }; + + setProperties = async function (userData) { + // set predefined and custom user property + }; + + increment = async function (keyName) { + // Increment custom user data by 1 + }; + + incrementBy = async function (keyName, keyValue) { + // Increment custom user data by a specified value + }; + + multiply = async function (keyName, keyValue) { + // Multiply custom user data by a specified value + }; + + saveMax = async function (keyName, keyValue) { + // Save the max value between current and provided value. + }; + + saveMin = async function (keyName, keyValue) { + // Save the min value between current and provided value. + }; + + setOnce = async function (keyName, keyValue) { + // Set the property value if it does not exist. + }; + + pushUniqueValue = async function (keyName, keyValue) { + // Add value to custom property (array) if value does not exist within. + }; + + pushValue = async function (keyName, keyValue) { + // Add value to custom property (array). + }; + + pullValue = async function (keyName, keyValue) { + // Remove value to custom property (array). + }; +} + +export default UserProfile; \ No newline at end of file diff --git a/Validators.js b/Validators.js index 9f09e424..fe28069e 100644 --- a/Validators.js +++ b/Validators.js @@ -159,4 +159,17 @@ function areEventParametersValid(functionName, eventName, segmentation, eventCou return true; } -export { validateUserDataValue as UserDataValue, validateString as String, validateParseInt as ParseInt, validateValidUserData as ValidUserData, validateUserDataType as UserDataType, areEventParametersValid }; +function isValidPrimitiveOrArray(value) { + return ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' || + Array.isArray(value) && value.every(item => ( + typeof item === 'string' || + typeof item === 'number' || + typeof item === 'boolean' + )) + ); +}; + +export { validateUserDataValue as UserDataValue, validateString as String, validateParseInt as ParseInt, validateValidUserData as ValidUserData, validateUserDataType as UserDataType, areEventParametersValid, isValidPrimitiveOrArray }; From 3e2711b3ea59ce61150b5bbd82e2111eb96bbe56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20R=C4=B1za=20Kat?= Date: Tue, 5 Nov 2024 22:14:31 +0300 Subject: [PATCH 02/13] setProperties --- UserProfile.js | 51 ++++++++++++++++++- .../android/sdk/react/CountlyReactNative.java | 16 ++++++ ios/src/CountlyReactNative.m | 22 ++++++-- 3 files changed, 83 insertions(+), 6 deletions(-) diff --git a/UserProfile.js b/UserProfile.js index 14c814a9..c02bb9c5 100644 --- a/UserProfile.js +++ b/UserProfile.js @@ -30,8 +30,55 @@ class UserProfile { }; setProperties = async function (userData) { - // set predefined and custom user property - }; + if (!this.#state.isInitialized) { + L.w("setProperties, 'init' must be called before 'setProperties'"); + return; + } + if (!userData) { + L.w("setProperties, User profile data should not be null or undefined"); + return; + } + if (typeof userData !== "object") { + L.w(`setProperties, unsupported data type of user data '${typeof userData}'`); + return; + } + L.d(`setProperties, Setting properties: [${JSON.stringify(userData)}]`); + + const predefinedKeys = { + name: "string", + username: "string", + email: "string", + organization: "string", + phone: "string", + picture: "string", + picturePath: "string", + gender: "string", + byear: "number", + }; + + const userProfile = {}; + + for (const key in userData) { + const value = userData[key]; + const expectedType = predefinedKeys[key]; + + if (expectedType) { + if (typeof value === expectedType || (key === "byear" && typeof value === "number")) { + userProfile[key] = key === "byear" ? value.toString() : value; + } else { + L.w(`setProperties, skipping key '${key}' due to type mismatch (expected: ${expectedType}, got: ${typeof value})`); + } + } else { + if (Validate.isValidPrimitiveOrArray(value)) { + userProfile[key] = value; + } else { + L.w(`setProperties, skipping custom key '${key}' due to unsupported data type '${typeof value}'`); + } + } + } + + await this.#state.CountlyReactNative.setProperties([userProfile]); + }; increment = async function (keyName) { // Increment custom user data by 1 diff --git a/android/src/main/java/ly/count/android/sdk/react/CountlyReactNative.java b/android/src/main/java/ly/count/android/sdk/react/CountlyReactNative.java index d3207f0d..42a3728f 100644 --- a/android/src/main/java/ly/count/android/sdk/react/CountlyReactNative.java +++ b/android/src/main/java/ly/count/android/sdk/react/CountlyReactNative.java @@ -793,6 +793,22 @@ public void setUserData(ReadableArray args, Promise promise) { promise.resolve("Success"); } + @ReactMethod + public void setProperties(ReadableArray args, Promise promise) { + Countly.sharedInstance(); + ReadableMap userData = args.getMap(0); + Map userDataObjectMap = userData.toHashMap(); + + for (Map.Entry entry : userDataObjectMap.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + userDataObjectMap.put(key, value); + } + Countly.sharedInstance().userProfile().setProperties(userDataObjectMap); + Countly.sharedInstance().userProfile().save(); + promise.resolve("Success"); + } + @ReactMethod public void sendPushToken(ReadableArray args) { String pushToken = args.getString(0); diff --git a/ios/src/CountlyReactNative.m b/ios/src/CountlyReactNative.m index 923dd17d..e367db4d 100644 --- a/ios/src/CountlyReactNative.m +++ b/ios/src/CountlyReactNative.m @@ -311,10 +311,10 @@ - (void) populateConfig:(id) json { }); } -RCT_REMAP_METHOD(setUserData, params : (NSArray *)arguments setUserDataWithResolver : (RCTPromiseResolveBlock)resolve rejecter : (RCTPromiseRejectBlock)reject) { +RCT_REMAP_METHOD(setProperties, params : (NSArray *)arguments setPropertiesWithResolver : (RCTPromiseResolveBlock)resolve rejecter : (RCTPromiseRejectBlock)reject) { dispatch_async(dispatch_get_main_queue(), ^{ NSDictionary *userData = [arguments objectAtIndex:0]; - [self setUserDataIntenral:userData]; + [self setPropertiesInternal:userData]; [Countly.user save]; resolve(@"Success"); }); @@ -776,7 +776,7 @@ - (CLLocationCoordinate2D)getCoordinate:(NSString *)gpsCoordinate { RCT_REMAP_METHOD(userDataBulk_setUserProperties, params : (NSDictionary *)userProperties userDataBulkSetUserPropertiesWithResolver : (RCTPromiseResolveBlock)resolve rejecter : (RCTPromiseRejectBlock)reject) { dispatch_async(dispatch_get_main_queue(), ^{ - [self setUserDataIntenral:userProperties]; + [self setPropertiesInternal:userProperties]; NSDictionary *customeProperties = [self removePredefinedUserProperties:userProperties]; Countly.user.custom = customeProperties; resolve(@"Success"); @@ -1346,7 +1346,7 @@ - (NSDictionary *)removePredefinedUserProperties:(NSDictionary *__nullable)userD return userProperties; } -- (void)setUserDataIntenral:(NSDictionary *__nullable)userData { +- (void)setPropertiesInternal:(NSDictionary *__nullable)userData { NSString *name = userData[NAME_KEY]; NSString *username = userData[USERNAME_KEY]; NSString *email = userData[EMAIL_KEY]; @@ -1380,6 +1380,20 @@ - (void)setUserDataIntenral:(NSDictionary *__nullable)userData { if (byear) { Countly.user.birthYear = @([byear integerValue]); } + + // Handle custom fields + NSMutableDictionary *customFields = [NSMutableDictionary dictionary]; + NSArray *predefinedKeys = @[NAME_KEY, USERNAME_KEY, EMAIL_KEY, ORG_KEY, PHONE_KEY, PICTURE_KEY, GENDER_KEY, BYEAR_KEY]; + + for (NSString *key in userData) { + if (![predefinedKeys containsObject:key]) { + customFields[key] = userData[key]; + } + } + + if (customFields.count > 0) { + Countly.user.custom = customFields; + } } @end From 6414a11a53476cf4fd2e24237d9fecd569756054 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20R=C4=B1za=20Kat?= Date: Wed, 6 Nov 2024 00:08:47 +0300 Subject: [PATCH 03/13] All Calls and Comments Added --- Countly.d.ts | 102 +++++++++++++++++++++++++- UserProfile.js | 193 ++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 265 insertions(+), 30 deletions(-) diff --git a/Countly.d.ts b/Countly.d.ts index 0b29143f..6ce7a4c7 100644 --- a/Countly.d.ts +++ b/Countly.d.ts @@ -580,17 +580,113 @@ declare module "countly-sdk-react-native-bridge" { export function setUserData(userData: CountlyUserData): string | Promise; namespace userProfile { + /** + * + * Set custom key and value pair for the current user + * + * @param {string} keyName - user property key + * @param {object} keyValue - user property value + * @returns {void} + */ export function setProperty(keyName: string, keyValue: any): Promise; + + /** + * + * Set predefined and/or custom key and value pairs for the current user + * + * @param {object} userData - custom key value pairs + * @returns {void} + */ export function setProperties(userData: CountlyUserData): Promise; + + /** + * + * Increment custom user data by 1 + * + * @param {string} keyName - user property key + * @returns {void} + */ export function increment(keyName: string): Promise; + + /** + * + * Increment custom user data by a specified value + * + * @param {string} keyName - user property key + * @param {number} keyValue - value to increment user property by + * @returns {void} + */ export function incrementBy(keyName: string, keyValue: any): Promise; + + /** + * + * Multiply custom user data by a specified value + * + * @param {string} keyName - user property key + * @param {number} keyValue - value to multiply user property by + * @returns {void} + */ export function multiply(keyName: string, keyValue: any): Promise; + + /** + * + * Save the max value between current and provided value + * + * @param {string} keyName - user property key + * @param {number} keyValue - user property value + * @returns {void} + */ export function saveMax(keyName: string, keyValue: any): Promise; + + /** + * + * Save the min value between current and provided value + * + * @param {string} keyName - user property key + * @param {number} keyValue - user property value + * @returns {void} + */ export function saveMin(keyName: string, keyValue: any): Promise; + + /** + * + * Set the property value if it does not exist + * + * @param {string} keyName - user property key + * @param {string} keyValue - user property value + * @returns {void} + */ export function setOnce(keyName: string, keyValue: any): Promise; - export function pushUniqueValue(keyName: string, keyValue: any): Promise; - export function pushValue(keyName: string, keyValue: any): Promise; - export function pullValue(keyName: string, keyValue: any): Promise; + + /** + * + * Add value to custom property (array) if value does not exist within + * + * @param {string} keyName user property key + * @param {string} keyValue user property value + * @returns {void} + */ + export function pushUnique(keyName: string, keyValue: any): Promise; + + /** + * + * Add value to custom property (array) + * + * @param {string} keyName user property key + * @param {string} keyValue user property value + * @returns {void} + */ + export function push(keyName: string, keyValue: any): Promise; + + /** + * + * Remove value from custom property (array) + * + * @param {string} keyName user property key + * @param {string} keyValue user property value + * @returns {void} + */ + export function pull(keyName: string, keyValue: any): Promise; } namespace userData { diff --git a/UserProfile.js b/UserProfile.js index c02bb9c5..cb7ff3fb 100644 --- a/UserProfile.js +++ b/UserProfile.js @@ -8,27 +8,51 @@ class UserProfile { this.#state = state; } - setProperty = async function (keyName, keyValue) { + #isValidUserProfileCall = function (keyName, keyValue, functionName) { if (!this.#state.isInitialized) { - L.w("setProperty, 'init' must be called before 'setProperty'"); - return; + L.w(`${functionName}, 'init' must be called before ${functionName}`); + return false; + } + // validate keyName + if(!keyName) { + return false; } - // Validate keyName if (typeof keyName !== 'string' || keyName.trim() === '') { - L.w("setProperty, 'keyName' must be a valid non-empty string"); - return; + L.w(`${functionName}, provided keyName is not a valid string`); + return false; } - // Validate keyValue + // validate keyValue if (keyValue === null || !Validate.isValidPrimitiveOrArray(keyValue)) { - L.w("setProperty, 'keyValue' must be a valid primitive type or an array of primitives"); - return; + L.w(`${functionName}, provided keyValue is not valid`); + return false; } + return true; + } + /** + * + * Set custom key and value pair for the current user + * + * @param {string} keyName - user property key + * @param {object} keyValue - user property value + * @returns {void} + */ + setProperty = async function (keyName, keyValue) { + if(!isValidUserProfileCall(keyName, keyValue, "setProperty")) { + return; + } let formattedKeyValue = keyValue.toString(); L.d(`setProperty, Setting user property: [${keyName}, ${formattedKeyValue}]`); await this.#state.CountlyReactNative.userData_setProperty([keyName, formattedKeyValue]); }; + /** + * + * Set predefined and/or custom key and value pairs for the current user + * + * @param {object} userData - custom key value pairs + * @returns {void} + */ setProperties = async function (userData) { if (!this.#state.isInitialized) { L.w("setProperties, 'init' must be called before 'setProperties'"); @@ -43,7 +67,6 @@ class UserProfile { return; } L.d(`setProperties, Setting properties: [${JSON.stringify(userData)}]`); - const predefinedKeys = { name: "string", username: "string", @@ -55,13 +78,10 @@ class UserProfile { gender: "string", byear: "number", }; - const userProfile = {}; - for (const key in userData) { const value = userData[key]; const expectedType = predefinedKeys[key]; - if (expectedType) { if (typeof value === expectedType || (key === "byear" && typeof value === "number")) { userProfile[key] = key === "byear" ? value.toString() : value; @@ -76,44 +96,163 @@ class UserProfile { } } } - await this.#state.CountlyReactNative.setProperties([userProfile]); }; + /** + * + * Increment custom user data by 1 + * + * @param {string} keyName - user property key + * @returns {void} + */ increment = async function (keyName) { - // Increment custom user data by 1 + if (!this.#state.isInitialized) { + L.w("increment, 'init' must be called before 'increment'"); + return; + } + if (typeof keyName !== 'string' || keyName === null) { + L.w("increment, provided keyName is not a valid string"); + return; + } + L.d(`increment, Incrementing user property: [${keyName}]`); + await this.#state.CountlyReactNative.userData_increment([keyName]); }; + /** + * + * Increment custom user data by a specified value + * + * @param {string} keyName - user property key + * @param {number} keyValue - value to increment user property by + * @returns {void} + */ incrementBy = async function (keyName, keyValue) { - // Increment custom user data by a specified value - }; + if(!isValidUserProfileCall(keyName, keyValue, "incrementBy")) { + return; + } + L.d(`incrementBy, Incrementing user property: [${keyName}, ${keyValue}]`); + const intValue = parseInt(keyValue, 10).toString(); + await this.#state.CountlyReactNative.userData_incrementBy([keyName, intValue]); + }; + /** + * + * Multiply custom user data by a specified value + * + * @param {string} keyName - user property key + * @param {number} keyValue - value to multiply user property by + * @returns {void} + */ multiply = async function (keyName, keyValue) { - // Multiply custom user data by a specified value + if(!isValidUserProfileCall(keyName, keyValue, "multiply")) { + return; + } + L.d(`multiply, Multiplying user property: [${keyName}, ${keyValue}]`); + const intValue = parseInt(keyValue, 10).toString(); + await this.#state.CountlyReactNative.userData_multiply([keyName, intValue]); }; + /** + * + * Save the max value between current and provided value + * + * @param {string} keyName - user property key + * @param {number} keyValue - user property value + * @returns {void} + */ saveMax = async function (keyName, keyValue) { - // Save the max value between current and provided value. + if(!isValidUserProfileCall(keyName, keyValue, "saveMax")) { + return; + } + L.d(`saveMax, Saving max user property: [${keyName}, ${keyValue}]`); + const intValue = parseInt(keyValue, 10).toString(); + await this.#state.CountlyReactNative.userData_saveMax([keyName, intValue]); }; + /** + * + * Save the min value between current and provided value + * + * @param {string} keyName - user property key + * @param {number} keyValue - user property value + * @returns {void} + */ saveMin = async function (keyName, keyValue) { - // Save the min value between current and provided value. + if(!isValidUserProfileCall(keyName, keyValue, "saveMin")) { + return; + } + L.d(`saveMin, Saving min user property: [${keyName}, ${keyValue}]`); + const intValue = parseInt(keyValue, 10).toString(); + await this.#state.CountlyReactNative.userData_saveMin([keyName, intValue]); }; + /** + * + * Set the property value if it does not exist + * + * @param {string} keyName - user property key + * @param {string} keyValue - user property value + * @returns {void} + */ setOnce = async function (keyName, keyValue) { - // Set the property value if it does not exist. + if(!isValidUserProfileCall(keyName, keyValue, "setOnce")) { + return; + } + keyValue = keyValue.toString(); + L.d(`setOnce, Setting once user property: [${keyName}, ${keyValue}]`); + await this.#state.CountlyReactNative.userData_setOnce([keyName, keyValue]); }; - pushUniqueValue = async function (keyName, keyValue) { - // Add value to custom property (array) if value does not exist within. + /** + * + * Add value to custom property (array) if value does not exist within + * + * @param {string} keyName user property key + * @param {string} keyValue user property value + * @returns {void} + */ + pushUnique = async function (keyName, keyValue) { + if(!isValidUserProfileCall(keyName, keyValue, "pushUnique")) { + return; + } + keyValue = keyValue.toString(); + L.d(`pushUnique, Pushing unique value to user property: [${keyName}, ${keyValue}]`); + await this.#state.CountlyReactNative.userData_pushUniqueValue([keyName, keyValue]); }; - pushValue = async function (keyName, keyValue) { - // Add value to custom property (array). + /** + * + * Add value to custom property (array) + * + * @param {string} keyName user property key + * @param {string} keyValue user property value + * @returns {void} + */ + push = async function (keyName, keyValue) { + if(!isValidUserProfileCall(keyName, keyValue, "push")) { + return; + } + keyValue = keyValue.toString(); + L.d(`push, Pushing value to user property: [${keyName}, ${keyValue}]`); + await this.#state.CountlyReactNative.userData_pushValue([keyName, keyValue]); }; - pullValue = async function (keyName, keyValue) { - // Remove value to custom property (array). + /** + * + * Remove value from custom property (array) + * + * @param {string} keyName user property key + * @param {string} keyValue user property value + * @returns {void} + */ + pull = async function (keyName, keyValue) { + if(!isValidUserProfileCall(keyName, keyValue, "push")) { + return; + } + keyValue = keyValue.toString(); + L.d(`push, Pulling value from user property: [${keyName}, ${keyValue}]`); + await this.#state.CountlyReactNative.userData_pullValue([keyName, keyValue]); }; } From 2465bc6e332a23f4918aff1d2fc1c820249a405f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20R=C4=B1za=20Kat?= Date: Wed, 6 Nov 2024 00:11:30 +0300 Subject: [PATCH 04/13] Update UserProfile.js --- UserProfile.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UserProfile.js b/UserProfile.js index cb7ff3fb..bdbc6143 100644 --- a/UserProfile.js +++ b/UserProfile.js @@ -8,7 +8,7 @@ class UserProfile { this.#state = state; } - #isValidUserProfileCall = function (keyName, keyValue, functionName) { + isValidUserProfileCall = function (keyName, keyValue, functionName) { if (!this.#state.isInitialized) { L.w(`${functionName}, 'init' must be called before ${functionName}`); return false; @@ -111,7 +111,7 @@ class UserProfile { L.w("increment, 'init' must be called before 'increment'"); return; } - if (typeof keyName !== 'string' || keyName === null) { + if (!keyName || typeof keyName !== 'string') { L.w("increment, provided keyName is not a valid string"); return; } From 49181207a2b6ac5f0f9331bd3865ba67da9a9e1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20R=C4=B1za=20Kat?= Date: Wed, 6 Nov 2024 00:20:42 +0300 Subject: [PATCH 05/13] Deprecating Old Calls --- Countly.d.ts | 21 +++++++++++---------- Countly.js | 20 ++++++++++---------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/Countly.d.ts b/Countly.d.ts index 6ce7a4c7..761a3925 100644 --- a/Countly.d.ts +++ b/Countly.d.ts @@ -572,6 +572,7 @@ declare module "countly-sdk-react-native-bridge" { /** * + * @deprecated 'setUserData' is deprecated use 'Countly.userProfile.setProperties' instead. * Used to send user data * * @param {object} userData user data @@ -691,7 +692,7 @@ declare module "countly-sdk-react-native-bridge" { namespace userData { /** - * + * @deprecated 'Countly.userData.setProperty' is deprecated, use 'Countly.userProfile.setProperty' instead. * Set custom key and value pair for the current user. * * @param {string} keyName user property key @@ -701,7 +702,7 @@ declare module "countly-sdk-react-native-bridge" { export function setProperty(keyName: string, keyValue: any): Promise | string; /** - * + * @deprecated 'Countly.userData.increment' is deprecated, use 'Countly.userProfile.increment' instead. * Increment custom user data by 1 * * @param {string} keyName user property key @@ -710,7 +711,7 @@ declare module "countly-sdk-react-native-bridge" { export function increment(keyName: string): Promise | string; /** - * + * @deprecated 'Countly.userData.incrementBy' is deprecated, use 'Countly.userProfile.incrementBy' instead. * Increment custom user data by a specified value * * @param {string} keyName user property key @@ -720,7 +721,7 @@ declare module "countly-sdk-react-native-bridge" { export function incrementBy(keyName: string, keyValue: any): Promise | string; /** - * + * @deprecated 'Countly.userData.multiply' is deprecated, use 'Countly.userProfile.multiply' instead. * Multiply custom user data by a specified value * * @param {string} keyName user property key @@ -730,7 +731,7 @@ declare module "countly-sdk-react-native-bridge" { export function multiply(keyName: string, keyValue: any): Promise | string; /** - * + * @deprecated 'Countly.userData.saveMax' is deprecated, use 'Countly.userProfile.saveMax' instead. * Save the max value between current and provided value. * * @param {string} keyName user property key @@ -740,7 +741,7 @@ declare module "countly-sdk-react-native-bridge" { export function saveMax(keyName: string, keyValue: any): Promise | string; /** - * + * @deprecated 'Countly.userData.saveMin' is deprecated, use 'Countly.userProfile.saveMin' instead. * Save the min value between current and provided value. * * @param {string} keyName user property key @@ -750,7 +751,7 @@ declare module "countly-sdk-react-native-bridge" { export function saveMin(keyName: string, keyValue: any): Promise | string; /** - * + * @deprecated 'Countly.userData.setOnce' is deprecated, use 'Countly.userProfile.setOnce' instead. * Set the property value if it does not exist. * * @param {string} keyName user property key @@ -760,7 +761,7 @@ declare module "countly-sdk-react-native-bridge" { export function setOnce(keyName: string, keyValue: any): Promise | string; /** - * + * @deprecated 'Countly.userData.pushUniqueValue' is deprecated, use 'Countly.userProfile.pushUnique' instead. * Add value to custom property (array) if value does not exist within. * * @param {string} keyName user property key @@ -770,7 +771,7 @@ declare module "countly-sdk-react-native-bridge" { export function pushUniqueValue(keyName: string, keyValue: any): Promise | string; /** - * + * @deprecated 'Countly.userData.pushValue' is deprecated, use 'Countly.userProfile.push' instead. * Add value to custom property (array). * * @param {string} keyName user property key @@ -780,7 +781,7 @@ declare module "countly-sdk-react-native-bridge" { export function pushValue(keyName: string, keyValue: any): Promise | string; /** - * + * @deprecated 'Countly.userData.pullValue' is deprecated, use 'Countly.userProfile.pull' instead. * Remove value to custom property (array). * * @param {string} keyName user property key diff --git a/Countly.js b/Countly.js index 2ae30f0b..92efa683 100644 --- a/Countly.js +++ b/Countly.js @@ -837,7 +837,7 @@ Countly.setUserData = async function (userData) { }; /** - * + * @deprecated 'Countly.userData.setProperty' is deprecated, use 'Countly.userProfile.setProperty' instead. * Set custom key and value pair for the current user. * * @param {string} keyName user property key @@ -868,7 +868,7 @@ Countly.userData.setProperty = async function (keyName, keyValue) { }; /** - * + * @deprecated 'Countly.userData.increment' is deprecated, use 'Countly.userProfile.increment' instead. * Increment custom user data by 1 * * @param {string} keyName user property key @@ -892,7 +892,7 @@ Countly.userData.increment = async function (keyName) { }; /** - * + * @deprecated 'Countly.userData.incrementBy' is deprecated, use 'Countly.userProfile.incrementBy' instead. * Increment custom user data by a specified value * * @param {string} keyName user property key @@ -919,7 +919,7 @@ Countly.userData.incrementBy = async function (keyName, keyValue) { }; /** - * + * @deprecated 'Countly.userData.multiply' is deprecated, use 'Countly.userProfile.multiply' instead. * Multiply custom user data by a specified value * * @param {string} keyName user property key @@ -946,7 +946,7 @@ Countly.userData.multiply = async function (keyName, keyValue) { }; /** - * + * @deprecated 'Countly.userData.saveMax' is deprecated, use 'Countly.userProfile.saveMax' instead. * Save the max value between current and provided value. * * @param {string} keyName user property key @@ -973,7 +973,7 @@ Countly.userData.saveMax = async function (keyName, keyValue) { }; /** - * + * @deprecated 'Countly.userData.saveMin' is deprecated, use 'Countly.userProfile.saveMin' instead. * Save the min value between current and provided value. * * @param {string} keyName user property key @@ -1000,7 +1000,7 @@ Countly.userData.saveMin = async function (keyName, keyValue) { }; /** - * + * @deprecated 'Countly.userData.setOnce' is deprecated, use 'Countly.userProfile.setOnce' instead. * Set the property value if it does not exist. * * @param {string} keyName user property key @@ -1029,7 +1029,7 @@ Countly.userData.setOnce = async function (keyName, keyValue) { }; /** - * + * @deprecated 'Countly.userData.pushUniqueValue' is deprecated, use 'Countly.userProfile.pushUnique' instead. * Add value to custom property (array) if value does not exist within. * * @param {string} keyName user property key @@ -1058,7 +1058,7 @@ Countly.userData.pushUniqueValue = async function (keyName, keyValue) { }; /** - * + * @deprecated 'Countly.userData.pushValue' is deprecated, use 'Countly.userProfile.push' instead. * Add value to custom property (array). * * @param {string} keyName user property key @@ -1087,7 +1087,7 @@ Countly.userData.pushValue = async function (keyName, keyValue) { }; /** - * + * @deprecated 'Countly.userData.pullValue' is deprecated, use 'Countly.userProfile.pull' instead. * Remove value to custom property (array). * * @param {string} keyName user property key From fc4349b48ddf8f8e43a3cb8dd34c57358680e791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20R=C4=B1za=20Kat?= Date: Wed, 6 Nov 2024 14:39:23 +0300 Subject: [PATCH 06/13] Example Update and Small Error Fix --- UserProfile.js | 18 ++-- example/CountlyRNExample/UserProfiles.tsx | 110 ++++++++++++++++++---- 2 files changed, 103 insertions(+), 25 deletions(-) diff --git a/UserProfile.js b/UserProfile.js index bdbc6143..cbe0b3e0 100644 --- a/UserProfile.js +++ b/UserProfile.js @@ -38,7 +38,7 @@ class UserProfile { * @returns {void} */ setProperty = async function (keyName, keyValue) { - if(!isValidUserProfileCall(keyName, keyValue, "setProperty")) { + if(!this.isValidUserProfileCall(keyName, keyValue, "setProperty")) { return; } let formattedKeyValue = keyValue.toString(); @@ -128,7 +128,7 @@ class UserProfile { * @returns {void} */ incrementBy = async function (keyName, keyValue) { - if(!isValidUserProfileCall(keyName, keyValue, "incrementBy")) { + if(!this.isValidUserProfileCall(keyName, keyValue, "incrementBy")) { return; } L.d(`incrementBy, Incrementing user property: [${keyName}, ${keyValue}]`); @@ -145,7 +145,7 @@ class UserProfile { * @returns {void} */ multiply = async function (keyName, keyValue) { - if(!isValidUserProfileCall(keyName, keyValue, "multiply")) { + if(!this.isValidUserProfileCall(keyName, keyValue, "multiply")) { return; } L.d(`multiply, Multiplying user property: [${keyName}, ${keyValue}]`); @@ -162,7 +162,7 @@ class UserProfile { * @returns {void} */ saveMax = async function (keyName, keyValue) { - if(!isValidUserProfileCall(keyName, keyValue, "saveMax")) { + if(!this.isValidUserProfileCall(keyName, keyValue, "saveMax")) { return; } L.d(`saveMax, Saving max user property: [${keyName}, ${keyValue}]`); @@ -179,7 +179,7 @@ class UserProfile { * @returns {void} */ saveMin = async function (keyName, keyValue) { - if(!isValidUserProfileCall(keyName, keyValue, "saveMin")) { + if(!this.isValidUserProfileCall(keyName, keyValue, "saveMin")) { return; } L.d(`saveMin, Saving min user property: [${keyName}, ${keyValue}]`); @@ -196,7 +196,7 @@ class UserProfile { * @returns {void} */ setOnce = async function (keyName, keyValue) { - if(!isValidUserProfileCall(keyName, keyValue, "setOnce")) { + if(!this.isValidUserProfileCall(keyName, keyValue, "setOnce")) { return; } keyValue = keyValue.toString(); @@ -213,7 +213,7 @@ class UserProfile { * @returns {void} */ pushUnique = async function (keyName, keyValue) { - if(!isValidUserProfileCall(keyName, keyValue, "pushUnique")) { + if(!this.isValidUserProfileCall(keyName, keyValue, "pushUnique")) { return; } keyValue = keyValue.toString(); @@ -230,7 +230,7 @@ class UserProfile { * @returns {void} */ push = async function (keyName, keyValue) { - if(!isValidUserProfileCall(keyName, keyValue, "push")) { + if(!this.isValidUserProfileCall(keyName, keyValue, "push")) { return; } keyValue = keyValue.toString(); @@ -247,7 +247,7 @@ class UserProfile { * @returns {void} */ pull = async function (keyName, keyValue) { - if(!isValidUserProfileCall(keyName, keyValue, "push")) { + if(!this.isValidUserProfileCall(keyName, keyValue, "push")) { return; } keyValue = keyValue.toString(); diff --git a/example/CountlyRNExample/UserProfiles.tsx b/example/CountlyRNExample/UserProfiles.tsx index 55cec93e..4bd9a8d0 100644 --- a/example/CountlyRNExample/UserProfiles.tsx +++ b/example/CountlyRNExample/UserProfiles.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState } from 'react'; import { ScrollView } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; import Countly from "countly-sdk-react-native-bridge"; @@ -135,24 +135,102 @@ const userData_pullValue = () => { Countly.userData.pullValue("type", "morning"); }; +const onSetProperty = () => { + Countly.userProfile.setProperty("Griselda", ["Conway", "Benny", "Westside Gunn"]); +}; + +const onSetProperties = () => { + const userData = { + name: "John Doe", // predefined key, valid type (string) + username: "johndoe123", // predefined key, valid type (string) + email: "john.doe@example.com", // predefined key, valid type (string) + organization: "Example Corp", // predefined key, valid type (string) + phone: "+1234567890", // predefined key, valid type (string) + picture: "https://example.com/avatar.jpg", // predefined key, valid type (string) + gender: "M", // predefined key, valid type (string) + byear: 1985, // predefined key, valid type (integer) + customKey1: "Custom Value 1", // custom key + customKey2: 42, // custom key + customKey3: ["item1", "item2"], // custom key (array) + }; + Countly.userProfile.setProperties(userData); +} + +const onIncrement = () => { + Countly.userProfile.increment("IncrementValue"); +} + +const onIncrementBy = () => { + Countly.userProfile.incrementBy("IncrementByValue", 2); +} + +const onMultiply = () => { + Countly.userProfile.multiply("MultiplyValue", 3); +} + +const onMax = () => { + Countly.userProfile.saveMax("Max", 4); +} + +const onMin = () => { + Countly.userProfile.saveMin("Min", 5); +} + +const onSetOnce = () => { + Countly.userProfile.setOnce("Once", 6); +} + +const onPushUnique = () => { + Countly.userProfile.pushUnique("Unique", 7); +} + +const onPush = () => { + Countly.userProfile.push("push", 8); +} + +const onPull = () => { + Countly.userProfile.pull("pull", 9); +} + function UserProfilesScreen({ navigation }) { - return ( + const [showDeprecated, setShowDeprecated] = useState(false); + + const toggleDeprecated = () => { + setShowDeprecated(prevState => !prevState); + }; + return ( - - - - - - - - - - - - - - + + + + + + + + + + + + + {showDeprecated && ( + + + + + + + + + + + + + + + + + )} ); From 18f0c9abc5c7ee1a4614c0628283188ac5cdef8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20R=C4=B1za=20Kat?= Date: Wed, 6 Nov 2024 14:43:12 +0300 Subject: [PATCH 07/13] Deprecating userData --- Countly.d.ts | 3 +++ Countly.js | 3 +++ 2 files changed, 6 insertions(+) diff --git a/Countly.d.ts b/Countly.d.ts index 761a3925..ac1613a9 100644 --- a/Countly.d.ts +++ b/Countly.d.ts @@ -690,6 +690,9 @@ declare module "countly-sdk-react-native-bridge" { export function pull(keyName: string, keyValue: any): Promise; } + /** + * @deprecated Countly.userData is deprecated, use Countly.userProfile instead + */ namespace userData { /** * @deprecated 'Countly.userData.setProperty' is deprecated, use 'Countly.userProfile.setProperty' instead. diff --git a/Countly.js b/Countly.js index 92efa683..9e05293c 100644 --- a/Countly.js +++ b/Countly.js @@ -33,6 +33,9 @@ Countly.userProfile = new UserProfile(CountlyState); let _isCrashReportingEnabled = false; +/** + * @deprecated Countly.userData is deprecated, use Countly.userProfile instead + */ Countly.userData = {}; // userData interface Countly.userDataBulk = {}; // userDataBulk interface From 5af6b4408e3fc975f6e8a30979f410f8afa823ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20R=C4=B1za=20Kat?= Date: Wed, 6 Nov 2024 14:50:35 +0300 Subject: [PATCH 08/13] Changelog Update --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 549677d2..296e6a55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,18 @@ * `enterContentZone`, to start Content checks (Experimental!) * `exitContentZone`, to stop Content checks (Experimental!) * Added `Countly.deviceId.setID` method for changing device ID based on the device ID type +* Deprecated `Countly.setUserData` and replaced with `Countly.userProfile.setProperties` +* Deprecated `Countly.userData` and replaced with `Countly.userProfile`, also following calls are deprecated and replaced: + * `Countly.userData.setProperty` (replaced with: `Countly.userProfile.setProperty`) + * `Countly.userData.increment` (replaced with: `Countly.userProfile.increment`) + * `Countly.userData.incrementBy` (replaced with: `Countly.userProfile.incrementBy`) + * `Countly.userData.multiply` (replaced with: `Countly.userProfile.multiply`) + * `Countly.userData.saveMax` (replaced with: `Countly.userProfile.saveMax`) + * `Countly.userData.saveMin` (replaced with: `Countly.userProfile.saveMin`) + * `Countly.userData.setOnce` (replaced with: `Countly.userProfile.setOnce`) + * `Countly.userData.pushUniqueValue` (replaced with: `Countly.userProfile.pushUnique`) + * `Countly.userData.pushValue` (replaced with: `Countly.userProfile.push`) + * `Countly.userData.pullValue` (replaced with: `Countly.userProfile.pull`) * Mitigated an issue where a session could have started while the app was in the background when the device ID was changed (non-merge). * Deprecated following SDK calls: * `Countly.getCurrentDeviceId` (replaced with: `Countly.deviceId.getID`) From ff94371a8b2fc8456a2528753a94fa9943e24e42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20R=C4=B1za=20Kat?= Date: Fri, 8 Nov 2024 14:29:00 +0300 Subject: [PATCH 09/13] Some Parts Updated --- UserProfile.js | 18 +++++++++--------- .../android/sdk/react/CountlyReactNative.java | 1 - 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/UserProfile.js b/UserProfile.js index cbe0b3e0..c80759cf 100644 --- a/UserProfile.js +++ b/UserProfile.js @@ -191,8 +191,8 @@ class UserProfile { * * Set the property value if it does not exist * - * @param {string} keyName - user property key - * @param {string} keyValue - user property value + * @param {string} keyName - The user property key. + * @param {boolean | number | string} keyValue - The user property value. * @returns {void} */ setOnce = async function (keyName, keyValue) { @@ -208,8 +208,8 @@ class UserProfile { * * Add value to custom property (array) if value does not exist within * - * @param {string} keyName user property key - * @param {string} keyValue user property value + * @param {string} keyName - The user property key. + * @param {boolean | number | string} keyValue - The user property value. * @returns {void} */ pushUnique = async function (keyName, keyValue) { @@ -223,10 +223,10 @@ class UserProfile { /** * - * Add value to custom property (array) + * Add a value to a custom property (array). * - * @param {string} keyName user property key - * @param {string} keyValue user property value + * @param {string} keyName - The user property key. + * @param {boolean | number | string} keyValue - The user property value. * @returns {void} */ push = async function (keyName, keyValue) { @@ -242,8 +242,8 @@ class UserProfile { * * Remove value from custom property (array) * - * @param {string} keyName user property key - * @param {string} keyValue user property value + * @param {string} keyName - The user property key. + * @param {boolean | number | string} keyValue - The user property value. * @returns {void} */ pull = async function (keyName, keyValue) { diff --git a/android/src/main/java/ly/count/android/sdk/react/CountlyReactNative.java b/android/src/main/java/ly/count/android/sdk/react/CountlyReactNative.java index 42a3728f..1c2db3a8 100644 --- a/android/src/main/java/ly/count/android/sdk/react/CountlyReactNative.java +++ b/android/src/main/java/ly/count/android/sdk/react/CountlyReactNative.java @@ -805,7 +805,6 @@ public void setProperties(ReadableArray args, Promise promise) { userDataObjectMap.put(key, value); } Countly.sharedInstance().userProfile().setProperties(userDataObjectMap); - Countly.sharedInstance().userProfile().save(); promise.resolve("Success"); } From 41e9221a7294ee583d9fde0e1d95f35add1dba0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20R=C4=B1za=20Kat?= Date: Tue, 12 Nov 2024 17:29:37 +0300 Subject: [PATCH 10/13] Update UserProfile.js --- UserProfile.js | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/UserProfile.js b/UserProfile.js index c80759cf..c5c0190e 100644 --- a/UserProfile.js +++ b/UserProfile.js @@ -8,6 +8,18 @@ class UserProfile { this.#state = state; } + static predefinedKeys = { + name: "string", + username: "string", + email: "string", + organization: "string", + phone: "string", + picture: "string", + picturePath: "string", + gender: "string", + byear: "number", + }; + isValidUserProfileCall = function (keyName, keyValue, functionName) { if (!this.#state.isInitialized) { L.w(`${functionName}, 'init' must be called before ${functionName}`); @@ -15,6 +27,7 @@ class UserProfile { } // validate keyName if(!keyName) { + L.w(`${functionName}, provided keyName is null`); return false; } if (typeof keyName !== 'string' || keyName.trim() === '') { @@ -67,21 +80,10 @@ class UserProfile { return; } L.d(`setProperties, Setting properties: [${JSON.stringify(userData)}]`); - const predefinedKeys = { - name: "string", - username: "string", - email: "string", - organization: "string", - phone: "string", - picture: "string", - picturePath: "string", - gender: "string", - byear: "number", - }; const userProfile = {}; for (const key in userData) { const value = userData[key]; - const expectedType = predefinedKeys[key]; + const expectedType = this.predefinedKeys[key]; if (expectedType) { if (typeof value === expectedType || (key === "byear" && typeof value === "number")) { userProfile[key] = key === "byear" ? value.toString() : value; @@ -97,7 +99,7 @@ class UserProfile { } } await this.#state.CountlyReactNative.setProperties([userProfile]); - }; + }; /** * From d1f72cdbd1b172a7a6b5a9f613fabfe561d4d1e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20R=C4=B1za=20Kat?= Date: Tue, 12 Nov 2024 17:36:02 +0300 Subject: [PATCH 11/13] Removed Array Check --- UserProfile.js | 6 +++--- Validators.js | 15 +-------------- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/UserProfile.js b/UserProfile.js index c5c0190e..7a3f2d2c 100644 --- a/UserProfile.js +++ b/UserProfile.js @@ -35,7 +35,7 @@ class UserProfile { return false; } // validate keyValue - if (keyValue === null || !Validate.isValidPrimitiveOrArray(keyValue)) { + if (keyValue === null) { L.w(`${functionName}, provided keyValue is not valid`); return false; } @@ -91,10 +91,10 @@ class UserProfile { L.w(`setProperties, skipping key '${key}' due to type mismatch (expected: ${expectedType}, got: ${typeof value})`); } } else { - if (Validate.isValidPrimitiveOrArray(value)) { + if (value) { userProfile[key] = value; } else { - L.w(`setProperties, skipping custom key '${key}' due to unsupported data type '${typeof value}'`); + L.w(`setProperties, skipping custom key '${key}' due to provided value is null`); } } } diff --git a/Validators.js b/Validators.js index fe28069e..9f09e424 100644 --- a/Validators.js +++ b/Validators.js @@ -159,17 +159,4 @@ function areEventParametersValid(functionName, eventName, segmentation, eventCou return true; } -function isValidPrimitiveOrArray(value) { - return ( - typeof value === 'string' || - typeof value === 'number' || - typeof value === 'boolean' || - Array.isArray(value) && value.every(item => ( - typeof item === 'string' || - typeof item === 'number' || - typeof item === 'boolean' - )) - ); -}; - -export { validateUserDataValue as UserDataValue, validateString as String, validateParseInt as ParseInt, validateValidUserData as ValidUserData, validateUserDataType as UserDataType, areEventParametersValid, isValidPrimitiveOrArray }; +export { validateUserDataValue as UserDataValue, validateString as String, validateParseInt as ParseInt, validateValidUserData as ValidUserData, validateUserDataType as UserDataType, areEventParametersValid }; From a2bf6f5489561111f7d7a021e18072b388d5d52e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20R=C4=B1za=20Kat?= Date: Tue, 12 Nov 2024 17:36:43 +0300 Subject: [PATCH 12/13] Update UserProfile.js --- UserProfile.js | 1 - 1 file changed, 1 deletion(-) diff --git a/UserProfile.js b/UserProfile.js index 7a3f2d2c..94739622 100644 --- a/UserProfile.js +++ b/UserProfile.js @@ -1,5 +1,4 @@ import * as L from "./Logger.js"; -import * as Validate from "./Validators.js"; class UserProfile { #state; From efeac956eca71c1d3ef78b71c50dc8bcd0c34402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20R=C4=B1za=20Kat?= Date: Tue, 12 Nov 2024 18:05:15 +0300 Subject: [PATCH 13/13] Updated Checks --- UserProfile.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/UserProfile.js b/UserProfile.js index 94739622..393eeea6 100644 --- a/UserProfile.js +++ b/UserProfile.js @@ -82,7 +82,13 @@ class UserProfile { const userProfile = {}; for (const key in userData) { const value = userData[key]; - const expectedType = this.predefinedKeys[key]; + let expectedType = null; + + // To check if the key exists in predefinedKeys + if (Object.hasOwn(UserProfile.predefinedKeys, key)) { + expectedType = UserProfile.predefinedKeys[key]; + } + if (expectedType) { if (typeof value === expectedType || (key === "byear" && typeof value === "number")) { userProfile[key] = key === "byear" ? value.toString() : value; @@ -90,11 +96,7 @@ class UserProfile { L.w(`setProperties, skipping key '${key}' due to type mismatch (expected: ${expectedType}, got: ${typeof value})`); } } else { - if (value) { - userProfile[key] = value; - } else { - L.w(`setProperties, skipping custom key '${key}' due to provided value is null`); - } + userProfile[key] = value; } } await this.#state.CountlyReactNative.setProperties([userProfile]);