@adadapted/react-native-sdk 3.2.0 → 3.4.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.
Files changed (72) hide show
  1. package/android/.project +1 -1
  2. package/android/.settings/org.eclipse.buildship.core.prefs +2 -2
  3. package/android/bin/build.gradle +13 -11
  4. package/android/bin/gradle.properties +4 -4
  5. package/android/build.gradle +19 -81
  6. package/android/gradle.properties +4 -4
  7. package/android/src/main/AndroidManifest.xml +1 -3
  8. package/android/src/main/java/com/adadaptedreactnativesdk/AdadaptedReactNativeSdkModule.kt +39 -54
  9. package/ios/AdadaptedReactNativeSdk.m +1 -1
  10. package/lib/commonjs/api/adadaptedApiRequests.js +3 -3
  11. package/lib/commonjs/api/adadaptedApiRequests.js.map +1 -1
  12. package/lib/commonjs/api/adadaptedApiRequests.mock.js +25 -8
  13. package/lib/commonjs/api/adadaptedApiRequests.mock.js.map +1 -1
  14. package/lib/commonjs/api/adadaptedApiTypes.js +68 -28
  15. package/lib/commonjs/api/adadaptedApiTypes.js.map +1 -1
  16. package/lib/commonjs/components/AdZone.js +101 -67
  17. package/lib/commonjs/components/AdZone.js.map +1 -1
  18. package/lib/commonjs/components/ReportAdButton.js +40 -0
  19. package/lib/commonjs/components/ReportAdButton.js.map +1 -0
  20. package/lib/commonjs/images/ReportIcon.png +0 -0
  21. package/lib/commonjs/index.js +139 -93
  22. package/lib/commonjs/index.js.map +1 -1
  23. package/lib/commonjs/package.json +1 -0
  24. package/lib/commonjs/types.js +4 -0
  25. package/lib/commonjs/types.js.map +1 -1
  26. package/lib/commonjs/util.js +14 -4
  27. package/lib/commonjs/util.js.map +1 -1
  28. package/lib/module/api/adadaptedApiRequests.js +2 -0
  29. package/lib/module/api/adadaptedApiRequests.js.map +1 -1
  30. package/lib/module/api/adadaptedApiRequests.mock.js +27 -9
  31. package/lib/module/api/adadaptedApiRequests.mock.js.map +1 -1
  32. package/lib/module/api/adadaptedApiTypes.js +85 -24
  33. package/lib/module/api/adadaptedApiTypes.js.map +1 -1
  34. package/lib/module/components/AdZone.js +90 -61
  35. package/lib/module/components/AdZone.js.map +1 -1
  36. package/lib/module/components/ReportAdButton.js +34 -0
  37. package/lib/module/components/ReportAdButton.js.map +1 -0
  38. package/lib/module/images/ReportIcon.png +0 -0
  39. package/lib/module/index.js +142 -89
  40. package/lib/module/index.js.map +1 -1
  41. package/lib/module/package.json +1 -0
  42. package/lib/module/types.js +2 -0
  43. package/lib/module/types.js.map +1 -1
  44. package/lib/module/util.js +16 -4
  45. package/lib/module/util.js.map +1 -1
  46. package/lib/typescript/src/api/adadaptedApiRequests.d.ts +1 -0
  47. package/lib/typescript/src/api/adadaptedApiRequests.d.ts.map +1 -0
  48. package/lib/typescript/src/api/adadaptedApiRequests.mock.d.ts +1 -0
  49. package/lib/typescript/src/api/adadaptedApiRequests.mock.d.ts.map +1 -0
  50. package/lib/typescript/src/api/adadaptedApiTypes.d.ts +5 -0
  51. package/lib/typescript/src/api/adadaptedApiTypes.d.ts.map +1 -0
  52. package/lib/typescript/src/components/AdZone.d.ts +11 -6
  53. package/lib/typescript/src/components/AdZone.d.ts.map +1 -0
  54. package/lib/typescript/src/components/ReportAdButton.d.ts +19 -0
  55. package/lib/typescript/src/components/ReportAdButton.d.ts.map +1 -0
  56. package/lib/typescript/src/index.d.ts +33 -17
  57. package/lib/typescript/src/index.d.ts.map +1 -0
  58. package/lib/typescript/src/types.d.ts +3 -2
  59. package/lib/typescript/src/types.d.ts.map +1 -0
  60. package/lib/typescript/src/util.d.ts +13 -0
  61. package/lib/typescript/src/util.d.ts.map +1 -0
  62. package/package.json +151 -155
  63. package/src/api/adadaptedApiRequests.mock.ts +25 -9
  64. package/src/api/adadaptedApiTypes.ts +5 -0
  65. package/src/components/AdZone.tsx +94 -46
  66. package/src/components/ReportAdButton.tsx +46 -0
  67. package/src/images/ReportIcon.png +0 -0
  68. package/src/index.tsx +146 -69
  69. package/src/util.ts +13 -0
  70. package/lib/typescript/example/index.d.ts +0 -1
  71. package/lib/typescript/example/src/App.d.ts +0 -9
  72. package/lib/typescript/jest.setup.d.ts +0 -5
