@jolibox/implement 1.1.19-beta.1 → 1.1.19

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 (44) hide show
  1. package/.rush/temp/package-deps_build.json +23 -16
  2. package/.rush/temp/shrinkwrap-deps.json +1 -1
  3. package/dist/common/context/index.d.ts +1 -0
  4. package/dist/common/rewards/index.d.ts +1 -10
  5. package/dist/common/rewards/registers/use-ads.d.ts +3 -2
  6. package/dist/common/rewards/registers/use-jolicoin-only.d.ts +10 -0
  7. package/dist/common/rewards/registers/utils/coins/index.d.ts +32 -0
  8. package/dist/common/rewards/registers/utils/event-listener.d.ts +15 -0
  9. package/dist/common/rewards/registers/utils/index.d.ts +1 -0
  10. package/dist/common/rewards/reward-emitter.d.ts +58 -0
  11. package/dist/common/rewards/reward-helper.d.ts +2 -1
  12. package/dist/common/utils/index.d.ts +49 -2
  13. package/dist/h5/api/ads.d.ts +1 -1
  14. package/dist/h5/http/index.d.ts +1 -1
  15. package/dist/h5/rewards/index.d.ts +4 -0
  16. package/dist/index.js +197 -3
  17. package/dist/index.native.js +402 -21
  18. package/dist/native/api/ads.d.ts +1 -1
  19. package/dist/native/api/login.d.ts +4 -1
  20. package/dist/native/rewards/index.d.ts +4 -0
  21. package/dist/native/ui/modal-iframe.d.ts +26 -0
  22. package/implement.build.log +2 -2
  23. package/package.json +5 -4
  24. package/src/common/context/index.ts +8 -4
  25. package/src/common/rewards/fetch-reward.ts +9 -3
  26. package/src/common/rewards/index.ts +1 -12
  27. package/src/common/rewards/registers/use-ads.ts +3 -2
  28. package/src/common/rewards/registers/use-jolicoin-only.ts +57 -0
  29. package/src/common/rewards/registers/use-jolicoin.ts +27 -77
  30. package/src/common/rewards/registers/utils/coins/index.ts +188 -0
  31. package/src/common/rewards/registers/utils/event-listener.ts +66 -0
  32. package/src/common/rewards/registers/utils/index.ts +8 -0
  33. package/src/common/rewards/reward-emitter.ts +79 -0
  34. package/src/common/rewards/reward-helper.ts +3 -2
  35. package/src/common/utils/index.ts +117 -4
  36. package/src/h5/api/ads.ts +65 -12
  37. package/src/h5/http/index.ts +2 -2
  38. package/src/h5/rewards/index.ts +69 -0
  39. package/src/native/api/ads.ts +95 -33
  40. package/src/native/api/login.ts +1 -1
  41. package/src/native/api/navigate.ts +2 -0
  42. package/src/native/bootstrap/index.ts +33 -0
  43. package/src/native/rewards/index.ts +138 -0
  44. package/src/native/ui/modal-iframe.ts +271 -0
