Skip to content

Commit e842477

Browse files
authored
feat!: add addAdEventListener method (invertase#110)
Fixes typing for listeners, fixes "only one listener at a time" bug * feat: add addAdEventListener method * refactor!: remove onAdEvent method * feat: add removeAllListeners method * test(e2e): replace onAdEvent with addAdEventListener * docs: add migration doc related to onAdEvent BREAKING CHANGES: AdEvent Listeners have changed how the work. There is a migration doc! The migration doc has code samples with the changes necessary, we hope it is easy - it is supposed to be easy https://github.com/invertase/react-native-google-mobile-ads/blob/main/docs/migrating-to-v6.mdx
1 parent 8e6fd48 commit e842477

19 files changed

+474
-197
lines changed

.spellcheck.dict.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,3 +150,4 @@ firebase-admin
150150
SSV
151151
CP-User
152152
Intellisense
153+
onAdEvent

__tests__/interstitial.test.ts

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { InterstitialAd } from '../src';
1+
import { AdEventType, InterstitialAd } from '../src';
22

33
describe('Google Mobile Ads Interstitial', function () {
44
describe('createForAdRequest', function () {
@@ -34,17 +34,44 @@ describe('Google Mobile Ads Interstitial', function () {
3434
});
3535
});
3636

37-
describe('onAdEvent', function () {
38-
it('throws if handler is not a function', function () {
37+
describe('addAdEventsListener', function () {
38+
it('throws if listener is not a function', function () {
3939
const i = InterstitialAd.createForAdRequest('abc');
4040

4141
// @ts-ignore
42-
expect(() => i.onAdEvent('foo')).toThrowError("'handler' expected a function");
42+
expect(() => i.addAdEventsListener('foo')).toThrowError("'listener' expected a function");
4343
});
4444

4545
it('returns an unsubscriber function', function () {
4646
const i = InterstitialAd.createForAdRequest('abc');
47-
const unsub = i.onAdEvent(() => {});
47+
const unsub = i.addAdEventsListener(() => {});
48+
expect(unsub).toBeDefined();
49+
unsub();
50+
});
51+
});
52+
53+
describe('addAdEventListener', function () {
54+
it('throws if type is not a AdEventType', function () {
55+
const i = InterstitialAd.createForAdRequest('abc');
56+
57+
// @ts-ignore
58+
expect(() => i.addAdEventListener('foo')).toThrowError(
59+
"'type' expected a valid event type value.",
60+
);
61+
});
62+
63+
it('throws if listener is not a function', function () {
64+
const i = InterstitialAd.createForAdRequest('abc');
65+
66+
// @ts-ignore
67+
expect(() => i.addAdEventListener(AdEventType.LOADED, 'foo')).toThrowError(
68+
"'listener' expected a function",
69+
);
70+
});
71+
72+
it('returns an unsubscriber function', function () {
73+
const i = InterstitialAd.createForAdRequest('abc');
74+
const unsub = i.addAdEventListener(AdEventType.LOADED, () => {});
4875
expect(unsub).toBeDefined();
4976
unsub();
5077
});

docs.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
["Displaying Ads via Hook", "/displaying-ads-hook"],
88
["European User Consent", "/european-user-consent"],
99
["Common Reasons For Ads Not Showing", "/common-reasons-for-ads-not-showing"],
10-
["Migrating to v5", "/migrating-to-v5"]
10+
["Migrating to v5", "/migrating-to-v5"],
11+
["Migrating to v6", "/migrating-to-v6"]
1112
]
1213
}

docs/displaying-ads.mdx

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ The call to `createForAdRequest` returns an instance of the [`InterstitialAd`](/
8989
which provides a number of utilities for loading and displaying interstitials.
9090

9191
To listen to events, such as when the advert from the network has loaded or when an error occurs, we can subscribe via the
92-
`onAdEvent` method:
92+
`addAdEventListener` method:
9393

9494
```jsx
9595
import React, { useEffect, useState } from 'react';
@@ -107,19 +107,15 @@ function App() {
107107
const [loaded, setLoaded] = useState(false);
108108

109109
useEffect(() => {
110-
const eventListener = interstitial.onAdEvent(type => {
111-
if (type === AdEventType.LOADED) {
112-
setLoaded(true);
113-
}
110+
const unsubscribe = interstitial.addAdEventListener(AdEventType.LOADED => {
111+
setLoaded(true);
114112
});
115113

116114
// Start loading the interstitial straight away
117115
interstitial.load();
118116

119117
// Unsubscribe from events on unmount
120-
return () => {
121-
eventListener();
122-
};
118+
return unsubscribe;
123119
}, []);
124120

125121
// No advert ready to show yet
@@ -138,12 +134,12 @@ function App() {
138134
}
139135
```
140136

141-
The code above subscribes to the interstitial events (via `onAdEvent()`) and immediately starts to load a new advert from
137+
The code above subscribes to the interstitial events (via `addAdEventListener()`) and immediately starts to load a new advert from
142138
the network (via `load()`). Once an advert is available, local state is set, re-rendering the component showing a `Button`.
143139
When pressed, the `show` method on the interstitial instance is called and the advert is shown over-the-top of your
144140
application.
145141

146-
The `onAdEvent` listener also triggers when events inside of the application occur, such as if the user clicks the advert,
142+
You can subscribe to other various events with `addAdEventListener` listener such as if the user clicks the advert,
147143
or closes the advert and returns back to your app. To view a full list of events which are available, view the
148144
[`AdEventType`](/reference/admob/adeventtype) documentation.
149145

@@ -181,7 +177,7 @@ The call to `createForAdRequest` returns an instance of the [`RewardedAd`](/refe
181177
which provides a number of utilities for loading and displaying rewarded ads.
182178

183179
To listen to events, such as when the advert from the network has loaded or when an error occurs, we can subscribe via the
184-
`onAdEvent` method:
180+
`addAdEventListener` method:
185181

186182
```js
187183
import React, { useEffect, useState } from 'react';
@@ -199,22 +195,23 @@ function App() {
199195
const [loaded, setLoaded] = useState(false);
200196

201197
useEffect(() => {
202-
const eventListener = rewarded.onAdEvent((type, error, reward) => {
203-
if (type === RewardedAdEventType.LOADED) {
204-
setLoaded(true);
205-
}
206-
207-
if (type === RewardedAdEventType.EARNED_REWARD) {
208-
console.log('User earned reward of ', reward);
209-
}
198+
const unsubscribeLoaded = rewarded.addAdEventListener(RewardedAdEventType.LOADED, () => {
199+
setLoaded(true);
210200
});
201+
const unsubscribeEarned = rewarded.addAdEventListener(
202+
RewardedAdEventType.EARNED_REWARD,
203+
reward => {
204+
console.log('User earned reward of ', reward);
205+
},
206+
);
211207

212208
// Start loading the rewarded ad straight away
213209
rewarded.load();
214210

215211
// Unsubscribe from events on unmount
216212
return () => {
217-
eventListener();
213+
unsubscribeLoaded();
214+
unsubscribeEarned();
218215
};
219216
}, []);
220217

@@ -234,15 +231,14 @@ function App() {
234231
}
235232
```
236233

237-
The code above subscribes to the rewarded ad events (via `onAdEvent()`) and immediately starts to load a new advert from
234+
The code above subscribes to the rewarded ad events (via `addAdEventListener()`) and immediately starts to load a new advert from
238235
the network (via `load()`). Once an advert is available, local state is set, re-rendering the component showing a `Button`.
239236
When pressed, the `show` method on the rewarded ad instance is called and the advert is shown over-the-top of your
240237
application.
241238

242-
Like Interstitial Ads, the events returns from the `onAdEvent` listener trigger when the user clicks the advert or closes
243-
the advert and returns back to your app. However, an extra `EARNED_REWARD` event can be triggered if the user completes the
244-
advert action. An additional `reward` property is sent with the event, containing the amount and type of rewarded (specified via the dashboard).
245-
An additional `reward` property is sent with the event, containing the amount and type of rewarded (specified via the dashboard).
239+
Like Interstitial Ads, you can listen to the events with the `addAdEventListener` such as when the user clicks the advert or closes
240+
the advert and returns back to your app. However, you can listen to an extra `EARNED_REWARD` event which is triggered when user completes the
241+
advert action. An additional `reward` payload is sent with the event, containing the amount and type of rewarded (specified via the dashboard).
246242

247243
To learn more, view the [`RewardedAdEventType`](/reference/admob/rewardedadeventtype) documentation.
248244

docs/migrating-to-v6.mdx

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# Migrating to v6
2+
3+
## `onAdEvent` Removed
4+
5+
`onAdEvent` had two problems:
6+
7+
- Only one listener could be registered to an ad instance. Attempting to register a second listener replaced previous listeners
8+
- There were type conflicts around the event payload because typescript didn't know which event was sent to the listener
9+
10+
In order to resolve these problems, `onAdEvent` is replaced by `addAdEventListener` and `addAdEventsListener`.
11+
12+
### `addAdEventListener`
13+
14+
With each call to `addAdEventListener`, you add a listener for one specific event type. The event type to listen for is the first argument and your event handler is the second argument. The type event payload type for the handler is automatically inferred from the event type you passed.
15+
16+
```diff
17+
import {
18+
AdEventType,
19+
RewardedAd,
20+
RewardedAdEventType
21+
} from 'react-native-google-mobile-ads';
22+
23+
const rewardedAd = RewardedAd.createForAdRequest('...');
24+
-rewardedAd.onAdEvent((type, error, reward) => {
25+
- if (type === RewardedAdEventType.LOADED) {
26+
- console.log('Ad has loaded');
27+
- }
28+
- if (type === RewardedAdEventType.EARNED_REWARD) {
29+
- console.log('User earned reward of ', reward);
30+
- }
31+
- if (type === AdEventType.ERROR) {
32+
- console.log('Ad failed to load with error: ', error);
33+
- }
34+
-}
35+
+rewardedAd.addAdEventListener(RewardedAdEventType.Loaded, () => {
36+
+ console.log('Ad has loaded');
37+
+});
38+
+rewardedAd.addAdEventListener(RewardedAdEventType.EARNED_REWARD, (reward) => {
39+
+ console.log('User earned reward of ', reward);
40+
+});
41+
+rewardedAd.addAdEventListener(AdEventType.ERROR, (error) => {
42+
+ console.log('Ad failed to load with error: ', error);
43+
+});
44+
```
45+
46+
To unsubscribe from an event, call the function returned from `addAdEventListener`.
47+
48+
```js
49+
const unsubscribe = interstitialAd.addAdEventListener(AdEventType.Loaded, () => {
50+
console.log('Ad has loaded');
51+
});
52+
53+
// Sometime later...
54+
unsubscribe();
55+
```
56+
57+
### `addAdEventsListener`
58+
59+
With `addAdEventsListener`, you can listen for all of event types as legacy `onAdEvent` did. The handler now passes an object with event type and payload. You have to cast the payload to a corresponding type in order to use in typescript.
60+
61+
```diff
62+
import {
63+
AdEventType,
64+
RewardedAd,
65+
RewardedAdEventType,
66+
RewardedAdReward
67+
} from 'react-native-google-mobile-ads';
68+
69+
const rewardedAd = RewardedAd.createForAdRequest('...');
70+
-rewardedAd.onAdEvent((type, error, reward) => {
71+
+rewardedAd.addAdEventsListener(({ type, payload }) => {
72+
if (type === RewardedAdEventType.LOADED) {
73+
console.log('Ad has loaded');
74+
}
75+
if (type === RewardedAdEventType.EARNED_REWARD) {
76+
+ const reward = payload as RewardedAdReward;
77+
console.log(`User earned reward, name: ${reward.name}, amount: ${reward.amount}`);
78+
}
79+
if (type === AdEventType.ERROR) {
80+
+ const error = payload as Error;
81+
console.log('Ad failed to load with error: ', error.message);
82+
}
83+
}
84+
```
85+
86+
To unsubscribe from events, call the function returned from `addAdEventsListener`.
87+
88+
```js
89+
const unsubscribe = interstitialAd.addAdEventsListener(({ type, payload }) => {
90+
console.log('Ad event: ', type, payload);
91+
});
92+
93+
// Sometime later...
94+
unsubscribe();
95+
```
96+
97+
### `removeAllListeners`
98+
99+
You can remove all listeners registered with `addAdEventListener` and `addAdEventsListener` by calling `removeAllListeners` method.
100+
101+
```js
102+
interstitialAd.addAdEventListener(AdEventType.LOADED, () => {
103+
console.log('Ad has loaded');
104+
});
105+
interstitialAd.addAdEventsListener(({ type, payload }) => {
106+
console.log('Ad event: ', type, payload);
107+
});
108+
109+
// Sometime later...
110+
interstitialAd.removeAllListeners();
111+
```

e2e/interstitial.e2e.js

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,14 @@ describe('googleAds InterstitialAd', function () {
4343
requestAgent: 'CoolAds',
4444
});
4545

46-
i.onAdEvent(spy);
46+
i.addAdEventListener(googleAds.AdEventType.LOADED, spy);
4747
i.load();
4848
await Utils.spyToBeCalledOnceAsync(spy, 20000);
4949
i.loaded.should.eql(true);
50-
51-
spy.getCall(0).args[0].should.eql('loaded');
5250
});
5351
});
5452

55-
describe('onAdEvent', function () {
53+
describe('addAdEventListener', function () {
5654
it('unsubscribe should prevent events', async function () {
5755
// Ads on Android in CI load a webview and a bunch of other things so slowly the app ANRs.
5856
if (device.getPlatform() === 'android' && global.isCI == true) {
@@ -61,7 +59,7 @@ describe('googleAds InterstitialAd', function () {
6159

6260
const spy = sinon.spy();
6361
const i = InterstitialAd.createForAdRequest('abc');
64-
const unsub = i.onAdEvent(spy);
62+
const unsub = i.addAdEventListener(googleAds.AdEventType.LOADED, spy);
6563
unsub();
6664
i.load();
6765
await Utils.sleep(2000);
@@ -78,12 +76,10 @@ describe('googleAds InterstitialAd', function () {
7876

7977
const i = InterstitialAd.createForAdRequest(googleAds.TestIds.INTERSTITIAL);
8078

81-
i.onAdEvent(spy);
79+
i.addAdEventListener(googleAds.AdEventType.LOADED, spy);
8280
i.load();
8381
await Utils.spyToBeCalledOnceAsync(spy, 20000);
8482
i.loaded.should.eql(true);
85-
86-
spy.getCall(0).args[0].should.eql('loaded');
8783
});
8884

8985
it('errors with an invalid ad unit id', async function () {
@@ -96,12 +92,11 @@ describe('googleAds InterstitialAd', function () {
9692

9793
const i = InterstitialAd.createForAdRequest('123');
9894

99-
i.onAdEvent(spy);
95+
i.addAdEventListener(googleAds.AdEventType.ERROR, spy);
10096
i.load();
10197
await Utils.spyToBeCalledOnceAsync(spy);
10298

103-
spy.getCall(0).args[0].should.eql('error');
104-
const e = spy.getCall(0).args[1];
99+
const e = spy.getCall(0).args[0];
105100
e.code.should.containEql('googleAds/'); // android/ios different errors
106101
});
107102
});

0 commit comments

Comments
 (0)