@@ -2,7 +2,7 @@
2
2
  * Component for creating an {@link AdZone}.
3
3
  * @module
4
4
  */
5
- import * as React from "react";
5
+ import React, { useEffect, useState } from "react";
6
6
  import {
7
7
  DeviceEventEmitter,
8
8
  Linking,
@@ -21,7 +21,7 @@ import {
21
21
  import { WebView } from "react-native-webview";
22
22
  import { ApiEnv, DeviceOS } from "../index";
23
23
  import { safeInvoke } from "../util";
24
- import { useEffect, useState } from "react";
24
+ import { ReportAdButton } from "./ReportAdButton";
25
25
 
26
26
  /**
27
27
  * Props interface for {@link AdZone}.
@@ -66,13 +66,13 @@ interface Props {
66
66
  */
67
67
  onAddToListTriggered?(items: DetailedListItem[]): void;
68
68
  /**
69
- * Track the ad zone visibility in parent component.
69
+ * An ad zone that is not visible on screen for the initial render.
70
70
  */
71
- isAdZoneVisible?: boolean;
71
+ offScreenAdZone: boolean;
72
72
  /**
73
- * Set default ad zone visibility to false.
73
+ * Track the ad zone visibility in parent component. (for off-screen ads)
74
74
  */
75
- defaultToInvisibleAdZone?: boolean;
75
+ isAdZoneVisible?: boolean;
76
76
  }
77
77
 
78
78
  /**
@@ -101,6 +101,10 @@ interface StyleDef {
101
101
  * Styles for the WebView element.
102
102
  */
103
103
  webView: ViewStyle;
104
+ /**
105
+ * Styles for the ReportAdButton.
106
+ */
107
+ reportAd: ViewStyle;
104
108
  }
105
109
  /**
106
110
  * Timer used for cycling through ads in the zone
@@ -113,54 +117,65 @@ let cycleAdTimer: ReturnType<typeof setTimeout> | undefined;
113
117
  * @param props - properties passed to AdZone.
114
118
  * @returns an AdZone JSX Element.
115
119
  */