@@ -0,0 +1,79 @@
1
+ // rewards-emitter-wrapper.ts
2
+
3
+ import { EventEmitter } from '@jolibox/common';
4
+ import { IUnlockOption } from './type';
5
+ import { IJoliCoin } from './type';
6
+
7
+ export interface IUnlockOptionsEvent {
8
+ options: IUnlockOption[];
9
+ userJoliCoin: IJoliCoin;
10
+ }
11
+
12
+ export const UnlockOptionsEventName = 'UNLOCK_OPTIONS_CHANGED' as const;
13
+ export const InvokePaymentEventName = 'INVOKE_PAYMENT' as const;
14
+ export const PaymentResultEventName = 'ON_PAYMENT_RESULT' as const;
15
+ export const UseModalEventName = 'ON_USE_MODAL_EVENT' as const;
16
+ export const UseModalResultEventName = 'ON_USE_MODAL_RESULT' as const;
17
+
18
+ type IPaymentResult = 'SUCCESS' | 'FAILED' | 'CANCEL';
19
+ export interface IPaymentEvent {
20
+ userJoliCoin?: IJoliCoin;
21
+ paymentResult: IPaymentResult;
22
+ }
23
+
24
+ export interface IPaymentChoice {
25
+ productId: string;
26
+ totalAmountStr: string;
27
+ quantity: number;
28
+ }
29
+
30
+ export interface IInvokePaymentEvent {
31
+ userJoliCoin: IJoliCoin;
32
+ joliCoinQuantity: number;
33
+ enableAutoDeduct: boolean;
34
+ confirmButtonText: string;
35
+ cancelButtonText: string;
36
+ sequenceId?: string;
37
+ }
38
+
39
+ export interface IUseModalEvent {
40
+ enableAutoDeduct: boolean;
41
+ userJoliCoin: IJoliCoin;
42
+ joliCoinQuantity: number;
43
+ confirmButtonText: string;
44
+ cancelButtonText: string;
45
+ sequenceId?: string;
46
+ }
47
+
48
+ export interface IUseModalResultEvent {
49
+ useModalResult: 'CONFIRM' | 'CANCEL' | 'FAILED';
50
+ }
51
+
52
+ export interface RewardsEventMap extends Record<string, unknown[]> {
53
+ [UnlockOptionsEventName]: [IUnlockOptionsEvent];
54
+ [InvokePaymentEventName]: ['JOLI_COIN' | 'ADS-JOLI_COIN', IInvokePaymentEvent];
55
+ [PaymentResultEventName]: [IPaymentEvent];
56
+ [UseModalResultEventName]: [IUseModalResultEvent];
57
+ [UseModalEventName]: ['JOLI_COIN' | 'ADS-JOLI_COIN', IUseModalEvent];
58
+ }
59
+
60
+ export const originalRewardsEmitter = new EventEmitter<RewardsEventMap>();
61
+
62
+ export type RewardsEventType = keyof RewardsEventMap;
63
+
64
+ export interface RewardsEmitter {
65
+ on<T extends RewardsEventType>(event: T, callback: (...args: RewardsEventMap[T]) => void): void;
66
+ off<T extends RewardsEventType>(event: T, callback: (...args: RewardsEventMap[T]) => void): void;
67
+ emit<T extends RewardsEventType>(event: T, ...args: RewardsEventMap[T]): void;
68
+ }
69
+
70
+ // 创建包装器
71
+ export const createRewardsEmitterWrapper = (): RewardsEmitter => {
72
+ return {
73
+ on: originalRewardsEmitter.on.bind(originalRewardsEmitter),
74
+ off: originalRewardsEmitter.off.bind(originalRewardsEmitter),
75
+ emit: originalRewardsEmitter.emit.bind(originalRewardsEmitter)
76
+ };
77
+ };
78
+
79
+ export const rewardsEmitter = createRewardsEmitterWrapper();
@@ -1,11 +1,12 @@
1
- export type RewardType = 'ADS' | 'JOLI_COIN';
1
+ export type RewardType = 'ADS' | 'JOLI_COIN' | 'JOLI_COIN_ONLY';
2
2
 
3
3
  import { context } from '../context';
4
4
  import type { AdsRewardsHandler } from './registers/use-ads';
5
5
 
6
6
  export interface RewardHandlerMap {
7
7
  ADS: AdsRewardsHandler;
8
- JOLI_COIN: (params?: unknown) => Promise<boolean>;
8
+ JOLI_COIN: (params?: unknown) => Promise<boolean>; // coins + ads
9
+ JOLI_COIN_ONLY: (params?: unknown) => Promise<boolean>; // coins only
9
10
  }
10
11
 
11
12
  export type RewardHandler<T extends RewardType> = RewardHandlerMap[T];
@@ -1,5 +1,10 @@
1
+ /**
2
+ * event send to parent window
3
+ */
1
4
  const JOLIBOX_CUSTOM_ADS_EVENT_TYPE = 'JOLIBOX_ADS_EVENT';
2
5
  const JOLIBOX_CUSTOM_REWARDS_EVENT_TYPE = 'JOLIBOX_CUSTOM_REWARDS_EVENT';
6
+ const JOLIBOX_TOPUP_EVENT = 'JOLIBOX_TOPUP_EVENT';
7
+ const JOLIBOX_JOLI_COIN_USE_EVENT = 'JOLIBOX_JOLI_COIN_USE_EVENT';
3
8
 
