@cardsjd/portal 0.1.1 → 0.1.2

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.
@@ -0,0 +1,303 @@
1
+ import { BasePortal } from './BasePortal.js';
2
+
3
+ export class PokiPortal extends BasePortal {
4
+ name = 'PokiPortal';
5
+ autoLogin = true;
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
+
18
+ this.adProviderName = 'poki';
19
+ this.isSetup = false;
20
+ this.isSetupLoading = false;
21
+ }
22
+
23
+ async init() {
24
+ if (typeof globalThis.PokiSDK === 'undefined') {
25
+ try {
26
+ await this.loadScript();
27
+ } catch (err) {
28
+ console.error('Failed to load Poki SDK script:', err);
29
+ return;
30
+ }
31
+ }
32
+
33
+ try {
34
+ await globalThis.PokiSDK.init();
35
+ this.isSetup = true;
36
+ } catch (err) {
37
+ console.warn('Poki SDK initialized with adblock or error:', err);
38
+ // Set isSetup = true to allow the game to continue even with adblock
39
+ this.isSetup = true;
40
+ }
41
+ }
42
+
43
+ async loadScript() {
44
+ return new Promise((resolve, reject) => {
45
+ const script = document.createElement('script');
46
+ script.src = 'https://game-cdn.poki.com/scripts/v2/poki-sdk.js';
47
+ script.onload = () => resolve();
48
+ script.onerror = () => reject(new Error('Failed to load Poki SDK script'));
49
+ document.head.appendChild(script);
50
+ });
51
+ }
52
+
53
+ isReady() {
54
+ return typeof globalThis.PokiSDK !== 'undefined' && this.isSetup;
55
+ }
56
+
57
+ async load() {
58
+ if (!this.isReady()) {
59
+ return;
60
+ }
61
+
62
+ const data = {};
63
+ try {
64
+ data.userdata = await this.loadData();
65
+ data.user = await this.loadUser();
66
+ } catch (err) {
67
+ console.warn('Failed to call Poki load:', err);
68
+ }
69
+
70
+ return data;
71
+ }
72
+
73
+ async save(data, _forceSave) {
74
+ if (!this.isReady()) {
75
+ return;
76
+ }
77
+
78
+ try {
79
+ globalThis.localStorage.setItem('poki_userdata', JSON.stringify(data));
80
+ } catch (err) {
81
+ console.warn('Failed to save Poki userdata to localStorage:', err);
82
+ }
83
+ }
84
+
85
+ async delete() {
86
+ if (!this.isReady()) {
87
+ return;
88
+ }
89
+
90
+ try {
91
+ globalThis.localStorage.removeItem('poki_userdata');
92
+ } catch (err) {
93
+ console.warn('Failed to delete Poki userdata from localStorage:', err);
94
+ }
95
+ }
96
+
97
+ async loadData() {
98
+ try {
99
+ const stringified = globalThis.localStorage.getItem('poki_userdata');
100
+ return stringified ? JSON.parse(stringified) : null;
101
+ } catch (err) {
102
+ console.warn('Failed to load Poki userdata from localStorage:', err);
103
+ return null;
104
+ }
105
+ }
106
+
107
+ async loadUser() {
108
+ if (!this.isReady()) {
109
+ return null;
110
+ }
111
+
112
+ try {
113
+ const userdetails = await globalThis.PokiSDK.getUser();
114
+ if (!userdetails) {
115
+ return null;
116
+ }
117
+
118
+ const user = {};
119
+ if (userdetails.username) {
120
+ user.name = userdetails.username;
121
+ user.pokiId = userdetails.username;
122
+ }
123
+
124
+ if (userdetails.avatarUrl) {
125
+ user.avatar = userdetails.avatarUrl;
126
+ }
127
+
128
+ return user;
129
+ } catch (err) {
130
+ console.warn('Failed to get Poki user details:', err);
131
+ return null;
132
+ }
133
+ }
134
+
135
+ gameStart() {
136
+ if (!this.isReady()) {
137
+ return;
138
+ }
139
+
140
+ try {
141
+ globalThis.PokiSDK.gameplayStart();
142
+ } catch (err) {
143
+ console.warn('Failed to call gameplayStart:', err);
144
+ }
145
+ }
146
+
147
+ gameStop() {
148
+ if (!this.isReady()) {
149
+ return;
150
+ }
151
+
152
+ try {
153
+ globalThis.PokiSDK.gameplayStop();
154
+ } catch (err) {
155
+ console.warn('Failed to call gameplayStop:', err);
156
+ }
157
+ }
158
+
159
+ loadStart() {
160
+ // Not explicitly needed for Poki, but we implement the base method
161
+ }
162
+
163
+ loadStop() {
164
+ if (!this.isReady()) {
165
+ return;
166
+ }
167
+
168
+ try {
169
+ globalThis.PokiSDK.gameLoadingFinished();
170
+ } catch (err) {
171
+ console.warn('Failed to call gameLoadingFinished:', err);
172
+ }
173
+ }
174
+
175
+ // ----- Ad methods -----
176
+
177
+ async setupAds() {
178
+ if (!this.isReady()) {
179
+ return false;
180
+ }
181
+
182
+ return true;
183
+ }
184
+
185
+ async loadAd() {
186
+ // Poki commercial breaks do not require manual preloading
187
+ }
188
+
189
+ async showAd() {
190
+ if (!this.isReady()) {
191
+ return false;
192
+ }
193
+
194
+ const adType = 'interstitial';
195
+ const adProvider = this.adProviderName;
196
+
197
+ try {
198
+ await globalThis.PokiSDK.commercialBreak(() => {
199
+ if (typeof this.onAdOpened === 'function') {
200
+ this.onAdOpened({
201
+ adType,
202
+ adProvider,
203
+ });
204
+ }
205
+ });
206
+
207
+ if (typeof this.onAdClosed === 'function') {
208
+ this.onAdClosed({
209
+ adType,
210
+ adProvider,
211
+ });
212
+ }
213
+
214
+ return true;
215
+ } catch (error) {
216
+ if (typeof this.onAdError === 'function') {
217
+ this.onAdError({
218
+ error,
219
+ adType,
220
+ adProvider,
221
+ });
222
+ }
223
+
224
+ if (typeof this.onAdClosed === 'function') {
225
+ this.onAdClosed({
226
+ adType,
227
+ adProvider,
228
+ });
229
+ }
230
+
231
+ return false;
232
+ }
233
+ }
234
+
235
+ isAdLoaded() {
236
+ return this.isReady();
237
+ }
238
+
239
+ isRewardedAdLoaded() {
240
+ return this.isReady();
241
+ }
242
+
243
+ async showRewardedAd() {
244
+ if (!this.isReady()) {
245
+ return false;
246
+ }
247
+
248
+ const adType = 'rewarded';
249
+ const adProvider = this.adProviderName;
250
+
251
+ try {
252
+ const success = await globalThis.PokiSDK.rewardedBreak({
253
+ onStart: () => {
254
+ if (typeof this.onAdOpened === 'function') {
255
+ this.onAdOpened({
256
+ adType,
257
+ adProvider,
258
+ });
259
+ }
260
+ },
261
+ });
262
+
263
+ if (success) {
264
+ if (typeof this.onAdRewarded === 'function') {
265
+ this.onAdRewarded({
266
+ adType,
267
+ adProvider,
268
+ });
269
+ }
270
+ }
271
+
272
+ if (typeof this.onAdClosed === 'function') {
273
+ this.onAdClosed({
274
+ adType,
275
+ adProvider,
276
+ });
277
+ }
278
+
279
+ return success;
280
+ } catch (error) {
281
+ if (typeof this.onAdError === 'function') {
282
+ this.onAdError({
283
+ error,
284
+ adType,
285
+ adProvider,
286
+ });
287
+ }
288
+
289
+ if (typeof this.onAdClosed === 'function') {
290
+ this.onAdClosed({
291
+ adType,
292
+ adProvider,
293
+ });
294
+ }
295
+
296
+ return false;
297
+ }
298
+ }
299
+
300
+ async loadRewardedAd() {
301
+ // Poki rewarded breaks do not require manual preloading
302
+ }
303
+ }
package/lib/Portal.js CHANGED
@@ -1,43 +1,47 @@
1
- import { Device } from '@cardsjd/device';
2
1
  import { BasePortal } from './BasePortal.js';
