mixpanel-react-native
Version:
Official React Native Tracking Library for Mixpanel Analytics
562 lines (502 loc) • 16.4 kB
JavaScript
import {Platform} from "react-native";
import {MixpanelCore} from "./mixpanel-core";
import {MixpanelType} from "./mixpanel-constants";
import {MixpanelConfig} from "./mixpanel-config";
import {MixpanelPersistent} from "./mixpanel-persistent";
import {MixpanelLogger} from "mixpanel-react-native/javascript/mixpanel-logger";
import packageJson from "mixpanel-react-native/package.json";
export default class MixpanelMain {
constructor(token, trackAutomaticEvents, storage) {
this.token = token;
this.config = MixpanelConfig.getInstance();
this.core = MixpanelCore(storage);
this.core.initialize(token);
this.core.startProcessingQueue(token);
this.mixpanelPersistent = MixpanelPersistent.getInstance();
}
async initialize(
token,
trackAutomaticEvents = false,
optOutTrackingDefault = false,
superProperties = null,
serverURL = "https://api.mixpanel.com"
) {
MixpanelLogger.log(token, `Initializing Mixpanel`);
await this.mixpanelPersistent.initializationCompletePromise(token);
if (optOutTrackingDefault) {
await this.optOutTracking(token);
return;
} else {
await this._setOptedOutTrackingFlag(token, false);
}
this.setServerURL(token, serverURL);
await this.registerSuperProperties(token, {
...superProperties,
});
}
getMetaData() {
const {OS, Version, constants} = Platform;
const {Brand, Manufacturer, Model} = constants || {};
let metadata = {
$os: OS,
$os_version: Version,
...JSON.parse(JSON.stringify(packageJson.metadata)),
$lib_version: packageJson.version,
};
if (OS === "ios") {
metadata = {
...metadata,
$manufacturer: "Apple",
};
} else if (OS === "android") {
metadata = {
...metadata,
$android_brand: Brand,
$android_manufacturer: Manufacturer,
$android_model: Model,
};
}
return metadata;
}
async reset(token) {
await this.mixpanelPersistent.reset(token);
}
async track(token, eventName, properties) {
if (this.mixpanelPersistent.getOptedOut(token)) {
MixpanelLogger.log(
token,
`User has opted out of tracking, skipping tracking.`
);
return;
}
MixpanelLogger.log(
token,
`Track '${eventName}' with properties`,
properties
);
const superProperties = this.mixpanelPersistent.getSuperProperties(token);
const identityProps = {
distinct_id: this.mixpanelPersistent.getDistinctId(token),
$device_id: this.mixpanelPersistent.getDeviceId(token),
$user_id: this.mixpanelPersistent.getUserId(token),
};
const eventElapsedTime = await this.eventElapsedTime(token, eventName);
const eventProperties = Object.freeze({
token,
time: Date.now(),
...this.getMetaData(),
...superProperties,
...properties,
...identityProps,
...(eventElapsedTime !== null && {
$duration: eventElapsedTime,
}),
});
const eventData = Object.freeze({
event: eventName,
properties: eventProperties,
});
if (eventElapsedTime !== null) {
let timeEvents = this.mixpanelPersistent.getTimeEvents(token);
delete timeEvents[eventName];
this.mixpanelPersistent.updateTimeEvents(token, timeEvents);
await this.mixpanelPersistent.persistTimeEvents(token);
}
await this.core.addToMixpanelQueue(token, MixpanelType.EVENTS, eventData);
}
setLoggingEnabled(token, loggingEnabled) {
this.config.setLoggingEnabled(token, loggingEnabled);
}
setServerURL(token, serverURL) {
this.config.setServerURL(token, serverURL);
}
setUseIpAddressForGeolocation(token, useIpAddressForGeolocation) {
this.config.setUseIpAddressForGeolocation(
token,
useIpAddressForGeolocation
);
}
setFlushBatchSize(token, flushBatchSize) {
this.config.setFlushBatchSize(token, flushBatchSize);
}
flush(token) {
this.core.flush(token);
}
async optOutTracking(token) {
await this._setOptedOutTrackingFlag(token, true);
MixpanelLogger.log(token, "User has opted out of tracking");
await this.mixpanelPersistent.reset(token);
}
async optInTracking(token) {
await this._setOptedOutTrackingFlag(token, false);
MixpanelLogger.log(token, "User has opted in to tracking");
await this.track(token, "$opt_in");
}
async _setOptedOutTrackingFlag(token, optedOut) {
this.mixpanelPersistent.updateOptedOut(token, optedOut);
await this.mixpanelPersistent.persistOptedOut(token);
}
hasOptedOutTracking(token) {
return this.mixpanelPersistent.getOptOut(token);
}
async identify(token, newDistinctId) {
MixpanelLogger.log(token, `Identify '${newDistinctId}'`);
const oldDistinctId = this.mixpanelPersistent.getDistinctId(token);
if (oldDistinctId === newDistinctId) {
MixpanelLogger.log(
token,
`Distinct Id is already set to ${newDistinctId}, skipping identify.`
);
return;
}
this.mixpanelPersistent.updateDistinctId(token, newDistinctId);
this.mixpanelPersistent.updateUserId(token, newDistinctId);
const deviceId = this.mixpanelPersistent.getDeviceId(token);
await this.mixpanelPersistent.persistIdentity(token);
await this.track(token, "$identify", {
distinctId: newDistinctId,
$user_id: newDistinctId,
$anon_distinct_id: oldDistinctId,
$device_id: deviceId,
});
}
async alias(token, alias, distinctId) {
MixpanelLogger.log(token, `Alias '${alias}' to '${distinctId}'`);
await this.track(token, "$create_alias", {
alias,
distinct_id: distinctId,
});
await this.identify(token, distinctId);
}
async getDeviceId(token) {
if (!this.mixpanelPersistent.getDeviceId(token)) {
await this.mixpanelPersistent.loadIdentity(token);
}
return this.identity[token].deviceId;
}
async getDistinctId(token) {
if (!this.mixpanelPersistent.getDistinctId(token)) {
await this.mixpanelPersistent.loadIdentity(token);
}
return this.mixpanelPersistent.getDistinctId(token);
}
async _updateSuperProperties(token, properties) {
this.mixpanelPersistent.updateSuperProperties(token, properties);
await this.mixpanelPersistent.persistSuperProperties(token);
}
async registerSuperProperties(token, properties) {
MixpanelLogger.log(token, `Register super properties:`, properties);
const currentSuperProperties = this.mixpanelPersistent.getSuperProperties(
token
);
MixpanelLogger.log(
token,
`Current Super Properties:`,
currentSuperProperties
);
const updatedSuperProperties = {
...currentSuperProperties,
...properties,
};
this._updateSuperProperties(token, updatedSuperProperties);
MixpanelLogger.log(
token,
`Updated Super Properties:`,
updatedSuperProperties
);
}
async registerSuperPropertiesOnce(token, properties) {
MixpanelLogger.log(token, `Register super properties once`, properties);
const currentSuperProperties = this.mixpanelPersistent.getSuperProperties(
token
);
const updatedSuperProperties = {
...properties,
...currentSuperProperties,
};
this._updateSuperProperties(token, updatedSuperProperties);
MixpanelLogger.log(
token,
`Updated Super Properties:`,
updatedSuperProperties
);
}
async unregisterSuperProperty(token, propertyName) {
MixpanelLogger.log(token, `Unregister super property '${propertyName}'`);
let superProperties = this.mixpanelPersistent.getSuperProperties(token);
delete superProperties[propertyName];
this._updateSuperProperties(token, superProperties);
MixpanelLogger.log(token, `Updated Super Properties:`, superProperties);
}
async getSuperProperties(token) {
if (!this.mixpanelPersistent.getSuperProperties(token)) {
await this.mixpanelPersistent.loadSuperProperties(token);
}
return this.mixpanelPersistent.getSuperProperties(token);
}
async clearSuperProperties(token) {
MixpanelLogger.log(token, `Clear super properties`);
this._updateSuperProperties(token, {});
MixpanelLogger.log(token, `Updated Super Properties:`, {});
}
async timeEvent(token, eventName) {
const currentTime = Math.round(Date.now() / 1000);
MixpanelLogger.log(
token,
`Add time event '${eventName}' at`,
new Date(currentTime * 1000).toLocaleString()
);
this.mixpanelPersistent.updateTimeEvents(token, {
...this.mixpanelPersistent.getTimeEvents(token),
[eventName]: currentTime,
});
await this.mixpanelPersistent.persistTimeEvents(token);
}
async eventElapsedTime(token, eventName) {
if (!this.mixpanelPersistent.getTimeEvents(token)) {
await this.mixpanelPersistent.loadTimeEvents(token);
}
const timeEvents = this.mixpanelPersistent.getTimeEvents(token);
const startTime = timeEvents ? timeEvents[eventName] : undefined;
if (startTime) {
const duration = Math.round(Date.now() / 1000) - startTime;
return duration;
}
return null;
}
async sendProfileDataToMixpanel(token, action) {
const profileData = {
$token: token,
$time: Date.now(),
...action,
$distinct_id: this.mixpanelPersistent.getDistinctId(token),
$device_id: this.mixpanelPersistent.getDeviceId(token),
$user_id: this.mixpanelPersistent.getUserId(token),
};
await this.core.addToMixpanelQueue(token, MixpanelType.USER, profileData);
}
async sendGroupDataToMixpanel({token, groupKey, groupID, action}) {
const profileData = {
$token: token,
$time: Date.now(),
$group_key: groupKey,
$group_id: groupID,
...action,
};
await this.core.addToMixpanelQueue(token, MixpanelType.GROUPS, profileData);
}
async set(token, properties) {
MixpanelLogger.log(token, `Set properties: `, properties);
await this.sendProfileDataToMixpanel(token, {$set: properties});
}
async setOnce(token, properties) {
MixpanelLogger.log(token, `Set once properties: `, properties);
await this.sendProfileDataToMixpanel(token, {$set_once: properties});
}
async increment(token, properties) {
MixpanelLogger.log(token, `Increment properties: `, properties);
await this.sendProfileDataToMixpanel(token, {$add: properties});
}
async append(token, nameOrProperties, value) {
if (typeof nameOrProperties === "string" && value !== undefined) {
MixpanelLogger.log(token, `Append properties: `, {
[nameOrProperties]: value,
});
await this.sendProfileDataToMixpanel(token, {
$append: {[nameOrProperties]: value},
});
} else if (typeof nameOrProperties === "object") {
MixpanelLogger.log(token, `Append properties: `, nameOrProperties);
await this.sendProfileDataToMixpanel(token, {
$append: nameOrProperties,
});
}
}
async union(token, nameOrProperties, value) {
if (typeof nameOrProperties === "string" && value !== undefined) {
MixpanelLogger.log(token, `Union properties: `, {
[nameOrProperties]: value,
});
await this.sendProfileDataToMixpanel(token, {
$union: {[nameOrProperties]: value},
});
} else if (typeof nameOrProperties === "object") {
MixpanelLogger.log(token, `Union properties: `, nameOrProperties);
await this.sendProfileDataToMixpanel(token, {$union: nameOrProperties});
}
}
async remove(token, nameOrProperties, value) {
if (typeof nameOrProperties === "string" && value !== undefined) {
MixpanelLogger.log(token, `Remove properties: `, {
[nameOrProperties]: value,
});
await this.sendProfileDataToMixpanel(token, {
$remove: {[nameOrProperties]: value},
});
} else if (typeof nameOrProperties === "object") {
MixpanelLogger.log(token, `Remove properties: `, nameOrProperties);
await this.sendProfileDataToMixpanel(token, {
$remove: nameOrProperties,
});
}
}
async trackCharge(token, charge, properties) {
MixpanelLogger.log(token, `Track charge: `, charge, properties);
await this.append(token, {
$transactions: {$amount: charge, $time: Date.now(), ...properties},
});
}
async clearCharges(token) {
MixpanelLogger.log(token, `Clear charges`);
await this.set(token, {
$transactions: [],
});
}
async unset(token, property) {
MixpanelLogger.log(token, `Unset property: `, property);
await this.sendProfileDataToMixpanel(token, {$unset: [property]});
}
async deleteUser(token) {
MixpanelLogger.log(token, `Delete user`);
await this.sendProfileDataToMixpanel(token, {$delete: "null"});
}
async groupSetProperties(token, groupKey, groupID, properties) {
MixpanelLogger.log(
token,
`Group set properties: `,
groupKey,
groupID,
properties
);
await this.sendGroupDataToMixpanel({
token,
groupKey,
groupID,
action: {
$set: properties,
},
});
}
async groupSetPropertyOnce(token, groupKey, groupID, properties) {
MixpanelLogger.log(
token,
`Group set once properties: `,
groupKey,
groupID,
properties
);
await this.sendGroupDataToMixpanel({
token,
groupKey,
groupID,
action: {
$set_once: properties,
},
});
}
async groupUnsetProperty(token, groupKey, groupID, prop) {
MixpanelLogger.log(
token,
`Group unset property: `,
groupKey,
groupID,
prop
);
await this.sendGroupDataToMixpanel({
token,
groupKey,
groupID,
action: {
$unset: [prop],
},
});
}
async groupRemovePropertyValue(token, groupKey, groupID, name, value) {
MixpanelLogger.log(
token,
`Group remove property value: `,
groupKey,
groupID,
name,
value
);
await this.sendGroupDataToMixpanel({
token,
groupKey,
groupID,
action: {
$remove: {[name]: value},
},
});
}
async groupUnionProperty(token, groupKey, groupID, name, value) {
MixpanelLogger.log(
token,
`Group union property: `,
groupKey,
groupID,
name,
value
);
await this.sendGroupDataToMixpanel({
token,
groupKey,
groupID,
action: {
$union: {[name]: value},
},
});
}
async trackWithGroups(token, eventName, properties, groups) {
MixpanelLogger.log(
token,
`Track with groups: `,
eventName,
properties,
groups
);
await this.track(token, eventName, {...properties, ...groups});
}
async setGroup(token, groupKey, groupID) {
MixpanelLogger.log(token, `Set group: `, groupKey, groupID);
const properties = {[groupKey]: [groupID]};
await this.registerSuperProperties(token, properties);
await this.set(token, properties);
}
async addGroup(token, groupKey, groupID) {
MixpanelLogger.log(token, `Add group: `, groupKey, groupID);
const superProperties = this.mixpanelPersistent.getSuperProperties(token);
const groupArray = superProperties[groupKey] || [];
if (!groupArray.includes(groupID)) {
this.registerSuperProperties(token, {
[groupKey]: [...groupArray, groupID],
});
}
await this.union(token, {[groupKey]: [groupID]});
}
async removeGroup(token, groupKey, groupID) {
MixpanelLogger.log(token, `Remove group: `, groupKey, groupID);
const superProperties = this.mixpanelPersistent.getSuperProperties(token);
if (superProperties && superProperties[groupKey]) {
const filteredGroup = superProperties[groupKey].filter(
(id) => id !== groupID
);
this.registerSuperProperties(token, {[groupKey]: filteredGroup});
if (filteredGroup.length === 0) {
this.unregisterSuperProperty(token, groupKey);
}
}
await this.remove(token, {[groupKey]: groupID});
}
async deleteGroup(token, groupKey, groupID) {
MixpanelLogger.log(token, `Delete group: `, groupKey, groupID);
await this.sendGroupDataToMixpanel({
token,
groupKey,
groupID,
action: {
$delete: "null",
},
});
}
}