@jolibox/implement 1.1.31 → 1.1.33

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.
@@ -6,7 +6,10 @@
6
6
  export declare const checkUseModalFrequency: (config: {
7
7
  dailyMaxPopUps: number;
8
8
  minInterval: number;
9
- }) => Promise<boolean>;
9
+ }) => Promise<{
10
+ canShow: boolean;
11
+ isFirst: boolean;
12
+ }>;
10
13
  export declare const updateUseModalFrequency: () => Promise<void>;
11
14
  /**
12
15
  * check can show paymentModal
@@ -14,7 +17,10 @@ export declare const updateUseModalFrequency: () => Promise<void>;
14
17
  export declare const checkPaymentFrequency: (config: {
15
18
  dailyMaxPopUps: number;
16
19
  minInterval: number;
17
- }) => Promise<boolean>;
20
+ }) => Promise<{
21
+ canShow: boolean;
22
+ isFirst: boolean;
23
+ }>;
18
24
  /**
19
25
  * update paymentFrequency
20
26
  */
@@ -1,9 +1,9 @@
1
1
  Invoking: npm run clean && npm run build:esm && tsc
2
2
 
3
- > @jolibox/implement@1.1.31 clean
3
+ > @jolibox/implement@1.1.33 clean
4
4
  > rimraf ./dist
5
5
 
6
6
 
