@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.
- package/.rush/temp/package-deps_build.json +24 -20
- package/.rush/temp/shrinkwrap-deps.json +1 -1
- package/dist/common/ads/anti-cheating.d.ts +16 -53
- package/dist/common/ads/anti-cheating.test.d.ts +1 -0
- package/dist/common/context/index.d.ts +5 -0
- package/dist/common/context/url-parse.d.ts +8 -1
- package/dist/index.js +3 -3
- package/dist/index.native.js +110 -4
- package/dist/native/api/index.d.ts +1 -0
- package/dist/native/api/navigate.d.ts +1 -0
- package/dist/native/bootstrap/bridge.d.ts +1 -1
- package/dist/native/js-bridge/const.d.ts +1 -0
- package/dist/native/js-bridge/publish.d.ts +1 -0
- package/dist/native/js-bridge/subscribe.d.ts +2 -0
- package/dist/native/js-bridge/types.d.ts +4 -0
- package/dist/native/js-core/jolibox-js-core.d.ts +8 -3
- package/dist/native/network/create-fetch.d.ts +1 -0
- package/dist/native/ui/retention.d.ts +1 -0
- package/implement.build.log +2 -2
- package/package.json +4 -3
- package/src/common/ads/anti-cheating.test.ts +79 -0
- package/src/common/ads/anti-cheating.ts +222 -156
- package/src/common/ads/index.ts +21 -7
- package/src/common/context/index.ts +22 -3
- package/src/common/context/url-parse.ts +24 -1
- package/src/native/api/index.ts +1 -0
- package/src/native/api/lifecycle.ts +16 -4
- package/src/native/api/navigate.ts +61 -0
- package/src/native/api/request.ts +19 -10
- package/src/native/bootstrap/bridge.ts +10 -1
- package/src/native/bootstrap/index.ts +100 -23
- package/src/native/js-bridge/const.ts +2 -0
- package/src/native/js-bridge/js-bridge.ts +7 -2
- package/src/native/js-bridge/publish.ts +44 -0
- package/src/native/js-bridge/subscribe.ts +25 -1
- package/src/native/js-bridge/types.ts +10 -0
- package/src/native/js-core/jolibox-js-core.ts +30 -26
- package/src/native/network/create-fetch.ts +1 -0
- package/src/native/network/utils.ts +13 -6
- package/src/native/types/global.d.ts +1 -0
- package/src/native/types/native-method-map.d.ts +29 -0
- package/src/native/ui/retention.ts +152 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { InternalInvokeNativeError } from '@jolibox/common';
|
|
2
|
-
|
|
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:
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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',
|
|
@@ -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 [
|
|
82
|
+
const [baseUrl, search = ''] = url.split('?');
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
...data
|
|
87
|
-
});
|
|
84
|
+
// Parse existing query parameters
|
|
85
|
+
const dataParams = new URLSearchParams(search);
|
|
88
86
|
|
|
89
|
-
|
|
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
|
+
}
|