4
9
  interface JoliboxCustomEvent {
5
10
  [JOLIBOX_CUSTOM_ADS_EVENT_TYPE]: {
@@ -11,12 +16,28 @@ interface JoliboxCustomEvent {
11
16
  balance: number;
12
17
  };
13
18
  };
19
+ [JOLIBOX_TOPUP_EVENT]: {
20
+ ['JOLI_COIN']?: {
21
+ quantity: number;
22
+ balance: number;
23
+ enableAutoDeduct?: boolean;
24
+ };
25
+ type: 'ADS-JOLI_COIN' | 'JOLI_COIN';
26
+ sequenceId: string;
27
+ };
28
+ //
29
+ [JOLIBOX_JOLI_COIN_USE_EVENT]: {
30
+ ['JOLI_COIN']?: {
31
+ quantity: number;
32
+ balance: number;
33
+ enableAutoDeduct?: boolean;
34
+ };
35
+ type: 'ADS-JOLI_COIN' | 'JOLI_COIN';
36
+ sequenceId: string;
37
+ };
14
38
  }
15
39
 
16
- export const notifyCustomEvent = <T extends keyof JoliboxCustomEvent>(
17
- eventName: T,
18
- data: JoliboxCustomEvent[T]
19
- ) => {
40
+ const notifyCustomEvent = <T extends keyof JoliboxCustomEvent>(eventName: T, data: JoliboxCustomEvent[T]) => {
20
41
  window.dispatchEvent(new CustomEvent(eventName, { detail: data }));
21
42
  window.parent?.postMessage(
22
43
  {
@@ -28,3 +49,95 @@ export const notifyCustomEvent = <T extends keyof JoliboxCustomEvent>(
28
49
  '*'
29
50
  );
30
51
  };
52
+
53
+ /**
54
+ * received event to parent window
55
+ * @param eventName
56
+ * @param data
57
+ */
58
+
59
+ const ON_JOLIBOX_TOPUP_JOLI_COIN_RESULT = 'ON_JOLIBOX_TOPUP_JOLI_COIN_RESULT';
60
+ const ON_JOLIBOX_JOLI_COIN_USE_RESULT = 'ON_JOLIBOX_JOLI_COIN_USE_RESULT';
61
+
62
+ interface ReceivedJoliboxCustomEvent {
63
+ [ON_JOLIBOX_TOPUP_JOLI_COIN_RESULT]: {
64
+ sequenceId: string;
65
+ result: 'SUCCESS' | 'CANCEL' | 'ADS';
66
+ type: 'ADS-JOLI_COIN' | 'JOLI_COIN';
67
+ joliCoin: {
68
+ enableAutoDeduct: boolean;
69
+ balance: number;
70
+ };
71
+ };
72
+ [ON_JOLIBOX_JOLI_COIN_USE_RESULT]: {
73
+ sequenceId: string;
74
+ result: 'USE_COIN' | 'ADS' | 'CANCEL';
75
+ };
76
+ }
77
+
78
+ type ReceivedJoliboxCustomEventResult = keyof ReceivedJoliboxCustomEvent;
79
+
80
+ interface MessageData<T extends ReceivedJoliboxCustomEventResult> {
81
+ type: T;
82
+ data: ReceivedJoliboxCustomEvent[T];
83
+ }
84
+
85
+ const eventCallbacks = new Map<string, Set<(data: any) => void>>();
86
+
87
+ const globalMessageHandler = (event: MessageEvent) => {
88
+ if (event.source !== window.parent) {
89
+ return;
90
+ }
91
+
92
+ try {
93
+ const message = event.data as MessageData<any>;
94
+ if (message && message.type) {
95
+ const callbacks = eventCallbacks.get(message.type);
96
+ if (callbacks) {
97
+ callbacks.forEach((callback) => {
98
+ try {
99
+ callback(message.data);
100
+ } catch (error) {
101
+ console.warn('Callback execution failed:', error);
102
+ }
103
+ });
104
+ }
105
+ }
106
+ } catch (error) {
107
+ console.warn('Failed to parse message:', error);
108
+ }
109
+ };
110
+
111
+ let isInitialized = false;
112
+ const initializeGlobalListener = () => {
113
+ if (!isInitialized) {
114
+ window.addEventListener('message', globalMessageHandler);
115
+ isInitialized = true;
116
+ }
117
+ };
118
+
119
+ const onCustomEvent = <T extends ReceivedJoliboxCustomEventResult>(
120
+ eventName: T,
121
+ callback: (data: ReceivedJoliboxCustomEvent[T]) => void
122
+ ) => {
123
+ initializeGlobalListener();
124
+
125
+ if (!eventCallbacks.has(eventName)) {
126
+ eventCallbacks.set(eventName, new Set());
127
+ }
128
+ const callbacks = eventCallbacks.get(eventName)!;
129
+
130
+ callbacks.add(callback);
131
+ return () => {
132
+ callbacks.delete(callback);
133
+ if (callbacks.size === 0) {
134
+ eventCallbacks.delete(eventName);
135
+ }
136
+ if (eventCallbacks.size === 0) {
137
+ window.removeEventListener('message', globalMessageHandler);
138
+ isInitialized = false;
139
+ }
140
+ };
141
+ };
142
+
143
+ export { onCustomEvent, notifyCustomEvent };
package/src/h5/api/ads.ts CHANGED
@@ -1,23 +1,68 @@
1
- import { JoliboxAdsImpl, adEventEmitter } from '@/common/ads';
2
1
  import { track } from '../report';
3
- import { createCommands } from '@jolibox/common';
2
+ import { createCommands, EventEmitter, platform } from '@jolibox/common';
4
3
  import { httpClientManager } from '../http';
5
4
  import { notifyCustomEvent } from '@/common/utils';
6
- import { createAdsRewardHandler, rewardsHelper, createJolicoinRewardHandler } from '@/common/rewards';
5
+ import {
6
+ createAdsRewardHandler,
7
+ rewardsHelper,
8
+ createJolicoinRewardHandler,
9
+ createJolicoinOnlyRewardHandler
10
+ } from '@/common/rewards';
11
+ import { JoliboxAdsForGame, type IAdsContext } from '@jolibox/ads';
12
+ import { context } from '@/common/context';
13
+ import '../rewards';
7
14
 
8
15
  const commands = createCommands();
9
16
 
17
+ const adEventEmitter = new EventEmitter<{
18
+ isAdShowing: [boolean];
19
+ }>();
20
+
10
21
  const httpClient = httpClientManager.create();
11
- const ads = new JoliboxAdsImpl(track, httpClient, () => httpClientManager.getNetworkStatus());
22
+
23
+ const adsContext: IAdsContext<'GAME'> = {
24
+ httpClient,
25
+ checkNetwork: () => httpClientManager.getNetworkStatus(),
26
+ track,
27
+ eventEmitter: adEventEmitter,
28
+ getContextInfo: () => {
29
+ return {
30
+ hostAppId: context.hostInfo?.aid ?? '1',
31
+ deviceId: context.deviceInfo.did ?? '',
32
+ adId: context.deviceInfo.adId ?? '',
33
+ sessionId: context.sessionId ?? '',
34
+ userId: context.hostUserInfo?.uid ?? '',
35
+ objectType: 'GAME',
36
+ objectId: context.mpId,
37
+ testAdsMode: context.testAdsMode,
38
+ channel: context.joliSource ?? '',
39
+ deviceBrand: context.deviceInfo.brand,
40
+ deviceModel: context.deviceInfo.model,
41
+ osType: platform.isAndroid ? 'ANDROID' : platform.isiOS ? 'IOS' : 'PC',
42
+ runtimeType: 'WEB',
43
+ platform: 1000 // WebSDK
44
+ };
45
+ }
46
+ };
47
+
48
+ const handleUnlockSuccess = (params: { quantity: number; balance: number }) => {
49
+ notifyCustomEvent('JOLIBOX_CUSTOM_REWARDS_EVENT', {
50
+ JOLI_COIN: params
51
+ });
52
+ };
53
+
54
+ const ads = new JoliboxAdsForGame(adsContext);
12
55
  rewardsHelper.registerRewardHandler('ADS', createAdsRewardHandler(ads));
13
56
  rewardsHelper.registerRewardHandler(
14
57
  '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
- }
58
+ createJolicoinRewardHandler(httpClient, {
59
+ onUnlockSuccess: handleUnlockSuccess.bind(this)
60
+ }) as unknown as (params?: unknown) => Promise<boolean>
61
+ );
62
+ rewardsHelper.registerRewardHandler(
63
+ 'JOLI_COIN_ONLY',
64
+ createJolicoinOnlyRewardHandler(httpClient, {
65
+ onUnlockSuccess: handleUnlockSuccess.bind(this)
21
66
  }) as unknown as (params?: unknown) => Promise<boolean>
22
67
  );
23
68
 
@@ -35,8 +80,16 @@ commands.registerCommand('AdsSDK.adConfig', (params) => {
35
80
 
36
81
  commands.registerCommand('AdsSDK.adBreak', (params) => {
37
82
  if (params.type === 'reward') {
38
- rewardsHelper.getRewardsTypes(ads.httpClient).then((rewardsTypes) => {
39
- rewardsHelper.handleReward(rewardsTypes, params);
83
+ rewardsHelper.getRewardsTypes(httpClient).then((rewardsTypes) => {
84
+ rewardsHelper.handleReward(rewardsTypes, params).catch((e) => {
85
+ // error should terminate the reward handler, invoke unlock failed
86
+ console.info('handleReward failed', e);
87
+ params.adBreakDone?.({
88
+ breakType: params.type,
89
+ breakFormat: params.type === 'reward' ? 'reward' : 'interstitial',
90
+ breakStatus: 'noAdPreloaded'
91
+ });
92
+ });
40
93
  });
41
94
  } else {
42
95
  ads.adBreak(params);
@@ -46,7 +46,7 @@ export class HttpClientManager {
46
46
  }
47
47
  }
48
48
 
49
- getNetworkStatus(): boolean {
49
+ getNetworkStatus = (): boolean => {
50
50
  if (this.networkRequests.length === 0) {
51
51
  return true;
52
52
  }
@@ -55,7 +55,7 @@ export class HttpClientManager {
55
55
  const successRate = successCount / this.networkRequests.length;
56
56
 
57
57
  return successRate >= 0.6;
58
- }
58
+ };
59
59
  }
60
60
 
61
61
  class JoliboxHttpClient implements IHttpClient {
@@ -0,0 +1,69 @@
1
+ /**
2
+ * rewards event handlers
3
+ */
4
+
5
+ import {
6
+ rewardsEmitter,
7
+ UseModalEventName,
8
+ IUseModalEvent,
9
+ PaymentResultEventName,
10
+ UseModalResultEventName,
11
+ InvokePaymentEventName,
12
+ IInvokePaymentEvent,
13
+ IUseModalResultEvent
14
+ } from '@/common/rewards/reward-emitter';
15
+ import { notifyCustomEvent, onCustomEvent } from '@/common/utils';
16
+ import { uuidv4 as v4 } from '@jolibox/common';
17
+
18
+ /**
19
+ * confirm jolicoin modal
20
+ */
21
+ const UseCoinEventMap: Record<string, IUseModalResultEvent['useModalResult']> = {
22
+ USE_COIN: 'CONFIRM',
23
+ ADS: 'FAILED',
24
+ CANCEL: 'CANCEL'
25
+ };
26
+ onCustomEvent('ON_JOLIBOX_JOLI_COIN_USE_RESULT', (data) => {
27
+ console.log('onCustomEvent ON_JOLIBOX_JOLI_COIN_USE_RESULT', UseCoinEventMap[data.result], data.result);
28
+
29
+ rewardsEmitter.emit(UseModalResultEventName, {
30
+ useModalResult: UseCoinEventMap[data.result]
31
+ });
32
+ });
33
+ rewardsEmitter.on(UseModalEventName, (type: 'JOLI_COIN' | 'ADS-JOLI_COIN', params: IUseModalEvent) => {
34
+ notifyCustomEvent('JOLIBOX_JOLI_COIN_USE_EVENT', {
35
+ JOLI_COIN: {
36
+ quantity: params.joliCoinQuantity,
37
+ balance: params.userJoliCoin.balance,
38
+ enableAutoDeduct: params.enableAutoDeduct
39
+ },
40
+ type,
41
+ sequenceId: params.sequenceId ?? v4()
42
+ });
43
+ });
44
+
45
+ /**
46
+ * payment jolicoin modal
47
+ */
48
+ onCustomEvent('ON_JOLIBOX_TOPUP_JOLI_COIN_RESULT', (data) => {
49
+ rewardsEmitter.emit(PaymentResultEventName, {
50
+ paymentResult: data.result == 'ADS' ? 'FAILED' : data.result,
51
+ userJoliCoin: data.joliCoin
52
+ });
53
+ });
54
+
55
+ rewardsEmitter.on(
56
+ InvokePaymentEventName,
57
+ async (type: 'JOLI_COIN' | 'ADS-JOLI_COIN', params: IInvokePaymentEvent) => {
58
+ // 充值面板
59
+ notifyCustomEvent('JOLIBOX_TOPUP_EVENT', {
60
+ JOLI_COIN: {
61
+ quantity: params.joliCoinQuantity,
62
+ balance: params.userJoliCoin.balance,
63
+ enableAutoDeduct: params.enableAutoDeduct
64
+ },
65
+ type,
66
+ sequenceId: params.sequenceId ?? v4()
67
+ });
68
+ }
69
+ );
@@ -1,61 +1,116 @@
1
1
  import { createCommands } from '@jolibox/common';
2
2
  import { createSyncAPI, registerCanIUse } from './base';
3
3
  import { createToast } from '@jolibox/ui';
4
+ import '../rewards';
4
5
 
5
6
  const commands = createCommands();
6
7
 
7
- import { IAdsInitParams, JoliboxAdsImpl, IAdConfigParams, IAdBreakParams, IAdUnitParams } from '@/common/ads';
8
+ // import { IAdsInitParams, JoliboxAdsImpl, IAdConfigParams, IAdBreakParams, IAdUnitParams } from '@/common/ads';
9
+ import {
10
+ JoliboxAdsForGame,
11
+ type IAdsInitParams,
12
+ type IAdsContext,
13
+ type IAdConfigParams,
14
+ type IAdBreakParams,
15
+ type IAdUnitParams
16
+ } from '@jolibox/ads';
8
17
  import { track } from '../report';
18
+ import { context } from '@/common/context';
9
19
 
10
20
  import { innerFetch as fetch } from '../network';
11
21
  import { invokeNative } from '@jolibox/native-bridge';
12
- import { rewardsHelper, createAdsRewardHandler, createJolicoinRewardHandler } from '@/common/rewards';
22
+ import {
23
+ rewardsHelper,
24
+ createAdsRewardHandler,
25
+ createJolicoinRewardHandler,
26
+ createJolicoinOnlyRewardHandler
27
+ } from '@/common/rewards';
13
28
 
14
29
  const checkNetworkStatus = () => {
15
30
  const { data } = invokeNative('getNetworkStatusSync');
16
31
  return !!data?.isConnected;
17
32
  };
18
- const ads = new JoliboxAdsImpl(
33
+
34
+ const httpClient = {
35
+ get: <T>(url: string, options?: any) =>
36
+ fetch<T>(url, {
37
+ method: 'GET',
38
+ responseType: 'json',
39
+ appendHostCookie: true,
40
+ ...options
41
+ }).then((res) => res.response.data as T),
42
+ post: <T>(url: string, options?: any) =>
43
+ fetch<T>(url, {
44
+ method: 'POST',
45
+ responseType: 'json',
46
+ appendHostCookie: true,
47
+ ...options
48
+ }).then((res) => res.response.data as T)
49
+ };
50
+
51
+ const adsContext: IAdsContext<'GAME'> = {
52
+ httpClient,
53
+ checkNetwork: checkNetworkStatus,
19
54
  track,
20
- {
21
- get: <T>(url: string, options?: any) =>
22
- fetch<T>(url, {
23
- method: 'GET',
24
- responseType: 'json',
25
- appendHostCookie: true,
26
- ...options
27
- }).then((res) => res.response.data as T),
28
- post: <T>(url: string, data: any, options?: any) =>
29
- fetch<T>(url, {
30
- method: 'POST',
31
- responseType: 'json',
32
- appendHostCookie: true,
33
- ...options
34
- }).then((res) => res.response.data as T)
35
- },
36
- checkNetworkStatus
37
- );
55
+ getContextInfo: () => {
56
+ return {
57
+ hostAppId: context.hostInfo?.aid ?? '1',
58
+ deviceId: context.deviceInfo.did ?? '',
59
+ adId: context.deviceInfo.adId ?? '',
60
+ sessionId: context.sessionId ?? '',
61
+ userId: context.hostUserInfo?.uid ?? '',
62
+ objectType: 'GAME',
63
+ objectId: context.mpId,
64
+ testAdsMode: context.testAdsMode,
65
+ channel: context.joliSource ?? '',
66
+ deviceBrand: context.deviceInfo.brand,
67
+ deviceModel: context.deviceInfo.model,
68
+ osType: context.platform === 'android' ? 'ANDROID' : context.platform === 'ios' ? 'IOS' : 'PC',
69
+ runtimeType: 'APP',
70
+ platform: 1000 // WebSDK
71
+ };
72
+ }
73
+ };
74
+
75
+ const ads = new JoliboxAdsForGame(adsContext);
38
76
 
39
77
  rewardsHelper.registerRewardHandler('ADS', createAdsRewardHandler(ads));
40
78
  rewardsHelper.registerRewardHandler(
41
79
  'JOLI_COIN',
42
- createJolicoinRewardHandler(ads.httpClient, {
80
+ createJolicoinRewardHandler(httpClient, {
43
81
  onUnlockSuccess: (params) => {
44
- const { quantity, balance } = params;
45
- const balanceStr = balance >= 1000 ? '999+' : balance;
46
- createToast(`{slot-checkmark} −${quantity} {slot-coin} | Balance: ${balanceStr} {slot-coin}`, {
47
- customStyle: {
48
- mark: {
49
- marginRight: '8px'
50
- }
51
- }
52
- });
82
+ showUnlockSuccessToast(params);
53
83
  },
54
84
  onUnlockFailed: () => {
55
85
  console.log('onUnlockFailed');
56
86
  }
57
87
  }) as unknown as (params?: unknown) => Promise<boolean>
58
88
  );
89
+
90
+ rewardsHelper.registerRewardHandler(
91
+ 'JOLI_COIN_ONLY',
92
+ createJolicoinOnlyRewardHandler(httpClient, {
93
+ onUnlockSuccess: (params) => {
94
+ showUnlockSuccessToast(params);
95
+ },
96
+ onUnlockFailed: () => {
97
+ console.log('onUnlockFailed');
98
+ }
99
+ }) as unknown as (params?: unknown) => Promise<boolean>
100
+ );
101
+
102
+ const showUnlockSuccessToast = (params: { quantity: number; balance: number }) => {
103
+ const { quantity } = params;
104
+ const toastTemplate = `{slot-coin} ${quantity} JoliCoins used`;
105
+ createToast(toastTemplate, {
106
+ customStyle: {
107
+ mark: {
108
+ marginRight: '8px'
109
+ }
110
+ }
111
+ });
112
+ };
113
+
59
114
  const adInit = createSyncAPI('adInit', {
60
115
  implement: (config?: IAdsInitParams) => {
61
116
  ads.init(config);
@@ -71,8 +126,15 @@ const adConfig = createSyncAPI('adConfig', {
71
126
  const adBreak = createSyncAPI('adBreak', {
72
127
  implement: (params: IAdBreakParams) => {
73
128
  if (params.type === 'reward') {
74
- rewardsHelper.getRewardsTypes(ads.httpClient).then((rewardsTypes) => {
75
- rewardsHelper.handleReward(rewardsTypes, params);
129
+ rewardsHelper.getRewardsTypes(httpClient).then((rewardsTypes) => {
130
+ rewardsHelper.handleReward(rewardsTypes, params).catch((e) => {
131
+ console.info('handleReward failed in native', e);
132
+ params.adBreakDone?.({
133
+ breakType: params.type,
134
+ breakFormat: params.type === 'reward' ? 'reward' : 'interstitial',
135
+ breakStatus: 'noAdPreloaded'
136
+ });
137
+ });
76
138
  });
77
139
  } else {
78
140
  ads.adBreak(params);
@@ -23,7 +23,7 @@ registerCanIUse('checkSession', {
23
23
  }
24
24
  });
25
25
 
26
- const login = createAPI('login', {
26
+ export const login = createAPI('login', {
27
27
  async implement() {
28
28
  const {
29
29
  data: { isLogin }
@@ -48,6 +48,8 @@ const interceptSystemExitSync = createSyncAPI('interceptSystemExitSync', {
48
48
  paramsSchema: t.tuple(t.boolean()),
49
49
  implement: (intercept) => {
50
50
  hostEmitter.emit('onInterceptSystemExit', { intercept });
51
+ // 同步拦截物理退出
52
+ hostEmitter.emit('onInterceptBackPress', { intercept });
51
53
  }
52
54
  });
53
55
 
@@ -7,6 +7,7 @@ import { adEventEmitter } from '@/common/ads';
7
7
  import { openRetentionSchema } from '../ui/retention';
8
8
  import { innerFetch } from '../network';
9
9
  import { Env } from '@jolibox/types';
10
+ import { createIframeModal, registerIframeModalToGlobal } from '../ui/modal-iframe';
10
11
  interface IBasicMetaConfig {
11
12
  canShowRecommended: boolean;
12
13
  }
@@ -16,6 +17,7 @@ declare const globalThis: {
16
17
  doExit: (uuid: string) => void;
17
18
  onDocumentReady: (path: string) => void;
18
19
  env: (params: { onError: (error: Error) => void }) => Env | undefined;
20
+ doBackPress: (uuid: string) => void;
19
21
  };
20
22
  };
21
23
 
@@ -27,6 +29,7 @@ declare const globalThis: {
27
29
  * 清除safari jolibox样式
28
30
  */
29
31
  let cleanStyles: () => void;
32
+ let unregisterIframeModal: () => void;
30
33
 
31
34
  /**
32
35
  * 启动时间戳
@@ -45,6 +48,11 @@ let baskcMeta: IBasicMetaConfig | null = null;
45
48
  */
46
49
  let isInterceptSystemExit = false;
47
50
 
51
+ /**
52
+ * android全局拦截物理按键
53
+ */
54
+ let isInterceptBackPress = false;
55
+
48
56
  RuntimeLoader.onReady(() => {
49
57
  // TODO: merge some env config
50
58
  });
@@ -73,6 +81,20 @@ function addInterceptSystemExitListener() {
73
81
  });
74
82
  }
75
83
 
84
+ /**
85
+ * android 全局拦截物理退出
86
+ */
87
+ function addBackPressListener() {
88
+ hostEmitter.on('onInterceptBackPress', ({ intercept }) => {
89
+ isInterceptBackPress = intercept;
90
+ });
91
+
92
+ onNative('onBeforeBackPress', ({ uuid }) => {
93
+ hostEmitter.emit('onBackPress', {});
94
+ globalThis.joliboxJSCore?.doBackPress?.(uuid);
95
+ });
96
+ }
97
+
76
98
  /**
77
99
  * The DOMContentLoaded event might be triggered very early,
78
100
  * so the global loaded listener should be executed during the bootstrap process.
@@ -135,6 +157,7 @@ function addDoExitLoader() {
135
157
  timestamp: Date.now()
136
158
  });
137
159
  cleanStyles?.();
160
+ unregisterIframeModal?.();
138
161
  taskTracker.close(Date.now() - start_timestamp);
139
162
  };
140
163
 
@@ -169,6 +192,13 @@ function addDoExitLoader() {
169
192
  doActualExit();
170
193
  return false;
171
194
  });
195
+
196
+ RuntimeLoader.onDoBackPress(async () => {
197
+ if (shouldInterceptSystemExit()) {
198
+ return true; // Forbid exit on watching ads
199
+ }
200
+ return false;
201
+ });
172
202
  }
173
203
 
174
204
  async function fetchMetaConfig() {
@@ -187,9 +217,12 @@ export function config(): void {
187
217
  addGameServiceReadyListener();
188
218
  addShowAdListener();
189
219
  addInterceptSystemExitListener();
220
+ addBackPressListener();
190
221
  addDoExitLoader();
191
222
  addWebviewReadyListener();
192
223
  addI18nChangedListener();
193
224
  fetchMetaConfig();
225
+ unregisterIframeModal = registerIframeModalToGlobal();
226
+
194
227
  cleanStyles = initializeNativeEnv();
195
228
  }