3
2
  import { CrazyGamesPortal } from './CrazyGamesPortal.js';
4
3
  import { FacebookPortal } from './FacebookPortal.js';
4
+ import { GameDistribution } from './GameDistributionPortal.js';
5
+ import { AndroidPortal } from './AndroidPortal.js';
6
+ import { iOSPortal } from './iOSPortal.js';
5
7
  import { WebPortal } from './WebPortal.js';
8
+ import { PokiPortal } from './PokiPortal.js';
6
9
  import { SteamPortal } from './SteamPortal.js';
7
10
  import { DiscordPortal } from './DiscordPortal.js';
8
- //import { GameDistributionPortal } from './GameDistributionPortal';
11
+
12
+ import { Device } from '../../device';
9
13
 
10
14
  const PortalLoaders = {
15
+ android: AndroidPortal,
16
+ amazon: AndroidPortal,
11
17
  crazygames: CrazyGamesPortal,
12
18
  facebook: FacebookPortal,
19
+ ios: iOSPortal,
13
20
  web: WebPortal,
14
21
  windowsPWA: WebPortal,
15
22
  windows: WebPortal,
16
- stream: SteamPortal,
23
+ gameDistribution: GameDistribution,
24
+ poki: PokiPortal,
25
+ steam: SteamPortal,
17
26
  discord: DiscordPortal,
18
- Discord: DiscordPortal
19
- //gamedistribution: GameDistributionPortal,
20
27
  };
