@jolibox/implement 1.1.11 → 1.1.13-beta.1

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 (42) hide show
  1. package/.rush/temp/package-deps_build.json +24 -20
  2. package/.rush/temp/shrinkwrap-deps.json +1 -1
  3. package/dist/common/ads/anti-cheating.d.ts +16 -53
  4. package/dist/common/ads/anti-cheating.test.d.ts +1 -0
  5. package/dist/common/context/index.d.ts +5 -0
  6. package/dist/common/context/url-parse.d.ts +8 -1
  7. package/dist/index.js +3 -3
  8. package/dist/index.native.js +110 -4
  9. package/dist/native/api/index.d.ts +1 -0
  10. package/dist/native/api/navigate.d.ts +1 -0
  11. package/dist/native/bootstrap/bridge.d.ts +1 -1
  12. package/dist/native/js-bridge/const.d.ts +1 -0
  13. package/dist/native/js-bridge/publish.d.ts +1 -0
  14. package/dist/native/js-bridge/subscribe.d.ts +2 -0
  15. package/dist/native/js-bridge/types.d.ts +4 -0
  16. package/dist/native/js-core/jolibox-js-core.d.ts +8 -3
  17. package/dist/native/network/create-fetch.d.ts +1 -0
  18. package/dist/native/ui/retention.d.ts +1 -0
  19. package/implement.build.log +2 -2
  20. package/package.json +4 -3
  21. package/src/common/ads/anti-cheating.test.ts +79 -0
  22. package/src/common/ads/anti-cheating.ts +222 -156
  23. package/src/common/ads/index.ts +21 -7
  24. package/src/common/context/index.ts +22 -3
  25. package/src/common/context/url-parse.ts +24 -1
  26. package/src/native/api/index.ts +1 -0
  27. package/src/native/api/lifecycle.ts +16 -4
  28. package/src/native/api/navigate.ts +61 -0
  29. package/src/native/api/request.ts +19 -10
  30. package/src/native/bootstrap/bridge.ts +10 -1
  31. package/src/native/bootstrap/index.ts +100 -23
  32. package/src/native/js-bridge/const.ts +2 -0
  33. package/src/native/js-bridge/js-bridge.ts +7 -2
  34. package/src/native/js-bridge/publish.ts +44 -0
  35. package/src/native/js-bridge/subscribe.ts +25 -1
  36. package/src/native/js-bridge/types.ts +10 -0
  37. package/src/native/js-core/jolibox-js-core.ts +30 -26
  38. package/src/native/network/create-fetch.ts +1 -0
  39. package/src/native/network/utils.ts +13 -6
  40. package/src/native/types/global.d.ts +1 -0
  41. package/src/native/types/native-method-map.d.ts +29 -0
  42. package/src/native/ui/retention.ts +152 -0
@@ -1,5 +1,5 @@
1
1
  import { InternalInvokeNativeError } from '@jolibox/common';
2
- // import { normalizeParams } from './utils';
2
+ import { normalizeParams } from './utils';
3
3
  import { Env } from '@jolibox/types';
4
4
  import { reportError } from '@/common/report/errors/report';
5
5
 
@@ -23,6 +23,7 @@ declare global {
23
23
  invoke: (invokeString: string) => void;
24
24
  onDocumentReady: (paramsstr: string) => void;
25
25
  doExit: (uuidString: string) => void;
26
+ publish: (params: { event: string; webviewIds: number[] | '*'; paramsString: string }) => void;
26
27
  };