116
- export function AdZone(props: Props): JSX.Element {
117
- // Generates a random number between 0 and (number of available ads - 1).
118
- const startingAdIndex = Math.floor(
119
- Math.random() * props.adZoneData.ads.length
120
- );
121
-
120
+ export const AdZone = (props: Props): React.ReactElement => {
122
121
  /**
123
122
  * Tracks the current ad index being shown.
124
123
  */
125
- const [adIndexShown, setAdIndexShown] = useState(startingAdIndex);
124
+ const [adIndexShown, setAdIndexShown] = useState(
125
+ Math.floor(Math.random() * props.adZoneData.ads.length)
126
+ );
126
127
  /**
127
128
  * Tracks the coordinates when the user started touching the Ad View.
128
129
  */
129
- const [touchStartCoords, setTouchStartCoords] = useState({ x: 0, y: 0 });
130
+ const [touchStartCoords, setTouchStartCoords] = useState<TouchCoordinates>({
131
+ x: 0,
132
+ y: 0,
133
+ });
134
+
130
135
  /**
131
136
  * Track ad visibility (for off-screen ads).
132
137
  */
133
- const [isAdVisible, setIsAdVisibile] = useState(
134
- props.defaultToInvisibleAdZone ? false : true
135
- );
138
+ const [isAdZoneVisible, setIsAdVisibile] = useState(props.isAdZoneVisible);
136
139
 
137
- // - Define all useEffect triggers.
140
+ // Setup device listeners.
138
141
  useEffect(() => {
139
- DeviceEventEmitter.addListener("visibility-event", (event) => {
142
+ DeviceEventEmitter.addListener("visibility-event", (event: boolean) => {
140
143
  setIsAdVisibile(event);
141
144
  });
142
- DeviceEventEmitter.addListener("acknowledge", (itemName) => {
145
+
146
+ DeviceEventEmitter.addListener("acknowledge", (itemName: string) => {
143
147
  acknowledge(itemName);
144
148
  });
149
+
145
150
  return () => {
146
151
  clearTimeout(cycleAdTimer);
147
- DeviceEventEmitter.removeAllListeners("visibility-event");
148
152
  DeviceEventEmitter.removeAllListeners("acknowledge");
149
153
  };
154
+ // eslint-disable-next-line react-hooks/exhaustive-deps
150
155
  }, []);
151
156
 
157
+ // Send impression on ad cycle.
152
158
  useEffect(() => {
153
159
  startAdTimer();
154
- if (isAdVisible) {
160
+ if (isAdZoneVisible) {
155
161
  sendAdImpression();
156
162
  }
163
+ // eslint-disable-next-line react-hooks/exhaustive-deps
157
164
  }, [adIndexShown]);
158
165
 
159
166
  useEffect(() => {
160
- if (isAdVisible) {
167
+ if (
168
+ props.offScreenAdZone &&
169
+ isAdZoneVisible &&
170
+ props.adZoneData &&
171
+ props.adZoneData.ads &&
172
+ props.adZoneData.ads.length > adIndexShown &&
173
+ !props.adZoneData.ads[adIndexShown].impression_tracked
174
+ ) {
161
175
  sendAdImpression();
162
176
  }
163
- }, [isAdVisible]);
177
+ // eslint-disable-next-line react-hooks/exhaustive-deps
178
+ }, [isAdZoneVisible]);
164
179
 
165
180
  /**
166
181
  * Generates all component related styles.
@@ -176,20 +191,26 @@ export function AdZone(props: Props): JSX.Element {
176
191
  width: "100%",
177
192
  height: "100%",
178
193
  },
194
+ reportAd: {
195
+ position: "absolute",
196
+ top: 10,
197
+ right: 10,
198
+ },
179
199
  });
180
200
  }
181
201
 
182
202
  // Generate the styles each render in case the ad is updated with
183
203
  // new settings that need to be reflected in the styles of the view.
184
204
  const styles = generateStyles();
185
- const currentAd: Ad | undefined =
186
- props.adZoneData.ads[adIndexShown] || undefined;
187
205
  const finalMainViewStyle = styles.mainView;
188
206
 
189
- if (!currentAd || !currentAd.creative_url) {
207
+ if (
208
+ !props.adZoneData.ads[adIndexShown] ||
209
+ !props.adZoneData.ads[adIndexShown].creative_url
210
+ ) {
190
211
  // If there is no ad to display, make the view take up no space.
191
- finalMainViewStyle.width = "0";
192
- finalMainViewStyle.height = "0";
212
+ finalMainViewStyle.width = 0;
213
+ finalMainViewStyle.height = 0;
193
214
  }
194
215
 
195
216
  /**
@@ -225,11 +246,16 @@ export function AdZone(props: Props): JSX.Element {
225
246
  function acknowledge(itemName: string): void {
226
247
  if (props.adZoneData.ads) {
227
248
  props.adZoneData.ads.forEach((ad) => {
228
- ad.payload.detailed_list_items.forEach((item) => {
229
- if (item.product_title === itemName) {
230
- triggerReportAdEvent(ad, ReportedEventType.INTERACTION);
231
- }
232
- });
249
+ if (ad.action_type === "c") {
250
+ ad.payload.detailed_list_items.forEach((item) => {
251
+ if (item.product_title === itemName) {
252
+ triggerReportAdEvent(
253
+ ad,
254
+ ReportedEventType.INTERACTION
255
+ );
256
+ }
257
+ });
258
+ }
233
259
  });
234
260
  }
235
261
  }
@@ -273,10 +299,15 @@ export function AdZone(props: Props): JSX.Element {
273
299
  function startAdTimer(): void {
274
300
  clearTimeout(cycleAdTimer);
275
301
 
276
- if (props.adZoneData.ads.length > 0) {
302
+ if (
303
+ props.adZoneData.ads.length > 0 &&
304
+ props.adZoneData.ads[adIndexShown]
305
+ ) {
277
306
  const refreshTime: number =
278
307
  props.adZoneData.ads[adIndexShown].refresh_time * 1000;
279
308
  cycleAdTimer = setTimeout(cycleDisplayedAd, refreshTime);
309
+ } else {
310
+ cycleAdTimer = setTimeout(cycleDisplayedAd, 30000);
280
311
  }
281
312
  }
282
313
 
@@ -284,18 +315,18 @@ export function AdZone(props: Props): JSX.Element {
284
315
  * Cycles to the next ad to display in the current available sequence of ads.
285
316
  */
286
317
  function cycleDisplayedAd(): void {
287
- // Start by determining the next ad index to display.
288
318
  let nextAdIndex = 0;
319
+
320
+ // Start by determining the next ad index to display.
289
321
  const lastAd = props.adZoneData.ads[adIndexShown];
290
322
 
291
323
  if (adIndexShown < props.adZoneData.ads.length - 1) {
292
324
  nextAdIndex = adIndexShown + 1;
325
+ } else {
326
+ nextAdIndex = 0;
293
327
  }
294
328
 
295
- if (nextAdIndex !== adIndexShown && lastAd.impression_tracked) {
296
- // Reset ad impression tracking status.
297
- lastAd.impression_tracked = false;
298
- } else {
329
+ if (!lastAd.impression_tracked && !isAdZoneVisible) {
299
330
  // Send invisible ad impression if ad was not visible before end of timer cycle.
300
331
  triggerReportAdEvent(
301
332
  lastAd,
@@ -303,6 +334,11 @@ export function AdZone(props: Props): JSX.Element {
303
334
  );
304
335
  }
305
336
 
337
+ if (lastAd.impression_tracked) {
338
+ // Reset ad impression tracking status.
339
+ lastAd.impression_tracked = false;
340
+ }
341
+
306
342
  setAdIndexShown(nextAdIndex);
307
343
  }
308
344
 
@@ -312,8 +348,7 @@ export function AdZone(props: Props): JSX.Element {
312
348
  function sendAdImpression(): void {
313
349
  const ad = props.adZoneData.ads[adIndexShown];
314
350
 
315
- // Trigger an impression event for the ad.
316
- if (!ad.impression_tracked) {
351
+ if (ad.impression_tracked === undefined || !ad.impression_tracked) {
317
352
  triggerReportAdEvent(ad, ReportedEventType.IMPRESSION);
318
353
  ad.impression_tracked = true;
319
354
  }
@@ -322,10 +357,11 @@ export function AdZone(props: Props): JSX.Element {
322
357
  // Returned JSX.
323
358
  return (
324
359
  <View style={finalMainViewStyle}>
325
- {currentAd && currentAd.creative_url ? (
360
+ {props.adZoneData.ads[adIndexShown] &&
361
+ props.adZoneData.ads[adIndexShown].creative_url ? (
326
362
  <WebView
327
363
  source={{
328
- uri: currentAd.creative_url,
364
+ uri: props.adZoneData.ads[adIndexShown].creative_url,
329
365
  }}
330
366
  androidLayerType="hardware"
331
367
  automaticallyAdjustContentInsets={false}
@@ -351,7 +387,9 @@ export function AdZone(props: Props): JSX.Element {
351
387
  touchStartCoords.y - touchEndCoords.y
352
388
  ) < props.xyDragDistanceAllowed
353
389
  ) {
354
- onAdZoneSelected(currentAd);
390
+ onAdZoneSelected(
391
+ props.adZoneData.ads[adIndexShown]
392
+ );
355
393
  }
356
394
 
357
395
  // Make sure to reset the start coords
@@ -360,6 +398,16 @@ export function AdZone(props: Props): JSX.Element {
360
398
  }}
361
399
  />
362
400
  ) : undefined}
401
+ <View style={styles.reportAd}>
402
+ {props.adZoneData && props.adZoneData.ads[adIndexShown] ? (
403
+ <ReportAdButton
404
+ adId={props.adZoneData.ads[adIndexShown].ad_id}
405
+ udid={props.udid}
406
+ />
407
+ ) : (
408
+ <></>
409
+ )}
410
+ </View>
363
411
  </View>
364
412
  );
365
- }
413
+ };
@@ -0,0 +1,46 @@
1
+ import React, { useEffect } from "react";
2
+ import { Image, Linking, StyleSheet, TouchableOpacity } from "react-native";
3
+
4
+ interface Props {
5
+ /**
6
+ * The ad id of the current ad.
7
+ */
8
+ adId: string;
9
+ /**
10
+ * The current user's udid.
11
+ */
12
+ udid: string;
13
+ }
14
+
15
+ /**
16
+ * Creates the ReportAdButton component.
17
+ * @param props The component's props.
18
+ * @returns a reportSdButton JSX Element.
19
+ */
20
+ export function ReportAdButton(props: Props): React.JSX.Element {
21
+ const reportAdUrlBase = new URL("https://feedback.add-it.io/");
22
+
23
+ const styles = StyleSheet.create({
24
+ buttonStyle: {
25
+ width: 14,
26
+ height: 14,
27
+ },
28
+ });
29
+
30
+ useEffect(() => {
31
+ reportAdUrlBase.searchParams.append("aid", props.adId);
32
+ reportAdUrlBase.searchParams.append("uid", props.udid);
33
+ // eslint-disable-next-line react-hooks/exhaustive-deps
34
+ }, [props]);
35
+
36
+ return (
37
+ <TouchableOpacity
38
+ style={styles.buttonStyle}
39
+ onPress={() => {
40
+ Linking.openURL(reportAdUrlBase.toString());
41
+ }}
42
+ >
43
+ <Image source={require("../images/ReportIcon.png")} />
44
+ </TouchableOpacity>
45
+ );
46
+ }
Binary file