mimz-react-native-tracker 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +246 -0
- package/bin/mimz-setup.js +275 -0
- package/package.json +47 -0
- package/src/index.d.ts +131 -0
- package/src/index.js +47 -0
- package/src/network.js +134 -0
- package/src/storage.js +117 -0
- package/src/tracker.js +647 -0
- package/src/utils.js +180 -0
package/src/utils.js
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MimzUtils - Utility helpers for the Mimz React Native SDK
|
|
3
|
+
* Replaces browser-specific utilities (URL parsing, device info, etc.)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { Platform, Dimensions } = require('react-native');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Generate a unique ID (replaces browser Date.now().toString(36) + random)
|
|
10
|
+
* @returns {string}
|
|
11
|
+
*/
|
|
12
|
+
const generateUniqueId = () => {
|
|
13
|
+
return Date.now().toString(36) + Math.random().toString(36).substring(2);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Parse UTM parameters from a URL string (e.g., a deep link)
|
|
18
|
+
* In native apps, UTMs come from deep links, not the current URL bar
|
|
19
|
+
* @param {string} url - The deep link URL to parse
|
|
20
|
+
* @returns {object} - Object containing all found UTM and tracking params
|
|
21
|
+
*/
|
|
22
|
+
const parseUtmFromUrl = (url) => {
|
|
23
|
+
const utmParams = {};
|
|
24
|
+
|
|
25
|
+
if (!url || typeof url !== 'string') return utmParams;
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
// Handle both full URLs and query strings
|
|
29
|
+
let queryString = '';
|
|
30
|
+
if (url.includes('?')) {
|
|
31
|
+
queryString = url.split('?')[1];
|
|
32
|
+
} else if (url.includes('&') || url.includes('=')) {
|
|
33
|
+
queryString = url;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!queryString) return utmParams;
|
|
37
|
+
|
|
38
|
+
// Standard UTM parameters
|
|
39
|
+
const trackedFields = [
|
|
40
|
+
'utm_source',
|
|
41
|
+
'utm_medium',
|
|
42
|
+
'utm_campaign',
|
|
43
|
+
'utm_term',
|
|
44
|
+
'utm_content',
|
|
45
|
+
'utm_device',
|
|
46
|
+
'utm_devicemodel',
|
|
47
|
+
// Additional tracking parameters
|
|
48
|
+
'fbclid',
|
|
49
|
+
'msclkid',
|
|
50
|
+
'gclid',
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
// Parse query string manually (no URLSearchParams in some RN environments)
|
|
54
|
+
const pairs = queryString.split('&');
|
|
55
|
+
pairs.forEach((pair) => {
|
|
56
|
+
const [key, value] = pair.split('=');
|
|
57
|
+
if (key && value) {
|
|
58
|
+
const decodedKey = decodeURIComponent(key.trim());
|
|
59
|
+
const decodedValue = decodeURIComponent(value.trim());
|
|
60
|
+
if (trackedFields.includes(decodedKey)) {
|
|
61
|
+
utmParams[decodedKey] = decodedValue;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.warn('[MimzTracker] Error parsing UTM from URL:', error.message);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return utmParams;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get device information (replaces browser userAgent-based detection)
|
|
74
|
+
* @returns {object} - Device info object
|
|
75
|
+
*/
|
|
76
|
+
const getDeviceInfo = () => {
|
|
77
|
+
const { width, height } = Dimensions.get('window');
|
|
78
|
+
const pixelRatio = require('react-native').PixelRatio?.get() || 1;
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
screenWidth: width,
|
|
82
|
+
screenHeight: height,
|
|
83
|
+
deviceType: Platform.isPad ? 'Tablet' : 'Mobile',
|
|
84
|
+
deviceModel: Platform.constants?.Model || Platform.constants?.Brand || 'Unknown',
|
|
85
|
+
os: Platform.OS === 'ios' ? 'iOS' : Platform.OS === 'android' ? 'Android' : Platform.OS,
|
|
86
|
+
osVersion: Platform.Version ? String(Platform.Version) : 'Unknown',
|
|
87
|
+
browser: 'Native App',
|
|
88
|
+
language: 'en', // Can be overridden via react-native-localize
|
|
89
|
+
colorDepth: 24,
|
|
90
|
+
pixelRatio: pixelRatio,
|
|
91
|
+
touchScreen: true,
|
|
92
|
+
userAgent: `MimzSDK/1.0 (${Platform.OS}; ${Platform.Version})`,
|
|
93
|
+
platform: 'react-native',
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Determine traffic source from a referral URL (deep link referrer)
|
|
99
|
+
* @param {string} referrerUrl - The referrer/deep link source URL
|
|
100
|
+
* @returns {object} - Traffic source details
|
|
101
|
+
*/
|
|
102
|
+
const getTrafficSource = (referrerUrl) => {
|
|
103
|
+
let source = 'Direct';
|
|
104
|
+
let trafficSource = 'Direct';
|
|
105
|
+
|
|
106
|
+
if (!referrerUrl || typeof referrerUrl !== 'string') {
|
|
107
|
+
return { trafficSource, source, organicSource: null, organicSourceStr: null };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const lowerUrl = referrerUrl.toLowerCase();
|
|
111
|
+
|
|
112
|
+
// Detect source from the referrer URL
|
|
113
|
+
if (lowerUrl.includes('google')) source = 'Google';
|
|
114
|
+
else if (lowerUrl.includes('facebook') || lowerUrl.includes('fb.com')) source = 'Facebook';
|
|
115
|
+
else if (lowerUrl.includes('instagram')) source = 'Instagram';
|
|
116
|
+
else if (lowerUrl.includes('twitter') || lowerUrl.includes('t.co')) source = 'Twitter';
|
|
117
|
+
else if (lowerUrl.includes('linkedin')) source = 'LinkedIn';
|
|
118
|
+
else if (lowerUrl.includes('youtube')) source = 'YouTube';
|
|
119
|
+
else if (lowerUrl.includes('snapchat')) source = 'Snapchat';
|
|
120
|
+
else if (lowerUrl.includes('pinterest')) source = 'Pinterest';
|
|
121
|
+
else if (lowerUrl.includes('bing')) source = 'Bing';
|
|
122
|
+
else if (lowerUrl.includes('yahoo')) source = 'Yahoo';
|
|
123
|
+
else if (lowerUrl.includes('duckduckgo')) source = 'Duckduckgo';
|
|
124
|
+
else if (lowerUrl.includes('tumblr')) source = 'Tumblr';
|
|
125
|
+
else source = 'Other';
|
|
126
|
+
|
|
127
|
+
// Classify traffic type
|
|
128
|
+
const searchEngines = ['Google', 'Bing', 'Yahoo', 'Duckduckgo'];
|
|
129
|
+
const socialPlatforms = ['Facebook', 'Twitter', 'Instagram', 'Snapchat', 'YouTube', 'Pinterest', 'LinkedIn', 'Tumblr'];
|
|
130
|
+
|
|
131
|
+
if (searchEngines.includes(source)) {
|
|
132
|
+
trafficSource = 'Organic';
|
|
133
|
+
} else if (socialPlatforms.includes(source)) {
|
|
134
|
+
trafficSource = 'Social';
|
|
135
|
+
} else if (source === 'Direct') {
|
|
136
|
+
trafficSource = 'Direct';
|
|
137
|
+
} else {
|
|
138
|
+
trafficSource = 'Referral';
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
trafficSource,
|
|
143
|
+
source,
|
|
144
|
+
organicSource: trafficSource === 'Organic' ? referrerUrl : null,
|
|
145
|
+
organicSourceStr: trafficSource === 'Organic' ? source : null,
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Get the user's IP address using an external service
|
|
151
|
+
* @returns {Promise<string>}
|
|
152
|
+
*/
|
|
153
|
+
const getUserIp = async () => {
|
|
154
|
+
try {
|
|
155
|
+
const controller = typeof AbortController !== 'undefined' ? new AbortController() : null;
|
|
156
|
+
const timeoutId = controller ? setTimeout(() => controller.abort(), 2000) : null;
|
|
157
|
+
|
|
158
|
+
const response = await fetch('https://api.ipify.org?format=json', {
|
|
159
|
+
signal: controller?.signal,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
163
|
+
|
|
164
|
+
if (response.ok) {
|
|
165
|
+
const data = await response.json();
|
|
166
|
+
return data?.ip || '0.0.0.0';
|
|
167
|
+
}
|
|
168
|
+
return '0.0.0.0';
|
|
169
|
+
} catch (error) {
|
|
170
|
+
return '0.0.0.0';
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
module.exports = {
|
|
175
|
+
generateUniqueId,
|
|
176
|
+
parseUtmFromUrl,
|
|
177
|
+
getDeviceInfo,
|
|
178
|
+
getTrafficSource,
|
|
179
|
+
getUserIp,
|
|
180
|
+
};
|