@jolibox/implement 1.1.11-beta.1 → 1.1.11-beta.10
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 +13 -12
- package/.rush/temp/shrinkwrap-deps.json +1 -1
- package/dist/common/ads/anti-cheating.d.ts +24 -51
- package/dist/common/ads/anti-cheating.test.d.ts +1 -0
- package/dist/common/ads/index.d.ts +4 -9
- package/dist/common/context/url-parse.d.ts +1 -1
- package/dist/index.js +3 -3
- package/dist/index.native.js +110 -4
- 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 +228 -139
- package/src/common/ads/index.ts +33 -20
- package/src/common/context/index.ts +4 -5
- package/src/common/context/url-parse.ts +9 -6
- package/src/native/api/ads.ts +7 -0
- package/src/native/api/navigate.ts +42 -4
- package/src/native/bootstrap/index.ts +6 -6
- package/src/native/js-core/jolibox-js-core.ts +1 -1
- package/src/native/types/native-method-map.d.ts +17 -0
- package/src/native/ui/retention.ts +152 -0
- package/src/native/bootstrap/retention.ts +0 -40
- /package/dist/native/{bootstrap → ui}/retention.d.ts +0 -0
|
@@ -76,10 +76,13 @@ export const parseUrlQuery = (url: string): QueryParams => {
|
|
|
76
76
|
}
|
|
77
77
|
};
|
|
78
78
|
|
|
79
|
-
export const encodeJoliSourceQuery = (
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
79
|
+
export const encodeJoliSourceQuery = (payloadJson: PayloadJson, originJoliSource: string): string => {
|
|
80
|
+
const joli_source = originJoliSource.split('.');
|
|
81
|
+
if (joli_source && joli_source.length === 3) {
|
|
82
|
+
const headerJsonStr = joli_source[0];
|
|
83
|
+
const payloadJsonStr = base64UrlEncode(payloadJson);
|
|
84
|
+
const signatureJsonStr = joli_source[2];
|
|
85
|
+
return `${headerJsonStr}.${payloadJsonStr}.${signatureJsonStr}`;
|
|
86
|
+
}
|
|
87
|
+
return originJoliSource;
|
|
85
88
|
};
|
package/src/native/api/ads.ts
CHANGED
|
@@ -22,6 +22,13 @@ const ads = new JoliboxAdsImpl(
|
|
|
22
22
|
responseType: 'json',
|
|
23
23
|
appendHostCookie: true,
|
|
24
24
|
...options
|
|
25
|
+
}).then((res) => res.response.data as T),
|
|
26
|
+
post: <T>(url: string, data: any, options?: any) =>
|
|
27
|
+
fetch<T>(url, {
|
|
28
|
+
method: 'POST',
|
|
29
|
+
responseType: 'json',
|
|
30
|
+
appendHostCookie: true,
|
|
31
|
+
...options
|
|
25
32
|
}).then((res) => res.response.data as T)
|
|
26
33
|
},
|
|
27
34
|
checkNetworkStatus
|
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
import { createCommands, UserCustomError } from '@jolibox/common';
|
|
2
2
|
import { invokeNative } from '../bootstrap/bridge';
|
|
3
3
|
import { createSyncAPI, t, registerCanIUse } from './base';
|
|
4
|
+
import { context } from '@/common/context';
|
|
4
5
|
|
|
5
6
|
const commands = createCommands();
|
|
6
7
|
|
|
7
8
|
const openSchemaSync = createSyncAPI('openSchemaSync', {
|
|
8
9
|
paramsSchema: t.tuple(t.string()),
|
|
9
10
|
implement: (schema) => {
|
|
11
|
+
// Add compatibility logic for incomplete URLs
|
|
12
|
+
let finalSchema = schema;
|
|
13
|
+
if (!schema.match(/^https?:\/\//)) {
|
|
14
|
+
finalSchema = `https://${context.mpId}.app.jolibox.com${schema.startsWith('/') ? '' : '/'}${schema}`;
|
|
15
|
+
}
|
|
16
|
+
|
|
10
17
|
const res = invokeNative('openSchemaSync', {
|
|
11
|
-
schema
|
|
18
|
+
schema: finalSchema
|
|
12
19
|
});
|
|
13
20
|
if (res.errNo !== 0) {
|
|
14
21
|
throw new UserCustomError(res.errMsg, res.errNo);
|
|
@@ -16,8 +23,39 @@ const openSchemaSync = createSyncAPI('openSchemaSync', {
|
|
|
16
23
|
}
|
|
17
24
|
});
|
|
18
25
|
|
|
19
|
-
|
|
26
|
+
const openPageSync = createSyncAPI('openPageSync', {
|
|
27
|
+
paramsSchema: t.tuple(t.string()),
|
|
28
|
+
implement: (page) => {
|
|
29
|
+
const { errNo, errMsg, data } = invokeNative('openPageSync', { url: page });
|
|
30
|
+
if (errNo !== 0 || !data) {
|
|
31
|
+
throw new UserCustomError(errMsg, errNo);
|
|
32
|
+
}
|
|
33
|
+
return { webviewId: data.webviewId };
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const closePageSync = createSyncAPI('closePageSync', {
|
|
38
|
+
paramsSchema: t.tuple(t.number()),
|
|
39
|
+
implement: (webviewId) => {
|
|
40
|
+
const { errNo, errMsg } = invokeNative('closePageSync', { webviewId });
|
|
41
|
+
if (errNo !== 0) {
|
|
42
|
+
throw new UserCustomError(errMsg, errNo);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
commands.registerCommand('RouterSDK.openSchema', openSchemaSync);
|
|
48
|
+
commands.registerCommand('RouterSDK.openPage', openPageSync);
|
|
49
|
+
commands.registerCommand('RouterSDK.closePage', closePageSync);
|
|
50
|
+
|
|
51
|
+
registerCanIUse('router.openSchema', {
|
|
52
|
+
version: '1.1.10'
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
registerCanIUse('router.openPage', {
|
|
56
|
+
version: '1.1.10'
|
|
57
|
+
});
|
|
20
58
|
|
|
21
|
-
registerCanIUse('
|
|
22
|
-
version: '1.1.
|
|
59
|
+
registerCanIUse('router.closePage', {
|
|
60
|
+
version: '1.1.10'
|
|
23
61
|
});
|
|
@@ -5,7 +5,7 @@ import { hostEmitter, isBoolean } from '@jolibox/common';
|
|
|
5
5
|
import { taskTracker, track } from '../report';
|
|
6
6
|
import { initializeNativeEnv } from './init-env';
|
|
7
7
|
import { adEventEmitter } from '@/common/ads';
|
|
8
|
-
import { openRetentionSchema } from '
|
|
8
|
+
import { openRetentionSchema } from '../ui/retention';
|
|
9
9
|
|
|
10
10
|
let cleanStyles: () => void;
|
|
11
11
|
RuntimeLoader.onReady(() => {
|
|
@@ -42,11 +42,11 @@ RuntimeLoader.doExit(async () => {
|
|
|
42
42
|
return context.shouldInterupt;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
const stay = await openRetentionSchema();
|
|
46
|
+
if (stay) {
|
|
47
|
+
// 挽留成功,打断退出
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
50
|
|
|
51
51
|
// 退出,对应上报
|
|
52
52
|
//埋点上报
|
|
@@ -84,7 +84,7 @@ if (window.webkit) {
|
|
|
84
84
|
};
|
|
85
85
|
const doExit = async (uuid: string) => {
|
|
86
86
|
const shouldInterrupt = await RuntimeLoader.exit();
|
|
87
|
-
_joliboxJSCore.doExit.postMessage({ uuid, shouldInterrupt });
|
|
87
|
+
_joliboxJSCore.doExit.postMessage({ uuid, shouldInterrupt: shouldInterrupt });
|
|
88
88
|
};
|
|
89
89
|
|
|
90
90
|
joliboxJSCore = {
|
|
@@ -262,6 +262,23 @@ declare global {
|
|
|
262
262
|
/** 错误码 */
|
|
263
263
|
errNo?: number;
|
|
264
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
|
+
};
|
|
265
282
|
}
|
|
266
283
|
|
|
267
284
|
interface NativeEventMap {
|
|
@@ -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
|
+
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { context } from '@/common/context';
|
|
2
|
-
import { invokeNative, subscribe } from './bridge';
|
|
3
|
-
import { Deferred } from '@jolibox/common';
|
|
4
|
-
|
|
5
|
-
export async function openRetentionSchema() {
|
|
6
|
-
const { data } = invokeNative('envSync');
|
|
7
|
-
const { orientation, webviewId } = data;
|
|
8
|
-
let joliPayload: Record<string, unknown> = {
|
|
9
|
-
__mpType: 'miniApp',
|
|
10
|
-
__transparent: true,
|
|
11
|
-
// set entryPath
|
|
12
|
-
__orientation: orientation ?? 'VERTICAL', // 默认竖屏
|
|
13
|
-
__showStatusBar: false,
|
|
14
|
-
__shouldInterupt: false
|
|
15
|
-
};
|
|
16
|
-
if (webviewId) {
|
|
17
|
-
joliPayload = {
|
|
18
|
-
...joliPayload,
|
|
19
|
-
__from: webviewId
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const joliSource = context.encodeJoliSourceQuery(joliPayload);
|
|
24
|
-
|
|
25
|
-
const host = context.testMode ? 'stg-game.jolibox.com' : 'game.jolibox.com';
|
|
26
|
-
const retentionSchema = `https://${host}/game/G32115508989327465281365749294/?appId=G32115508989327465281365749294&joliSource=${joliSource}`;
|
|
27
|
-
|
|
28
|
-
const quitResultDeffer = new Deferred<boolean>();
|
|
29
|
-
|
|
30
|
-
subscribe('onRetentionResult', ({ shouldStay }) => {
|
|
31
|
-
quitResultDeffer.resolve(shouldStay);
|
|
32
|
-
});
|
|
33
|
-
// 异步
|
|
34
|
-
setTimeout(() => {
|
|
35
|
-
invokeNative('openSchemaSync', {
|
|
36
|
-
schema: retentionSchema
|
|
37
|
-
});
|
|
38
|
-
}, 0);
|
|
39
|
-
return quitResultDeffer.promise;
|
|
40
|
-
}
|
|
File without changes
|