react-native-test-adx 0.1.3 → 0.1.5

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.
@@ -1,18 +1,16 @@
1
1
  "use strict";
2
2
 
3
3
  import { AdaxonClient } from "./core/AdaxonClient.js";
4
- import { MEDIATION_PROVIDER, OFFER_OPENED_EVENT } from "./config/ApiConstants.js";
4
+ import { MEDIATION_PROVIDER, OFFER_OPENED_EVENT, RECOMMENDATION_VIEW_EVENT } from "./config/ApiConstants.js";
5
+ import { dismissInterstitial, presentInterstitial } from "./components/InterstitialAd.js";
5
6
  import { AdaxonSdkError } from "./errors/AdaxonSdkError.js";
6
7
  import { detectReactNativePlatform, reactNativeAppInfoReader, reactNativeDeviceReader, reactNativeNetworkInfoReader, reactNativeNetworkStatusReader, reactNativeUrlOpener } from "./platform/reactNative.js";
7
8
  import { buildAddToCartProperties, buildPurchaseProperties, normalizeEventValues } from "./services/event/normalizeProperties.js";
8
9
  import { AdxEvent } from "./services/event/predefined.js";
9
10
  export class AdaxonSdk {
10
- static sharedInstance = new AdaxonSdk();
11
+ static shared = new AdaxonSdk();
11
12
  client = null;
12
13
  testMode = false;
13
- static get shared() {
14
- return AdaxonSdk.sharedInstance;
15
- }
16
14
  isInitialized() {
17
15
  return this.client !== null;
18
16
  }
@@ -70,58 +68,95 @@ export class AdaxonSdk {
70
68
  async trackCompleteRegistration(params = {}, options = {}) {
71
69
  return this.logEvent(AdxEvent.COMPLETE_REGISTRATION, params, options);
72
70
  }
73
- async loadRecommendations(eventName, properties = {}, listener) {
71
+ async showAd(options = {}) {
74
72
  try {
75
- const response = await this.requireClient().getRecommendations({
76
- eventName,
77
- properties: {
78
- ...properties,
79
- mediation_provider: MEDIATION_PROVIDER,
80
- test_mode: this.testMode
73
+ const offers = await this.loadRecommendations(RECOMMENDATION_VIEW_EVENT);
74
+ if (offers.length === 0) {
75
+ console.warn('[Adaxon] No offer available.');
76
+ return {
77
+ shown: false,
78
+ reason: 'no_offer'
79
+ };
80
+ }
81
+ const excluded = new Set(options.excludeOfferIds ?? []);
82
+ const offer = offers.find(item => !excluded.has(item.offer_id));
83
+ if (!offer) {
84
+ console.warn('[Adaxon] No eligible offer available.');
85
+ return {
86
+ shown: false,
87
+ reason: 'no_eligible_offer'
88
+ };
89
+ }
90
+ return await new Promise(resolve => {
91
+ const displayed = presentInterstitial({
92
+ offer,
93
+ interact: () => {
94
+ options.onOfferClicked?.(offer.offer_id);
95
+ this.openOfferUrl(offer).then(() => dismissInterstitial('click')).catch(error => {
96
+ console.warn('[Adaxon] Failed to open offer URL.', AdaxonSdkError.messageOf(error, 'Failed to open offer.'));
97
+ dismissInterstitial('close');
98
+ });
99
+ },
100
+ onDismiss: dismissReason => {
101
+ resolve({
102
+ shown: true,
103
+ offerId: offer.offer_id,
104
+ dismissReason
105
+ });
106
+ }
107
+ });
108
+ if (!displayed) {
109
+ console.warn('[Adaxon] Interstitial host is not mounted. Wrap the app in AdaxonProvider.');
110
+ resolve({
111
+ shown: false,
112
+ reason: 'not_mounted'
113
+ });
81
114
  }
82
115
  });
83
- listener?.onRecommendationsLoaded(response.offers);
84
- return response.offers;
85
116
  } catch (error) {
86
- const message = AdaxonSdkError.messageOf(error, 'Failed to load recommendations.');
87
- const code = error instanceof AdaxonSdkError ? error.code : undefined;
88
- listener?.onRecommendationsFailedToLoad(message, code);
89
- throw error;
90
- }
91
- }
92
- async showOffer(offer, listener) {
93
- try {
94
- const client = this.requireClient();
95
- const targetUrl = offer.offer_url;
96
- const canOpen = await reactNativeUrlOpener.canOpenURL(targetUrl);
97
- if (!canOpen) {
98
- throw new AdaxonSdkError('VALIDATION_ERROR', `Cannot open offer URL: ${targetUrl}`);
117
+ console.warn('[Adaxon] Failed to show ad.', AdaxonSdkError.messageOf(error, 'Failed to show ad.'));
118
+ if (this.testMode) {
119
+ throw error;
99
120
  }
100
- await reactNativeUrlOpener.openURL(targetUrl);
101
- await client.track(OFFER_OPENED_EVENT, {
102
- offer_id: offer.offer_id,
103
- offer_name: offer.offer_name,
104
- ad_type: offer.ad_type,
105
- mediation_provider: MEDIATION_PROVIDER
106
- });
107
- listener?.onOfferOpened(offer);
108
- } catch (error) {
109
- const message = AdaxonSdkError.messageOf(error, 'Failed to open offer.');
110
- listener?.onOfferFailedToOpen(offer, message);
111
- throw error;
121
+ return {
122
+ shown: false,
123
+ reason: 'no_offer'
124
+ };
112
125
  }
113
126
  }
114
- setExternalUserId(externalUserId) {
115
- this.requireClient().setExternalUserId(externalUserId);
116
- }
117
127
  isNetworkAvailable() {
118
128
  return this.client?.isNetworkAvailable() ?? false;
119
129
  }
120
130
  destroy() {
131
+ dismissInterstitial();
121
132
  this.client?.destroy();
122
133
  this.client = null;
123
134
  this.testMode = false;
124
135
  }
136
+ async loadRecommendations(eventName, properties = {}) {
137
+ const response = await this.requireClient().getRecommendations({
138
+ eventName,
139
+ properties: {
140
+ ...properties,
141
+ mediation_provider: MEDIATION_PROVIDER,
142
+ test_mode: this.testMode
143
+ }
144
+ });
145
+ return response.offers;
146
+ }
147
+ async openOfferUrl(offer) {
148
+ const client = this.requireClient();
149
+ if (!(await reactNativeUrlOpener.canOpenURL(offer.offer_url))) {
150
+ throw new AdaxonSdkError('VALIDATION_ERROR', `Cannot open offer URL: ${offer.offer_url}`);
151
+ }
152
+ await reactNativeUrlOpener.openURL(offer.offer_url);
153
+ await client.track(OFFER_OPENED_EVENT, {
154
+ offer_id: offer.offer_id,
155
+ offer_name: offer.offer_name,
156
+ ad_type: offer.ad_type,
157
+ mediation_provider: MEDIATION_PROVIDER
158
+ });
159
+ }
125
160
  requireClient() {
126
161
  if (!this.client) {
127
162
  throw new AdaxonSdkError('INVALID_CONFIG', 'Adaxon SDK is not initialized. Call Adaxon.initialize(...) first.');
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
2
 
3
3
  import { useEffect, useMemo, useRef, useState } from 'react';
4
+ import { StyleSheet, View } from "../platform/reactNative.js";
4
5
  import { AdaxonSdk } from "../AdaxonSdk.js";
5
6
  import { AdaxonSdkError } from "../errors/AdaxonSdkError.js";
6
7
  import { AdaxonContext } from "../state/AdaxonContext.js";
7
- import { jsx as _jsx } from "react/jsx-runtime";
8
+ import { InterstitialAd } from "./InterstitialAd.js";
9
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
8
10
  export function AdaxonProvider({
9
11
  configuration,
10
12
  listener,
@@ -44,6 +46,14 @@ export function AdaxonProvider({
44
46
  }), [initializationError, isInitialized, sdk]);
45
47
  return /*#__PURE__*/_jsx(AdaxonContext.Provider, {
46
48
  value: value,
47
- children: children
49
+ children: /*#__PURE__*/_jsxs(View, {
50
+ style: styles.root,
51
+ children: [children, /*#__PURE__*/_jsx(InterstitialAd, {})]
52
+ })
48
53
  });
49
- }
54
+ }
55
+ const styles = StyleSheet.create({
56
+ root: {
57
+ flex: 1
58
+ }
59
+ });
@@ -0,0 +1,284 @@
1
+ "use strict";
2
+
3
+ import { useEffect, useState } from 'react';
4
+ import { BackHandler, Image, Platform, Pressable, SafeAreaView, StatusBar, StyleSheet, Text, View } from "../platform/reactNative.js";
5
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
6
+ const CLOSE_EDGE_INSET = 12;
7
+ const CLOSE_HIT_SIZE = 44;
8
+ let listener = null;
9
+ let activeSession = null;
10
+ export function presentInterstitial(session) {
11
+ if (!listener) {
12
+ return false;
13
+ }
14
+ activeSession = session;
15
+ listener(session);
16
+ return true;
17
+ }
18
+ export function dismissInterstitial(reason = 'close') {
19
+ const ended = activeSession;
20
+ activeSession = null;
21
+ listener?.(null);
22
+ ended?.onDismiss?.(reason);
23
+ }
24
+ const emptySafeAreaInsets = {
25
+ top: 0,
26
+ right: 0,
27
+ bottom: 0,
28
+ left: 0
29
+ };
30
+ function useEmptySafeAreaInsets() {
31
+ return emptySafeAreaInsets;
32
+ }
33
+ const useSafeAreaInsets = loadUseSafeAreaInsets() ?? useEmptySafeAreaInsets;
34
+ function loadUseSafeAreaInsets() {
35
+ try {
36
+ const module = require('react-native-safe-area-context');
37
+ return typeof module.useSafeAreaInsets === 'function' ? module.useSafeAreaInsets : undefined;
38
+ } catch {
39
+ return undefined;
40
+ }
41
+ }
42
+ function useTopInset() {
43
+ const safeArea = useSafeAreaInsets();
44
+ if (safeArea.top > 0) {
45
+ return safeArea.top;
46
+ }
47
+ if (Platform.OS === 'android') {
48
+ return StatusBar.currentHeight ?? 0;
49
+ }
50
+ return 0;
51
+ }
52
+ export function InterstitialAd() {
53
+ const [session, setSession] = useState(null);
54
+ const [closeTone, setCloseTone] = useState('onLightSurface');
55
+ const safeArea = useSafeAreaInsets();
56
+ const topInset = useTopInset();
57
+ const offerId = session?.offer.offer_id;
58
+ useEffect(() => {
59
+ listener = setSession;
60
+ return () => {
61
+ if (listener === setSession) {
62
+ listener = null;
63
+ }
64
+ };
65
+ }, []);
66
+ useEffect(() => {
67
+ setCloseTone('onLightSurface');
68
+ }, [offerId]);
69
+ useEffect(() => {
70
+ if (!session) {
71
+ return;
72
+ }
73
+ const onBackPress = () => {
74
+ dismissInterstitial('back');
75
+ return true;
76
+ };
77
+ const subscription = BackHandler.addEventListener('hardwareBackPress', onBackPress);
78
+ return () => subscription.remove();
79
+ }, [session]);
80
+ if (!session) {
81
+ return null;
82
+ }
83
+ const {
84
+ offer,
85
+ interact
86
+ } = session;
87
+ const closeAppearance = closeButtonAppearance(closeTone);
88
+ const closeRightInset = Platform.OS === 'android' ? CLOSE_EDGE_INSET + safeArea.right : CLOSE_EDGE_INSET;
89
+ const adFrame = /*#__PURE__*/_jsx(_Fragment, {
90
+ children: /*#__PURE__*/_jsxs(View, {
91
+ style: styles.adContainer,
92
+ children: [/*#__PURE__*/_jsx(Pressable, {
93
+ accessibilityRole: "button",
94
+ accessibilityLabel: "Close ad",
95
+ onPress: () => dismissInterstitial('close'),
96
+ style: [styles.closeButton, closeAppearance.button, {
97
+ top: CLOSE_EDGE_INSET,
98
+ right: closeRightInset
99
+ }],
100
+ testID: "adaxon-interstitial-close",
101
+ children: /*#__PURE__*/_jsx(Text, {
102
+ style: [styles.closeLabel, closeAppearance.label],
103
+ children: "\xD7"
104
+ })
105
+ }), /*#__PURE__*/_jsx(Pressable, {
106
+ accessibilityRole: "button",
107
+ accessibilityLabel: offer.offer_name,
108
+ onPress: interact,
109
+ style: styles.offer,
110
+ testID: "adaxon-interstitial-offer",
111
+ children: /*#__PURE__*/_jsx(OfferCreative, {
112
+ offer: offer,
113
+ onCloseToneChange: setCloseTone
114
+ })
115
+ })]
116
+ })
117
+ });
118
+ return /*#__PURE__*/_jsx(View, {
119
+ style: styles.overlay,
120
+ testID: "adaxon-interstitial",
121
+ children: Platform.OS === 'ios' ? /*#__PURE__*/_jsx(SafeAreaView, {
122
+ style: styles.safeFrame,
123
+ children: adFrame
124
+ }) : /*#__PURE__*/_jsx(View, {
125
+ style: [styles.safeFrame, {
126
+ paddingTop: topInset
127
+ }],
128
+ children: adFrame
129
+ })
130
+ });
131
+ }
132
+ function OfferCreative({
133
+ offer,
134
+ onCloseToneChange
135
+ }) {
136
+ const imageUrl = remoteImageUrl(offer.creative_url) ?? remoteImageUrl(offer.preview_url);
137
+ const [failed, setFailed] = useState(false);
138
+ useEffect(() => {
139
+ setFailed(false);
140
+ }, [imageUrl]);
141
+ useEffect(() => {
142
+ if (!imageUrl || failed) {
143
+ onCloseToneChange('onDarkSurface');
144
+ return;
145
+ }
146
+ onCloseToneChange('onLightSurface');
147
+ }, [failed, imageUrl, onCloseToneChange]);
148
+ if (!imageUrl || failed) {
149
+ return /*#__PURE__*/_jsx(View, {
150
+ style: styles.fallback,
151
+ testID: "adaxon-interstitial-fallback",
152
+ children: /*#__PURE__*/_jsx(Text, {
153
+ style: styles.offerName,
154
+ children: offer.offer_name
155
+ })
156
+ });
157
+ }
158
+ return /*#__PURE__*/_jsx(Image, {
159
+ source: {
160
+ uri: imageUrl
161
+ },
162
+ style: styles.creativeImage,
163
+ resizeMode: "contain",
164
+ onError: () => setFailed(true),
165
+ testID: "adaxon-interstitial-image"
166
+ });
167
+ }
168
+ function closeButtonAppearance(tone) {
169
+ if (tone === 'onDarkSurface') {
170
+ return {
171
+ button: {
172
+ backgroundColor: 'rgba(255, 255, 255, 0.94)',
173
+ borderColor: 'rgba(0, 0, 0, 0.35)'
174
+ },
175
+ label: {
176
+ color: '#111111',
177
+ textShadowColor: 'rgba(255, 255, 255, 0.85)',
178
+ textShadowRadius: 2
179
+ }
180
+ };
181
+ }
182
+ return {
183
+ button: {
184
+ backgroundColor: 'rgba(0, 0, 0, 0.62)',
185
+ borderColor: 'rgba(255, 255, 255, 0.55)'
186
+ },
187
+ label: {
188
+ color: '#ffffff',
189
+ textShadowColor: 'rgba(0, 0, 0, 0.75)',
190
+ textShadowRadius: 3
191
+ }
192
+ };
193
+ }
194
+ function remoteImageUrl(value) {
195
+ const trimmed = value?.trim();
196
+ if (!trimmed) {
197
+ return null;
198
+ }
199
+ try {
200
+ const url = new URL(trimmed);
201
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') {
202
+ return null;
203
+ }
204
+ return trimmed;
205
+ } catch {
206
+ return null;
207
+ }
208
+ }
209
+ const styles = StyleSheet.create({
210
+ overlay: {
211
+ position: 'absolute',
212
+ top: 0,
213
+ right: 0,
214
+ bottom: 0,
215
+ left: 0,
216
+ zIndex: 9999,
217
+ elevation: 9999,
218
+ backgroundColor: '#000000'
219
+ },
220
+ safeFrame: {
221
+ flex: 1,
222
+ width: '100%'
223
+ },
224
+ adContainer: {
225
+ flex: 1,
226
+ width: '100%',
227
+ position: 'relative'
228
+ },
229
+ closeButton: {
230
+ position: 'absolute',
231
+ zIndex: 2,
232
+ minWidth: CLOSE_HIT_SIZE,
233
+ minHeight: CLOSE_HIT_SIZE,
234
+ width: CLOSE_HIT_SIZE,
235
+ height: CLOSE_HIT_SIZE,
236
+ alignItems: 'center',
237
+ justifyContent: 'center',
238
+ borderRadius: CLOSE_HIT_SIZE / 2,
239
+ borderWidth: 1.5,
240
+ shadowColor: '#000000',
241
+ shadowOffset: {
242
+ width: 0,
243
+ height: 2
244
+ },
245
+ shadowOpacity: 0.45,
246
+ shadowRadius: 4,
247
+ elevation: 8
248
+ },
249
+ closeLabel: {
250
+ fontSize: 32,
251
+ lineHeight: 36,
252
+ textShadowOffset: {
253
+ width: 0,
254
+ height: 0
255
+ }
256
+ },
257
+ offer: {
258
+ flex: 1,
259
+ width: '100%',
260
+ alignSelf: 'stretch',
261
+ overflow: 'hidden'
262
+ },
263
+ creativeImage: {
264
+ position: 'absolute',
265
+ top: 0,
266
+ right: 0,
267
+ bottom: 0,
268
+ left: 0,
269
+ width: '100%',
270
+ height: '100%'
271
+ },
272
+ fallback: {
273
+ flex: 1,
274
+ width: '100%',
275
+ alignItems: 'center',
276
+ justifyContent: 'center',
277
+ paddingHorizontal: 24
278
+ },
279
+ offerName: {
280
+ color: '#ffffff',
281
+ fontSize: 22,
282
+ textAlign: 'center'
283
+ }
284
+ });
@@ -1,12 +1,16 @@
1
1
  "use strict";
2
2
 
3
- export const SDK_VERSION = '0.1.3';
3
+ import packageJson from '../../package.json';
4
+ export const SDK_VERSION = packageJson.version;
4
5
  export const MEDIATION_PROVIDER = 'mts';
5
- export const BASE_URL = 'http://localhost:3002';
6
+
7
+ //export const BASE_URL = 'http://localhost:3002';
8
+ export const BASE_URL = 'https://mts-tracking.mobupps.com';
6
9
  export const EVENT_TRACKING_PATH = '/event/tracking';
7
10
  export const RECOMMENDATIONS_PATH = '/api/mts/recommendations';
8
11
  export const CLICK_TRACKING_PATH = '/click/tracking';
9
12
  export const OFFER_OPENED_EVENT = 'offer_opened';
13
+ export const RECOMMENDATION_VIEW_EVENT = 'recommendation_view';
10
14
  export const DEFAULT_TIMEOUT_MS = 10_000;
11
15
  export const PUBLIC_IP_LOOKUP_URL = 'https://api.ipify.org?format=json';
12
16
  export const PUBLIC_IP_LOOKUP_TIMEOUT_MS = 2_000;
@@ -29,8 +29,7 @@ export class AdaxonClient {
29
29
  deviceInfo: new DeviceInfoService(config.platform ?? 'web', config.deviceInfoReader, config.storage, auth.appId),
30
30
  appInfo: new AppInfoService(Boolean(config.testMode), config.appInfoReader),
31
31
  networkInfo,
32
- validator: this.validator,
33
- externalUserId: config.externalUserId
32
+ validator: this.validator
34
33
  });
35
34
  this.events = new EventService({
36
35
  api,
@@ -115,9 +114,6 @@ export class AdaxonClient {
115
114
  });
116
115
  return `${BASE_URL}${CLICK_TRACKING_PATH}?${query}`;
117
116
  }
118
- setExternalUserId(externalUserId) {
119
- this.builder.setExternalUserId(externalUserId);
120
- }
121
117
  toQueryString(params) {
122
118
  return Object.entries(params).filter(([, value]) => value !== undefined).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`).join('&');
123
119
  }
@@ -23,9 +23,7 @@ export function useAdaxon() {
23
23
  trackAddToCart: sdk.trackAddToCart.bind(sdk),
24
24
  trackLogin: sdk.trackLogin.bind(sdk),
25
25
  trackCompleteRegistration: sdk.trackCompleteRegistration.bind(sdk),
26
- loadRecommendations: sdk.loadRecommendations.bind(sdk),
27
- showOffer: sdk.showOffer.bind(sdk),
28
- setExternalUserId: sdk.setExternalUserId.bind(sdk)
26
+ showAd: sdk.showAd.bind(sdk)
29
27
  }), [sdk]);
30
28
  return {
31
29
  isInitialized,
@@ -1,12 +1,53 @@
1
1
  "use strict";
2
2
 
3
3
  import { nonEmptyString, setIfPresent } from "../utils/strings.js";
4
- export function appFieldsFromNative(constants) {
4
+ export function appFieldsFromNative(...sources) {
5
+ const value = key => lastPresent(sources, key);
6
+ const expo = expoConfig(value('expoConfig'), value('manifest'), value('extra'));
5
7
  const fields = {};
6
- setIfPresent(fields, 'package_name', firstNonEmpty(constants.applicationId, constants.bundleId, constants.androidPackage, constants.iosBundleIdentifier));
7
- setIfPresent(fields, 'app_version', firstNonEmpty(constants.nativeApplicationVersion, constants.appVersion, constants.nativeAppVersion));
8
+ setIfPresent(fields, 'package_name', firstNonEmpty(value('applicationId'), value('bundleId'), value('packageName'), value('androidPackage'), value('iosBundleIdentifier'), asRecord(value('android'))?.package, asRecord(value('ios'))?.bundleIdentifier, asRecord(expo?.android)?.package, asRecord(expo?.ios)?.bundleIdentifier));
9
+ setIfPresent(fields, 'app_version', firstNonEmpty(value('nativeApplicationVersion'), value('appVersion'), value('nativeAppVersion'), value('version'), expo?.version));
8
10
  return fields;
9
11
  }
12
+ function expoConfig(expoConfigValue, manifestValue, extraValue) {
13
+ const manifest = asRecord(manifestValue);
14
+ return firstRecord(expoConfigValue, asRecord(manifest?.extra)?.expoClient, manifest?.expoClient, manifest, asRecord(extraValue)?.expoClient);
15
+ }
16
+ function lastPresent(sources, key) {
17
+ let found;
18
+ for (const source of sources) {
19
+ if (!source) {
20
+ continue;
21
+ }
22
+ const next = source[key];
23
+ if (next != null && next !== '') {
24
+ found = next;
25
+ }
26
+ }
27
+ return found;
28
+ }
29
+ function firstRecord(...values) {
30
+ for (const value of values) {
31
+ const record = asRecord(value);
32
+ if (record) {
33
+ return record;
34
+ }
35
+ }
36
+ return undefined;
37
+ }
38
+ function asRecord(value) {
39
+ if (typeof value === 'string') {
40
+ try {
41
+ return asRecord(JSON.parse(value));
42
+ } catch {
43
+ return undefined;
44
+ }
45
+ }
46
+ if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
47
+ return value;
48
+ }
49
+ return undefined;
50
+ }
10
51
  function firstNonEmpty(...values) {
11
52
  for (const value of values) {
12
53
  const next = nonEmptyString(value);
@@ -1,14 +1,23 @@
1
1
  "use strict";
2
2
 
3
3
  import { Linking, NativeEventEmitter, NativeModules, Platform } from 'react-native';
4
+ export { BackHandler, Dimensions, Image, Modal, Platform, Pressable, SafeAreaView, StatusBar, StyleSheet, Text, View } from 'react-native';
4
5
  import { nonEmptyString, setIfPresent } from "../utils/strings.js";
5
6
  import { readLocaleFields } from "../utils/locale.js";
6
7
  import { appFieldsFromNative } from "./app.js";
7
8
  import { deviceFieldsFromAndroid } from "./android.js";
8
9
  import { deviceFieldsFromIos } from "./ios.js";
9
10
  import { connectionTypeFromNativeState, ipAddressFromNativeState, isOnlineFromNativeState, usableIpAddress } from "./network.js";
11
+ function expoNativeModule(name) {
12
+ try {
13
+ const expoGlobal = globalThis;
14
+ return expoGlobal.expo?.modules?.[name];
15
+ } catch {
16
+ return undefined;
17
+ }
18
+ }
10
19
  function nativeModule(name) {
11
- return (NativeModules ?? {})[name];
20
+ return (NativeModules ?? {})[name] ?? expoNativeModule(name);
12
21
  }
13
22
  async function callNative(method, arg) {
14
23
  if (typeof method !== 'function') {
@@ -53,13 +62,6 @@ async function readUniqueIdFromNative() {
53
62
  const deviceInfo = nativeModule('RNDeviceInfo');
54
63
  return nonEmptyString(await callNative(deviceInfo?.getUniqueId)) ?? '';
55
64
  }
56
- function readNativeAppConstants() {
57
- return {
58
- ...nativeModule('RNDeviceInfo'),
59
- ...nativeModule('ExponentConstants'),
60
- ...nativeModule('ExpoApplication')
61
- };
62
- }
63
65
  function nativeNetworkModules() {
64
66
  return {
65
67
  netInfo: nativeModule('RNCNetInfo'),
@@ -121,7 +123,7 @@ export const reactNativeDeviceReader = {
121
123
  getUniqueId: readUniqueIdFromNative
122
124
  };
123
125
  export const reactNativeAppInfoReader = {
124
- readAppInfo: () => appFieldsFromNative(readNativeAppConstants())
126
+ readAppInfo: () => appFieldsFromNative(nativeModule('RNDeviceInfo'), nativeModule('ExponentConstants'), nativeModule('ExpoApplication'))
125
127
  };
126
128
  export const reactNativeNetworkInfoReader = {
127
129
  readNetworkInfo: collectFromNativeNetwork
@@ -1,15 +1,10 @@
1
1
  "use strict";
2
2
 
3
3
  import { createUuid } from "../../utils/uuid.js";
4
- import { nonEmptyString, setIfPresent } from "../../utils/strings.js";
5
4
  export class EventBuilder {
6
5
  sessionId = createUuid();
7
6
  constructor(config) {
8
7
  this.config = config;
9
- this.externalUserId = config.externalUserId;
10
- }
11
- setExternalUserId(externalUserId) {
12
- this.externalUserId = externalUserId;
13
8
  }
14
9
  async build(input) {
15
10
  const {
@@ -23,7 +18,6 @@ export class EventBuilder {
23
18
  const user = {
24
19
  anonymous_id: await deviceInfo.getUniqueId()
25
20
  };
26
- setIfPresent(user, 'external_user_id', nonEmptyString(this.externalUserId));
27
21
  return {
28
22
  event_id: input.eventId ?? createUuid(),
29
23
  event_name: input.eventName,
@@ -19,11 +19,13 @@ export class EventService {
19
19
  }
20
20
  async getRecommendations(input) {
21
21
  const eventPayload = await this.builder.build(input);
22
- const payload = {
23
- ...eventPayload,
24
- event_type: 'recommendation'
25
- };
26
- const response = await this.api.post(RECOMMENDATIONS_PATH, payload);
22
+ await this.postEvents([eventPayload]);
23
+ const response = await this.api.post(RECOMMENDATIONS_PATH, {
24
+ app_id: eventPayload.app_id,
25
+ device: {
26
+ platform: eventPayload.device.platform
27
+ }
28
+ });
27
29
  if (!Array.isArray(response.offers)) {
28
30
  throw new AdaxonSdkError('HTTP_ERROR', 'Adaxon recommendation response is missing the offers array.');
29
31
  }
@@ -1,3 +1 @@
1
- "use strict";
2
-
3
- export {};
1
+ "use strict";