UNPKG

mixpanel-react-native

Version:

Official React Native Tracking Library for Mixpanel Analytics

997 lines (924 loc) 33.5 kB
"use strict"; import {Platform, NativeModules} from "react-native"; import packageJson from "./package.json"; const {MixpanelReactNative} = NativeModules; import MixpanelMain from "mixpanel-react-native/javascript/mixpanel-main" const DevicePlatform = { Unknown: "Unknown", Android: "android", iOS: "ios", }; const ERROR_MESSAGE = { INVALID_OBJECT: " is not a valid json object", INVALID_STRING: " is not a valid string", REQUIRED_DOUBLE: " is not a valid number", }; const PARAMS = { TOKEN: "token", DISTINCT_ID: "distinctId", ALIAS: "alias", EVENT_NAME: "eventName", GROUP_KEY: "groupKey", PROPERTIES: "properties", PROPERTY_NAME: "propertyName", PROP: "prop", NAME: "name", CHARGE: "charge", PROPERTY_VALUE: "property value", }; const DEFAULT_OPT_OUT = false; /** * The primary class for integrating Mixpanel with your app. */ export class Mixpanel { constructor(token, trackAutomaticEvents, useNative = true, storage) { if (!StringHelper.isValid(token)) { StringHelper.raiseError(PARAMS.TOKEN); } if (trackAutomaticEvents == null) { throw new Error(`trackAutomaticEvents is undefined`); } this.token = token; this.trackAutomaticEvents = trackAutomaticEvents; if (useNative && MixpanelReactNative) { this.mixpanelImpl = MixpanelReactNative; return; } else if (useNative) { console.warn( "MixpanelReactNative is not available; using JavaScript mode. If you prefer not to use the JavaScript mode, please follow the guide in the GitHub repository: https://github.com/mixpanel/mixpanel-react-native." ); } this.mixpanelImpl = new MixpanelMain(token, trackAutomaticEvents, storage); } /** * Initializes Mixpanel * * @param {boolean} optOutTrackingDefault Optional Whether or not Mixpanel can start tracking by default. See optOutTracking() * @param {object} superProperties Optional A Map containing the key value pairs of the super properties to register * @param {string} serverURL Optional Set the base URL used for Mixpanel API requests. See setServerURL() * */ async init( optOutTrackingDefault = DEFAULT_OPT_OUT, superProperties = {}, serverURL = "https://api.mixpanel.com" ) { await this.mixpanelImpl.initialize( this.token, this.trackAutomaticEvents, optOutTrackingDefault, {...Helper.getMetaData(), ...superProperties}, serverURL ); } /** * @deprecated since version 1.3.0. To initialize Mixpanel, please use the instance method `init` instead. See the example below: * * <pre><code> * const trackAutomaticEvents = true; * const mixpanel = new Mixpanel('your project token', trackAutomaticEvents); * mixpanel.init(); * </code></pre> * * Initializes Mixpanel and return an instance of Mixpanel the given project token. * * @param {string} token your project token. * @param {boolean} trackAutomaticEvents Whether or not to automatically track common mobile events * @param {boolean} Optional Whether or not Mixpanel can start tracking by default. See optOutTracking() * */ static async init( token, trackAutomaticEvents, optOutTrackingDefault = DEFAULT_OPT_OUT ) { await MixpanelReactNative.initialize( token, trackAutomaticEvents, optOutTrackingDefault, Helper.getMetaData(), "https://api.mixpanel.com" ); return new Mixpanel(token, trackAutomaticEvents); } /** * Set the base URL used for Mixpanel API requests. * Useful if you need to proxy Mixpanel requests. Defaults to https://api.mixpanel.com. * To route data to Mixpanel's EU servers, set to https://api-eu.mixpanel.com * * @param {string} serverURL the base URL used for Mixpanel API requests * */ setServerURL(serverURL) { this.mixpanelImpl.setServerURL(this.token, serverURL); } /** * This allows enabling or disabling of all Mixpanel logs at run time. * All logging is disabled by default. Usually, this is only required if * you are running into issues with the SDK that you want to debug * * @param {boolean} loggingEnabled whether to enable logging * */ setLoggingEnabled(loggingEnabled) { this.mixpanelImpl.setLoggingEnabled(this.token, loggingEnabled); } /** * This allows enabling or disabling whether or not Mixpanel flushes events * when the app enters the background on iOS. This is set to true by default. * * @param {boolean} flushOnBackground whether to enable logging * */ setFlushOnBackground(flushOnBackground) { if (Platform.OS === "ios") { MixpanelReactNative.setFlushOnBackground(this.token, flushOnBackground); } else { console.warn( "Mixpanel setFlushOnBackground was called and ignored because this method only works on iOS." ); } } /** * This controls whether to automatically send the client IP Address as part of event tracking. * With an IP address, geo-location is possible down to neighborhoods within a city, * although the Mixpanel Dashboard will just show you city level location specificity. * * @param {boolean} useIpAddressForGeolocation whether to automatically send the client IP Address. * Defaults to true. * */ setUseIpAddressForGeolocation(useIpAddressForGeolocation) { this.mixpanelImpl.setUseIpAddressForGeolocation( this.token, useIpAddressForGeolocation ); } /** * Set the number of events sent in a single network request to the Mixpanel server. * By configuring this value, you can optimize network usage and manage the frequency of communication between the client and the server. The maximum size is 50; any value over 50 will default to 50. * * @param {integer} flushBatchSize whether to automatically send the client IP Address. * Defaults to true. * */ setFlushBatchSize(flushBatchSize) { this.mixpanelImpl.setFlushBatchSize(this.token, flushBatchSize); } /** * Will return true if the user has opted out from tracking. * * @return {Promise<boolean>} true if user has opted out from tracking. Defaults to false. */ hasOptedOutTracking() { return this.mixpanelImpl.hasOptedOutTracking(this.token); } /** * Use this method to opt-in an already opted-out user from tracking. People updates and track * calls will be sent to Mixpanel after using this method. * This method will internally track an opt-in event to your project. * */ optInTracking() { this.mixpanelImpl.optInTracking(this.token); } /** * Use this method to opt-out a user from tracking. Events and people updates that haven't been * flushed yet will be deleted. Use flush() before calling this method if you want * to send all the queues to Mixpanel before. * * This method will also remove any user-related information from the device. */ optOutTracking() { this.mixpanelImpl.optOutTracking(this.token); } /** * Associate all future calls to track() with the user identified by * the given distinct id. * * <p>Calls to track() made before corresponding calls to identify * will use an anonymous locally generated distinct id, which means it is best to call identify * early to ensure that your Mixpanel funnels and retention analytics can continue to track the * user throughout their lifetime. We recommend calling identify when the user authenticates. * * <p>Once identify is called, the local distinct id persists across restarts of * your application. * * @param {string} distinctId a string uniquely identifying this user. Events sent to * Mixpanel using the same disinct_id will be considered associated with the * same visitor/customer for retention and funnel reporting, so be sure that the given * value is globally unique for each individual user you intend to track. * @returns {Promise} A promise that resolves when the identify is successful. * It does not return any value. * */ identify(distinctId) { return new Promise((resolve, reject) => { if (!StringHelper.isValid(distinctId)) { StringHelper.raiseError(PARAMS.DISTINCT_ID); reject(new Error("Invalid distinctId")); } this.mixpanelImpl .identify(this.token, distinctId) .then(() => { resolve(); }) .catch((err) => { reject(err); }); }); } /** * @deprecated The alias method creates an alias which Mixpanel will use to remap one id to another. * Multiple aliases can point to the same identifier. * * `mixpane.alias("New ID", mixpane.distinctId)` * `mixpane.alias("Newer ID", mixpane.distinctId)` * * <p>This call does not identify the user after. You must still call identify() * if you wish the new alias to be used for Events and People. * * @param {string} alias A unique identifier that you want to use as an identifier for this user. * @param {string} distinctId the current distinct_id that alias will be mapped to. */ alias(alias, distinctId) { if (!StringHelper.isValid(alias)) { StringHelper.raiseError(PARAMS.ALIAS); } if (!StringHelper.isValid(distinctId)) { StringHelper.raiseError(PARAMS.DISTINCT_ID); } this.mixpanelImpl.alias(this.token, alias, distinctId); } /** * Track an event. * * <p>Every call to track eventually results in a data point sent to Mixpanel. These data points * are what are measured, counted, and broken down to create your Mixpanel reports. Events * have a string name, and an optional set of name/value pairs that describe the properties of * that event. * * @param {string} eventName The name of the event to send * @param {object} properties A Map containing the key value pairs of the properties to include in this event. * Pass null if no extra properties exist. */ track(eventName, properties) { if (!StringHelper.isValid(eventName)) { StringHelper.raiseError(PARAMS.EVENT_NAME); } if (!ObjectHelper.isValidOrUndefined(properties)) { ObjectHelper.raiseError(PARAMS.PROPERTIES); } this.mixpanelImpl.track(this.token, eventName, { ...Helper.getMetaData(), ...properties, }); } /** * Returns a Mixpanel People object that can be used to set and increment * People Analytics properties. * * @return {People} an instance of People that you can use to update * records in Mixpanel People Analytics */ getPeople() { if (this.people) { return this.people; } else { this.people = new People(this.token, this.mixpanelImpl); return this.people; } } /** * Track an event with specific groups. * * <p>Every call to track eventually results in a data point sent to Mixpanel. These data points * are what are measured, counted, and broken down to create your Mixpanel reports. Events * have a string name, and an optional set of name/value pairs that describe the properties of * that event. Group key/value pairs are upserted into the property map before tracking. * * @param {string} eventName The name of the event to send * @param {object} properties A Map containing the key value pairs of the properties to include in this event. * Pass null if no extra properties exist. * @param {object} groups A Map containing the group key value pairs for this event. * */ trackWithGroups(eventName, properties, groups) { if (!StringHelper.isValid(eventName)) { StringHelper.raiseError(PARAMS.EVENT_NAME); } if (!ObjectHelper.isValidOrUndefined(properties)) { ObjectHelper.raiseError(PARAMS.PROPERTIES); } this.mixpanelImpl.trackWithGroups( this.token, eventName, { ...Helper.getMetaData(), ...properties, }, groups ); } /** * Set the group this user belongs to. * * @param {string} groupKey The property name associated with this group type (must already have been set up). * @param {object} groupID The group the user belongs to. */ setGroup(groupKey, groupID) { if (!StringHelper.isValid(groupKey)) { StringHelper.raiseError(PARAMS.GROUP_KEY); } this.mixpanelImpl.setGroup(this.token, groupKey, groupID); } /** * Returns a MixpanelGroup object that can be used to set and increment * Group Analytics properties. * * @param {string} groupKey String identifying the type of group (must be already in use as a group key) * @param {object} groupID Object identifying the specific group * @return an instance of MixpanelGroup that you can use to update * records in Mixpanel Group Analytics */ getGroup(groupKey, groupID) { if (this.group) { return this.group; } else { this.group = new MixpanelGroup( this.token, groupKey, groupID, this.mixpanelImpl ); return this.group; } } /** * Add a group to this user's membership for a particular group key * * @param {string} groupKey The property name associated with this group type (must already have been set up). * @param {object} groupID The new group the user belongs to. */ addGroup(groupKey, groupID) { if (!StringHelper.isValid(groupKey)) { StringHelper.raiseError(PARAMS.GROUP_KEY); } this.mixpanelImpl.addGroup(this.token, groupKey, groupID); } /** * Remove a group from this user's membership for a particular group key * * @param {string} groupKey The property name associated with this group type (must already have been set up). * @param {object} groupID The group value to remove. */ removeGroup(groupKey, groupID) { if (!StringHelper.isValid(groupKey)) { StringHelper.raiseError(PARAMS.GROUP_KEY); } this.mixpanelImpl.removeGroup(this.token, groupKey, groupID); } /** * Permanently deletes this group's record from Group Analytics. * * @param {string} groupKey String identifying the type of group (must be already in use as a group key) * @param {object} groupID Object identifying the specific group * <p>Calling deleteGroup deletes an entire record completely. Any future calls * to Group Analytics using the same group value will create and store new values. */ deleteGroup(groupKey, groupID) { if (!StringHelper.isValid(groupKey)) { StringHelper.raiseError(PARAMS.GROUP_KEY); } this.mixpanelImpl.deleteGroup(this.token, groupKey, groupID); } /** * Register properties that will be sent with every subsequent call to track(). * * <p>SuperProperties are a collection of properties that will be sent with every event to Mixpanel, * and persist beyond the lifetime of your application. * * <p>Setting a superProperty with registerSuperProperties will store a new superProperty, * possibly overwriting any existing superProperty with the same name (to set a * superProperty only if it is currently unset, use registerSuperPropertiesOnce()) * * <p>SuperProperties will persist even if your application is taken completely out of memory. * to remove a superProperty, call unregisterSuperProperty() or clearSuperProperties() * * @param {object} properties A Map containing super properties to register */ registerSuperProperties(properties) { if (!ObjectHelper.isValidOrUndefined(properties)) { ObjectHelper.raiseError(PARAMS.PROPERTIES); } this.mixpanelImpl.registerSuperProperties(this.token, properties || {}); } /** * Register super properties for events, only if no other super property with the * same names has already been registered. * * <p>Calling registerSuperPropertiesOnce will never overwrite existing properties. * * @param {object} properties A Map containing the super properties to register. */ registerSuperPropertiesOnce(properties) { if (!ObjectHelper.isValidOrUndefined(properties)) { ObjectHelper.raiseError(PARAMS.PROPERTIES); } this.mixpanelImpl.registerSuperPropertiesOnce(this.token, properties || {}); } /** * Remove a single superProperty, so that it will not be sent with future calls to track(). * * <p>If there is a superProperty registered with the given name, it will be permanently * removed from the existing superProperties. * To clear all superProperties, use clearSuperProperties() * * @param {string} propertyName name of the property to unregister */ unregisterSuperProperty(propertyName) { if (!StringHelper.isValid(propertyName)) { StringHelper.raiseError(PARAMS.PROPERTY_NAME); } this.mixpanelImpl.unregisterSuperProperty(this.token, propertyName); } /** * Returns a json object of the user's current super properties * *<p>SuperProperties are a collection of properties that will be sent with every event to Mixpanel, * and persist beyond the lifetime of your application. * * @return {Promise<object>} Super properties for this Mixpanel instance. */ getSuperProperties() { return this.mixpanelImpl.getSuperProperties(this.token); } /** * Erase all currently registered superProperties. * * <p>Future tracking calls to Mixpanel will not contain the specific * superProperties registered before the clearSuperProperties method was called. * * <p>To remove a single superProperty, use unregisterSuperProperty() */ clearSuperProperties() { this.mixpanelImpl.clearSuperProperties(this.token); } /** * Begin timing of an event. Calling timeEvent("Thing") will not send an event, but * when you eventually call track("Thing"), your tracked event will be sent with a "$duration" * property, representing the number of seconds between your calls. * * @param {string} eventName the name of the event to track with timing. */ timeEvent(eventName) { if (!StringHelper.isValid(eventName)) { StringHelper.raiseError(PARAMS.EVENT_NAME); } this.mixpanelImpl.timeEvent(this.token, eventName); } /** * Retrieves the time elapsed for the named event since timeEvent() was called. * * @param {string} eventName the name of the event to be tracked that was previously called with timeEvent() * * @return {Promise<number>} Time elapsed since timeEvent(String) was called for the given eventName. */ eventElapsedTime(eventName) { if (!StringHelper.isValid(eventName)) { StringHelper.raiseError(PARAMS.EVENT_NAME); } return this.mixpanelImpl.eventElapsedTime(this.token, eventName); } /** Clear super properties and generates a new random distinctId for this instance. Useful for clearing data when a user logs out. */ reset() { this.mixpanelImpl.reset(this.token); } /** * Returns the current distinct id of the user. * This is either the id automatically generated by the library or the id that has been passed by a call to identify(). * * example of usage: * <pre> * <code> * const distinctId = await mixpanel.getDistinctId(); * </code> * </pre> * * @return {Promise<string>} A Promise to the distinct id associated with Mixpanel event and People Analytics * */ getDistinctId() { return this.mixpanelImpl.getDistinctId(this.token); } /** * Returns the current device id of the device. * This id automatically generated by the library and regenerated when logout or reset is called. * * example of usage: * <pre> * <code> * const deviceId = await mixpanel.getDeviceId(); * </code> * </pre> * * @return {Promise<string>} A Promise to the device id * */ getDeviceId() { return this.mixpanelImpl.getDeviceId(this.token); } /** * Push all queued Mixpanel events and People Analytics changes to Mixpanel servers. * * <p>Events and People messages are pushed gradually throughout * the lifetime of your application. This means that to ensure that all messages * are sent to Mixpanel when your application is shut down, you will * need to call flush() to let the Mixpanel library know it should * send all remaining messages to the server. */ flush() { this.mixpanelImpl.flush(this.token); } } /** * Core class for using Mixpanel People Analytics features. * * <p>The People object is used to update properties in a user's People Analytics record, * and to manage the receipt of push notifications sent via Mixpanel Engage. * For this reason, it's important to call identify(String) on the People * object before you work with it. Once you call identify, the user identity will * persist across stops and starts of your application, until you make another * call to identify using a different id. * */ export class People { constructor(token, mixpanelImpl) { if (!StringHelper.isValid(token)) { StringHelper.raiseError(PARAMS.TOKEN); } this.token = token; this.mixpanelImpl = mixpanelImpl; } /** * Sets a single property with the given name and value for this user. * The given name and value will be assigned to the user in Mixpanel People Analytics, * possibly overwriting an existing property with the same name. * * @param {string} prop The name of the Mixpanel property. This must be a String, for example "Zip Code" * @param {object} to The value of the Mixpanel property. For "Zip Code", this value might be the String "90210" */ set(prop, to) { let properties = {}; if (ObjectHelper.isValid(prop)) { properties = JSON.parse(JSON.stringify(prop || {})); } else { if (!StringHelper.isValid(prop)) { StringHelper.raiseError(PARAMS.PROP); } properties[prop] = to; } this.mixpanelImpl.set(this.token, properties); } /** * Works just like set(), except it will not overwrite existing property values. This is useful for properties like "First login date". * * @param {string} prop The name of the Mixpanel property. This must be a String, for example "Zip Code" * @param {object} to The value of the Mixpanel property. For "Zip Code", this value might be the String "90210" */ setOnce(prop, to) { let properties = {}; if (ObjectHelper.isValid(prop)) { prop = prop || {}; properties = JSON.parse(JSON.stringify(prop)); } else { if (!StringHelper.isValid(prop)) { StringHelper.raiseError(PARAMS.PROP); } properties[prop] = to; } this.mixpanelImpl.setOnce(this.token, properties); } /** * Add the given amount to an existing property on the identified user. If the user does not already * have the associated property, the amount will be added to zero. To reduce a property, * provide a negative number for the value. * * @param {string} prop the People Analytics property that should have its value changed * @param {number} by the amount to be added to the current value of the named property * */ increment(prop, by) { var add = {}; if (ObjectHelper.isValid(prop)) { Object.keys(prop).forEach(function (key) { var val = prop[key]; if (isNaN(parseFloat(val))) { throw new Error( `${PARAMS.PROPERTY_VALUE}${ERROR_MESSAGE.REQUIRED_DOUBLE}` ); } add[key] = val; }); } else { by = by || 1; if (isNaN(parseFloat(by))) { throw new Error( `${PARAMS.PROPERTY_VALUE}${ERROR_MESSAGE.REQUIRED_DOUBLE}` ); } if (!StringHelper.isValid(prop)) { StringHelper.raiseError(PARAMS.NAME); } add[prop] = by; } this.mixpanelImpl.increment(this.token, add); } /** * Appends a value to a list-valued property. If the property does not currently exist, * it will be created as a list of one element. If the property does exist and doesn't * currently have a list value, the append will be ignored. * @param {string} name the People Analytics property that should have it's value appended to * @param {object} value the new value that will appear at the end of the property's list */ append(name, value) { let appendProp = {}; if (!StringHelper.isValid(name)) { StringHelper.raiseError(PARAMS.NAME); } else { appendProp[name] = value; } if (DevicePlatform.iOS === Helper.getDevicePlatform()) { this.mixpanelImpl.append(this.token, appendProp); } else { this.mixpanelImpl.append(this.token, name, value); } } /** * Adds values to a list-valued property only if they are not already present in the list. * If the property does not currently exist, it will be created with the given list as it's value. * If the property exists and is not list-valued, the union will be ignored. * * @param {string} name name of the list-valued property to set or modify * @param {array} value an array of values to add to the property value if not already present */ union(name, value) { if (!StringHelper.isValid(name)) { StringHelper.raiseError(PARAMS.NAME); } value = Array.isArray(value) ? value : [value]; if (DevicePlatform.iOS === Helper.getDevicePlatform()) { this.mixpanelImpl.union(this.token, {[name]: value}); this.mixpanelImpl.union(this.token, {[name]: value}); } else { this.mixpanelImpl.union(this.token, name, value); } } /** * Remove value from a list-valued property only if they are already present in the list. * If the property does not currently exist, the remove will be ignored. * If the property exists and is not list-valued, the remove will be ignored. * @param {string} name the People Analytics property that should have it's value removed from * @param {object} value the value that will be removed from the property's list */ remove(name, value) { let removeProp = {}; if (!StringHelper.isValid(name)) { StringHelper.raiseError(PARAMS.NAME); } else { removeProp[name] = value; } if (DevicePlatform.iOS === Helper.getDevicePlatform()) { this.mixpanelImpl.remove(this.token, removeProp); } else { this.mixpanelImpl.remove(this.token, name, value); } } /** * permanently removes the property with the given name from the user's profile * @param {string} name name of a property to unset */ unset(name) { if (!StringHelper.isValid(name)) { StringHelper.raiseError(PARAMS.PROPERTY_NAME); } this.mixpanelImpl.unset(this.token, name); } /** * Track a revenue transaction for the identified people profile. * * @param {number} charge the amount of money exchanged. Positive amounts represent purchases or income from the customer, negative amounts represent refunds or payments to the customer. * @param {object} properties an optional collection of properties to associate with this transaction. */ trackCharge(charge, properties) { if (isNaN(parseFloat(charge))) { throw new Error(`${PARAMS.CHARGE}${ERROR_MESSAGE.REQUIRED_DOUBLE}`); } if (!ObjectHelper.isValidOrUndefined(properties)) { ObjectHelper.raiseError(PARAMS.PROPERTIES); } this.mixpanelImpl.trackCharge(this.token, charge, properties || {}); } /** * Permanently clear the whole transaction history for the identified people profile. */ clearCharges() { this.mixpanelImpl.clearCharges(this.token); } /** * Permanently deletes the identified user's record from People Analytics. * * <p>Calling deleteUser deletes an entire record completely. Any future calls * to People Analytics using the same distinct id will create and store new values. */ deleteUser() { this.mixpanelImpl.deleteUser(this.token); } } /** * Core class for using Mixpanel Group Analytics features. * * <p>The MixpanelGroup object is used to update properties in a group's Group Analytics record. */ export class MixpanelGroup { constructor(token, groupKey, groupID, mixpanelImpl) { if (!StringHelper.isValid(token)) { StringHelper.raiseError(PARAMS.TOKEN); } this.token = token; this.groupKey = groupKey; this.groupID = groupID; this.mixpanelImpl = mixpanelImpl; } /** * Sets a single property with the given name and value for this group. * The given name and value will be assigned to the user in Mixpanel Group Analytics, * possibly overwriting an existing property with the same name. * * @param {string} prop The name of the Mixpanel property. This must be a String, for example "Zip Code" * @param {string} to The value to set on the given property name. For "Zip Code", this value might be the String "90210" */ set(prop, to) { let properties = {}; if (ObjectHelper.isValid(prop)) { properties = JSON.parse(JSON.stringify(prop || {})); } else { if (!StringHelper.isValid(prop)) { StringHelper.raiseError(PARAMS.PROP); } properties[prop] = to; } this.mixpanelImpl.groupSetProperties( this.token, this.groupKey, this.groupID, properties ); } /** * Works just like groupSet() except it will not overwrite existing property values. This is useful for properties like "First login date". * * @param {string} prop The name of the Mixpanel property. This must be a String, for example "Zip Code" * @param {string} to The value to set on the given property name. For "Zip Code", this value might be the String "90210" */ setOnce(prop, to) { let properties = {}; if (ObjectHelper.isValid(prop)) { properties = JSON.parse(JSON.stringify(prop || {})); } else { if (!StringHelper.isValid(prop)) { StringHelper.raiseError(PARAMS.PROP); } properties[prop] = to; } this.mixpanelImpl.groupSetPropertyOnce( this.token, this.groupKey, this.groupID, properties ); } /** * Permanently removes the property with the given name from the group's profile * * @param {string} prop name of a property to unset */ unset(prop) { if (!StringHelper.isValid(prop)) { StringHelper.raiseError(PARAMS.PROPERTY_NAME); } this.mixpanelImpl.groupUnsetProperty( this.token, this.groupKey, this.groupID, prop ); } /** * Remove value from a list-valued property only if it is already present in the list. * If the property does not currently exist, the remove will be ignored. * If the property exists and is not list-valued, the remove will be ignored. * * @param {string} name the Group Analytics list-valued property that should have a value removed * @param {any} value the value that will be removed from the list */ remove(name, value) { if (!StringHelper.isValid(name)) { StringHelper.raiseError(PARAMS.PROPERTY_NAME); } this.mixpanelImpl.groupRemovePropertyValue( this.token, this.groupKey, this.groupID, name, value ); } /** * Adds values to a list-valued property only if they are not already present in the list. * If the property does not currently exist, it will be created with the given list as its value. * If the property exists and is not list-valued, the union will be ignored. * * @param {string} name name of the list-valued property to set or modify * @param {array} value an array of values to add to the property value if not already present */ union(name, value) { if (!StringHelper.isValid(name)) { StringHelper.raiseError(PARAMS.PROPERTY_NAME); } value = Array.isArray(value) ? value : [value]; this.mixpanelImpl.groupUnionProperty( this.token, this.groupKey, this.groupID, name, value ); } } class Helper { /** Get the library data from package.json file. */ static getMetaData() { let metadata = JSON.parse(JSON.stringify(packageJson.metadata)); metadata["$lib_version"] = packageJson.version; return metadata; } /** Get current device platform. */ static getDevicePlatform() { switch (Platform.OS) { case "android": return DevicePlatform.Android; case "ios": return DevicePlatform.iOS; default: return DevicePlatform.Unknown; } } } class StringHelper { /** Check whether the parameter is not a blank string. */ static isValid(str) { return typeof str === "string" && !/^\s*$/.test(str); } /** Check whether the parameter is undefined or not a blank string. */ static isValidOrUndefined(str) { return str === undefined || StringHelper.isValid(str); } /** Raise a string validation error. */ static raiseError(paramName) { throw new Error(`${paramName}${ERROR_MESSAGE.INVALID_STRING}`); } } class ObjectHelper { /** Check whether the parameter is an object. */ static isValid(obj) { return typeof obj === "object"; } /** Check whether the parameter is undefined or an object. */ static isValidOrUndefined(obj) { return obj === undefined || ObjectHelper.isValid(obj); } /** Raise an object validation error. */ static raiseError(paramName) { throw new Error(`${paramName}${ERROR_MESSAGE.INVALID_OBJECT}`); } }