@cardsjd/portal 0.1.9 → 0.1.11

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 CHANGED
Binary file
@@ -1,17 +1,6 @@
1
- import { AdMobPortal } from './AdMobPortal';
1
+ import { NextGenAdMobPortal } from './NextGenAdMobPortal.js';
2
2
 
3
- export class AndroidPortal extends AdMobPortal {
3
+ export class AndroidPortal extends NextGenAdMobPortal {
4
4
  name = 'AndroidPortal';
5
5
 
6
- updateConfig(config = {}, adConfig = {}) {
7
- const configToUpdate = structuredClone(config);
8
-
9
- // Map android-specific ad config items to generic "ad" config items
10
- configToUpdate.interstitialAdId = adConfig.admobInterstitialAdId_Android;
11
- configToUpdate.rewardedAdId = adConfig.admobRewardedAdId_Android;
12
- configToUpdate.appOpenAdId = adConfig.admobAppOpenAdId_Android;
13
- configToUpdate.appId = adConfig.admobAppId_Android;
14
-
15
- return configToUpdate;
16
- }
17
- }
6
+ }
package/lib/BasePortal.js CHANGED
@@ -42,9 +42,15 @@ export class BasePortal {
42
42
 
43
43
  isAdLoaded() { }
44
44
 
45
- isRewardedAdLoaded() { }
46
-
47
- logError(_errorCode) { }
45
+ isRewardedAdLoaded() { }
46
+
47
+ isPrivacyOptionsRequired() {
48
+ return false;
49
+ }
50
+
51
+ async showPrivacyOptions() { }
52
+
53
+ logError(_errorCode) { }
48
54
 
49
55
  isErrorState() { }
50
56
 
@@ -1,4 +1,4 @@
1
- import { BasePortal } from './BasePortal';
1
+ import { BasePortal } from './BasePortal';
2
2
  import { FBInstantGame } from '@cardsjd/fbinstantgame';
3
3
 
4
4
  export class FacebookPortal extends BasePortal {
@@ -32,15 +32,20 @@ export class FacebookPortal extends BasePortal {
32
32
 
33
33
  async load() {
34
34
  const fbUserdata = await this.fBInstantGame.loadData();
35
- const fbProfile = await this.fBInstantGame.getProfile();
36
- const data = {};
37
- data.userdata = fbUserdata;
38
- data.user = fbProfile;
35
+ const fbProfile = await this.fBInstantGame.getProfile();
36
+ const data = {};
37
+ data.userdata = fbUserdata;
38
+ data.user = fbProfile ? {
39
+ facebookId: fbProfile.facebookId ?? fbProfile.id,
40
+ name: fbProfile.name,
41
+ avatar: fbProfile.avatar ?? fbProfile.pic,
42
+ locale: fbProfile.locale,
43
+ } : null;
39
44
  return data;
40
45
  }
41
46
 
42
- async save(data, forceSave) {
43
- this.fBInstantGame.saveData(data, forceSave).catch(function () { });
47
+ async save(data, forceSave) {
48
+ this.fBInstantGame.saveData(data, forceSave).catch(function () { });
44
49
  }
45
50
 
46
51
  gameStart() {
@@ -60,10 +65,10 @@ export class FacebookPortal extends BasePortal {
60
65
 
61
66
  this.subscribeBot();
62
67
  }
63
- createShortCut() {
64
- this.portal?.createShortCut?.();
65
- }
66
- async subscribeBot() {
68
+ createShortCut() {
69
+ this.portal?.createShortCut?.();
70
+ }
71
+ async subscribeBot() {
67
72
  await this.fBInstantGame.subscribeBot();
68
73
  }
69
74
 
@@ -183,7 +188,7 @@ export class FacebookPortal extends BasePortal {
183
188
 
184
189
  this._isAdLoading = true;
185
190
 
186
- const interstitialAdId = this.appConfig?.interstitalAdId;
191
+ const interstitialAdId = this.appConfig?.interstitialAdId;
187
192
 
188
193
  console.log('FB_Ads.getInterstitialAdAsync start: ' + interstitialAdId);
189
194
  if (!this.isApiSupported('getInterstitialAdAsync')) {
@@ -285,7 +290,7 @@ export class FacebookPortal extends BasePortal {
285
290
  console.log('FBInstant plugin not ready'); return false;
286
291
  }
287
292
 
288
- const interstitialAdId = this.appConfig?.interstitalAdId;
293
+ const interstitialAdId = this.appConfig?.interstitialAdId;
289
294
  if (!this.isApiSupported('getInterstitialAdAsync')) {
290
295
  this.logError('CLIENT_UNSUPPORTED_OPERATION');
291
296
  console.warn('FBInstant.getInterstitialAdAsync is not supported on this client');
@@ -0,0 +1,400 @@
1
+ import { BasePortal } from './BasePortal.js';
2
+
3
+ export class NextGenAdMobPortal extends BasePortal {
4
+ name = 'NextGenAdMobPortal';
5
+ autoLogin = false;
6
+
7
+ constructor(appConfig) {
8
+ super();
9
+ this.appConfig = appConfig;
10
+
11
+ // Ad callback properties
12
+ this.onAdLoaded = null;
13
+ this.onAdOpened = null;
14
+ this.onAdClosed = null;
15
+ this.onAdError = null;
16
+ this.onAdRewarded = null;
17
+ this.onBeforeTrackingAuthorization = null;
18
+ this.onPrivacyOptionsRequirementChanged = null;
19
+
20
+ this.adProviderName = 'CapgoAdmob';
21
+ this.isSetup = false;
22
+ this.isSetupLoading = false;
23
+ this.isInterstitialAdReady = false;
24
+ this._isRewardedAdLoaded = false;
25
+ this._receivedReward = false;
26
+ this._rewardInfo = null;
27
+ this._interstitialAd = null;
28
+ this._interstitialLoadPromise = null;
29
+ this._rewardedAd = null;
30
+ this._adMobModule = null;
31
+ this._umpModule = null;
32
+ this._consentInfoUpdated = false;
33
+ this._consentGathered = false;
34
+ this._canRequestAds = false;
35
+ this._privacyOptionsRequired = false;
36
+ this.isTesting = false;
37
+ }
38
+
39
+ async getAdMob() {
40
+ if (!this._adMobModule) {
41
+ // eslint-disable-next-line import/no-unresolved
42
+ this._adMobModule = await import('@capgo/capacitor-admob');
43
+ }
44
+
45
+ return this._adMobModule;
46
+ }
47
+
48
+ async loadUmp() {
49
+ if (!this._umpModule) {
50
+ // eslint-disable-next-line import/no-unresolved
51
+ const { GoogleUmpConsent } = await import('@cardsjd/google-ump');
52
+ this._umpModule = GoogleUmpConsent;
53
+ }
54
+ }
55
+
56
+ async requestTrackingAuthorization(_consentInfo) {
57
+ return false;
58
+ }
59
+
60
+ async canStartAdsWithoutPrompt() {
61
+ return true;
62
+ }
63
+
64
+ setConsentInfo(consentInfo) {
65
+ const wasRequired = this._privacyOptionsRequired;
66
+ if (typeof consentInfo?.canRequestAds === 'boolean') {
67
+ this._canRequestAds = consentInfo.canRequestAds;
68
+ }
69
+
70
+ this._privacyOptionsRequired = consentInfo?.privacyOptionsRequired === true;
71
+
72
+ if (
73
+ wasRequired !== this._privacyOptionsRequired &&
74
+ this.onPrivacyOptionsRequirementChanged
75
+ ) {
76
+ this.onPrivacyOptionsRequirementChanged(this._privacyOptionsRequired);
77
+ }
78
+ }
79
+
80
+ isPrivacyOptionsRequired() {
81
+ return this._privacyOptionsRequired;
82
+ }
83
+
84
+ async showPrivacyOptions() {
85
+ await this.loadUmp();
86
+ const consentInfo = await this._umpModule.showPrivacyOptions();
87
+ this.setConsentInfo(consentInfo);
88
+ return consentInfo;
89
+ }
90
+
91
+ async updateConsentInfo() {
92
+ await this.loadUmp();
93
+ const consentInfo = await this._umpModule.updateConsentInfo();
94
+ this._consentInfoUpdated = true;
95
+ this.setConsentInfo(consentInfo);
96
+ return consentInfo;
97
+ }
98
+
99
+ async gatherConsent() {
100
+ await this.loadUmp();
101
+ const consentInfo = await this._umpModule.gatherConsent();
102
+ this._consentInfoUpdated = true;
103
+ this._consentGathered = true;
104
+ this.setConsentInfo(consentInfo);
105
+ return consentInfo;
106
+ }
107
+
108
+ getAdError(info, fallbackMessage) {
109
+ return new Error(info?.error?.message || info?.message || fallbackMessage);
110
+ }
111
+
112
+ // ----- Ad methods -----
113
+
114
+ async setupAds() {
115
+ if (this.isSetup || this._consentInfoUpdated) {
116
+ return;
117
+ }
118
+
119
+ if (this.isSetupLoading) {
120
+ throw Error('Capgo AdMob setup is in progress already');
121
+ }
122
+
123
+ this.isSetupLoading = true;
124
+
125
+ try {
126
+ await this.updateConsentInfo();
127
+ } catch (error) {
128
+ console.error('Failed to update UMP consent info:', error);
129
+ } finally {
130
+ this.isSetupLoading = false;
131
+ }
132
+ }
133
+
134
+ async ensureAdsReady() {
135
+ if (this.isSetup) {
136
+ return {
137
+ canRequestAds: true,
138
+ promptShown: false,
139
+ };
140
+ }
141
+
142
+ let consentInfo;
143
+ try {
144
+ if (!this._consentInfoUpdated) {
145
+ await this.updateConsentInfo();
146
+ }
147
+
148
+ consentInfo = this._consentGathered ? {
149
+ canRequestAds: this._canRequestAds,
150
+ } : await this.gatherConsent();
151
+ } catch (error) {
152
+ if (this.onAdError) {
153
+ this.onAdError(error);
154
+ }
155
+
156
+ return {
157
+ canRequestAds: false,
158
+ promptShown: false,
159
+ };
160
+ }
161
+
162
+ const trackingPromptShown = await this.requestTrackingAuthorization(consentInfo);
163
+ const promptShown = consentInfo?.formShown === true || trackingPromptShown;
164
+
165
+ if (consentInfo.canRequestAds !== true) {
166
+ return {
167
+ canRequestAds: false,
168
+ promptShown,
169
+ };
170
+ }
171
+
172
+ await this.startAds();
173
+ return {
174
+ canRequestAds: true,
175
+ promptShown,
176
+ };
177
+ }
178
+
179
+ async prepareAdsWithoutPrompt() {
180
+ if (this.isSetup) {
181
+ return true;
182
+ }
183
+
184
+ if (!this._consentInfoUpdated) {
185
+ await this.setupAds();
186
+ }
187
+
188
+ if (!this._canRequestAds || !await this.canStartAdsWithoutPrompt()) {
189
+ return false;
190
+ }
191
+
192
+ await this.startAds();
193
+ return true;
194
+ }
195
+
196
+ async startAds() {
197
+ if (this.isSetup) {
198
+ return;
199
+ }
200
+
201
+ if (this.isSetupLoading) {
202
+ throw Error('Capgo AdMob setup is in progress already');
203
+ }
204
+
205
+ this.isSetupLoading = true;
206
+
207
+ try {
208
+ const { AdMob } = await this.getAdMob();
209
+ await AdMob.start();
210
+
211
+ await AdMob.addListener('interstitial.show', (info) => {
212
+ if (info.id === this._interstitialAd?.id) {
213
+ this.isInterstitialAdReady = false;
214
+ if (this.onAdOpened) {
215
+ this.onAdOpened({
216
+ adType: 'interstitial',
217
+ adProvider: this.adProviderName,
218
+ });
219
+ }
220
+ }
221
+ });
222
+ await AdMob.addListener('interstitial.dismiss', (info) => {
223
+ if (info.id === this._interstitialAd?.id) {
224
+ this.isInterstitialAdReady = false;
225
+ this._interstitialAd = null;
226
+ if (this.onAdClosed) {
227
+ this.onAdClosed({
228
+ adType: 'interstitial',
229
+ adProvider: this.adProviderName,
230
+ });
231
+ }
232
+ }
233
+ });
234
+ await AdMob.addListener('interstitial.loadfail', (info) => {
235
+ if (info.id === this._interstitialAd?.id) {
236
+ this.isInterstitialAdReady = false;
237
+ this._interstitialAd = null;
238
+ if (this.onAdError) {
239
+ this.onAdError(this.getAdError(info, 'InterstitialAd failed to load'));
240
+ }
241
+ }
242
+ });
243
+ await AdMob.addListener('interstitial.showfail', (info) => {
244
+ if (info.id === this._interstitialAd?.id) {
245
+ this.isInterstitialAdReady = false;
246
+ this._interstitialAd = null;
247
+ if (this.onAdError) {
248
+ this.onAdError(this.getAdError(info, 'InterstitialAd failed to show'));
249
+ }
250
+ }
251
+ });
252
+ await AdMob.addListener('rewarded.dismiss', (info) => {
253
+ if (info.id === this._rewardedAd?.id) {
254
+ this._isRewardedAdLoaded = false;
255
+ this._rewardedAd = null;
256
+ if (this.onAdClosed) {
257
+ this.onAdClosed({
258
+ adType: 'rewarded',
259
+ adProvider: this.adProviderName,
260
+ });
261
+ }
262
+
263
+ if (this._receivedReward && this.onAdRewarded) {
264
+ this.onAdRewarded(this._rewardInfo);
265
+ }
266
+ }
267
+ });
268
+ await AdMob.addListener('rewarded.loadfail', (info) => {
269
+ if (info.id === this._rewardedAd?.id) {
270
+ this._isRewardedAdLoaded = false;
271
+ this._rewardedAd = null;
272
+ if (this.onAdError) {
273
+ this.onAdError(this.getAdError(info, 'RewardAd failed to load'));
274
+ }
275
+ }
276
+ });
277
+ await AdMob.addListener('rewarded.showfail', (info) => {
278
+ if (info.id === this._rewardedAd?.id) {
279
+ this._isRewardedAdLoaded = false;
280
+ this._rewardedAd = null;
281
+ if (this.onAdError) {
282
+ this.onAdError(this.getAdError(info, 'RewardAd failed to show'));
283
+ }
284
+ }
285
+ });
286
+ await AdMob.addListener('rewarded.reward', (info) => {
287
+ if (info.id === this._rewardedAd?.id) {
288
+ this._receivedReward = true;
289
+ this._rewardInfo = info.reward;
290
+ }
291
+ });
292
+
293
+ this.isSetup = true;
294
+ } finally {
295
+ this.isSetupLoading = false;
296
+ }
297
+ }
298
+
299
+ isAdLoaded() {
300
+ return this.isInterstitialAdReady;
301
+ }
302
+
303
+ isRewardedAdLoaded() {
304
+ return this._isRewardedAdLoaded;
305
+ }
306
+
307
+ async loadInterstitialAd() {
308
+ if (this.isAdLoaded()) {
309
+ return;
310
+ }
311
+
312
+ if (!this._interstitialLoadPromise) {
313
+ this._interstitialLoadPromise = (async () => {
314
+ if (!await this.prepareAdsWithoutPrompt()) {
315
+ return;
316
+ }
317
+
318
+ const { InterstitialAd } = await this.getAdMob();
319
+ this._interstitialAd = new InterstitialAd({
320
+ adUnitId: this.appConfig?.interstitialAdId,
321
+ });
322
+
323
+ await this._interstitialAd.load();
324
+ this.isInterstitialAdReady = true;
325
+ })();
326
+ }
327
+
328
+ const loadPromise = this._interstitialLoadPromise;
329
+ try {
330
+ await loadPromise;
331
+ } catch (error) {
332
+ this._interstitialAd = null;
333
+ throw error;
334
+ } finally {
335
+ if (this._interstitialLoadPromise === loadPromise) {
336
+ this._interstitialLoadPromise = null;
337
+ }
338
+ }
339
+ }
340
+
341
+ async showInterstitialAd() {
342
+ const setup = await this.ensureAdsReady();
343
+ if (!setup.canRequestAds || setup.promptShown) {
344
+ return;
345
+ }
346
+
347
+ if (!this.isAdLoaded()) {
348
+ let timeoutId;
349
+ await Promise.race([this.loadInterstitialAd(), new Promise(resolve => {
350
+ timeoutId = setTimeout(resolve, 2000);
351
+ })]);
352
+ clearTimeout(timeoutId);
353
+ if (!this.isAdLoaded()) {
354
+ return;
355
+ }
356
+ }
357
+
358
+ this.isInterstitialAdReady = false;
359
+ return this._interstitialAd.show();
360
+ }
361
+
362
+ async loadRewardedAd() {
363
+ if (!await this.prepareAdsWithoutPrompt()) {
364
+ return;
365
+ }
366
+
367
+ if (this.isRewardedAdLoaded()) {
368
+ return;
369
+ }
370
+
371
+ const { RewardedAd } = await this.getAdMob();
372
+ this._rewardedAd = new RewardedAd({
373
+ adUnitId: this.appConfig?.rewardedAdId,
374
+ });
375
+
376
+ try {
377
+ await this._rewardedAd.load();
378
+ this._isRewardedAdLoaded = true;
379
+ this._receivedReward = false;
380
+ this._rewardInfo = null;
381
+ } catch (error) {
382
+ this._rewardedAd = null;
383
+ throw error;
384
+ }
385
+ }
386
+
387
+ async showRewardedAd() {
388
+ const setup = await this.ensureAdsReady();
389
+ if (!setup.canRequestAds || setup.promptShown) {
390
+ return;
391
+ }
392
+
393
+ if (!this.isRewardedAdLoaded()) {
394
+ await this.loadRewardedAd();
395
+ }
396
+
397
+ this._isRewardedAdLoaded = false;
398
+ return this._rewardedAd.show();
399
+ }
400
+ }
package/lib/iOSPortal.js CHANGED
@@ -1,7 +1,31 @@
1
- import { AdMobPortal } from './AdMobPortal';
2
-
3
- export class iOSPortal extends AdMobPortal {
4
- name = 'iOSPortal';
5
-
6
-
7
- }
1
+ import { NextGenAdMobPortal } from './NextGenAdMobPortal.js';
2
+
3
+ export class iOSPortal extends NextGenAdMobPortal {
4
+ name = 'iOSPortal';
5
+
6
+ async canStartAdsWithoutPrompt() {
7
+ const { AdMob, TrackingAuthorizationStatus } = await this.getAdMob();
8
+ const trackingInfo = await AdMob.trackingAuthorizationStatus();
9
+ return trackingInfo.status !== TrackingAuthorizationStatus.notDetermined;
10
+ }
11
+
12
+ async requestTrackingAuthorization(consentInfo) {
13
+ const { AdMob, TrackingAuthorizationStatus } = await this.getAdMob();
14
+ const trackingInfo = await AdMob.trackingAuthorizationStatus();
15
+
16
+ if (trackingInfo.status === TrackingAuthorizationStatus.notDetermined) {
17
+ // UMP reaches this fallback only when no IDFA explainer handled ATT.
18
+ if (this.onBeforeTrackingAuthorization) {
19
+ const shouldContinue = await this.onBeforeTrackingAuthorization();
20
+ if (shouldContinue === false) {
21
+ return false;
22
+ }
23
+ }
24
+
25
+ await AdMob.requestTrackingAuthorization();
26
+ return true;
27
+ }
28
+
29
+ return consentInfo?.trackingAuthorizationChanged === true;
30
+ }
31
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cardsjd/portal",
3
3
  "private": false,
4
- "version": "0.1.9",
4
+ "version": "0.1.11",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "publishConfig": {
@@ -17,8 +17,24 @@
17
17
  },
18
18
  "devDependencies": {},
19
19
  "dependencies": {
20
- "@capacitor-community/admob": "^7.0.3",
20
+ "@cardsjd/device": "^0.1.15",
21
+ "@cardsjd/fbinstantgame": "^0.1.12",
21
22
  "@discord/embedded-app-sdk": "^2.4.0"
22
23
  },
23
- "peerDependencies": {}
24
+ "peerDependencies": {
25
+ "@capacitor-community/admob": ">=7 <9",
26
+ "@capgo/capacitor-admob": ">=8 <9",
27
+ "@cardsjd/google-ump": ">=0.0.1 <1"
28
+ },
29
+ "peerDependenciesMeta": {
30
+ "@capacitor-community/admob": {
31
+ "optional": true
32
+ },
33
+ "@capgo/capacitor-admob": {
34
+ "optional": true
35
+ },
36
+ "@cardsjd/google-ump": {
37
+ "optional": true
38
+ }
39
+ }
24
40
  }