21
28
 
22
-
23
29
  //Factory class for creating platform-specific portal instances.
24
30
  export class Portal {
25
31
  /**
26
32
  * Creates and initializes a portal instance based on the current platform.
27
- * @param {string} platform - The platform identifier
28
33
  * @param {Object} appConfig - Configuration object to pass to the portal
29
- * @param {Object} advertisementConfig - Configuration object to pass to the portal
34
+ * @param {string} platform - Platform to load, if undefined, will use Device class to determine platform
30
35
  * @returns {BasePortal} The initialized portal instance
31
36
  */
32
- static create(platform, appConfig, advertisementConfig) {
37
+ static create(appConfig, platform) {
33
38
 
34
39
  if (!platform) {
35
- const device = new Device();
36
- platform = device.getPlatform();
40
+ platform = Device.getPlatform();
37
41
  }
38
42
 
39
- const portalClass = PortalLoaders[platform] || BasePortal;
40
- const portal = new portalClass(appConfig, advertisementConfig);
43
+ const portalClass = PortalLoaders[platform];
44
+ const portal = portalClass ? new portalClass(appConfig) : new BasePortal(appConfig);
41
45
 
42
46
  return portal;
43
47
  }
package/lib/WebPortal.js CHANGED
@@ -6,25 +6,252 @@ export class WebPortal extends BasePortal {
6
6
  config = {};
7
7
  constructor(config) {
8
8
  super();
9
- this.config.version = config?.version;
9
+ this.config = config;
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
+
18
+ this.page_url = 'cardsjd.com';
19
+ this.adProviderName = 'GoogleH5';
20
+ this.isSetup = false;
21
+ this.isSetupLoading = false;
10
22
  }
23
+
11
24
  async init() {
12
25
  //set update google analytics dynamically
13
26
  //some platforms may block google analytics, so only add it for web portal
14
27
  this.addGoogleAnalytics(this.config.version); //no await no - fire and forget
15
28
  }
29
+
16
30
  //todo: move to lib so other portals can use it too
17
31
  async addGoogleAnalytics(version) {
18
32
  console.log('WebPortal: Initializing Google Analytics');
19
33
  //set update google analytics dynamically
20
34
  await import('https://www.googletagmanager.com/gtag/js?id=G-BMWB7H1PDJ');
21
- window.dataLayer = window.dataLayer || [];
22
- function gtag() { dataLayer.push(arguments); }
35
+ globalThis.dataLayer = globalThis.dataLayer || [];
36
+ function gtag() {
37
+ globalThis.dataLayer.push(arguments);
38
+ }
39
+
23
40
  gtag('js', new Date());
24
41
 
25
42
  gtag('config', 'G-BMWB7H1PDJ', {
26
- app_version: version
43
+ app_version: version,
27
44
  });// for Cribbage all platforms
28
45
  gtag('config', 'G-SBZ83TJLW6');// for website cardsjd.com
29
46
  }
30
- }
47
+
48
+ // ----- Ad methods -----
49
+
50
+ async setupAds() {
51
+ if (this.isSetupLoading) {
52
+ throw Error('GoogleH5 setup is in progress already');
53
+ }
54
+
55
+ this.isSetupLoading = true; //prevent async loading
56
+
57
+ if (this.isSetup) {
58
+ console.log('GoogleH5.isSetup is already done');
59
+ return;
60
+ }
61
+
62
+ try {
63
+ await this.loadScript();
64
+ console.log('adsbygoogle loaded');
65
+ } catch (error) {
66
+ console.log(error);
67
+ return;
68
+ }
69
+
70
+ if (typeof adConfig === 'undefined') {
71
+ console.log('GoogleH5 adBreak plugin not ready');
72
+ throw Error('GoogleH5 adBreak plugin not ready');
73
+ }
74
+
75
+ globalThis.adConfig({
76
+ preloadAdBreaks: 'on',
77
+ sound: 'on', //todo: check if muted or not
78
+ onReady: () => {
79
+ console.log('GoogleH5.isSetup is ready');
80
+ },
81
+ });
82
+ this.isSetup = true;
83
+ this.isSetupLoading = false;
84
+ }
85
+
86
+ async loadScript() {
87
+ return new Promise((resolve, reject) => {
88
+ window.adsbygoogle = window.adsbygoogle || [];
89
+
90
+ window.adBreak = window.adConfig = function (o) {
91
+ globalThis.adsbygoogle.push(o);
92
+ };
93
+
94
+ const ad_client = this.config?.adClient;
95
+ const ad_channel = this.config?.interstitialAdId;
96
+ const page_url = this.page_url;
97
+ const script = document.createElement('script');
98
+ script.src = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js';
99
+ script.setAttribute('async', '');
100
+ script.setAttribute('data-ad-client', ad_client);
101
+ script.setAttribute('data-ad-channel', ad_channel);
102
+ script.setAttribute('data-ad-frequency-hint', '60s');
103
+ script.setAttribute('data-page-url', page_url);
104
+ script.setAttribute('crossorigin', 'anonymous');
105
+
106
+ script.onload = () => {
107
+ resolve('adsbygoogle loaded');
108
+ };
109
+
110
+ script.onerror = () => {
111
+ reject('Error loading JS adsbygoogle');
112
+ };
113
+
114
+ document.head.appendChild(script);
115
+ });
116
+ }
117
+
118
+ loadAd() {
119
+ console.log('GoogleH5.loadAd');
120
+ }
121
+
122
+ showAd() {
123
+ console.log('GoogleH5.showAd');
124
+
125
+ if (typeof adBreak === 'undefined' || !globalThis.adBreak) {
126
+ console.log('GoogleH5 adBreak plugin not ready'); return;
127
+ }
128
+
129
+ if (!this.isSetup) {
130
+ console.log('GoogleH5.showAd - isSetup is false');
131
+ return;
132
+ }
133
+
134
+ globalThis.adBreak({
135
+ type: 'next', // The type of this placement (required)
136
+ name: 'post_game', // A descriptive name for this placement
137
+ beforeAd: () => {
138
+ if (this.onAdOpened) {
139
+ this.onAdOpened({
140
+ adType: 'interstitial',
141
+ adProvider: this.adProviderName,
142
+ });
143
+ }
144
+ },
145
+ afterAd: () => {
146
+ if (this.onAdClosed) {
147
+ this.onAdClosed({
148
+ adType: 'interstitial',
149
+ adProvider: this.adProviderName,
150
+ });
151
+ }
152
+ },
153
+ adBreakDone: (placementInfo) => {
154
+ console.log('placementInfo', placementInfo);
155
+ },
156
+ });
157
+ }
158
+
159
+ isAdLoaded() {
160
+ return true;
161
+ }
162
+
163
+ isRewardedAdLoaded() {
164
+ return true;
165
+ }
166
+
167
+ showRewardedAd() {
168
+ console.log('GoogleH5.ShowRewardedAd');
169
+
170
+ if (typeof globalThis.adBreak === 'undefined' || !globalThis.adBreak) {
171
+ console.log('GoogleH5 adBreak plugin not ready'); return;
172
+ }
173
+
174
+ globalThis.adBreak({
175
+ type: 'reward',
176
+ name: 'reward_ad',
177
+ beforeAd: () => {
178
+ if (this.onAdOpened) {
179
+ this.onAdOpened({
180
+ adType: 'rewarded',
181
+ adProvider: this.adProviderName,
182
+ });
183
+ }
184
+ },
185
+ afterAd: () => { },
186
+ beforeReward: (showAdFn) => {
187
+ showAdFn();
188
+ },
189
+ adDismissed: () => { },
190
+ adViewed: () => {
191
+ if (this.onAdClosed) {
192
+ this.onAdClosed({
193
+ adType: 'rewarded',
194
+ adProvider: this.adProviderName,
195
+ });
196
+ }
197
+
198
+ if (this.onAdRewarded) {
199
+ this.onAdRewarded({
200
+ adType: 'rewarded',
201
+ rewardAmount: 150,
202
+ adProvider: this.adProviderName,
203
+ });
204
+ }
205
+ },
206
+ adBreakDone: (placementInfo) => {
207
+ console.log('placementInfo', placementInfo);
208
+ },
209
+ });
210
+
211
+ }
212
+
213
+ loadRewardedAd() {
214
+ console.log('GoogleH5.loadRewardedAd');
215
+ }
216
+
217
+ async showAppOpenAd(successCallback) {
218
+ console.log('GoogleH5.ShowAppOpenAd');
219
+
220
+ if (typeof globalThis.adBreak === 'undefined' || !globalThis.adBreak) {
221
+ console.log('GoogleH5 adBreak plugin not ready'); return;
222
+ }
223
+
224
+ const adStatus = await new Promise((resolve) => {
225
+ globalThis.adBreak({
226
+ type: 'preroll',
227
+ adBreakDone: (e) => {
228
+ const status = e.breakStatus;
229
+
230
+ if (successCallback) {
231
+ successCallback(e);
232
+ }
233
+
234
+ resolve(status);
235
+ },
236
+ });
237
+ });
238
+
239
+ if (adStatus === 'viewed') {
240
+ if (this.onAdOpened) {
241
+ this.onAdOpened({
242
+ adType: 'app_open',
243
+ adProvider: this.adProviderName,
244
+ });
245
+ }
246
+
247
+ if (this.onAdClosed) {
248
+ this.onAdClosed({
249
+ adType: 'app_open',
250
+ adProvider: this.adProviderName,
251
+ });
252
+ }
253
+ }
254
+
255
+ return adStatus;
256
+ }
257
+ }
@@ -0,0 +1,7 @@
1
+ import { AdMobPortal } from './AdMobPortal';
2
+
3
+ export class iOSPortal extends AdMobPortal {
4
+ name = 'iOSPortal';
5
+
6
+
7
+ }
package/lib/index.js CHANGED
@@ -1,5 +1,3 @@
1
1
  import { Portal } from './Portal';
2
2
 
3
- export {
4
- Portal
5
- };
3
+ export { Portal };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
- "name": "@cardsjd/portal",
2
+ "name": "@cardsjd/portal",
3
3
  "private": false,
4
- "version": "0.1.1",
4
+ "version": "0.1.2",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "publishConfig": {
@@ -14,12 +14,10 @@
14
14
  "lint": "eslint lib --report-unused-disable-directives --max-warnings 0",
15
15
  "lint:fix": "npm run lint -- --fix"
16
16
  },
17
+ "devDependencies": {},
17
18
  "dependencies": {
18
- "@cardsjd/device": "^0.1.14",
19
- "@cardsjd/fbinstantgame": "^0.1.12",
19
+ "@capacitor-community/admob": "^7.0.3",
20
20
  "@discord/embedded-app-sdk": "^2.4.0"
21
21
  },
22
- "peerDependencies": {
23
- "@sagi.io/globalthis": "^0.0.2"
24
- }
22
+ "peerDependencies": {}
25
23
  }