@attryio/react-native 0.1.1 → 0.1.4
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 +21 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +46 -40
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -10,13 +10,31 @@ const attry = await createAttryReactNative({
|
|
|
10
10
|
apiKey: "attry_live_..."
|
|
11
11
|
});
|
|
12
12
|
|
|
13
|
-
await attry.
|
|
13
|
+
await attry.initiatePurchase({
|
|
14
14
|
properties: {
|
|
15
|
-
|
|
15
|
+
productId: "pro_monthly",
|
|
16
|
+
placement: "paywall"
|
|
16
17
|
}
|
|
17
18
|
});
|
|
18
19
|
|
|
19
|
-
await attry.
|
|
20
|
+
await attry.purchase({
|
|
21
|
+
value: 31.42,
|
|
22
|
+
currency: "USD",
|
|
23
|
+
productId: "pro_monthly",
|
|
24
|
+
transactionId: "txn_123"
|
|
25
|
+
});
|
|
20
26
|
```
|
|
21
27
|
|
|
22
28
|
The SDK auto-tracks first SDK install, app opens, sessions, foreground/background transitions, deep link opens, Apple AdServices tokens on iOS, and Google Play Install Referrer on Android when the native modules are available.
|
|
29
|
+
|
|
30
|
+
## Standard events
|
|
31
|
+
|
|
32
|
+
Attry accepts custom event names, but these standard names are reserved for dashboard reporting:
|
|
33
|
+
|
|
34
|
+
- Lifecycle: `install`, `open`, `session_started`, `session_ended`, `app_foreground`, `app_background`, `deep_link_opened`
|
|
35
|
+
- Revenue: `purchase`
|
|
36
|
+
- Commerce/content: `initiate_purchase`
|
|
37
|
+
|
|
38
|
+
`identify()` is a convenience method that attaches a known user ID to later events; it is not sent as a standard event. Any other product action can still be sent with `track("your_event_name", { properties })`.
|
|
39
|
+
|
|
40
|
+
Purchase events must include `value` or `amountMinor` plus `currency`. Attry stores them as stable minor-unit revenue fields so dashboard revenue is not guessed from arbitrary properties.
|
package/dist/index.d.ts
CHANGED
|
@@ -53,11 +53,11 @@ export interface ReactNativeAttryConfig extends Omit<AttryConfig, "platform" | "
|
|
|
53
53
|
}
|
|
54
54
|
export { parseAttryUrl };
|
|
55
55
|
export declare function createAttryReactNative(config: ReactNativeAttryConfig): Promise<Attry>;
|
|
56
|
-
export declare function collectReactNativeContext(rn?: ReactNativeLike
|
|
56
|
+
export declare function collectReactNativeContext(rn?: ReactNativeLike, configuredContext?: AttryConfig["context"]): Promise<AttryContext>;
|
|
57
57
|
export declare function attachReactNativeLifecycleTracking(client: Attry, rn: ReactNativeLike | undefined, storage: AttryStorage | undefined, options: {
|
|
58
58
|
appId: string;
|
|
59
59
|
sessionTimeoutMs?: number;
|
|
60
60
|
}): Promise<void>;
|
|
61
|
-
export declare function attachDeepLinkTracking(client: Attry, rn?: ReactNativeLike
|
|
62
|
-
export declare function collectInstallAttribution(client: Attry, rn?: ReactNativeLike
|
|
61
|
+
export declare function attachDeepLinkTracking(client: Attry, rn?: ReactNativeLike): Promise<void>;
|
|
62
|
+
export declare function collectInstallAttribution(client: Attry, rn?: ReactNativeLike): Promise<void>;
|
|
63
63
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { Attry, MemoryStorage, parseAttryUrl } from "@attryio/sdk-core";
|
|
1
|
+
import { Attry, ATTRY_EVENTS, MemoryStorage, parseAttryUrl } from "@attryio/sdk-core";
|
|
2
2
|
const SDK_NAME = "attry-react-native";
|
|
3
|
-
const SDK_VERSION = "0.1.
|
|
3
|
+
const SDK_VERSION = "0.1.4";
|
|
4
4
|
export { parseAttryUrl };
|
|
5
5
|
export async function createAttryReactNative(config) {
|
|
6
|
-
const rn =
|
|
6
|
+
const rn = await loadReactNative();
|
|
7
7
|
const platform = rn?.Platform?.OS === "ios" || rn?.Platform?.OS === "android"
|
|
8
8
|
? rn.Platform.OS
|
|
9
9
|
: "unknown";
|
|
10
|
-
const storage = config.storage ?? createAsyncStorageAdapter() ?? new MemoryStorage();
|
|
10
|
+
const storage = config.storage ?? (await createAsyncStorageAdapter()) ?? new MemoryStorage();
|
|
11
11
|
const context = await collectReactNativeContext(rn, config.context);
|
|
12
12
|
const client = await new Attry({
|
|
13
13
|
...config,
|
|
@@ -29,18 +29,19 @@ export async function createAttryReactNative(config) {
|
|
|
29
29
|
}
|
|
30
30
|
return client;
|
|
31
31
|
}
|
|
32
|
-
export async function collectReactNativeContext(rn
|
|
33
|
-
const
|
|
32
|
+
export async function collectReactNativeContext(rn, configuredContext = {}) {
|
|
33
|
+
const native = rn ?? (await loadReactNative());
|
|
34
|
+
const modules = native?.NativeModules;
|
|
34
35
|
const deviceInfo = modules?.RNDeviceInfo;
|
|
35
36
|
const expoConstants = modules?.ExpoConstants ?? modules?.ExponentConstants;
|
|
36
37
|
const platformConstants = modules?.PlatformConstants;
|
|
37
38
|
const settings = modules?.SettingsManager;
|
|
38
|
-
const screen =
|
|
39
|
+
const screen = native?.Dimensions?.get?.("screen");
|
|
39
40
|
const locale = configuredContext?.locale ??
|
|
40
41
|
readNestedString(settings?.settings, ["AppleLocale"]) ??
|
|
41
42
|
readFirstString(readNestedUnknown(settings?.settings, ["AppleLanguages"])) ??
|
|
42
43
|
readNestedString(deviceInfo, ["deviceLocale"]) ??
|
|
43
|
-
|
|
44
|
+
native?.I18nManager?.localeIdentifier;
|
|
44
45
|
return compactContext({
|
|
45
46
|
sdkName: SDK_NAME,
|
|
46
47
|
sdkVersion: SDK_VERSION,
|
|
@@ -74,10 +75,12 @@ export async function collectReactNativeContext(rn = getReactNative(), configure
|
|
|
74
75
|
(await readMaybeString(deviceInfo?.getBuildNumber)) ??
|
|
75
76
|
readNestedString(expoConstants, ["expoConfig", "ios", "buildNumber"]) ??
|
|
76
77
|
readNestedString(expoConstants, ["expoConfig", "android", "versionCode"]),
|
|
77
|
-
osVersion:
|
|
78
|
+
osVersion: native?.Platform?.Version
|
|
79
|
+
? String(native.Platform.Version)
|
|
80
|
+
: undefined,
|
|
78
81
|
systemName: configuredContext?.systemName ??
|
|
79
82
|
(await readMaybeString(deviceInfo?.systemName)) ??
|
|
80
|
-
(
|
|
83
|
+
(native?.Platform?.OS ? String(native.Platform.OS) : undefined),
|
|
81
84
|
deviceModel: configuredContext?.deviceModel ??
|
|
82
85
|
(await readMaybeString(deviceInfo?.model)) ??
|
|
83
86
|
(await readMaybeString(deviceInfo?.getModel)) ??
|
|
@@ -124,13 +127,14 @@ export async function collectReactNativeContext(rn = getReactNative(), configure
|
|
|
124
127
|
...configuredContext
|
|
125
128
|
});
|
|
126
129
|
}
|
|
127
|
-
export async function attachReactNativeLifecycleTracking(client, rn
|
|
130
|
+
export async function attachReactNativeLifecycleTracking(client, rn, storage = new MemoryStorage(), options) {
|
|
131
|
+
const native = rn ?? (await loadReactNative());
|
|
128
132
|
const installKey = `attry.${options.appId}.install_tracked`;
|
|
129
133
|
const sessionStartedAt = Date.now();
|
|
130
134
|
const sessionId = `ses_${sessionStartedAt.toString(36)}`;
|
|
131
135
|
if (!(await storage.getItem(installKey))) {
|
|
132
136
|
await storage.setItem(installKey, new Date().toISOString());
|
|
133
|
-
await client.track(
|
|
137
|
+
await client.track(ATTRY_EVENTS.INSTALL, {
|
|
134
138
|
properties: {
|
|
135
139
|
auto: true,
|
|
136
140
|
firstSdkOpen: true
|
|
@@ -140,16 +144,16 @@ export async function attachReactNativeLifecycleTracking(client, rn = getReactNa
|
|
|
140
144
|
}
|
|
141
145
|
});
|
|
142
146
|
}
|
|
143
|
-
await client.track(
|
|
147
|
+
await client.track(ATTRY_EVENTS.OPEN, {
|
|
144
148
|
properties: {
|
|
145
149
|
auto: true,
|
|
146
|
-
appState:
|
|
150
|
+
appState: native?.AppState?.currentState ?? "active"
|
|
147
151
|
},
|
|
148
152
|
context: {
|
|
149
153
|
sessionId
|
|
150
154
|
}
|
|
151
155
|
});
|
|
152
|
-
await client.track(
|
|
156
|
+
await client.track(ATTRY_EVENTS.SESSION_STARTED, {
|
|
153
157
|
properties: {
|
|
154
158
|
auto: true
|
|
155
159
|
},
|
|
@@ -157,15 +161,15 @@ export async function attachReactNativeLifecycleTracking(client, rn = getReactNa
|
|
|
157
161
|
sessionId
|
|
158
162
|
}
|
|
159
163
|
});
|
|
160
|
-
let active =
|
|
164
|
+
let active = native?.AppState?.currentState !== "background";
|
|
161
165
|
let lastActiveAt = sessionStartedAt;
|
|
162
166
|
const sessionTimeoutMs = options.sessionTimeoutMs ?? 30 * 60 * 1000;
|
|
163
|
-
|
|
167
|
+
native?.AppState?.addEventListener?.("change", (state) => {
|
|
164
168
|
const now = Date.now();
|
|
165
169
|
if (state === "active" && !active) {
|
|
166
170
|
active = true;
|
|
167
171
|
lastActiveAt = now;
|
|
168
|
-
void client.track(
|
|
172
|
+
void client.track(ATTRY_EVENTS.APP_FOREGROUND, {
|
|
169
173
|
properties: {
|
|
170
174
|
auto: true
|
|
171
175
|
},
|
|
@@ -178,7 +182,7 @@ export async function attachReactNativeLifecycleTracking(client, rn = getReactNa
|
|
|
178
182
|
if ((state === "background" || state === "inactive") && active) {
|
|
179
183
|
active = false;
|
|
180
184
|
const durationMs = Math.max(0, now - lastActiveAt);
|
|
181
|
-
void client.track(
|
|
185
|
+
void client.track(ATTRY_EVENTS.APP_BACKGROUND, {
|
|
182
186
|
properties: {
|
|
183
187
|
auto: true,
|
|
184
188
|
durationMs
|
|
@@ -188,7 +192,7 @@ export async function attachReactNativeLifecycleTracking(client, rn = getReactNa
|
|
|
188
192
|
}
|
|
189
193
|
});
|
|
190
194
|
if (durationMs >= sessionTimeoutMs || state === "background") {
|
|
191
|
-
void client.track(
|
|
195
|
+
void client.track(ATTRY_EVENTS.SESSION_ENDED, {
|
|
192
196
|
properties: {
|
|
193
197
|
auto: true,
|
|
194
198
|
durationMs: Math.max(0, now - sessionStartedAt)
|
|
@@ -202,18 +206,20 @@ export async function attachReactNativeLifecycleTracking(client, rn = getReactNa
|
|
|
202
206
|
}
|
|
203
207
|
});
|
|
204
208
|
}
|
|
205
|
-
export async function attachDeepLinkTracking(client, rn
|
|
206
|
-
const
|
|
209
|
+
export async function attachDeepLinkTracking(client, rn) {
|
|
210
|
+
const native = rn ?? (await loadReactNative());
|
|
211
|
+
const initialUrl = await native?.Linking?.getInitialURL?.();
|
|
207
212
|
if (initialUrl) {
|
|
208
|
-
await trackDeepLinkOpen(client, initialUrl, "initial_url",
|
|
213
|
+
await trackDeepLinkOpen(client, initialUrl, "initial_url", native);
|
|
209
214
|
}
|
|
210
|
-
|
|
211
|
-
void trackDeepLinkOpen(client, url, "linking_event",
|
|
215
|
+
native?.Linking?.addEventListener?.("url", ({ url }) => {
|
|
216
|
+
void trackDeepLinkOpen(client, url, "linking_event", native);
|
|
212
217
|
});
|
|
213
218
|
}
|
|
214
|
-
export async function collectInstallAttribution(client, rn
|
|
215
|
-
|
|
216
|
-
|
|
219
|
+
export async function collectInstallAttribution(client, rn) {
|
|
220
|
+
const native = rn ?? (await loadReactNative());
|
|
221
|
+
if (native?.Platform?.OS === "ios") {
|
|
222
|
+
const token = await native.NativeModules?.AttryAppleAds?.attributionToken?.();
|
|
217
223
|
if (token) {
|
|
218
224
|
const response = await client.submitAppleAdsToken(token);
|
|
219
225
|
await client.track("apple_ads_token_collected", {
|
|
@@ -225,8 +231,8 @@ export async function collectInstallAttribution(client, rn = getReactNative()) {
|
|
|
225
231
|
}
|
|
226
232
|
return;
|
|
227
233
|
}
|
|
228
|
-
if (
|
|
229
|
-
const payload = await
|
|
234
|
+
if (native?.Platform?.OS === "android") {
|
|
235
|
+
const payload = await native.NativeModules?.AttryInstallReferrer?.getInstallReferrer?.();
|
|
230
236
|
if (payload?.installReferrer) {
|
|
231
237
|
const response = await client.resolveInstall({
|
|
232
238
|
installReferrer: payload.installReferrer
|
|
@@ -242,10 +248,11 @@ export async function collectInstallAttribution(client, rn = getReactNative()) {
|
|
|
242
248
|
}
|
|
243
249
|
}
|
|
244
250
|
}
|
|
245
|
-
async function trackDeepLinkOpen(client, url, openType, rn
|
|
251
|
+
async function trackDeepLinkOpen(client, url, openType, rn) {
|
|
252
|
+
const native = rn ?? (await loadReactNative());
|
|
246
253
|
const parsed = parseAttryUrl(url);
|
|
247
|
-
const openedFromUniversalLink =
|
|
248
|
-
const openedFromAndroidAppLink =
|
|
254
|
+
const openedFromUniversalLink = native?.Platform?.OS === "ios";
|
|
255
|
+
const openedFromAndroidAppLink = native?.Platform?.OS === "android";
|
|
249
256
|
const resolved = await client.resolveDeepLink({
|
|
250
257
|
url,
|
|
251
258
|
openedFromUniversalLink,
|
|
@@ -261,7 +268,7 @@ async function trackDeepLinkOpen(client, url, openType, rn = getReactNative()) {
|
|
|
261
268
|
openedFromAndroidAppLink
|
|
262
269
|
});
|
|
263
270
|
}
|
|
264
|
-
await client.track(
|
|
271
|
+
await client.track(ATTRY_EVENTS.DEEP_LINK_OPENED, {
|
|
265
272
|
properties: {
|
|
266
273
|
openType,
|
|
267
274
|
url: parsed.url,
|
|
@@ -341,19 +348,18 @@ function safeTimezone() {
|
|
|
341
348
|
return undefined;
|
|
342
349
|
}
|
|
343
350
|
}
|
|
344
|
-
function
|
|
351
|
+
async function loadReactNative() {
|
|
345
352
|
try {
|
|
346
|
-
|
|
347
|
-
return dynamicRequire("react-native");
|
|
353
|
+
return (await import("react-native"));
|
|
348
354
|
}
|
|
349
355
|
catch {
|
|
350
356
|
return undefined;
|
|
351
357
|
}
|
|
352
358
|
}
|
|
353
|
-
function createAsyncStorageAdapter() {
|
|
359
|
+
async function createAsyncStorageAdapter() {
|
|
354
360
|
try {
|
|
355
|
-
const
|
|
356
|
-
const asyncStorage =
|
|
361
|
+
const asyncStorageModule = (await import("@react-native-async-storage/async-storage"));
|
|
362
|
+
const asyncStorage = asyncStorageModule.default;
|
|
357
363
|
return asyncStorage;
|
|
358
364
|
}
|
|
359
365
|
catch {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@attryio/react-native",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
"access": "public"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@attryio/sdk-core": "0.1.
|
|
32
|
+
"@attryio/sdk-core": "0.1.3",
|
|
33
|
+
"@react-native-async-storage/async-storage": "^2.2.0"
|
|
33
34
|
},
|
|
34
35
|
"peerDependencies": {
|
|
35
36
|
"react-native": ">=0.72"
|