@jolibox/implement 1.1.18 → 1.1.19-beta.6
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 +23 -16
- package/.rush/temp/shrinkwrap-deps.json +1 -1
- package/dist/common/rewards/index.d.ts +1 -10
- package/dist/common/rewards/registers/use-ads.d.ts +3 -2
- package/dist/common/rewards/registers/use-jolicoin-only.d.ts +10 -0
- package/dist/common/rewards/registers/utils/coins/index.d.ts +32 -0
- package/dist/common/rewards/registers/utils/event-listener.d.ts +15 -0
- package/dist/common/rewards/registers/utils/index.d.ts +1 -0
- package/dist/common/rewards/reward-emitter.d.ts +58 -0
- package/dist/common/rewards/reward-helper.d.ts +2 -1
- package/dist/common/utils/index.d.ts +49 -2
- package/dist/h5/api/ads.d.ts +1 -1
- package/dist/h5/http/index.d.ts +1 -1
- package/dist/h5/rewards/index.d.ts +4 -0
- package/dist/index.js +197 -3
- package/dist/index.native.js +402 -21
- package/dist/native/api/ads.d.ts +1 -1
- package/dist/native/api/login.d.ts +4 -1
- package/dist/native/rewards/index.d.ts +4 -0
- package/dist/native/ui/modal-iframe.d.ts +26 -0
- package/implement.build.log +2 -2
- package/package.json +5 -4
- package/src/common/context/index.ts +3 -3
- package/src/common/rewards/fetch-reward.ts +9 -3
- package/src/common/rewards/index.ts +1 -12
- package/src/common/rewards/registers/use-ads.ts +3 -2
- package/src/common/rewards/registers/use-jolicoin-only.ts +57 -0
- package/src/common/rewards/registers/use-jolicoin.ts +27 -77
- package/src/common/rewards/registers/utils/coins/index.ts +186 -0
- package/src/common/rewards/registers/utils/event-listener.ts +66 -0
- package/src/common/rewards/registers/utils/index.ts +8 -0
- package/src/common/rewards/reward-emitter.ts +79 -0
- package/src/common/rewards/reward-helper.ts +3 -2
- package/src/common/utils/index.ts +117 -4
- package/src/h5/api/ads.ts +65 -12
- package/src/h5/http/index.ts +2 -2
- package/src/h5/rewards/index.ts +69 -0
- package/src/native/api/ads.ts +95 -33
- package/src/native/api/login.ts +1 -1
- package/src/native/api/navigate.ts +13 -1
- package/src/native/bootstrap/index.ts +25 -1
- package/src/native/rewards/index.ts +138 -0
- package/src/native/ui/modal-iframe.ts +271 -0
|
@@ -7,6 +7,7 @@ import { adEventEmitter } from '@/common/ads';
|
|
|
7
7
|
import { openRetentionSchema } from '../ui/retention';
|
|
8
8
|
import { innerFetch } from '../network';
|
|
9
9
|
import { Env } from '@jolibox/types';
|
|
10
|
+
import { createIframeModal, registerIframeModalToGlobal } from '../ui/modal-iframe';
|
|
10
11
|
interface IBasicMetaConfig {
|
|
11
12
|
canShowRecommended: boolean;
|
|
12
13
|
}
|
|
@@ -27,6 +28,7 @@ declare const globalThis: {
|
|
|
27
28
|
* 清除safari jolibox样式
|
|
28
29
|
*/
|
|
29
30
|
let cleanStyles: () => void;
|
|
31
|
+
let unregisterIframeModal: () => void;
|
|
30
32
|
|
|
31
33
|
/**
|
|
32
34
|
* 启动时间戳
|
|
@@ -40,6 +42,11 @@ let isAdShowing = false;
|
|
|
40
42
|
|
|
41
43
|
let baskcMeta: IBasicMetaConfig | null = null;
|
|
42
44
|
|
|
45
|
+
/**
|
|
46
|
+
* 是否全局系统退出
|
|
47
|
+
*/
|
|
48
|
+
let isInterceptSystemExit = false;
|
|
49
|
+
|
|
43
50
|
RuntimeLoader.onReady(() => {
|
|
44
51
|
// TODO: merge some env config
|
|
45
52
|
});
|
|
@@ -59,6 +66,15 @@ function addShowAdListener() {
|
|
|
59
66
|
});
|
|
60
67
|
}
|
|
61
68
|
|
|
69
|
+
/**
|
|
70
|
+
* 添加系统退出拦截监听
|
|
71
|
+
*/
|
|
72
|
+
function addInterceptSystemExitListener() {
|
|
73
|
+
hostEmitter.on('onInterceptSystemExit', ({ intercept }) => {
|
|
74
|
+
isInterceptSystemExit = intercept;
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
62
78
|
/**
|
|
63
79
|
* The DOMContentLoaded event might be triggered very early,
|
|
64
80
|
* so the global loaded listener should be executed during the bootstrap process.
|
|
@@ -121,11 +137,16 @@ function addDoExitLoader() {
|
|
|
121
137
|
timestamp: Date.now()
|
|
122
138
|
});
|
|
123
139
|
cleanStyles?.();
|
|
140
|
+
unregisterIframeModal?.();
|
|
124
141
|
taskTracker.close(Date.now() - start_timestamp);
|
|
125
142
|
};
|
|
126
143
|
|
|
144
|
+
const shouldInterceptSystemExit = () => {
|
|
145
|
+
return isAdShowing || isInterceptSystemExit;
|
|
146
|
+
};
|
|
147
|
+
|
|
127
148
|
RuntimeLoader.onDoExit(async () => {
|
|
128
|
-
if (
|
|
149
|
+
if (shouldInterceptSystemExit()) {
|
|
129
150
|
return true; // Forbid exit on watching ads
|
|
130
151
|
}
|
|
131
152
|
|
|
@@ -168,9 +189,12 @@ export function config(): void {
|
|
|
168
189
|
start_timestamp = Date.now();
|
|
169
190
|
addGameServiceReadyListener();
|
|
170
191
|
addShowAdListener();
|
|
192
|
+
addInterceptSystemExitListener();
|
|
171
193
|
addDoExitLoader();
|
|
172
194
|
addWebviewReadyListener();
|
|
173
195
|
addI18nChangedListener();
|
|
174
196
|
fetchMetaConfig();
|
|
197
|
+
unregisterIframeModal = registerIframeModalToGlobal();
|
|
198
|
+
|
|
175
199
|
cleanStyles = initializeNativeEnv();
|
|
176
200
|
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* rewards event handlers
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
rewardsEmitter,
|
|
7
|
+
UseModalEventName,
|
|
8
|
+
IUseModalEvent,
|
|
9
|
+
PaymentResultEventName,
|
|
10
|
+
UseModalResultEventName,
|
|
11
|
+
InvokePaymentEventName,
|
|
12
|
+
IInvokePaymentEvent,
|
|
13
|
+
IPaymentChoice
|
|
14
|
+
} from '@/common/rewards/reward-emitter';
|
|
15
|
+
import { createConfirmJolicoinModal, createPaymentJolicoinModal } from '@jolibox/ui';
|
|
16
|
+
import { innerFetch as fetch } from '../network';
|
|
17
|
+
import { StandardResponse } from '@jolibox/types';
|
|
18
|
+
import { context } from '@/common/context';
|
|
19
|
+
import { login } from '../api/login';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* confirm jolicoin modal
|
|
23
|
+
*/
|
|
24
|
+
rewardsEmitter.on(UseModalEventName, (type: 'JOLI_COIN' | 'ADS-JOLI_COIN', params: IUseModalEvent) => {
|
|
25
|
+
if (type === 'ADS-JOLI_COIN') {
|
|
26
|
+
//TODO
|
|
27
|
+
console.log('use modal show by frequency');
|
|
28
|
+
}
|
|
29
|
+
const modal = createConfirmJolicoinModal({
|
|
30
|
+
data: {
|
|
31
|
+
enableAutoDeduct: params.enableAutoDeduct,
|
|
32
|
+
userJolicoin: params.userJoliCoin,
|
|
33
|
+
joliCoinQuantity: params.joliCoinQuantity
|
|
34
|
+
},
|
|
35
|
+
buttons: {
|
|
36
|
+
confirm: {
|
|
37
|
+
text: params.confirmButtonText,
|
|
38
|
+
onPress: () => {
|
|
39
|
+
rewardsEmitter.emit(UseModalResultEventName, { useModalResult: 'CONFIRM' });
|
|
40
|
+
modal.destroy();
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
cancel: {
|
|
44
|
+
text: params.cancelButtonText,
|
|
45
|
+
onPress: ({ type }) => {
|
|
46
|
+
rewardsEmitter.emit(UseModalResultEventName, {
|
|
47
|
+
useModalResult: (type ?? '') === 'CANCEL' ? 'CANCEL' : 'FAILED'
|
|
48
|
+
});
|
|
49
|
+
modal.destroy();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* payment jolicoin modal
|
|
58
|
+
*/
|
|
59
|
+
rewardsEmitter.on(
|
|
60
|
+
InvokePaymentEventName,
|
|
61
|
+
async (type: 'JOLI_COIN' | 'ADS-JOLI_COIN', params: IInvokePaymentEvent) => {
|
|
62
|
+
try {
|
|
63
|
+
// handle showup frequecy
|
|
64
|
+
if (type === 'ADS-JOLI_COIN') {
|
|
65
|
+
//
|
|
66
|
+
console.log('show by frequency');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const balenceDetails = await getBalenceDetails();
|
|
70
|
+
if (!balenceDetails) {
|
|
71
|
+
rewardsEmitter.emit(PaymentResultEventName, { paymentResult: 'FAILED' });
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const modal = createPaymentJolicoinModal({
|
|
75
|
+
data: {
|
|
76
|
+
userJolicoin: params.userJoliCoin,
|
|
77
|
+
joliCoinQuantity: params.joliCoinQuantity,
|
|
78
|
+
paymentChoices: balenceDetails.paymentChoices,
|
|
79
|
+
enableAutoDeduct: balenceDetails.enableAutoDeduct
|
|
80
|
+
},
|
|
81
|
+
buttons: {
|
|
82
|
+
confirm: {
|
|
83
|
+
text: params.confirmButtonText,
|
|
84
|
+
onPress: async (productId: string) => {
|
|
85
|
+
// TODO: native payment
|
|
86
|
+
// await invokeNativePayment(productId);
|
|
87
|
+
if (!context.hostUserInfo?.isLogin) {
|
|
88
|
+
const { data } = await login();
|
|
89
|
+
if (!data?.isLogin) {
|
|
90
|
+
console.log('login failed');
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
console.log('invokeNativePayment', productId);
|
|
95
|
+
rewardsEmitter.emit(PaymentResultEventName, { paymentResult: 'SUCCESS' });
|
|
96
|
+
modal.destroy();
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
cancel: {
|
|
100
|
+
text: params.cancelButtonText,
|
|
101
|
+
onPress: ({ type }) => {
|
|
102
|
+
rewardsEmitter.emit(PaymentResultEventName, {
|
|
103
|
+
paymentResult: (type ?? '') === 'CANCEL' ? 'CANCEL' : 'FAILED'
|
|
104
|
+
});
|
|
105
|
+
modal.destroy();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.info('payment failed', error);
|
|
112
|
+
rewardsEmitter.emit(PaymentResultEventName, { paymentResult: 'FAILED' });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
const getBalenceDetails = async (): Promise<
|
|
118
|
+
| {
|
|
119
|
+
balance: number;
|
|
120
|
+
enableAutoDeduct: boolean;
|
|
121
|
+
paymentChoices: IPaymentChoice[];
|
|
122
|
+
}
|
|
123
|
+
| undefined
|
|
124
|
+
> => {
|
|
125
|
+
const { response } = await fetch<
|
|
126
|
+
StandardResponse<{
|
|
127
|
+
balance: number;
|
|
128
|
+
enableAutoDeduct: boolean;
|
|
129
|
+
paymentChoices: IPaymentChoice[];
|
|
130
|
+
}>
|
|
131
|
+
>('/api/joli-coin/balance-detail', {
|
|
132
|
+
method: 'GET',
|
|
133
|
+
appendHostCookie: true,
|
|
134
|
+
responseType: 'json'
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
return response.data?.data;
|
|
138
|
+
};
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { invokeNative } from '@jolibox/native-bridge';
|
|
2
|
+
import { context } from '@/common/context';
|
|
3
|
+
import { hostEmitter } from '@jolibox/common';
|
|
4
|
+
|
|
5
|
+
interface IframeModalOptions {
|
|
6
|
+
url: string;
|
|
7
|
+
height?: string; // default '50%'
|
|
8
|
+
showCloseButton?: boolean; // default true
|
|
9
|
+
enableDragClose?: boolean; // default true
|
|
10
|
+
useAnimation?: boolean; // default true
|
|
11
|
+
onClose?: () => void;
|
|
12
|
+
onLoad?: () => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const DEFAULT_OPTIONS: Partial<IframeModalOptions> = {
|
|
16
|
+
height: '50%',
|
|
17
|
+
showCloseButton: true,
|
|
18
|
+
enableDragClose: true,
|
|
19
|
+
useAnimation: true
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
let modalOverlay: HTMLElement | null = null;
|
|
23
|
+
let modalContainer: HTMLElement | null = null;
|
|
24
|
+
let iframe: HTMLIFrameElement | null = null;
|
|
25
|
+
/**
|
|
26
|
+
* create a half-screen iframe modal
|
|
27
|
+
*/
|
|
28
|
+
export function createIframeModal(options: IframeModalOptions): { close: () => void } {
|
|
29
|
+
const mergedOptions = { ...DEFAULT_OPTIONS, ...options };
|
|
30
|
+
const { url, height, showCloseButton, enableDragClose, useAnimation, onClose, onLoad } = mergedOptions;
|
|
31
|
+
|
|
32
|
+
let startY = 0;
|
|
33
|
+
let currentY = 0;
|
|
34
|
+
let isModalVisible = false;
|
|
35
|
+
|
|
36
|
+
const getAnimationStyles = () => {
|
|
37
|
+
if (useAnimation) {
|
|
38
|
+
return {
|
|
39
|
+
overlayTransition: 'opacity 0.3s ease',
|
|
40
|
+
containerTransition: 'transform 0.3s ease',
|
|
41
|
+
iframeTransition: 'opacity 0.3s ease'
|
|
42
|
+
};
|
|
43
|
+
} else {
|
|
44
|
+
return {
|
|
45
|
+
overlayTransition: 'none',
|
|
46
|
+
containerTransition: 'none',
|
|
47
|
+
iframeTransition: 'none'
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// 获取动画延迟时间
|
|
53
|
+
const getAnimationDelay = () => (useAnimation ? 300 : 0);
|
|
54
|
+
|
|
55
|
+
// create modal dom
|
|
56
|
+
function createModalDOM() {
|
|
57
|
+
const { overlayTransition, containerTransition, iframeTransition } = getAnimationStyles();
|
|
58
|
+
|
|
59
|
+
modalOverlay = document.createElement('div');
|
|
60
|
+
modalOverlay.id = 'jolibox-modal-overlay';
|
|
61
|
+
modalOverlay.style.cssText = `position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(0,0,0,0.5); z-index:1000; display:flex; justify-content:center; align-items:flex-end; transition:${overlayTransition}; opacity:0; visibility:hidden;`;
|
|
62
|
+
|
|
63
|
+
modalContainer = document.createElement('div');
|
|
64
|
+
modalContainer.id = 'jolibox-modal-container';
|
|
65
|
+
modalContainer.style.cssText = `width:100%; height:${height}; background:white; border-radius:12px 12px 0 0; overflow:hidden; box-shadow:0 -2px 10px rgba(0,0,0,0.2); transform:translateY(100%); transition:${containerTransition};`;
|
|
66
|
+
|
|
67
|
+
const loadingIndicator = document.createElement('div');
|
|
68
|
+
loadingIndicator.id = 'jolibox-modal-loading';
|
|
69
|
+
loadingIndicator.style.cssText =
|
|
70
|
+
'position:absolute; top:50%; left:50%; transform:translate(-50%, -50%); width:40px; height:40px; border:4px solid #f3f3f3; border-top:4px solid #3498db; border-radius:50%; animation:spin 1s linear infinite;';
|
|
71
|
+
|
|
72
|
+
const styleElement = document.createElement('style');
|
|
73
|
+
styleElement.textContent =
|
|
74
|
+
'@keyframes spin { 0% { transform: translate(-50%, -50%) rotate(0deg); } 100% { transform: translate(-50%, -50%) rotate(360deg); } }';
|
|
75
|
+
document.head.appendChild(styleElement);
|
|
76
|
+
|
|
77
|
+
// safe area adapter
|
|
78
|
+
const safeAreaDiv = document.createElement('div');
|
|
79
|
+
safeAreaDiv.style.cssText =
|
|
80
|
+
'width:100%; height:100%; display:flex; flex-direction:column; padding-bottom:env(safe-area-inset-bottom, 0); position:relative;';
|
|
81
|
+
|
|
82
|
+
// create iframe
|
|
83
|
+
iframe = document.createElement('iframe');
|
|
84
|
+
iframe.style.cssText = `width:100%; flex:1; border:none; opacity:0; transition:${iframeTransition};`;
|
|
85
|
+
iframe.src = url;
|
|
86
|
+
|
|
87
|
+
// iframe loaded event
|
|
88
|
+
iframe.onload = () => {
|
|
89
|
+
if (loadingIndicator.parentNode) {
|
|
90
|
+
loadingIndicator.parentNode.removeChild(loadingIndicator);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 显示iframe内容
|
|
94
|
+
iframe!.style.opacity = '1';
|
|
95
|
+
|
|
96
|
+
// 显示modal
|
|
97
|
+
showModal();
|
|
98
|
+
|
|
99
|
+
// 触发加载完成回调
|
|
100
|
+
onLoad?.();
|
|
101
|
+
|
|
102
|
+
// 发送全局事件
|
|
103
|
+
hostEmitter.emit('onModalIframeLoaded', { url });
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// 组装DOM
|
|
107
|
+
safeAreaDiv.appendChild(iframe);
|
|
108
|
+
safeAreaDiv.appendChild(loadingIndicator);
|
|
109
|
+
modalContainer.appendChild(safeAreaDiv);
|
|
110
|
+
|
|
111
|
+
// 添加关闭按钮
|
|
112
|
+
if (showCloseButton) {
|
|
113
|
+
const closeBtn = document.createElement('div');
|
|
114
|
+
closeBtn.style.cssText =
|
|
115
|
+
'position:absolute; top:10px; right:10px; width:30px; height:30px; background:rgba(0,0,0,0.1); border-radius:15px; display:flex; justify-content:center; align-items:center; cursor:pointer; z-index:10;';
|
|
116
|
+
closeBtn.innerHTML = '✕';
|
|
117
|
+
closeBtn.onclick = closeModal;
|
|
118
|
+
modalContainer.appendChild(closeBtn);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
modalOverlay.appendChild(modalContainer);
|
|
122
|
+
document.body.appendChild(modalOverlay);
|
|
123
|
+
|
|
124
|
+
// 点击遮罩关闭
|
|
125
|
+
modalOverlay.addEventListener('click', function (e) {
|
|
126
|
+
if (e.target === modalOverlay) closeModal();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
if (enableDragClose) {
|
|
130
|
+
addDragSupport();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// show modal
|
|
135
|
+
function showModal() {
|
|
136
|
+
if (!modalOverlay || !modalContainer || isModalVisible) return;
|
|
137
|
+
|
|
138
|
+
modalOverlay.style.visibility = 'visible';
|
|
139
|
+
|
|
140
|
+
if (useAnimation) {
|
|
141
|
+
requestAnimationFrame(() => {
|
|
142
|
+
if (modalOverlay) modalOverlay.style.opacity = '1';
|
|
143
|
+
if (modalContainer) modalContainer.style.transform = 'translateY(0)';
|
|
144
|
+
isModalVisible = true;
|
|
145
|
+
});
|
|
146
|
+
} else {
|
|
147
|
+
if (modalOverlay) modalOverlay.style.opacity = '1';
|
|
148
|
+
if (modalContainer) modalContainer.style.transform = 'translateY(0)';
|
|
149
|
+
isModalVisible = true;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
invokeNative('updateContainerConfigSync', {
|
|
153
|
+
displayCapsuleButton: false,
|
|
154
|
+
webviewId: context.webviewId
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function addDragSupport() {
|
|
159
|
+
if (!modalContainer) return;
|
|
160
|
+
|
|
161
|
+
const handleTouchStart = (e: TouchEvent) => {
|
|
162
|
+
startY = e.touches[0].clientY;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const handleTouchMove = (e: TouchEvent) => {
|
|
166
|
+
if (!isModalVisible) return;
|
|
167
|
+
|
|
168
|
+
currentY = e.touches[0].clientY;
|
|
169
|
+
const deltaY = currentY - startY;
|
|
170
|
+
|
|
171
|
+
if (deltaY > 0) {
|
|
172
|
+
if (modalContainer) {
|
|
173
|
+
// 拖动时始终不使用过渡效果
|
|
174
|
+
modalContainer.style.transition = 'none';
|
|
175
|
+
modalContainer.style.transform = `translateY(${deltaY}px)`;
|
|
176
|
+
}
|
|
177
|
+
e.preventDefault();
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const handleTouchEnd = () => {
|
|
182
|
+
if (!isModalVisible || !modalContainer) return;
|
|
183
|
+
|
|
184
|
+
modalContainer.style.transition = useAnimation ? 'transform 0.3s ease' : 'none';
|
|
185
|
+
|
|
186
|
+
if (currentY - startY > 80) {
|
|
187
|
+
closeModal();
|
|
188
|
+
} else {
|
|
189
|
+
modalContainer.style.transform = 'translateY(0)';
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
modalContainer.addEventListener('touchstart', handleTouchStart);
|
|
194
|
+
modalContainer.addEventListener('touchmove', handleTouchMove);
|
|
195
|
+
modalContainer.addEventListener('touchend', handleTouchEnd);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// close
|
|
199
|
+
function closeModal() {
|
|
200
|
+
if (!modalOverlay || !modalContainer || !isModalVisible) return;
|
|
201
|
+
|
|
202
|
+
if (useAnimation) {
|
|
203
|
+
modalContainer.style.transform = 'translateY(100%)';
|
|
204
|
+
modalOverlay.style.opacity = '0';
|
|
205
|
+
} else {
|
|
206
|
+
modalOverlay.style.visibility = 'hidden';
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
isModalVisible = false;
|
|
210
|
+
|
|
211
|
+
// 恢复原生UI
|
|
212
|
+
invokeNative('updateContainerConfigSync', {
|
|
213
|
+
displayCapsuleButton: true,
|
|
214
|
+
webviewId: context.webviewId
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
setTimeout(() => {
|
|
218
|
+
if (modalOverlay) {
|
|
219
|
+
modalOverlay.remove();
|
|
220
|
+
modalOverlay = null;
|
|
221
|
+
modalContainer = null;
|
|
222
|
+
iframe = null;
|
|
223
|
+
|
|
224
|
+
// 触发关闭回调
|
|
225
|
+
onClose?.();
|
|
226
|
+
|
|
227
|
+
// 发送全局事件
|
|
228
|
+
hostEmitter.emit('onModalIframeClosed', { url });
|
|
229
|
+
}
|
|
230
|
+
}, getAnimationDelay());
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
createModalDOM();
|
|
234
|
+
|
|
235
|
+
return { close: closeModal };
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
declare global {
|
|
239
|
+
interface Window {
|
|
240
|
+
joliboxUI?: {
|
|
241
|
+
createIframeModal: (options: IframeModalOptions) => { close: () => void };
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export function registerIframeModalToGlobal() {
|
|
247
|
+
window.joliboxUI = {
|
|
248
|
+
...window.joliboxUI,
|
|
249
|
+
createIframeModal
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
return () => {
|
|
253
|
+
window.joliboxUI = undefined;
|
|
254
|
+
if (modalOverlay && modalOverlay.parentNode) {
|
|
255
|
+
modalOverlay.parentNode.removeChild(modalOverlay);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (modalContainer && modalContainer.parentNode) {
|
|
259
|
+
modalContainer.parentNode.removeChild(modalContainer);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (iframe && iframe.parentNode) {
|
|
263
|
+
iframe.parentNode.removeChild(iframe);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// 清空引用
|
|
267
|
+
modalOverlay = null;
|
|
268
|
+
modalContainer = null;
|
|
269
|
+
iframe = null;
|
|
270
|
+
};
|
|
271
|
+
}
|