@jolibox/implement 1.1.13-beta.1 → 1.1.13-beta.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/.rush/temp/package-deps_build.json +30 -39
- package/.rush/temp/shrinkwrap-deps.json +1 -1
- package/dist/common/ads/anti-cheating.d.ts +5 -0
- package/dist/common/rewards/__tests__/can-use-jolicoin.test.d.ts +1 -0
- package/dist/common/rewards/fetch-reward.d.ts +2 -0
- package/dist/common/rewards/index.d.ts +18 -0
- package/dist/common/rewards/registers/use-ads.d.ts +3 -0
- package/dist/common/rewards/registers/use-jolicoin.d.ts +10 -0
- package/dist/common/rewards/registers/utils/index.d.ts +2 -0
- package/dist/common/rewards/reward-helper.d.ts +14 -0
- package/dist/common/rewards/type.d.ts +23 -0
- package/dist/common/utils/index.d.ts +7 -0
- package/dist/index.js +3 -3
- package/dist/index.native.js +28 -7
- package/dist/native/api/keyboard.d.ts +1 -1
- package/implement.build.log +2 -2
- package/package.json +4 -3
- package/src/common/ads/anti-cheating.test.ts +4 -2
- package/src/common/ads/anti-cheating.ts +12 -4
- package/src/common/ads/index.ts +51 -33
- package/src/common/rewards/__tests__/can-use-jolicoin.test.ts +94 -0
- package/src/common/rewards/fetch-reward.ts +39 -0
- package/src/common/rewards/index.ts +20 -0
- package/src/common/rewards/registers/use-ads.ts +9 -0
- package/src/common/rewards/registers/use-jolicoin.ts +96 -0
- package/src/common/rewards/registers/utils/index.ts +11 -0
- package/src/common/rewards/reward-helper.ts +56 -0
- package/src/common/rewards/type.ts +25 -0
- package/src/common/utils/index.ts +7 -0
- package/src/h5/api/ads.ts +20 -1
- package/src/h5/api/get-system-info.ts +1 -1
- package/src/h5/http/utils/__tests__/xua.test.ts +1 -1
- package/src/native/api/ads.ts +31 -3
- package/src/native/api/get-system-info.ts +2 -2
- package/src/native/api/keyboard.ts +1 -1
- package/src/native/api/lifecycle.ts +1 -1
- package/src/native/api/login.ts +1 -1
- package/src/native/api/navigate.ts +1 -1
- package/src/native/api/storage.ts +1 -1
- package/src/native/bootstrap/index.ts +13 -6
- package/src/native/network/create-fetch.ts +6 -2
- package/src/native/report/errors/index.ts +1 -1
- package/src/native/report/index.ts +1 -1
- package/src/native/report/task-tracker.ts +1 -1
- package/src/native/ui/retention.ts +4 -3
- package/dist/native/bootstrap/bridge.d.ts +0 -4
- package/dist/native/js-bridge/const.d.ts +0 -6
- package/dist/native/js-bridge/index.d.ts +0 -2
- package/dist/native/js-bridge/invoke.d.ts +0 -21
- package/dist/native/js-bridge/js-bridge.d.ts +0 -6
- package/dist/native/js-bridge/publish.d.ts +0 -1
- package/dist/native/js-bridge/report.d.ts +0 -63
- package/dist/native/js-bridge/subscribe.d.ts +0 -10
- package/dist/native/js-bridge/types.d.ts +0 -18
- package/dist/native/js-bridge/utils.d.ts +0 -17
- package/dist/native/js-core/index.d.ts +0 -3
- package/dist/native/js-core/jolibox-js-core.d.ts +0 -50
- package/dist/native/js-core/message-port.d.ts +0 -12
- package/dist/native/js-core/utils.d.ts +0 -7
- package/src/native/bootstrap/bridge.ts +0 -68
- package/src/native/js-bridge/const.ts +0 -13
- package/src/native/js-bridge/index.ts +0 -2
- package/src/native/js-bridge/invoke.ts +0 -208
- package/src/native/js-bridge/js-bridge.ts +0 -28
- package/src/native/js-bridge/publish.ts +0 -44
- package/src/native/js-bridge/report.ts +0 -311
- package/src/native/js-bridge/subscribe.ts +0 -74
- package/src/native/js-bridge/types.ts +0 -36
- package/src/native/js-bridge/utils.ts +0 -116
- package/src/native/js-core/index.ts +0 -4
- package/src/native/js-core/jolibox-js-core.ts +0 -192
- package/src/native/js-core/message-port.ts +0 -52
- package/src/native/js-core/utils.ts +0 -9
- package/src/native/types/global.d.ts +0 -27
- package/src/native/types/native-method-map.d.ts +0 -329
- package/src/native/types/native-method.d.ts +0 -30
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export declare const showKeyboard: (params: {
|
|
2
2
|
defaultValue?: string | undefined;
|
|
3
|
-
maxLength?: unknown;
|
|
4
3
|
multiple?: boolean | undefined;
|
|
4
|
+
maxLength?: unknown;
|
|
5
5
|
}) => import("@jolibox/types").StandardResponse<void>;
|
|
6
6
|
export declare const updateKeyboard: (params: {
|
|
7
7
|
value: string;
|
package/implement.build.log
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Invoking: npm run clean && npm run build:esm && tsc
|
|
2
2
|
|
|
3
|
-
> @jolibox/implement@1.1.13-beta.
|
|
3
|
+
> @jolibox/implement@1.1.13-beta.11 clean
|
|
4
4
|
> rimraf ./dist
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
> @jolibox/implement@1.1.13-beta.
|
|
7
|
+
> @jolibox/implement@1.1.13-beta.11 build:esm
|
|
8
8
|
> BUILD_VERSION=$(node -p "require('./package.json').version") node esbuild.config.js --format=esm
|
|
9
9
|
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jolibox/implement",
|
|
3
3
|
"description": "This project is Jolibox JS-SDk implement for Native && H5",
|
|
4
|
-
"version": "1.1.13-beta.
|
|
4
|
+
"version": "1.1.13-beta.11",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"typings": "dist/index.d.ts",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@jolibox/common": "1.1.13-beta.
|
|
10
|
-
"@jolibox/types": "1.1.13-beta.
|
|
9
|
+
"@jolibox/common": "1.1.13-beta.11",
|
|
10
|
+
"@jolibox/types": "1.1.13-beta.11",
|
|
11
|
+
"@jolibox/native-bridge": "1.1.13-beta.11",
|
|
11
12
|
"localforage": "1.10.0",
|
|
12
13
|
"@jolibox/ui": "1.0.0",
|
|
13
14
|
"web-vitals": "4.2.4"
|
|
@@ -20,7 +20,9 @@ describe('createLeadingNotifiableDebounce', () => {
|
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
it('should ban initial call', async () => {
|
|
23
|
-
|
|
23
|
+
(window as any).__joliboxLocalStorage__ = window.localStorage;
|
|
24
|
+
|
|
25
|
+
(window as any).__joliboxLocalStorage__.clear();
|
|
24
26
|
|
|
25
27
|
const adsAntiCheating = new AdsAntiCheating(track, http, () => true, config);
|
|
26
28
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
@@ -32,7 +34,7 @@ describe('createLeadingNotifiableDebounce', () => {
|
|
|
32
34
|
});
|
|
33
35
|
|
|
34
36
|
it('should ban continous calling', async () => {
|
|
35
|
-
|
|
37
|
+
(window as any).__joliboxLocalStorage__.clear();
|
|
36
38
|
|
|
37
39
|
const adsAntiCheating = new AdsAntiCheating(track, http, () => true, config);
|
|
38
40
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { IHttpClient } from '../http';
|
|
2
2
|
import type { Track } from '../report';
|
|
3
|
+
import { getOriginalLocalStorage } from '@jolibox/common';
|
|
4
|
+
getOriginalLocalStorage();
|
|
3
5
|
|
|
4
6
|
export type AdsDisplayPermission =
|
|
5
7
|
| 'BLOCK_INITIAL'
|
|
@@ -24,6 +26,12 @@ export interface IAdsAntiCheatingConfig {
|
|
|
24
26
|
highFreqThreshold?: number; // default 2000 (2s)
|
|
25
27
|
}
|
|
26
28
|
|
|
29
|
+
declare global {
|
|
30
|
+
interface Window {
|
|
31
|
+
__joliboxLocalStorage__: Storage;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
27
35
|
export class AdsAntiCheating {
|
|
28
36
|
private checkShouldBanInitial: ReturnType<typeof createInitialStrategy>;
|
|
29
37
|
private checkShouldBanHighFreq: ReturnType<typeof createHighFreqStrategy>;
|
|
@@ -177,7 +185,7 @@ const createBanForTimeStrategy = (
|
|
|
177
185
|
_records: [] as CallAdsHistory[],
|
|
178
186
|
init() {
|
|
179
187
|
try {
|
|
180
|
-
const fromStorage = JSON.parse(window.
|
|
188
|
+
const fromStorage = JSON.parse(window.__joliboxLocalStorage__.getItem(TIMESTAMP_STORAGE_KEY) ?? '[]');
|
|
181
189
|
if (Array.isArray(fromStorage)) {
|
|
182
190
|
this._records = fromStorage;
|
|
183
191
|
} else {
|
|
@@ -192,7 +200,7 @@ const createBanForTimeStrategy = (
|
|
|
192
200
|
},
|
|
193
201
|
set values(value: CallAdsHistory[]) {
|
|
194
202
|
try {
|
|
195
|
-
window.
|
|
203
|
+
window.__joliboxLocalStorage__.setItem(TIMESTAMP_STORAGE_KEY, JSON.stringify(value));
|
|
196
204
|
} catch {
|
|
197
205
|
// console.error('Failed to save timestamps');
|
|
198
206
|
// do nothing
|
|
@@ -205,7 +213,7 @@ const createBanForTimeStrategy = (
|
|
|
205
213
|
_record: false,
|
|
206
214
|
init() {
|
|
207
215
|
try {
|
|
208
|
-
const fromStorage = JSON.parse(window.
|
|
216
|
+
const fromStorage = JSON.parse(window.__joliboxLocalStorage__.getItem(BAN_FOR_TIME_KEY) ?? 'false');
|
|
209
217
|
if (typeof fromStorage === 'boolean') {
|
|
210
218
|
this._record = fromStorage;
|
|
211
219
|
}
|
|
@@ -228,7 +236,7 @@ const createBanForTimeStrategy = (
|
|
|
228
236
|
},
|
|
229
237
|
set value(value: boolean) {
|
|
230
238
|
try {
|
|
231
|
-
window.
|
|
239
|
+
window.__joliboxLocalStorage__.setItem(BAN_FOR_TIME_KEY, JSON.stringify(value));
|
|
232
240
|
} catch {
|
|
233
241
|
// do nothing
|
|
234
242
|
}
|
package/src/common/ads/index.ts
CHANGED
|
@@ -377,6 +377,55 @@ export class JoliboxAdsImpl {
|
|
|
377
377
|
* @returns IAdBreakParams
|
|
378
378
|
*/
|
|
379
379
|
private wrapAdBreakParams = (params: IAdBreakParams): IAdBreakParams => {
|
|
380
|
+
let googleHasResponse = false;
|
|
381
|
+
|
|
382
|
+
/* hook adBreakDone to track adBreakDone event -- start */
|
|
383
|
+
const originAdBreakDone = params.adBreakDone;
|
|
384
|
+
const adBreakDone = (placementInfo: IPlacementInfo) => {
|
|
385
|
+
googleHasResponse = true;
|
|
386
|
+
this.adsActionDetection.adBreakIsShowing = false;
|
|
387
|
+
this.track('CallAdBreakDone', {
|
|
388
|
+
breakType: placementInfo.breakType,
|
|
389
|
+
breakName: placementInfo.breakName ?? '',
|
|
390
|
+
breakFormat: placementInfo.breakFormat,
|
|
391
|
+
breakStatus: placementInfo.breakStatus
|
|
392
|
+
});
|
|
393
|
+
if (originAdBreakDone) {
|
|
394
|
+
originAdBreakDone(placementInfo);
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
params.adBreakDone = adBreakDone;
|
|
398
|
+
/* hook adBreakDone to track adBreakDone event -- end */
|
|
399
|
+
|
|
400
|
+
/* hook for reward -- start */
|
|
401
|
+
if (params.type === 'reward') {
|
|
402
|
+
const originBeforeReward = params.beforeReward;
|
|
403
|
+
const wrapShowAdFn = (originShowAdFn: () => void) => () => {
|
|
404
|
+
this.track('CallShowAdFn', null);
|
|
405
|
+
originShowAdFn();
|
|
406
|
+
};
|
|
407
|
+
const beforeReward = (originShowAdFn: () => void) => {
|
|
408
|
+
googleHasResponse = true;
|
|
409
|
+
if (originBeforeReward) {
|
|
410
|
+
originBeforeReward(wrapShowAdFn(originShowAdFn));
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
params.beforeReward = beforeReward;
|
|
414
|
+
|
|
415
|
+
window.setTimeout(() => {
|
|
416
|
+
if (!googleHasResponse) {
|
|
417
|
+
this.track('CallAdBreakTimeout', { type: params.type });
|
|
418
|
+
params.adBreakDone?.({
|
|
419
|
+
breakType: params.type,
|
|
420
|
+
breakName: params.name ?? '',
|
|
421
|
+
breakFormat: 'reward',
|
|
422
|
+
breakStatus: 'timeout'
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
}, 5000); // Timeout duration in milliseconds, 5 seconds
|
|
426
|
+
}
|
|
427
|
+
/* hook for reward -- end */
|
|
428
|
+
|
|
380
429
|
const isPreroll = (params: IAdBreakParams): params is IPrerollParams => {
|
|
381
430
|
return params.type === 'preroll';
|
|
382
431
|
};
|
|
@@ -396,6 +445,7 @@ export class JoliboxAdsImpl {
|
|
|
396
445
|
const originAfterAd = params.afterAd;
|
|
397
446
|
|
|
398
447
|
const beforeAd = () => {
|
|
448
|
+
googleHasResponse = true;
|
|
399
449
|
adEventEmitter.emit('isAdShowing', true);
|
|
400
450
|
this.track('CallBeforeAd', {
|
|
401
451
|
type: params.type,
|
|
@@ -407,6 +457,7 @@ export class JoliboxAdsImpl {
|
|
|
407
457
|
};
|
|
408
458
|
|
|
409
459
|
const afterAd = () => {
|
|
460
|
+
googleHasResponse = true;
|
|
410
461
|
adEventEmitter.emit('isAdShowing', false);
|
|
411
462
|
this.track('CallAfterAd', {
|
|
412
463
|
type: params.type,
|
|
@@ -529,39 +580,6 @@ export class JoliboxAdsImpl {
|
|
|
529
580
|
|
|
530
581
|
const type = params.type;
|
|
531
582
|
|
|
532
|
-
/* hook adBreakDone to track adBreakDone event -- start */
|
|
533
|
-
const originAdBreakDone = params.adBreakDone;
|
|
534
|
-
const adBreakDone = (placementInfo: IPlacementInfo) => {
|
|
535
|
-
this.adsActionDetection.adBreakIsShowing = false;
|
|
536
|
-
this.track('CallAdBreakDone', {
|
|
537
|
-
breakType: placementInfo.breakType,
|
|
538
|
-
breakName: placementInfo.breakName ?? '',
|
|
539
|
-
breakFormat: placementInfo.breakFormat,
|
|
540
|
-
breakStatus: placementInfo.breakStatus
|
|
541
|
-
});
|
|
542
|
-
if (originAdBreakDone) {
|
|
543
|
-
originAdBreakDone(placementInfo);
|
|
544
|
-
}
|
|
545
|
-
};
|
|
546
|
-
params.adBreakDone = adBreakDone;
|
|
547
|
-
/* hook adBreakDone to track adBreakDone event -- end */
|
|
548
|
-
|
|
549
|
-
/* hook beforeAd to track beforeAd event -- start */
|
|
550
|
-
if (params.type === 'reward') {
|
|
551
|
-
const originBeforeReward = params.beforeReward;
|
|
552
|
-
const wrapShowAdFn = (originShowAdFn: () => void) => () => {
|
|
553
|
-
this.track('CallShowAdFn', null);
|
|
554
|
-
originShowAdFn();
|
|
555
|
-
};
|
|
556
|
-
const beforeReward = (originShowAdFn: () => void) => {
|
|
557
|
-
if (originBeforeReward) {
|
|
558
|
-
originBeforeReward(wrapShowAdFn(originShowAdFn));
|
|
559
|
-
}
|
|
560
|
-
};
|
|
561
|
-
params.beforeReward = beforeReward;
|
|
562
|
-
}
|
|
563
|
-
/* hook beforeAd to track beforeAd event -- end */
|
|
564
|
-
|
|
565
583
|
let paramsToTrack;
|
|
566
584
|
switch (type) {
|
|
567
585
|
case 'preroll':
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { IUnlockOptionType } from '../type';
|
|
2
|
+
import { canUseJolicoin } from '../registers/utils';
|
|
3
|
+
|
|
4
|
+
describe('canUseJolicoin', () => {
|
|
5
|
+
it('should return true when joliCoin balance is sufficient and autoDeduct is enabled', () => {
|
|
6
|
+
const unlockOptions = [
|
|
7
|
+
{
|
|
8
|
+
type: 'JOLI_COIN' as IUnlockOptionType,
|
|
9
|
+
joliCoinChoices: [{ joliCoinQuantity: 100 }]
|
|
10
|
+
}
|
|
11
|
+
];
|
|
12
|
+
const joliCoin = { balance: 100, enableAutoDeduct: true };
|
|
13
|
+
|
|
14
|
+
expect(canUseJolicoin(unlockOptions, joliCoin)).toBe(true);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should return true when joliCoin balance is more than required', () => {
|
|
18
|
+
const unlockOptions = [
|
|
19
|
+
{
|
|
20
|
+
type: 'JOLI_COIN' as IUnlockOptionType,
|
|
21
|
+
joliCoinChoices: [{ joliCoinQuantity: 50 }]
|
|
22
|
+
}
|
|
23
|
+
];
|
|
24
|
+
const joliCoin = { balance: 100, enableAutoDeduct: true };
|
|
25
|
+
|
|
26
|
+
expect(canUseJolicoin(unlockOptions, joliCoin)).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should return false when joliCoin balance is insufficient', () => {
|
|
30
|
+
const unlockOptions = [
|
|
31
|
+
{
|
|
32
|
+
type: 'JOLI_COIN' as IUnlockOptionType,
|
|
33
|
+
joliCoinChoices: [{ joliCoinQuantity: 150 }]
|
|
34
|
+
}
|
|
35
|
+
];
|
|
36
|
+
const joliCoin = { balance: 100, enableAutoDeduct: true };
|
|
37
|
+
|
|
38
|
+
expect(canUseJolicoin(unlockOptions, joliCoin)).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should return false when autoDeduct is disabled', () => {
|
|
42
|
+
const unlockOptions = [
|
|
43
|
+
{
|
|
44
|
+
type: 'JOLI_COIN' as IUnlockOptionType,
|
|
45
|
+
joliCoinChoices: [{ joliCoinQuantity: 50 }]
|
|
46
|
+
}
|
|
47
|
+
];
|
|
48
|
+
const joliCoin = { balance: 100, enableAutoDeduct: false };
|
|
49
|
+
|
|
50
|
+
expect(canUseJolicoin(unlockOptions, joliCoin)).toBe(false);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should return false when there is no JOLI_COIN option', () => {
|
|
54
|
+
const unlockOptions = [
|
|
55
|
+
{
|
|
56
|
+
type: 'ADS' as IUnlockOptionType,
|
|
57
|
+
joliCoinChoices: []
|
|
58
|
+
}
|
|
59
|
+
];
|
|
60
|
+
const joliCoin = { balance: 100, enableAutoDeduct: true };
|
|
61
|
+
|
|
62
|
+
expect(canUseJolicoin(unlockOptions, joliCoin)).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should return false with empty unlockOptions', () => {
|
|
66
|
+
const unlockOptions: any[] = [];
|
|
67
|
+
const joliCoin = { balance: 100, enableAutoDeduct: true };
|
|
68
|
+
|
|
69
|
+
expect(canUseJolicoin(unlockOptions, joliCoin)).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should return false when joliCoin is undefined', () => {
|
|
73
|
+
const unlockOptions = [
|
|
74
|
+
{
|
|
75
|
+
type: 'JOLI_COIN' as IUnlockOptionType,
|
|
76
|
+
joliCoinChoices: [{ joliCoinQuantity: 50 }]
|
|
77
|
+
}
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
expect(canUseJolicoin(unlockOptions, undefined)).toBe(false);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should return true when at least one choice is valid', () => {
|
|
84
|
+
const unlockOptions = [
|
|
85
|
+
{
|
|
86
|
+
type: 'JOLI_COIN' as IUnlockOptionType,
|
|
87
|
+
joliCoinChoices: [{ joliCoinQuantity: 200 }, { joliCoinQuantity: 50 }]
|
|
88
|
+
}
|
|
89
|
+
];
|
|
90
|
+
const joliCoin = { balance: 100, enableAutoDeduct: true };
|
|
91
|
+
|
|
92
|
+
expect(canUseJolicoin(unlockOptions, joliCoin)).toBe(true);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { UnlockOptionsEventName, unlockOptionsEmitter } from '.';
|
|
2
|
+
import { IHttpClient } from '../http';
|
|
3
|
+
import { RewardsHelper, RewardType } from './reward-helper';
|
|
4
|
+
import { IJolicoinRewardOption } from './type';
|
|
5
|
+
|
|
6
|
+
const priority = () => {
|
|
7
|
+
return (a: RewardType, b: RewardType) => {
|
|
8
|
+
if (a === 'JOLI_COIN' && b === 'ADS') return -1;
|
|
9
|
+
if (a === 'ADS' && b === 'JOLI_COIN') return 1;
|
|
10
|
+
return 0;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const createRewardFetcher = (rewardsHelper: RewardsHelper) => {
|
|
15
|
+
rewardsHelper.registerRewardsFetcher(async (httpClient: IHttpClient) => {
|
|
16
|
+
const defaultRewards: RewardType[] = ['ADS'];
|
|
17
|
+
try {
|
|
18
|
+
const res = await httpClient.post<IJolicoinRewardOption>('/api/games/unlock-options', {});
|
|
19
|
+
if (res.code !== 'SUCCESS') {
|
|
20
|
+
return defaultRewards;
|
|
21
|
+
}
|
|
22
|
+
unlockOptionsEmitter.emit(UnlockOptionsEventName, {
|
|
23
|
+
options: res.data?.unlockOptions || [],
|
|
24
|
+
userJoliCoin: res.extra?.joliCoin || {
|
|
25
|
+
balance: 0,
|
|
26
|
+
enableAutoDeduct: false
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const rewardsTypes =
|
|
31
|
+
res.data?.unlockOptions?.map((option) => option.type) || Array.from(defaultRewards);
|
|
32
|
+
// Sort reward types with JOLI_COIN having higher priority than ADS
|
|
33
|
+
return rewardsTypes.sort(priority());
|
|
34
|
+
} catch (e) {
|
|
35
|
+
console.error('getRewardOptions error:', e);
|
|
36
|
+
return defaultRewards;
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createRewardsHelper } from './reward-helper';
|
|
2
|
+
import { createRewardFetcher } from './fetch-reward';
|
|
3
|
+
import { EventEmitter } from '@jolibox/common';
|
|
4
|
+
import { IUnlockOption, IJoliCoin } from './type';
|
|
5
|
+
|
|
6
|
+
export const rewardsHelper = createRewardsHelper();
|
|
7
|
+
createRewardFetcher(rewardsHelper);
|
|
8
|
+
|
|
9
|
+
export * from './registers/use-ads';
|
|
10
|
+
export * from './registers/use-jolicoin';
|
|
11
|
+
|
|
12
|
+
export interface IUnlockOptionsEvent {
|
|
13
|
+
options: IUnlockOption[];
|
|
14
|
+
userJoliCoin: IJoliCoin;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const UnlockOptionsEventName = 'UNLOCK_OPTIONS_CHANGED' as const;
|
|
18
|
+
export const unlockOptionsEmitter = new EventEmitter<{
|
|
19
|
+
[UnlockOptionsEventName]: [IUnlockOptionsEvent];
|
|
20
|
+
}>();
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { JoliboxAdsImpl, IRewardParams } from '../../ads';
|
|
2
|
+
|
|
3
|
+
export type AdsRewardsHandler = (params: IRewardParams) => Promise<boolean>;
|
|
4
|
+
export const createAdsRewardHandler = (ads: JoliboxAdsImpl): AdsRewardsHandler => {
|
|
5
|
+
return async (params: IRewardParams) => {
|
|
6
|
+
ads.adBreak(params);
|
|
7
|
+
return true;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { IHttpClient } from '@/common/http';
|
|
2
|
+
import { context } from '@/common/context';
|
|
3
|
+
import { uuidv4 } from '@jolibox/common';
|
|
4
|
+
import { canUseJolicoin } from './utils';
|
|
5
|
+
import { unlockOptionsEmitter, UnlockOptionsEventName, IUnlockOptionsEvent } from '..';
|
|
6
|
+
import { IAdBreakParams } from '@/common/ads';
|
|
7
|
+
|
|
8
|
+
interface IJolicoinUnlockRes {
|
|
9
|
+
code: 'SUCCESS' | 'BALANCE_NOT_ENOUGH' | 'EPISODE_LOCK_JUMP' | 'EPISODE_UNLOCK_ALREADY';
|
|
10
|
+
message: string;
|
|
11
|
+
data: {
|
|
12
|
+
transactionId: string;
|
|
13
|
+
quantity: number;
|
|
14
|
+
balance: number;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type JolicoinRewardsHandler = (params: IAdBreakParams) => Promise<boolean>;
|
|
19
|
+
export const createJolicoinRewardHandler = (
|
|
20
|
+
httpClient: IHttpClient,
|
|
21
|
+
{
|
|
22
|
+
onUnlockSuccess,
|
|
23
|
+
onUnlockFailed
|
|
24
|
+
}: {
|
|
25
|
+
onUnlockSuccess?: (data: { quantity: number; balance: number }) => void;
|
|
26
|
+
onUnlockFailed?: () => void;
|
|
27
|
+
}
|
|
28
|
+
): JolicoinRewardsHandler => {
|
|
29
|
+
let unlockOptionsPromise: Promise<IUnlockOptionsEvent> | null = null;
|
|
30
|
+
let resolveUnlockOptions: ((value: IUnlockOptionsEvent) => void) | null = null;
|
|
31
|
+
let cachedUnlockOptions: IUnlockOptionsEvent | null = null;
|
|
32
|
+
|
|
33
|
+
const createUnlockOptionsPromise = () => {
|
|
34
|
+
if (cachedUnlockOptions) {
|
|
35
|
+
unlockOptionsPromise = Promise.resolve(cachedUnlockOptions);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
unlockOptionsPromise = new Promise<IUnlockOptionsEvent>((resolve) => {
|
|
40
|
+
resolveUnlockOptions = resolve;
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
createUnlockOptionsPromise();
|
|
45
|
+
|
|
46
|
+
unlockOptionsEmitter.on(UnlockOptionsEventName, (options) => {
|
|
47
|
+
cachedUnlockOptions = options;
|
|
48
|
+
if (resolveUnlockOptions) {
|
|
49
|
+
resolveUnlockOptions(options);
|
|
50
|
+
resolveUnlockOptions = null;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return async (params: IAdBreakParams) => {
|
|
55
|
+
try {
|
|
56
|
+
if (!unlockOptionsPromise) {
|
|
57
|
+
createUnlockOptionsPromise();
|
|
58
|
+
}
|
|
59
|
+
const unlockOptions = await unlockOptionsPromise!;
|
|
60
|
+
if (!canUseJolicoin(unlockOptions?.options || [], unlockOptions?.userJoliCoin)) {
|
|
61
|
+
onUnlockFailed?.();
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const unlockWithJolicoin = await httpClient.post<IJolicoinUnlockRes>('/api/joli-coin/unlock', {
|
|
66
|
+
data: {
|
|
67
|
+
// TODO: support drama
|
|
68
|
+
type: 'GAME_REWARD',
|
|
69
|
+
reqId: `${uuidv4()}-${context.mpType}-${Date.now()}`,
|
|
70
|
+
gameInfo: {
|
|
71
|
+
gameId: context.mpId
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
if (unlockWithJolicoin.code == 'SUCCESS') {
|
|
76
|
+
onUnlockSuccess?.({
|
|
77
|
+
quantity: unlockWithJolicoin.data.quantity,
|
|
78
|
+
balance: unlockWithJolicoin.data.balance
|
|
79
|
+
});
|
|
80
|
+
params.adBreakDone?.({
|
|
81
|
+
breakType: params.type,
|
|
82
|
+
breakName: 'name' in params ? params.name ?? '' : '',
|
|
83
|
+
breakFormat: 'reward',
|
|
84
|
+
breakStatus: 'viewed'
|
|
85
|
+
});
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
onUnlockFailed?.();
|
|
89
|
+
return false;
|
|
90
|
+
} catch (e) {
|
|
91
|
+
console.error(`JolicoinRewardHandler error:`, e);
|
|
92
|
+
onUnlockFailed?.();
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { IJoliCoin, IUnlockOption } from '@/common/rewards/type';
|
|
2
|
+
|
|
3
|
+
export const canUseJolicoin = (unlockOptions: IUnlockOption[], joliCoin?: IJoliCoin) => {
|
|
4
|
+
return unlockOptions.some(
|
|
5
|
+
(option) =>
|
|
6
|
+
option.type === 'JOLI_COIN' &&
|
|
7
|
+
option.joliCoinChoices.some(
|
|
8
|
+
(choice) => choice.joliCoinQuantity <= (joliCoin?.balance ?? 0) && !!joliCoin?.enableAutoDeduct
|
|
9
|
+
)
|
|
10
|
+
);
|
|
11
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export type RewardType = 'ADS' | 'JOLI_COIN';
|
|
2
|
+
|
|
3
|
+
import { context } from '../context';
|
|
4
|
+
import type { AdsRewardsHandler } from './registers/use-ads';
|
|
5
|
+
|
|
6
|
+
export interface RewardHandlerMap {
|
|
7
|
+
ADS: AdsRewardsHandler;
|
|
8
|
+
JOLI_COIN: (params?: unknown) => Promise<boolean>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type RewardHandler<T extends RewardType> = RewardHandlerMap[T];
|
|
12
|
+
|
|
13
|
+
const isTestMode = context.testMode;
|
|
14
|
+
export function createRewardsHelper() {
|
|
15
|
+
const rewardsHandlers = new Map<RewardType, RewardHandler<any>>();
|
|
16
|
+
let rewardFetcher: ((...args: any[]) => Promise<RewardType[]>) | undefined;
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
registerRewardHandler<T extends RewardType>(type: T, handler: RewardHandler<T>) {
|
|
20
|
+
rewardsHandlers.set(type, handler);
|
|
21
|
+
},
|
|
22
|
+
async handleReward<T extends RewardType>(rewardsTypes: T[], ...args: Parameters<RewardHandler<T>>) {
|
|
23
|
+
const result = await rewardsTypes.reduce(async (prevPromise, type) => {
|
|
24
|
+
const prevResult = await prevPromise;
|
|
25
|
+
if (prevResult === true) return true;
|
|
26
|
+
|
|
27
|
+
isTestMode && console.log(`handleReward ${type}`);
|
|
28
|
+
const handler = rewardsHandlers.get(type);
|
|
29
|
+
const nextResult = handler ? await handler(...args) : prevResult;
|
|
30
|
+
isTestMode && console.log(`handleReward ${type} ${nextResult}`);
|
|
31
|
+
return nextResult;
|
|
32
|
+
}, Promise.resolve(false));
|
|
33
|
+
|
|
34
|
+
return result;
|
|
35
|
+
},
|
|
36
|
+
async registerRewardsFetcher<T extends RewardType>(fetcher: (...args: any[]) => Promise<T[]>) {
|
|
37
|
+
rewardFetcher = async (...args: unknown[]) => {
|
|
38
|
+
try {
|
|
39
|
+
const rewardsTypes = await fetcher(...args);
|
|
40
|
+
return rewardsTypes;
|
|
41
|
+
} catch (e) {
|
|
42
|
+
console.error(`getRewardOptions error:`, e);
|
|
43
|
+
return ['ADS'];
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
},
|
|
47
|
+
async getRewardsTypes(...args: unknown[]): Promise<RewardType[]> {
|
|
48
|
+
if (!rewardFetcher) {
|
|
49
|
+
return ['ADS'];
|
|
50
|
+
}
|
|
51
|
+
return await rewardFetcher(...args);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type RewardsHelper = ReturnType<typeof createRewardsHelper>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface IJoliCoin {
|
|
2
|
+
balance: number;
|
|
3
|
+
enableAutoDeduct: boolean;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export type IUnlockOptionType = 'JOLI_COIN' | 'ADS';
|
|
7
|
+
|
|
8
|
+
interface IJoliCoinChoice {
|
|
9
|
+
joliCoinQuantity: number;
|
|
10
|
+
}
|
|
11
|
+
export interface IUnlockOption {
|
|
12
|
+
type: IUnlockOptionType;
|
|
13
|
+
joliCoinChoices: IJoliCoinChoice[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface IJolicoinRewardOption {
|
|
17
|
+
code: 'SUCCESS' | 'ERROR' | 'PARAMETER_ERROR' | 'EPISODE_LOCK_JUMP';
|
|
18
|
+
message: string;
|
|
19
|
+
data: {
|
|
20
|
+
unlockOptions?: IUnlockOption[];
|
|
21
|
+
};
|
|
22
|
+
extra: {
|
|
23
|
+
joliCoin: IJoliCoin;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
const JOLIBOX_CUSTOM_ADS_EVENT_TYPE = 'JOLIBOX_ADS_EVENT';
|
|
2
|
+
const JOLIBOX_CUSTOM_REWARDS_EVENT_TYPE = 'JOLIBOX_CUSTOM_REWARDS_EVENT';
|
|
2
3
|
|
|
3
4
|
interface JoliboxCustomEvent {
|
|
4
5
|
[JOLIBOX_CUSTOM_ADS_EVENT_TYPE]: {
|
|
5
6
|
isAdShowing: boolean;
|
|
6
7
|
};
|
|
8
|
+
[JOLIBOX_CUSTOM_REWARDS_EVENT_TYPE]: {
|
|
9
|
+
['JOLI_COIN']?: {
|
|
10
|
+
quantity: number;
|
|
11
|
+
balance: number;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
7
14
|
}
|
|
8
15
|
|
|
9
16
|
export const notifyCustomEvent = <T extends keyof JoliboxCustomEvent>(
|
package/src/h5/api/ads.ts
CHANGED
|
@@ -3,10 +3,23 @@ import { track } from '../report';
|
|
|
3
3
|
import { createCommands } from '@jolibox/common';
|
|
4
4
|
import { httpClientManager } from '../http';
|
|
5
5
|
import { notifyCustomEvent } from '@/common/utils';
|
|
6
|
+
import { createAdsRewardHandler, rewardsHelper, createJolicoinRewardHandler } from '@/common/rewards';
|
|
7
|
+
|
|
6
8
|
const commands = createCommands();
|
|
7
9
|
|
|
8
10
|
const httpClient = httpClientManager.create();
|
|
9
11
|
const ads = new JoliboxAdsImpl(track, httpClient, () => httpClientManager.getNetworkStatus());
|
|
12
|
+
rewardsHelper.registerRewardHandler('ADS', createAdsRewardHandler(ads));
|
|
13
|
+
rewardsHelper.registerRewardHandler(
|
|
14
|
+
'JOLI_COIN',
|
|
15
|
+
createJolicoinRewardHandler(ads.httpClient, {
|
|
16
|
+
onUnlockSuccess: (params: { quantity: number; balance: number }) => {
|
|
17
|
+
notifyCustomEvent('JOLIBOX_CUSTOM_REWARDS_EVENT', {
|
|
18
|
+
JOLI_COIN: params
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}) as unknown as (params?: unknown) => Promise<boolean>
|
|
22
|
+
);
|
|
10
23
|
|
|
11
24
|
adEventEmitter.on('isAdShowing', (isAdShowing) => {
|
|
12
25
|
notifyCustomEvent('JOLIBOX_ADS_EVENT', { isAdShowing });
|
|
@@ -21,7 +34,13 @@ commands.registerCommand('AdsSDK.adConfig', (params) => {
|
|
|
21
34
|
});
|
|
22
35
|
|
|
23
36
|
commands.registerCommand('AdsSDK.adBreak', (params) => {
|
|
24
|
-
|
|
37
|
+
if (params.type === 'reward') {
|
|
38
|
+
rewardsHelper.getRewardsTypes(ads.httpClient).then((rewardsTypes) => {
|
|
39
|
+
rewardsHelper.handleReward(rewardsTypes, params);
|
|
40
|
+
});
|
|
41
|
+
} else {
|
|
42
|
+
ads.adBreak(params);
|
|
43
|
+
}
|
|
25
44
|
});
|
|
26
45
|
|
|
27
46
|
commands.registerCommand('AdsSDK.adUnit', (params) => {
|
|
@@ -11,7 +11,7 @@ const API_GET_SYSTEM_SYNC = 'getSystemInfoSync';
|
|
|
11
11
|
const getSystemInfo = () => {
|
|
12
12
|
let config = {
|
|
13
13
|
system: context.deviceInfo.system,
|
|
14
|
-
platform: context.deviceInfo.platform,
|
|
14
|
+
platform: context.deviceInfo.platform.toLowerCase(),
|
|
15
15
|
brand: context.deviceInfo.brand,
|
|
16
16
|
pixelRatio: context.deviceInfo.pixelRatio,
|
|
17
17
|
language: context.deviceInfo.lang,
|
|
@@ -4,7 +4,7 @@ import { isValidUUIDV4 } from '@jolibox/common';
|
|
|
4
4
|
|
|
5
5
|
describe('xua', () => {
|
|
6
6
|
it('test xua', () => {
|
|
7
|
-
|
|
7
|
+
(window as any).__joliboxLocalStorage__ = window.localStorage;
|
|
8
8
|
const xua = xUserAgent();
|
|
9
9
|
const deviceInfoRegex = /.* (\(.*\) ).*/;
|
|
10
10
|
const deviceInfoMatch = xua.match(deviceInfoRegex);
|