@jolibox/implement 1.1.32 → 1.1.34

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.
@@ -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,8 +219,19 @@ 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
- const { data } = await login();
232
+ const { data } = await login({
233
+ skipLogin: true
234
+ });
184
235
  if (!data?.isLogin) {
185
236
  console.log('login failed');
186
237
  return;
@@ -211,6 +262,16 @@ rewardsEmitter.on(
211
262
  appStoreProductId
212
263
  });
213
264
  loading.hide();
265
+ track('order_pay_result', {
266
+ eventType: EventType.Other,
267
+ targetType: 'game',
268
+ payWay: 'app_iap',
269
+ coinAmount: params.joliCoinQuantity,
270
+ orderPrice:
271
+ balenceDetails.paymentChoices.find((choice) => choice.productId === productId)
272
+ ?.totalAmountStr ?? '',
273
+ payResult: code
274
+ });
214
275
  if (code !== 'SUCCESS') {
215
276
  /** add timeout for google panel closed */
216
277
  console.info('[JoliboxSDK] payment failed in payment.invokePaymet');
@@ -226,12 +287,26 @@ rewardsEmitter.on(
226
287
  rewardsEmitter.emit(PaymentResultEventName, {
227
288
  paymentResult: (type ?? '') === 'CANCEL' ? 'CANCEL' : 'FAILED'
228
289
  });
290
+ track('ad_unlock_click', {
291
+ targetType: 'game',
292
+ eventType: EventType.Click
293
+ });
229
294
  modal.destroy();
230
295
  }
231
296
  },
232
297
  onEnableDeductChanged: async (enabled: boolean) => {
233
298
  await updateAutoDeductConfig(enabled);
234
299
  }
300
+ },
301
+ onSelect: (productId: string) => {
302
+ track('coinorder_click', {
303
+ targetType: 'game',
304
+ eventType: EventType.Click,
305
+ coinAmount: params.joliCoinQuantity,
306
+ orderPrice:
307
+ balenceDetails.paymentChoices.find((choice) => choice.productId === productId)
308
+ ?.totalAmountStr ?? ''
309
+ });
235
310
  }
236
311
  });
237
312
 
@@ -292,5 +367,8 @@ const getBalenceDetails = async (): Promise<
292
367
  mergeResponseData(response.data?.data, data);
293
368
  }
294
369
  console.info('productDetails', response.data?.data);
370
+ if (response.data?.data?.paymentChoices.length === 0) {
371
+ throw new Error('paymentChoices is empty');
372
+ }
295
373
  return response.data?.data;
296
374
  };
@@ -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
  }