7
- > @jolibox/implement@1.1.31 build:esm
7
+ > @jolibox/implement@1.1.33 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,15 +1,15 @@
1
1
  {
2
2
  "name": "@jolibox/implement",
3
3
  "description": "This project is Jolibox JS-SDk implement for Native && H5",
4
- "version": "1.1.31",
4
+ "version": "1.1.33",
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.31",
10
- "@jolibox/types": "1.1.31",
11
- "@jolibox/native-bridge": "1.1.31",
12
- "@jolibox/ads": "1.1.31",
9
+ "@jolibox/common": "1.1.33",
10
+ "@jolibox/types": "1.1.33",
11
+ "@jolibox/native-bridge": "1.1.33",
12
+ "@jolibox/ads": "1.1.33",
13
13
  "localforage": "1.10.0",
14
14
  "@jolibox/ui": "1.0.0",
15
15
  "web-vitals": "4.2.4"
@@ -1,4 +1,4 @@
1
- import { debounce } from '@jolibox/common';
1
+ import { debounce, EventType } from '@jolibox/common';
2
2
  import { ReportHandler, Track, TrackPerformance, CommonReportConfig } from './types';
3
3
  import { PerformanceType, TrackEvent } from '@jolibox/types';
4
4
  import { InternalGlobalJSError } from '@jolibox/common';
@@ -7,11 +7,13 @@ import { reportError } from './errors/report';
7
7
  // Track system event, wrap common config
8
8
  export function createTrack(reportHandler: ReportHandler, common: CommonReportConfig): Track {
9
9
  const track = (tag: TrackEvent, info: Record<string, unknown> | null, webviewId?: number): void => {
10
+ const { eventType, ...rest } = info ?? {};
10
11
  const data = {
11
12
  tag,
13
+ eventType: (eventType ?? EventType.Other) as number,
12
14
  ...common,
13
15
  extra: {
14
- ...info
16
+ ...rest
15
17
  }
16
18
  };
17
19
  if (tag == 'globalJsError') {
@@ -1,8 +1,8 @@
1
1
  import { PerformanceType, TrackEvent } from '@jolibox/types';
2
- import { EProject } from '@jolibox/common';
2
+ import { EProject, EventType } from '@jolibox/common';
3
3
 
4
4
  export interface Track {
5
- (tag: TrackEvent, info: Record<string, unknown> | null): void;
5
+ (tag: TrackEvent, info: Record<string, unknown> | null, eventType?: number): void;
6
6
  debounce: (tag: TrackEvent, info: Record<string, unknown>) => void;
7
7
  }
8
8
 
@@ -13,7 +13,7 @@ export interface TrackPerformance {
13
13
 
14
14
  export type ReportHandler = (
15
15
  event: string,
16
- data: { tag: TrackEvent; data?: Record<string, string | number | boolean | null> },
16
+ data: { tag: TrackEvent; eventType?: number; data?: Record<string, string | number | boolean | null> },
17
17
  webviewId?: number
18
18
  ) => void;
19
19
 
@@ -81,9 +81,7 @@ export const createShowUnlockWithJolicoinModal = (
81
81
  };
82
82
 
83
83
  const checkIfCancel = (result: IPaymentResult | IUseModalResult) => {
84
- if (result == 'CANCEL') {
85
- throw new Error('CANCEL');
86
- }
84
+ return result == 'CANCEL';
87
85
  };
88
86
 
89
87
  export const createCommonJolicoinRewardHandler = (
@@ -118,7 +116,6 @@ export const createCommonJolicoinRewardHandler = (
118
116
  userJoliCoin: unlockOptions?.userJoliCoin,
119
117
  joliCoinQuantity
120
118
  });
121
- checkIfCancel(paymentResult);
122
119
 
123
120
  if (paymentResult !== 'SUCCESS') {
124
121
  handleUnlockFailed?.(params);
@@ -137,7 +134,6 @@ export const createCommonJolicoinRewardHandler = (
137
134
  joliCoinQuantity
138
135
  });
139
136
 
140
- checkIfCancel(shouldUnlock);
141
137
  if (shouldUnlock !== 'CONFIRM') {
142
138
  handleUnlockFailed?.(params);
143
139
  return false;
@@ -1,6 +1,7 @@
1
1
  import { createCommands } from '@jolibox/common';
2
2
  import { createSyncAPI, registerCanIUse } from './base';
3
3
  import { invokeNative } from '@jolibox/native-bridge';
4
+ import { context } from '@/common/context';
4
5
 
5
6
  const commands = createCommands();
6
7
 
@@ -19,7 +20,11 @@ const getSystemInfoSync = createSyncAPI(API_GET_SYSTEM_SYNC, {
19
20
  language: data.deviceInfo.lang,
20
21
  brand: data.deviceInfo.brand,
21
22
  appName: data.hostInfo?.appName,
22
- SDKVersion: data.sdkInfo.jssdkVersion
23
+ SDKVersion: data.sdkInfo.jssdkVersion,
24
+ viewport: {
25
+ statusBarHeight: context.viewport.statusBarHeight,
26
+ navigationBarHeight: context.viewport.navigationBarHeight
27
+ }
23
28
  };
24
29
  }
25
30
  });
@@ -40,5 +45,8 @@ registerCanIUse('env', {
40
45
  });
41
46
 
42
47
  registerCanIUse('getSystemInfoSync', {
43
- version: '1.0.0'
48
+ version: '1.0.0',
49
+ properties: {
50
+ viewport: '1.0.33'
51
+ }
44
52
  });
@@ -58,11 +58,13 @@ onNative('onPaymentStateChange', (data) => {
58
58
  );
59
59
  }
60
60
  }
61
- const failedStatus = IPaymentIAPFailedStatusMap[status] ?? 'unlockFailed';
62
- createToast(`{slot-error} {slot-i18n-jolicoin.${failedStatus}}`, {
63
- position: 'center',
64
- duration: 3000
65
- });
61
+ const failedStatus = IPaymentIAPFailedStatusMap[status];
62
+ if (failedStatus) {
63
+ createToast(`{slot-error} {slot-i18n-jolicoin.${failedStatus}}`, {
64
+ position: 'center',
65
+ duration: 3000
66
+ });
67
+ }
66
68
  deferred.resolve({
67
69
  code: 'FAILED' as ResponseType,
68
70
  message: 'jolicoin payment failed'
@@ -1,5 +1,5 @@
1
1
  import './errors';
2
- import { createCommands, isObject, isString, EventEmitter, EProject } from '@jolibox/common';
2
+ import { createCommands, isObject, isString, EventEmitter, EProject, EventType } from '@jolibox/common';
3
3
  export * from '../../common/report/types';
4
4
  import { createTracks, ReportHandler } from '@/common/report';
5
5
  import { context } from '@/common/context';
@@ -20,10 +20,13 @@ const reportNative: ReportHandler = (event, data, webviewId) => {
20
20
  session_id: context.sessionId,
21
21
  user_id: context.hostUserInfo?.uid ?? ''
22
22
  };
23
+ const eventType = (_data.eventType ?? EventType.Other) as number;
24
+
23
25
  invokeNative('trackAsync', {
24
26
  event: data.tag ?? event,
25
27
  data: extra,
26
- webviewId
28
+ webviewId,
29
+ eventType
27
30
  });
28
31
  };
29
32
 
@@ -22,19 +22,31 @@ export const checkUseModalFrequency = async (config: { dailyMaxPopUps: number; m
22
22
  const res = (await getGlobalStorage('joli_coin_use_modal_frequency')) as StandardResponse<string>;
23
23
  console.log('checkUseModalFrequency', res.data);
24
24
  if (!res.data) {
25
- return true;
25
+ return {
26
+ canShow: true,
27
+ isFirst: true
28
+ };
26
29
  }
27
30
 
28
31
  const fequeycies = parseFrequency(res.data);
29
32
  const todayFequencies = filterTodayTimestamps(fequeycies);
30
33
  if (todayFequencies.length >= dailyMaxPopUps) {
31
- return false;
34
+ return {
35
+ canShow: false,
36
+ isFirst: false
37
+ };
32
38
  }
33
39
  const now = Date.now();
34
40
  if (now - todayFequencies[todayFequencies.length - 1] < minInterval) {
35
- return false;
41
+ return {
42
+ canShow: false,
43
+ isFirst: false
44
+ };
36
45
  }
37
- return true;
46
+ return {
47
+ canShow: true,
48
+ isFirst: false
49
+ };
38
50
  };
39
51
 
40
52
  export const updateUseModalFrequency = async () => {
@@ -56,18 +68,30 @@ export const checkPaymentFrequency = async (config: { dailyMaxPopUps: number; mi
56
68
  const res = (await getGlobalStorage('joli_coin_payment_frequency')) as StandardResponse<string>;
57
69
  console.log('checkPaymentFrequency', res.data);
58
70
  if (!res.data) {
59
- return true;
71
+ return {
72
+ canShow: true,
73
+ isFirst: true
74
+ };
60
75
  }
61
76
  const frequencies = parseFrequency(res.data);
62
77
  const todayFequencies = filterTodayTimestamps(frequencies);
63
78
  if (todayFequencies.length >= dailyMaxPopUps) {
64
- return false;
79
+ return {
80
+ canShow: false,
81
+ isFirst: false
82
+ };
65
83
  }
66
84
  const now = Date.now();
67
85
  if (now - todayFequencies[todayFequencies.length - 1] < minInterval) {
68
- return false;
86
+ return {
87
+ canShow: false,
88
+ isFirst: false
89
+ };
69
90
  }
70
- return true;
91
+ return {
92
+ canShow: true,
93
+ isFirst: false
94
+ };
71
95
  };
72
96
 
73
97
  /**
@@ -19,6 +19,7 @@ import { innerFetch as fetch } from '../network';
19
19
  import { StandardResponse } from '@jolibox/types';
20
20
  import { context } from '@/common/context';
21
21
  import { login } from '../api/login';
22
+ import { EventType } from '@jolibox/common';
22
23
 
23
24
  import { createEventPromiseHandler } from '@/common/rewards/registers/utils/event-listener';
24
25
  import {
@@ -32,6 +33,7 @@ import { createLoading } from '@jolibox/ui';
32
33
  import { canIUseNative } from '../api/base';
33
34
  import { applyNative } from '@jolibox/native-bridge';
34
35
  import { isUndefinedOrNull } from '@jolibox/common';
36
+ import { track } from '../report';
35
37
 
36
38
  const modalUseFrequencyConfig = createEventPromiseHandler<
37
39
  IUseModalFrequencyConfig,
@@ -77,15 +79,33 @@ rewardsEmitter.on(UseModalEventName, async (type: 'JOLI_COIN' | 'ADS-JOLI_COIN',
77
79
  duration: 3000
78
80
  });
79
81
  const config = await modalUseFrequencyConfig.getData();
80
- const canShowUseModal = await checkUseModalFrequency(config.joliCoinUseAndCharge.useJolicoin);
81
- console.log('use modal show by frequency', canShowUseModal);
82
+ const { canShow: canShowUseModal, isFirst: isFirstUseModal } = await checkUseModalFrequency(
83
+ config.joliCoinUseAndCharge.useJolicoin
84
+ );
85
+ console.log('use modal show by frequency', canShowUseModal, isFirstUseModal);
82
86
  loading.hide();
87
+
88
+ // First, check for direct use: sufficient balance, auto-deduct enabled, and not the first modal.
89
+ const { balance } = params.userJoliCoin;
90
+ const useDirectly = balance >= params.joliCoinQuantity && params.enableAutoDeduct;
91
+ if (useDirectly && !isFirstUseModal) {
92
+ rewardsEmitter.emit(UseModalResultEventName, { useModalResult: 'CONFIRM' });
93
+ return;
94
+ }
95
+
96
+ // If not used directly, then check frequency control.
83
97
  if (!canShowUseModal) {
84
- // return by frequency control
98
+ // confirm by frequency control
85
99
  rewardsEmitter.emit(UseModalResultEventName, { useModalResult: 'CONFIRM' });
86
100
  return;
87
101
  }
88
102
  }
103
+
104
+ track('unlock_popup_show', {
105
+ eventType: EventType.View,
106
+ targetType: 'game'
107
+ });
108
+
89
109
  const modal = createConfirmJolicoinModal({
90
110
  data: {
91
111
  enableAutoDeduct: params.enableAutoDeduct,
@@ -96,6 +116,12 @@ rewardsEmitter.on(UseModalEventName, async (type: 'JOLI_COIN' | 'ADS-JOLI_COIN',
96
116
  confirm: {
97
117
  text: params.confirmButtonText,
98
118
  onPress: () => {
119
+ track('coin_unlock_click', {
120
+ targetType: 'game',
121
+ eventType: EventType.Click,
122
+ coinConsume: params.joliCoinQuantity,
123
+ ifAutoUnlock: params.enableAutoDeduct
124
+ });
99
125
  rewardsEmitter.emit(UseModalResultEventName, { useModalResult: 'CONFIRM' });
100
126
  modal.destroy();
101
127
  }
@@ -103,6 +129,12 @@ rewardsEmitter.on(UseModalEventName, async (type: 'JOLI_COIN' | 'ADS-JOLI_COIN',
103
129
  cancel: {
104
130
  text: params.cancelButtonText,
105
131
  onPress: ({ type }) => {
132
+ if (type === 'FAILED') {
133
+ track('ad_unlock_click', {
134
+ targetType: 'game',
135
+ eventType: EventType.Click
136
+ });
137
+ }
106
138
  rewardsEmitter.emit(UseModalResultEventName, {
107
139
  useModalResult: (type ?? '') === 'CANCEL' ? 'CANCEL' : 'FAILED'
108
140
  });
@@ -140,7 +172,9 @@ rewardsEmitter.on(
140
172
  duration: 3000
141
173
  });
142
174
  const config = await modalUseFrequencyConfig.getData();
143
- const canShowPaymentModal = await checkPaymentFrequency(config.joliCoinUseAndCharge.charge);
175
+ const { canShow: canShowPaymentModal } = await checkPaymentFrequency(
176
+ config.joliCoinUseAndCharge.charge
177
+ );
144
178
  console.log('use payment show by frequency', canShowPaymentModal);
145
179
  loading.hide();
146
180
  if (!canShowPaymentModal) {
@@ -164,6 +198,12 @@ rewardsEmitter.on(
164
198
  rewardsEmitter.emit(PaymentResultEventName, { paymentResult: 'FAILED' });
165
199
  return;
166
200
  }
201
+
202
+ track('coinorder_show', {
203
+ targetType: 'game',
204
+ eventType: EventType.View
205
+ });
206
+
167
207
  const modal = createPaymentJolicoinModal({
168
208
  data: {
169
209
  userJolicoin: params.userJoliCoin,
@@ -179,6 +219,15 @@ rewardsEmitter.on(
179
219
  confirm: {
180
220
  text: params.confirmButtonText,
181
221
  onPress: async (productId: string) => {
222
+ track('order_pay_ensure', {
223
+ targetType: 'game',
224
+ eventType: EventType.Click,
225
+ payWay: 'app_iap',
226
+ coinAmount: params.joliCoinQuantity,
227
+ orderPrice:
228
+ balenceDetails.paymentChoices.find((choice) => choice.productId === productId)
229
+ ?.totalAmountStr ?? ''
230
+ });
182
231
  if (!context.hostUserInfo?.isLogin) {
183
232
  const { data } = await login();
184
233
  if (!data?.isLogin) {
@@ -211,6 +260,16 @@ rewardsEmitter.on(
211
260
  appStoreProductId
212
261
  });
213
262
  loading.hide();
263
+ track('order_pay_result', {
264
+ eventType: EventType.Other,
265
+ targetType: 'game',
266
+ payWay: 'app_iap',
267
+ coinAmount: params.joliCoinQuantity,
268
+ orderPrice:
269
+ balenceDetails.paymentChoices.find((choice) => choice.productId === productId)
270
+ ?.totalAmountStr ?? '',
271
+ payResult: code
272
+ });
214
273
  if (code !== 'SUCCESS') {
215
274
  /** add timeout for google panel closed */
216
275
  console.info('[JoliboxSDK] payment failed in payment.invokePaymet');
@@ -226,12 +285,26 @@ rewardsEmitter.on(
226
285
  rewardsEmitter.emit(PaymentResultEventName, {
227
286
  paymentResult: (type ?? '') === 'CANCEL' ? 'CANCEL' : 'FAILED'
228
287
  });
288
+ track('ad_unlock_click', {
289
+ targetType: 'game',
290
+ eventType: EventType.Click
291
+ });
229
292
  modal.destroy();
230
293
  }
231
294
  },
232
295
  onEnableDeductChanged: async (enabled: boolean) => {
233
296
  await updateAutoDeductConfig(enabled);
234
297
  }
298
+ },
299
+ onSelect: (productId: string) => {
300
+ track('coinorder_click', {
301
+ targetType: 'game',
302
+ eventType: EventType.Click,
303
+ coinAmount: params.joliCoinQuantity,
304
+ orderPrice:
305
+ balenceDetails.paymentChoices.find((choice) => choice.productId === productId)
306
+ ?.totalAmountStr ?? ''
307
+ });
235
308
  }
236
309
  });
237
310
 
@@ -292,5 +365,8 @@ const getBalenceDetails = async (): Promise<
292
365
  mergeResponseData(response.data?.data, data);
293
366
  }
294
367
  console.info('productDetails', response.data?.data);
368
+ if (response.data?.data?.paymentChoices.length === 0) {
369
+ throw new Error('paymentChoices is empty');
370
+ }
295
371
  return response.data?.data;
296
372
  };
@@ -1,8 +1,15 @@
1
1
  import { context } from '@/common/context';
2
2
  import { invokeNative, subscribe } from '@jolibox/native-bridge';
3
- import { Deferred } from '@jolibox/common';
3
+ import { Deferred, EventType } from '@jolibox/common';
4
4
  import { createRecommendModal, IGame, IRecommendationButton, RecommendModalOnCloseParams } from '@jolibox/ui';
5
5
  import { innerFetch as fetch } from '../network';
6
+ import { track } from '../report';
7
+
8
+ enum RecommendGuideType {
9
+ Hide = 'Hide',
10
+ View = 'View',
11
+ Click = 'Click'
12
+ }
6
13
 
7
14
  let exitRecommendationsCache: {
8
15
  code: string;
@@ -96,6 +103,17 @@ export async function openRetentionSchema() {
96
103
  const { gameListInfo, title, buttons } = data.data;
97
104
 
98
105
  isOpenRetentionSchema = true;
106
+
107
+ const modalContentForReport = {
108
+ domain: 'GAME',
109
+ buttons: data.data.buttons.map((btn) => `${btn.type}-${btn.text}`).join(','),
110
+ list: gameListInfo.games.map((game) => game.gameId).join(',')
111
+ };
112
+
113
+ track('RecommendedGuide', {
114
+ eventType: EventType.View,
115
+ ...modalContentForReport
116
+ });
99
117
  const modal = createRecommendModal({
100
118
  games: gameListInfo.games,
101
119
  title,
@@ -104,17 +122,33 @@ export async function openRetentionSchema() {
104
122
  isOpenRetentionSchema = false;
105
123
  switch (params.type) {
106
124
  case 'quit':
125
+ track('RecommendedGuide', {
126
+ eventType: EventType.Click,
127
+ type: 'QUIT',
128
+ domain: 'GAME'
129
+ });
107
130
  quitResultDeffer.resolve(false);
108
131
  break;
109
132
  case 'dismiss':
133
+ track('RecommendedGuide', {
134
+ eventType: EventType.Hide,
135
+ ...modalContentForReport
136
+ });
110
137
  quitResultDeffer.resolve(true);
111
138
  modal.destroy();
139
+
112
140
  break;
113
141
  case 'navigate':
114
142
  // TODO: 跳转游戏
115
143
  if (params.data?.game) {
116
144
  const game = params.data.game;
117
145
  openGameSchema(game);
146
+ track('GameItem', {
147
+ eventType: EventType.Click,
148
+ gameId: game.gameId,
149
+ layerName: 'RecommendedGuide',
150
+ domain: 'GAME'
151
+ });
118
152
  setTimeout(() => {
119
153
  quitResultDeffer.resolve(false);
120
154
  }, 0);
@@ -125,6 +159,10 @@ export async function openRetentionSchema() {
125
159
  break;
126
160
  default:
127
161
  // 关闭弹框,留在当前游戏
162
+ track('RecommendedGuide', {
163
+ eventType: EventType.Hide,
164
+ ...modalContentForReport
165
+ });
128
166
  quitResultDeffer.resolve(true);
129
167
  break;
130
168
  }