27
28
  webkit?: {
28
29
  messageHandlers: {
@@ -30,7 +31,7 @@ declare global {
30
31
  postMessage: (params: { event: string; callbackId: number; paramsString: string }) => void;
31
32
  };
32
33
  publish: {
33
- postMessage: (params: { event: string; webviewIds: string; paramsString: string }) => void;
34
+ postMessage: (params: { event: string; webviewIds: number[] | '*'; paramsString: string }) => void;
34
35
  };
35
36
  onDocumentReady: {
36
37
  postMessage: (params: { path: string }) => void;
@@ -47,14 +48,14 @@ export const RuntimeLoader = {
47
48
  trigger() {
48
49
  // noop
49
50
  },
50
- exit(): boolean {
51
+ exit(): Promise<boolean> {
51
52
  //noop
52
- return false; // 默认不打断退出
53
+ return Promise.resolve(false); // 默认不打断退出
53
54
  },
54
55
  onReady(fn: () => void) {
55
56
  RuntimeLoader.trigger = fn;
56
57
  },
57
- doExit(fn: () => boolean) {
58
+ doExit(fn: () => Promise<boolean>) {
58
59
  RuntimeLoader.exit = fn;
59
60
  }
60
61
  };
@@ -81,9 +82,9 @@ if (window.webkit) {
81
82
  RuntimeLoader.trigger();
82
83
  _joliboxJSCore.onDocumentReady.postMessage({ path });
83
84
  };
84
- const doExit = (uuid: string) => {
85
- const shouldInterrupt = RuntimeLoader.exit();
86
- _joliboxJSCore.doExit.postMessage({ uuid, shouldInterrupt });
85
+ const doExit = async (uuid: string) => {
86
+ const shouldInterrupt = await RuntimeLoader.exit();
87
+ _joliboxJSCore.doExit.postMessage({ uuid, shouldInterrupt: shouldInterrupt });
87
88
  };
88
89
 
89
90
  joliboxJSCore = {
@@ -96,14 +97,17 @@ if (window.webkit) {
96
97
  callbackId
97
98
  });
98
99
  },
99
- // publish(event, params, webviewIds) {
100
- // const data = normalizeParams(params as Record<string, unknown>, 'joliboxJSCore');
101
- // _joliboxJSCore.publish.postMessage({
102
- // event,
103
- // paramsString: JSON.stringify(data),
104
- // webviewIds: JSON.stringify(webviewIds)
105
- // });
106
- // },
100
+ publish(event, params, webviewIds) {
101
+ const data = normalizeParams(params as Record<string, unknown>, 'joliboxJSCore');
102
+ window.prompt(
103
+ 'publish',
104
+ JSON.stringify({
105
+ event,
106
+ paramsString: JSON.stringify(data),
107
+ webviewIds
108
+ })
109
+ );
110
+ },
107
111
  call(method: string, params: Record<string, unknown>, callbackId) {
108
112
  const res = window.prompt(
109
113
  'invoke',
@@ -137,8 +141,8 @@ if (window.JoliAndroidSDKBridge) {
137
141
  _joliboxJSCore.onDocumentReady(JSON.stringify({ path }));
138
142
  };
139
143
 
140
- const doExit = (uuid: string) => {
141
- const shouldInterrupt = RuntimeLoader.exit();
144
+ const doExit = async (uuid: string) => {
145
+ const shouldInterrupt = await RuntimeLoader.exit();
142
146
  _joliboxJSCore.doExit(JSON.stringify({ uuid, shouldInterrupt }));
143
147
  };
144
148
 
@@ -154,14 +158,14 @@ if (window.JoliAndroidSDKBridge) {
154
158
  );
155
159
  },
156
160
  doExit,
157
- // publish(event, params, webviewIds) {
158
- // const data = normalizeParams(params as Record<string, unknown>, 'joliboxJSCore');
159
- // _joliboxJSCore.publish({
160
- // event,
161
- // paramsString: JSON.stringify(data),
162
- // webviewIds: JSON.stringify(webviewIds)
163
- // });
164
- // },
161
+ publish(event, params, webviewIds) {
162
+ const data = normalizeParams(params as Record<string, unknown>, 'joliboxJSCore');
163
+ _joliboxJSCore.publish({
164
+ event,
165
+ paramsString: JSON.stringify(data),
166
+ webviewIds
167
+ });
168
+ },
165
169
  call(method: string, params: Record<string, unknown>, callbackId) {
166
170
  const res = window.prompt(
167
171
  'invoke',
@@ -46,6 +46,7 @@ export function createFetch(
46
46
  operateMethod: 'operateRequestTaskSync',
47
47
  options?: {
48
48
  baseUrl?: string;
49
+ defaultHeaders?: Record<string, string>;
49
50
  type: 'public';
50
51
  }
51
52
  ): PublicFetch;
@@ -79,12 +79,19 @@ export function dirtyURL(url: string, method: HTTPMethod, data?: unknown) {
79
79
 
80
80
  if (!isObject(data)) return url;
81
81
 
82
- const [_url, search = ''] = url.split('?');
82
+ const [baseUrl, search = ''] = url.split('?');
83
83
 
84
- const query = JSON.stringify({
85
- ...JSON.parse(search),
86
- ...data
87
- });
84
+ // Parse existing query parameters
85
+ const dataParams = new URLSearchParams(search);
88
86
 
89
- return `${_url}?${query}`;
87
+ // Add new parameters from data object
88
+ for (const [key, value] of Object.entries(data)) {
89
+ if (value !== undefined && value !== null) {
90
+ dataParams.append(key, String(value));
91
+ }
92
+ }
93
+
94
+ const queryString = dataParams.toString();
95
+
96
+ return queryString ? `${baseUrl}?${queryString}` : baseUrl;
90
97
  }
@@ -15,6 +15,7 @@ declare global {
15
15
  onDocumentReady: (path: string) => void;
16
16
  doExit: (uuid: string) => void;
17
17
  bind?: (id: number, handler: (...args: unknown[]) => unknown) => void;
18
+ publish: (event: string, data: unknown, webviewIds: number[] | '*') => void;
18
19
  }
19
20
 
20
21
  type ApplyNativeReturn<T> = T extends Promise<infer U>
@@ -255,6 +255,30 @@ declare global {
255
255
  /** 错误码 */
256
256
  errNo?: number;
257
257
  };
258
+
259
+ openSchemaSync: (params: { schema: string }) => {
260
+ /** 错误消息 */
261
+ errMsg: string;
262
+ /** 错误码 */
263
+ errNo?: number;
264
+ };
265
+
266
+ openPageSync: (params: { url: string }) => {
267
+ /** 错误消息 */
268
+ errMsg: string;
269
+ /** 错误码 */
270
+ errNo?: number;
271
+ data?: {
272
+ webviewId: number;
273
+ };
274
+ };
275
+
276
+ closePageSync: (params: { webviewId: number }) => {
277
+ /** 错误消息 */
278
+ errMsg: string;
279
+ /** 错误码 */
280
+ errNo?: number;
281
+ };
258
282
  }
259
283
 
260
284
  interface NativeEventMap {
@@ -293,6 +317,11 @@ declare global {
293
317
  token?: string;
294
318
  }
295
319
  ];
320
+ onInfoTapped: [
321
+ {
322
+ uuid: string;
323
+ }
324
+ ];
296
325
  }
297
326
  }
298
327
  }
@@ -0,0 +1,152 @@
1
+ import { context } from '@/common/context';
2
+ import { invokeNative, subscribe } from '../bootstrap/bridge';
3
+ import { Deferred } from '@jolibox/common';
4
+ import { createRecommendModal, IGame, IRecommendationButton, RecommendModalOnCloseParams } from '@jolibox/ui';
5
+ import { innerFetch as fetch } from '../network';
6
+
7
+ let exitRecommendationsCache: {
8
+ code: string;
9
+ data: {
10
+ gameListInfo: {
11
+ games: IGame[];
12
+ };
13
+ title: string;
14
+ buttons: IRecommendationButton[];
15
+ };
16
+ } | null = null;
17
+
18
+ /**
19
+ * Fetches exit recommendations data from the API
20
+ */
21
+ async function fetchExitRecommendations() {
22
+ if (exitRecommendationsCache) {
23
+ return exitRecommendationsCache;
24
+ }
25
+ const host = context.testMode ? `https://stg-game.jolibox.com` : `https://game.jolibox.com`;
26
+ const url = `${host}/api/user-retention/exit-recommendations?objectId=${context.mpId}&from=GAME_DETAIL`;
27
+ const {
28
+ response: { data }
29
+ } = await fetch<{
30
+ code: string;
31
+ data: {
32
+ gameListInfo: {
33
+ games: IGame[];
34
+ };
35
+ title: string;
36
+ buttons: IRecommendationButton[];
37
+ };
38
+ }>(url);
39
+
40
+ exitRecommendationsCache = data;
41
+ return data;
42
+ }
43
+
44
+ //prefetch
45
+ fetchExitRecommendations();
46
+
47
+ const openGameSchema = (game: IGame) => {
48
+ const { data } = invokeNative('envSync');
49
+
50
+ // Parse the original URL to preserve its structure
51
+
52
+ const url = new URL(data.schema);
53
+ const originalSearch = new URLSearchParams(url.search);
54
+
55
+ // Set or replace gameId and joliSource parameters
56
+ originalSearch.set('gameId', game.gameId);
57
+ originalSearch.set(
58
+ 'joliSource',
59
+ context.encodeJoliSourceQuery({
60
+ __mpType: 'game',
61
+ __orientation: game.orientation ?? 'VERTICAL'
62
+ })
63
+ );
64
+
65
+ const host = `https://${game.gameId}.content.jolibox.com/`;
66
+ // Construct the final schema URL
67
+ const schema = `${host}index.html?${originalSearch.toString()}`;
68
+
69
+ invokeNative('openSchemaSync', {
70
+ schema
71
+ });
72
+ };
73
+
74
+ export async function openRetentionSchema() {
75
+ // const { data } = invokeNative('envSync');
76
+ // const { orientation, webviewId } = data;
77
+ // let joliPayload: Record<string, unknown> = {
78
+ // __mpType: 'miniApp',
79
+ // __transparent: true,
80
+ // // set entryPath
81
+ // __orientation: orientation ?? 'VERTICAL', // 默认竖屏
82
+ // __showStatusBar: false,
83
+ // __shouldInterupt: false,
84
+ // __showCapsuleButton: false
85
+ // };
86
+ // if (webviewId) {
87
+ // joliPayload = {
88
+ // ...joliPayload,
89
+ // __from: webviewId
90
+ // };
91
+ // }
92
+
93
+ // const joliSource = context.encodeJoliSourceQuery(joliPayload);
94
+
95
+ // const host = context.testMode
96
+ // ? `https://G32115508989327465281365749294.app.jolibox.com`
97
+ // : `https://G32115508989327465281365749294.app.jolibox.com`;
98
+ // const retentionSchema = `${host}/recommended-guide/${context.mpId}?appId=${context.mpId}&joliSource=${joliSource}&navigationStyle=present`;
99
+
100
+ const quitResultDeffer = new Deferred<boolean>();
101
+ // 小程序不走挽留逻辑
102
+ if (context.mpType !== 'game') {
103
+ quitResultDeffer.resolve(false);
104
+ return quitResultDeffer.promise;
105
+ }
106
+
107
+ const data = await fetchExitRecommendations();
108
+
109
+ if (data.code !== 'SUCCESS') {
110
+ quitResultDeffer.resolve(false);
111
+ return quitResultDeffer.promise;
112
+ }
113
+
114
+ const { gameListInfo, title, buttons } = data.data;
115
+
116
+ const modal = createRecommendModal({
117
+ games: gameListInfo.games,
118
+ title,
119
+ buttons,
120
+ onClose: (params: RecommendModalOnCloseParams) => {
121
+ switch (params.type) {
122
+ case 'quit':
123
+ quitResultDeffer.resolve(false);
124
+ break;
125
+ case 'dismiss':
126
+ quitResultDeffer.resolve(true);
127
+ modal.hide();
128
+ break;
129
+ case 'navigate':
130
+ // TODO: 跳转游戏
131
+ if (params.data?.game) {
132
+ openGameSchema(params.data.game);
133
+ } else {
134
+ quitResultDeffer.resolve(true);
135
+ }
136
+ modal.hide();
137
+ break;
138
+ default:
139
+ // 关闭弹框,留在当前游戏
140
+ quitResultDeffer.resolve(true);
141
+ break;
142
+ }
143
+ }
144
+ });
145
+ // // 异步
146
+ // setTimeout(() => {
147
+ // invokeNative('openSchemaSync', {
148
+ // schema: retentionSchema
149
+ // });
150
+ // }, 0);
151
+ return quitResultDeffer.promise;
152
+ }