From ca907fe598d1a13e97b821f1d7bd32468ce527d3 Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Mon, 23 Dec 2024 16:39:46 +0800 Subject: [PATCH 01/32] =?UTF-8?q?=E4=BF=AE=E6=AD=A3settitle=E5=90=88?= =?UTF-8?q?=E4=B8=A2=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/runtime/components/react/mpx-web-view.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx index b645d05094..fd6a893408 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx @@ -109,6 +109,7 @@ const _WebView = forwardRef, WebViewProps>((pr } }); } + true; ` const _changeUrl = function (navState: WebViewNavigation) { if (navState.navigationType) { // navigationType这个事件在页面开始加载时和页面加载完成时都会被触发所以判断这个避免其他无效触发执行该逻辑 @@ -211,16 +212,15 @@ const _WebView = forwardRef, WebViewProps>((pr onError: _error }) } - extendObject(events, { - onMessage: _message - }) return createElement(Portal, null, createElement(WebView, extendObject({ style: defaultWebViewStyle, source: { uri: src }, ref: webViewRef, javaScriptEnabled: true, - onNavigationStateChange: _changeUrl + onNavigationStateChange: _changeUrl, + onMessage: _message, + injectedJavaScript: injectedJavaScript }, events))) }) From f2abc48866bc7ce08234489ea39d5c36b91af4d5 Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Tue, 24 Dec 2024 12:11:18 +0800 Subject: [PATCH 02/32] =?UTF-8?q?=E6=94=B9=E6=88=90ontouchend?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/action-sheet/rnActionSheet.jsx | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/api-proxy/src/platform/api/action-sheet/rnActionSheet.jsx b/packages/api-proxy/src/platform/api/action-sheet/rnActionSheet.jsx index 280d04b0d6..5d3f33df96 100644 --- a/packages/api-proxy/src/platform/api/action-sheet/rnActionSheet.jsx +++ b/packages/api-proxy/src/platform/api/action-sheet/rnActionSheet.jsx @@ -1,4 +1,4 @@ -import { View, TouchableHighlight, Text, StyleSheet, TouchableOpacity } from 'react-native' +import { View, Text, StyleSheet } from 'react-native' import { successHandle, failHandle } from '../../../common/js' import { Portal } from '@ant-design/react-native' import { getWindowInfo } from '../system/rnSystem' @@ -64,7 +64,8 @@ function showActionSheet (options = {}) { offset.value = withTiming(0) - const selectAction = function (index) { + const selectAction = function (index, e) { + e.stopPropagation() const result = { errMsg: 'showActionSheet:ok', tapIndex: index @@ -83,7 +84,8 @@ function showActionSheet (options = {}) { } } - const cancelAction = function () { + const cancelAction = function (e) { + e.stopPropagation() const result = { errMsg: 'showActionSheet:fail cancel' } @@ -91,17 +93,17 @@ function showActionSheet (options = {}) { remove() } return ( - - + + { alertText ? {alertText} : null } - { itemList.map((item, index) => selectAction(index)} style={ [styles.itemStyle, itemList.length -1 === index ? { + { itemList.map((item, index) => selectAction(index, e)} style={ [styles.itemStyle, itemList.length -1 === index ? { borderBottomWidth: 6, borderBottomStyle: 'solid', borderBottomColor: '#f7f7f7' - } : {}] }>{item}) } - 取消 + } : {}] }>{item}) } + 取消 - + ) } From 6c8f977600994a1490f8f66120057151d4711acd Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Tue, 24 Dec 2024 15:25:43 +0800 Subject: [PATCH 03/32] =?UTF-8?q?=E4=BF=AE=E6=94=B9webview=E9=80=9A?= =?UTF-8?q?=E4=BF=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/mpx-webview/H5/webviewbridge.min.js | 2 +- .../runtime/components/react/mpx-web-view.tsx | 14 +++- .../dist/webviewbridge.esm.browser.js | 78 ++++++++++++------- .../dist/webviewbridge.esm.browser.min.js | 2 +- .../webview-bridge/dist/webviewbridge.esm.js | 71 ++++++++++------- packages/webview-bridge/dist/webviewbridge.js | 71 ++++++++++------- .../webview-bridge/dist/webviewbridge.min.js | 2 +- packages/webview-bridge/src/index.js | 78 ++++++++++++------- 8 files changed, 203 insertions(+), 115 deletions(-) diff --git a/examples/mpx-webview/H5/webviewbridge.min.js b/examples/mpx-webview/H5/webviewbridge.min.js index 7ac85a8e78..a8a6c7b985 100644 --- a/examples/mpx-webview/H5/webviewbridge.min.js +++ b/examples/mpx-webview/H5/webviewbridge.min.js @@ -3,4 +3,4 @@ * (c) 2024 @mpxjs team * @license Apache */ -var e,o;e=this,o=function(){"use strict";var e,o,a,t,n=Object.assign({wx:{url:"https://res.wx.qq.com/open/js/jweixin-1.3.2.js"},qq:{url:"https://qqq.gtimg.cn/miniprogram/webview_jssdk/qqjssdk-1.0.0.js"},my:{url:"https://appx/web-view.min.js"},swan:{url:"https://b.bdstatic.com/searchbox/icms/searchbox/js/swan-2.0.4.js"},tt:{url:"https://lf3-cdn-tos.bytegoofy.com/obj/goofy/developer/jssdk/jssdk-1.2.1.js"}},window.sdkUrlMap),i=null,r=0,c=(a=location.href,(t=/mpx_webview_id=(\d+)/g.exec(a))&&t[1]&&(o=+t[1]),o),s={},d=navigator.userAgent;d.indexOf("AlipayClient")>-1&&d.indexOf("MiniProgram")>-1?i="my":d.toLowerCase().indexOf("miniprogram")>-1?i=d.indexOf("QQ")>-1?"qq":"wx":d.indexOf("swan/")>-1?i="swan":d.indexOf("toutiao")>-1?i="tt":(i="web",window.addEventListener("message",(function(e){var o=e.data,a=o;try{"string"==typeof o&&(a=JSON.parse(o))}catch(e){}var t=a,n=t.callbackId,i=t.error,r=t.result;void 0!==n&&s[n]&&(i?s[n](i):s[n](null,r),delete s[n])}),!1));var g=!1;function w(o){g?o():e.then((function(){g=!0,o()}))}var p={config:function(e){"wx"===i?w((function(){window.wx&&window.wx.config(e)})):console.warn("\u975e\u5fae\u4fe1\u73af\u5883\u4e0d\u9700\u8981\u914d\u7f6econfig")}};function l(e){for(var o=arguments.length,a=new Array(o>1?o-1:0),t=1;t1&&void 0!==arguments[1]?arguments[1]:{},a=o.time,t=void 0===a?5e3:a,n=o.crossOrigin,i=void 0!==n&&n;function r(){return new Promise((function(o,a){var t=document.createElement("script");t.type="text/javascript",t.async="async",i&&(t.crossOrigin="anonymous"),t.onload=t.onreadystatechange=function(){this.readyState&&!/^(loaded|complete)$/.test(this.readyState)||(o(),t.onload=t.onreadystatechange=null)},t.onerror=function(){a(new Error("load ".concat(e," error"))),t.onerror=null},t.src=e,document.getElementsByTagName("head")[0].appendChild(t)}))}function c(){return new Promise((function(o,a){setTimeout((function(){a(new Error("load ".concat(e," timeout")))}),t)}))}return Promise.race([r(),c()])}(n[i].url):Promise.reject(new Error("\u672a\u627e\u5230\u5bf9\u5e94\u7684sdk")):Promise.resolve(),m(),p},"object"==typeof exports&&"undefined"!=typeof module?module.exports=o():"function"==typeof define&&define.amd?define(o):(e=e||self).mpx=o(); \ No newline at end of file +var e,a;e=this,a=function(){"use strict";var e,a,o,t,n=Object.assign({wx:{url:"https://res.wx.qq.com/open/js/jweixin-1.3.2.js"},qq:{url:"https://qqq.gtimg.cn/miniprogram/webview_jssdk/qqjssdk-1.0.0.js"},my:{url:"https://appx/web-view.min.js"},swan:{url:"https://b.bdstatic.com/searchbox/icms/searchbox/js/swan-2.0.4.js"},tt:{url:"https://lf3-cdn-tos.bytegoofy.com/obj/goofy/developer/jssdk/jssdk-1.2.1.js"}},window.sdkUrlMap),i=null,r=0,c=(o=location.href,(t=/mpx_webview_id=(\d+)/g.exec(o))&&t[1]&&(a=+t[1]),a),s={},d=function(e){var a=e.callbackId,o=e.error,t=e.result;void 0!==a&&s[a]&&(o?s[a](o):s[a](null,t),delete s[a])},w=navigator.userAgent;w.indexOf("AlipayClient")>-1&&w.indexOf("MiniProgram")>-1?i="my":w.toLowerCase().indexOf("miniprogram")>-1?i=w.indexOf("QQ")>-1?"qq":"wx":w.indexOf("swan/")>-1?i="swan":w.indexOf("toutiao")>-1&&(i="tt"),window.ReactNativeWebView?(i="rn",window.mpxWebviewMessageCallback=d):(i="web",window.addEventListener("message",(function(e){var a=e.data,o=a;try{"string"==typeof a&&(o=JSON.parse(a))}catch(e){}d(o)}),!1));var g=!1;function p(a){g?a():e.then((function(){g=!0,a()}))}var l={config:function(e){"wx"===i?p((function(){window.wx&&window.wx.config(e)})):console.warn("\u975e\u5fae\u4fe1\u73af\u5883\u4e0d\u9700\u8981\u914d\u7f6econfig")}};function v(e){for(var a=arguments.length,o=new Array(a>1?a-1:0),t=1;t1&&void 0!==arguments[1]?arguments[1]:{},o=a.time,t=void 0===o?5e3:o,n=a.crossOrigin,i=void 0!==n&&n;function r(){return new Promise((function(a,o){var t=document.createElement("script");t.type="text/javascript",t.async="async",i&&(t.crossOrigin="anonymous"),t.onload=t.onreadystatechange=function(){this.readyState&&!/^(loaded|complete)$/.test(this.readyState)||(a(),t.onload=t.onreadystatechange=null)},t.onerror=function(){o(new Error("load ".concat(e," error"))),t.onerror=null},t.src=e,document.getElementsByTagName("head")[0].appendChild(t)}))}function c(){return new Promise((function(a,o){setTimeout((function(){o(new Error("load ".concat(e," timeout")))}),t)}))}return Promise.race([r(),c()])}(n[i].url):Promise.reject(new Error("\u672a\u627e\u5230\u5bf9\u5e94\u7684sdk")):Promise.resolve(),m(),l},"object"==typeof exports&&"undefined"!=typeof module?module.exports=a():"function"==typeof define&&define.amd?define(a):(e=e||self).mpx=a(); \ No newline at end of file diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx index fd6a893408..787c94a3d8 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx @@ -111,6 +111,12 @@ const _WebView = forwardRef, WebViewProps>((pr } true; ` + const sendMessage = function(params: string) { + return ` + window.mpxWebviewMessageCallback(${params}) + true; + ` + } const _changeUrl = function (navState: WebViewNavigation) { if (navState.navigationType) { // navigationType这个事件在页面开始加载时和页面加载完成时都会被触发所以判断这个避免其他无效触发执行该逻辑 currentPage.__webViewUrl = navState.url @@ -182,21 +188,21 @@ const _WebView = forwardRef, WebViewProps>((pr asyncCallback && asyncCallback.then((res: any) => { if (webViewRef.current?.postMessage) { - const test = JSON.stringify({ + const result = JSON.stringify({ type, callbackId: data.callbackId, result: res }) - webViewRef.current.postMessage(test) + webViewRef.current.injectJavaScript(sendMessage(result)) } }).catch((error: any) => { if (webViewRef.current?.postMessage) { - const test = JSON.stringify({ + const result = JSON.stringify({ type, callbackId: data.callbackId, error }) - webViewRef.current.postMessage(test) + webViewRef.current.injectJavaScript(sendMessage(result)) } }) } diff --git a/packages/webview-bridge/dist/webviewbridge.esm.browser.js b/packages/webview-bridge/dist/webviewbridge.esm.browser.js index e8b97d2f10..409c17b1b9 100644 --- a/packages/webview-bridge/dist/webviewbridge.esm.browser.js +++ b/packages/webview-bridge/dist/webviewbridge.esm.browser.js @@ -75,6 +75,32 @@ let env = null; let callbackId = 0; const clientUid = getMpxWebViewId(); const callbacks = {}; + +const runCallback = (msgData) => { + const { callbackId, error, result } = msgData; + if (callbackId !== undefined && callbacks[callbackId]) { + if (error) { + callbacks[callbackId](error); + } else { + callbacks[callbackId](null, result); + } + delete callbacks[callbackId]; + } +}; + +const eventListener = (event) => { + // 接收web-view的回调 + const data = event.data; + let msgData = data; + try { + if (typeof data === 'string') { + msgData = JSON.parse(data); + } + } catch (e) { + } + runCallback(msgData); +}; + // 环境判断逻辑 const systemUA = navigator.userAgent; if (systemUA.indexOf('AlipayClient') > -1 && systemUA.indexOf('MiniProgram') > -1) { @@ -85,32 +111,16 @@ if (systemUA.indexOf('AlipayClient') > -1 && systemUA.indexOf('MiniProgram') > - env = 'swan'; } else if (systemUA.indexOf('toutiao') > -1) { env = 'tt'; +} if (window.ReactNativeWebView) { + env = 'rn'; + window.mpxWebviewMessageCallback = runCallback; } else { env = 'web'; - window.addEventListener('message', (event) => { - // 接收web-view的回调 - const data = event.data; - let msgData = data; - try { - if (typeof data === 'string') { - msgData = JSON.parse(data); - } - } catch (e) { - } - const { callbackId, error, result } = msgData; - if (callbackId !== undefined && callbacks[callbackId]) { - if (error) { - callbacks[callbackId](error); - } else { - callbacks[callbackId](null, result); - } - delete callbacks[callbackId]; - } - }, false); + window.addEventListener('message', eventListener, false); } const initWebviewBridge = () => { - sdkReady = env !== 'web' ? SDK_URL_MAP[env].url ? loadScript(SDK_URL_MAP[env].url) : Promise.reject(new Error('未找到对应的sdk')) : Promise.resolve(); + sdkReady = (env !== 'web' && env !== 'rn') ? SDK_URL_MAP[env].url ? loadScript(SDK_URL_MAP[env].url) : Promise.reject(new Error('未找到对应的sdk')) : Promise.resolve(); getWebviewApi(); }; @@ -145,7 +155,7 @@ function postMessage (type, ...extraData) { type = extraData[0]; extraData = extraData.slice(1); } - const data = extraData[0]; + const data = extraData[0] || {}; if (type !== 'getEnv') { const currentCallbackId = ++callbackId; callbacks[currentCallbackId] = (err, res) => { @@ -172,9 +182,15 @@ function postMessage (type, ...extraData) { window.parent.postMessage && window.parent.postMessage(JSON.stringify(postParams), '*'); } } else { - data({ + let result = { webapp: true - }); + }; + if (window.ReactNativeWebView) { + result = { + reactNative: true + }; + } + data(result); } } @@ -307,7 +323,17 @@ const getWebviewApi = () => { 'redirectTo', 'getEnv', 'postMessage', - 'getLoadError', + 'getLocation', + 'invoke' + ], + rn: [ + 'navigateTo', + 'navigateBack', + 'switchTab', + 'reLaunch', + 'redirectTo', + 'getEnv', + 'postMessage', 'getLocation', 'invoke' ], @@ -325,7 +351,7 @@ const getWebviewApi = () => { }); singleApi.forEach((item) => { webviewBridge[item] = (...args) => { - if (env === 'web') { + if (env === 'web' || env === 'rn') { postMessage(item, ...args); } else if (env === 'wx') { runWebviewApiMethod(() => { diff --git a/packages/webview-bridge/dist/webviewbridge.esm.browser.min.js b/packages/webview-bridge/dist/webviewbridge.esm.browser.min.js index efbeb4cfac..be460b7a68 100644 --- a/packages/webview-bridge/dist/webviewbridge.esm.browser.min.js +++ b/packages/webview-bridge/dist/webviewbridge.esm.browser.min.js @@ -3,4 +3,4 @@ * (c) 2024 @mpxjs team * @license Apache */ -let e;const o=Object.assign({wx:{url:"https://res.wx.qq.com/open/js/jweixin-1.3.2.js"},qq:{url:"https://qqq.gtimg.cn/miniprogram/webview_jssdk/qqjssdk-1.0.0.js"},my:{url:"https://appx/web-view.min.js"},swan:{url:"https://b.bdstatic.com/searchbox/icms/searchbox/js/swan-2.0.4.js"},tt:{url:"https://lf3-cdn-tos.bytegoofy.com/obj/goofy/developer/jssdk/jssdk-1.2.1.js"}},window.sdkUrlMap);let a=null,t=0;const n=function(){const e=location.href,o=/mpx_webview_id=(\d+)/g.exec(e);let a;return o&&o[1]&&(a=+o[1]),a}(),i={},s=navigator.userAgent;s.indexOf("AlipayClient")>-1&&s.indexOf("MiniProgram")>-1?a="my":s.toLowerCase().indexOf("miniprogram")>-1?a=s.indexOf("QQ")>-1?"qq":"wx":s.indexOf("swan/")>-1?a="swan":s.indexOf("toutiao")>-1?a="tt":(a="web",window.addEventListener("message",e=>{const o=e.data;let a=o;try{"string"==typeof o&&(a=JSON.parse(o))}catch(e){}const{callbackId:t,error:n,result:s}=a;void 0!==t&&i[t]&&(n?i[t](n):i[t](null,s),delete i[t])},!1));let r=!1;function c(o){r?o():e.then(()=>{r=!0,o()})}const d={config(e){"wx"===a?c(()=>{window.wx&&window.wx.config(e)}):console.warn("\u975e\u5fae\u4fe1\u73af\u5883\u4e0d\u9700\u8981\u914d\u7f6econfig")}};const g=()=>{const e={wx:{keyName:"miniProgram",api:["navigateTo","navigateBack","switchTab","reLaunch","redirectTo","postMessage","getEnv"]},tt:{keyName:"miniProgram",api:["redirectTo","navigateTo","switchTab","reLaunch","navigateBack","setSwipeBackModeSync","postMessage","getEnv","checkJsApi","chooseImage","compressImage","previewImage","uploadFile","getNetworkType","openLocation","getLocation"]},swan:{keyName:"webView",api:["navigateTo","navigateBack","switchTab","reLaunch","redirectTo","getEnv","postMessage"]},qq:{keyName:"miniProgram",api:["navigateTo","navigateBack","switchTab","reLaunch","redirectTo","getEnv","postMessage"]}}[a]||{},o={wx:["checkJSApi","chooseImage","previewImage","uploadImage","downloadImage","getLocalImgData","startRecord","stopRecord","onVoiceRecordEnd","playVoice","pauseVoice","stopVoice","onVoicePlayEnd","uploadVoice","downloadVoice","translateVoice","getNetworkType","openLocation","getLocation","startSearchBeacons","stopSearchBeacons","onSearchBeacons","scanQRCode","chooseCard","addCard","openCard"],my:["navigateTo","navigateBack","switchTab","reLaunch","redirectTo","chooseImage","previewImage","getLocation","openLocation","alert","showLoading","hideLoading","getNetworkType","startShare","tradePay","postMessage","onMessage","getEnv"],swan:["makePhoneCall","setClipboardData","getNetworkType","openLocation","getLocation","chooseLocation","chooseImage","previewImage","openShare","navigateToSmartProgram"],web:["navigateTo","navigateBack","switchTab","reLaunch","redirectTo","getEnv","postMessage","getLoadError","getLocation","invoke"],tt:[]}[a]||[];(e.api||[]).forEach(o=>{d[o]=(...t)=>{c(()=>{window[a][e.keyName][o](...t)})}}),o.forEach(e=>{d[e]=(...o)=>{"web"===a?function(e,...o){"invoke"===e&&(e=o[0],o=o.slice(1));const a=o[0];if("getEnv"!==e){const s=++t;i[s]=(e,o)=>{e?(a.fail&&a.fail(e),a.complete&&a.complete(e)):(a.success&&a.success(o),a.complete&&a.complete(o)),delete i[s]};const r={type:e,callbackId:t,args:o};void 0!==n&&(r.clientUid=n),window.ReactNativeWebView?window.ReactNativeWebView.postMessage&&window.ReactNativeWebView.postMessage(JSON.stringify(r)):window.parent.postMessage&&window.parent.postMessage(JSON.stringify(r),"*")}else a({webapp:!0})}(e,...o):c("wx"===a?()=>{window[a]&&window[a].ready(()=>{window[a][e](...o)})}:()=>{window[a][e](...o)})}})};e="web"!==a?o[a].url?function(e,{time:o=5e3,crossOrigin:a=!1}={}){return Promise.race([new Promise((o,t)=>{const n=document.createElement("script");n.type="text/javascript",n.async="async",a&&(n.crossOrigin="anonymous"),n.onload=n.onreadystatechange=function(){this.readyState&&!/^(loaded|complete)$/.test(this.readyState)||(o(),n.onload=n.onreadystatechange=null)},n.onerror=function(){t(new Error(`load ${e} error`)),n.onerror=null},n.src=e,document.getElementsByTagName("head")[0].appendChild(n)}),new Promise((a,t)=>{setTimeout(()=>{t(new Error(`load ${e} timeout`))},o)})])}(o[a].url):Promise.reject(new Error("\u672a\u627e\u5230\u5bf9\u5e94\u7684sdk")):Promise.resolve(),g();export default d; \ No newline at end of file +let e;const a=Object.assign({wx:{url:"https://res.wx.qq.com/open/js/jweixin-1.3.2.js"},qq:{url:"https://qqq.gtimg.cn/miniprogram/webview_jssdk/qqjssdk-1.0.0.js"},my:{url:"https://appx/web-view.min.js"},swan:{url:"https://b.bdstatic.com/searchbox/icms/searchbox/js/swan-2.0.4.js"},tt:{url:"https://lf3-cdn-tos.bytegoofy.com/obj/goofy/developer/jssdk/jssdk-1.2.1.js"}},window.sdkUrlMap);let o=null,t=0;const n=function(){const e=location.href,a=/mpx_webview_id=(\d+)/g.exec(e);let o;return a&&a[1]&&(o=+a[1]),o}(),i={},s=e=>{const{callbackId:a,error:o,result:t}=e;void 0!==a&&i[a]&&(o?i[a](o):i[a](null,t),delete i[a])},c=e=>{const a=e.data;let o=a;try{"string"==typeof a&&(o=JSON.parse(a))}catch(e){}s(o)},r=navigator.userAgent;r.indexOf("AlipayClient")>-1&&r.indexOf("MiniProgram")>-1?o="my":r.toLowerCase().indexOf("miniprogram")>-1?o=r.indexOf("QQ")>-1?"qq":"wx":r.indexOf("swan/")>-1?o="swan":r.indexOf("toutiao")>-1&&(o="tt"),window.ReactNativeWebView?(o="rn",window.mpxWebviewMessageCallback=s):(o="web",window.addEventListener("message",c,!1));let w=!1;function d(a){w?a():e.then(()=>{w=!0,a()})}const g={config(e){"wx"===o?d(()=>{window.wx&&window.wx.config(e)}):console.warn("\u975e\u5fae\u4fe1\u73af\u5883\u4e0d\u9700\u8981\u914d\u7f6econfig")}};const p=()=>{const e={wx:{keyName:"miniProgram",api:["navigateTo","navigateBack","switchTab","reLaunch","redirectTo","postMessage","getEnv"]},tt:{keyName:"miniProgram",api:["redirectTo","navigateTo","switchTab","reLaunch","navigateBack","setSwipeBackModeSync","postMessage","getEnv","checkJsApi","chooseImage","compressImage","previewImage","uploadFile","getNetworkType","openLocation","getLocation"]},swan:{keyName:"webView",api:["navigateTo","navigateBack","switchTab","reLaunch","redirectTo","getEnv","postMessage"]},qq:{keyName:"miniProgram",api:["navigateTo","navigateBack","switchTab","reLaunch","redirectTo","getEnv","postMessage"]}}[o]||{},a={wx:["checkJSApi","chooseImage","previewImage","uploadImage","downloadImage","getLocalImgData","startRecord","stopRecord","onVoiceRecordEnd","playVoice","pauseVoice","stopVoice","onVoicePlayEnd","uploadVoice","downloadVoice","translateVoice","getNetworkType","openLocation","getLocation","startSearchBeacons","stopSearchBeacons","onSearchBeacons","scanQRCode","chooseCard","addCard","openCard"],my:["navigateTo","navigateBack","switchTab","reLaunch","redirectTo","chooseImage","previewImage","getLocation","openLocation","alert","showLoading","hideLoading","getNetworkType","startShare","tradePay","postMessage","onMessage","getEnv"],swan:["makePhoneCall","setClipboardData","getNetworkType","openLocation","getLocation","chooseLocation","chooseImage","previewImage","openShare","navigateToSmartProgram"],web:["navigateTo","navigateBack","switchTab","reLaunch","redirectTo","getEnv","postMessage","getLocation","invoke"],rn:["navigateTo","navigateBack","switchTab","reLaunch","redirectTo","getEnv","postMessage","getLocation","invoke"],tt:[]}[o]||[];(e.api||[]).forEach(a=>{g[a]=(...t)=>{d(()=>{window[o][e.keyName][a](...t)})}}),a.forEach(e=>{g[e]=(...a)=>{"web"===o||"rn"===o?function(e,...a){"invoke"===e&&(e=a[0],a=a.slice(1));const o=a[0]||{};if("getEnv"!==e){const s=++t;i[s]=(e,a)=>{e?(o.fail&&o.fail(e),o.complete&&o.complete(e)):(o.success&&o.success(a),o.complete&&o.complete(a)),delete i[s]};const c={type:e,callbackId:t,args:a};void 0!==n&&(c.clientUid=n),window.ReactNativeWebView?window.ReactNativeWebView.postMessage&&window.ReactNativeWebView.postMessage(JSON.stringify(c)):window.parent.postMessage&&window.parent.postMessage(JSON.stringify(c),"*")}else{let e={webapp:!0};window.ReactNativeWebView&&(e={reactNative:!0}),o(e)}}(e,...a):d("wx"===o?()=>{window[o]&&window[o].ready(()=>{window[o][e](...a)})}:()=>{window[o][e](...a)})}})};e="web"!==o&&"rn"!==o?a[o].url?function(e,{time:a=5e3,crossOrigin:o=!1}={}){return Promise.race([new Promise((a,t)=>{const n=document.createElement("script");n.type="text/javascript",n.async="async",o&&(n.crossOrigin="anonymous"),n.onload=n.onreadystatechange=function(){this.readyState&&!/^(loaded|complete)$/.test(this.readyState)||(a(),n.onload=n.onreadystatechange=null)},n.onerror=function(){t(new Error(`load ${e} error`)),n.onerror=null},n.src=e,document.getElementsByTagName("head")[0].appendChild(n)}),new Promise((o,t)=>{setTimeout(()=>{t(new Error(`load ${e} timeout`))},a)})])}(a[o].url):Promise.reject(new Error("\u672a\u627e\u5230\u5bf9\u5e94\u7684sdk")):Promise.resolve(),p();export default g; \ No newline at end of file diff --git a/packages/webview-bridge/dist/webviewbridge.esm.js b/packages/webview-bridge/dist/webviewbridge.esm.js index f7c3d3d28d..69ea041843 100644 --- a/packages/webview-bridge/dist/webviewbridge.esm.js +++ b/packages/webview-bridge/dist/webviewbridge.esm.js @@ -75,6 +75,31 @@ var env = null; var callbackId = 0; var clientUid = getMpxWebViewId(); var callbacks = {}; +var runCallback = function runCallback(msgData) { + var callbackId = msgData.callbackId, + error = msgData.error, + result = msgData.result; + if (callbackId !== undefined && callbacks[callbackId]) { + if (error) { + callbacks[callbackId](error); + } else { + callbacks[callbackId](null, result); + } + delete callbacks[callbackId]; + } +}; +var eventListener = function eventListener(event) { + // 接收web-view的回调 + var data = event.data; + var msgData = data; + try { + if (typeof data === 'string') { + msgData = JSON.parse(data); + } + } catch (e) {} + runCallback(msgData); +}; + // 环境判断逻辑 var systemUA = navigator.userAgent; if (systemUA.indexOf('AlipayClient') > -1 && systemUA.indexOf('MiniProgram') > -1) { @@ -85,33 +110,16 @@ if (systemUA.indexOf('AlipayClient') > -1 && systemUA.indexOf('MiniProgram') > - env = 'swan'; } else if (systemUA.indexOf('toutiao') > -1) { env = 'tt'; +} +if (window.ReactNativeWebView) { + env = 'rn'; + window.mpxWebviewMessageCallback = runCallback; } else { env = 'web'; - window.addEventListener('message', function (event) { - // 接收web-view的回调 - var data = event.data; - var msgData = data; - try { - if (typeof data === 'string') { - msgData = JSON.parse(data); - } - } catch (e) {} - var _msgData = msgData, - callbackId = _msgData.callbackId, - error = _msgData.error, - result = _msgData.result; - if (callbackId !== undefined && callbacks[callbackId]) { - if (error) { - callbacks[callbackId](error); - } else { - callbacks[callbackId](null, result); - } - delete callbacks[callbackId]; - } - }, false); + window.addEventListener('message', eventListener, false); } var initWebviewBridge = function initWebviewBridge() { - sdkReady = env !== 'web' ? SDK_URL_MAP[env].url ? loadScript(SDK_URL_MAP[env].url) : Promise.reject(new Error('未找到对应的sdk')) : Promise.resolve(); + sdkReady = env !== 'web' && env !== 'rn' ? SDK_URL_MAP[env].url ? loadScript(SDK_URL_MAP[env].url) : Promise.reject(new Error('未找到对应的sdk')) : Promise.resolve(); getWebviewApi(); }; var webviewSdkready = false; @@ -146,7 +154,7 @@ function postMessage(type) { type = extraData[0]; extraData = extraData.slice(1); } - var data = extraData[0]; + var data = extraData[0] || {}; if (type !== 'getEnv') { var currentCallbackId = ++callbackId; callbacks[currentCallbackId] = function (err, res) { @@ -173,9 +181,15 @@ function postMessage(type) { window.parent.postMessage && window.parent.postMessage(JSON.stringify(postParams), '*'); } } else { - data({ + var result = { webapp: true - }); + }; + if (window.ReactNativeWebView) { + result = { + reactNative: true + }; + } + data(result); } } var getWebviewApi = function getWebviewApi() { @@ -201,7 +215,8 @@ var getWebviewApi = function getWebviewApi() { wx: ['checkJSApi', 'chooseImage', 'previewImage', 'uploadImage', 'downloadImage', 'getLocalImgData', 'startRecord', 'stopRecord', 'onVoiceRecordEnd', 'playVoice', 'pauseVoice', 'stopVoice', 'onVoicePlayEnd', 'uploadVoice', 'downloadVoice', 'translateVoice', 'getNetworkType', 'openLocation', 'getLocation', 'startSearchBeacons', 'stopSearchBeacons', 'onSearchBeacons', 'scanQRCode', 'chooseCard', 'addCard', 'openCard'], my: ['navigateTo', 'navigateBack', 'switchTab', 'reLaunch', 'redirectTo', 'chooseImage', 'previewImage', 'getLocation', 'openLocation', 'alert', 'showLoading', 'hideLoading', 'getNetworkType', 'startShare', 'tradePay', 'postMessage', 'onMessage', 'getEnv'], swan: ['makePhoneCall', 'setClipboardData', 'getNetworkType', 'openLocation', 'getLocation', 'chooseLocation', 'chooseImage', 'previewImage', 'openShare', 'navigateToSmartProgram'], - web: ['navigateTo', 'navigateBack', 'switchTab', 'reLaunch', 'redirectTo', 'getEnv', 'postMessage', 'getLoadError', 'getLocation', 'invoke'], + web: ['navigateTo', 'navigateBack', 'switchTab', 'reLaunch', 'redirectTo', 'getEnv', 'postMessage', 'getLocation', 'invoke'], + rn: ['navigateTo', 'navigateBack', 'switchTab', 'reLaunch', 'redirectTo', 'getEnv', 'postMessage', 'getLocation', 'invoke'], tt: [] }; var multiApi = multiApiMap[env] || {}; @@ -223,7 +238,7 @@ var getWebviewApi = function getWebviewApi() { for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { args[_key3] = arguments[_key3]; } - if (env === 'web') { + if (env === 'web' || env === 'rn') { postMessage.apply(void 0, [item].concat(args)); } else if (env === 'wx') { runWebviewApiMethod(function () { diff --git a/packages/webview-bridge/dist/webviewbridge.js b/packages/webview-bridge/dist/webviewbridge.js index e1a097571e..48a40f8bb9 100644 --- a/packages/webview-bridge/dist/webviewbridge.js +++ b/packages/webview-bridge/dist/webviewbridge.js @@ -81,6 +81,31 @@ var callbackId = 0; var clientUid = getMpxWebViewId(); var callbacks = {}; + var runCallback = function runCallback(msgData) { + var callbackId = msgData.callbackId, + error = msgData.error, + result = msgData.result; + if (callbackId !== undefined && callbacks[callbackId]) { + if (error) { + callbacks[callbackId](error); + } else { + callbacks[callbackId](null, result); + } + delete callbacks[callbackId]; + } + }; + var eventListener = function eventListener(event) { + // 接收web-view的回调 + var data = event.data; + var msgData = data; + try { + if (typeof data === 'string') { + msgData = JSON.parse(data); + } + } catch (e) {} + runCallback(msgData); + }; + // 环境判断逻辑 var systemUA = navigator.userAgent; if (systemUA.indexOf('AlipayClient') > -1 && systemUA.indexOf('MiniProgram') > -1) { @@ -91,33 +116,16 @@ env = 'swan'; } else if (systemUA.indexOf('toutiao') > -1) { env = 'tt'; + } + if (window.ReactNativeWebView) { + env = 'rn'; + window.mpxWebviewMessageCallback = runCallback; } else { env = 'web'; - window.addEventListener('message', function (event) { - // 接收web-view的回调 - var data = event.data; - var msgData = data; - try { - if (typeof data === 'string') { - msgData = JSON.parse(data); - } - } catch (e) {} - var _msgData = msgData, - callbackId = _msgData.callbackId, - error = _msgData.error, - result = _msgData.result; - if (callbackId !== undefined && callbacks[callbackId]) { - if (error) { - callbacks[callbackId](error); - } else { - callbacks[callbackId](null, result); - } - delete callbacks[callbackId]; - } - }, false); + window.addEventListener('message', eventListener, false); } var initWebviewBridge = function initWebviewBridge() { - sdkReady = env !== 'web' ? SDK_URL_MAP[env].url ? loadScript(SDK_URL_MAP[env].url) : Promise.reject(new Error('未找到对应的sdk')) : Promise.resolve(); + sdkReady = env !== 'web' && env !== 'rn' ? SDK_URL_MAP[env].url ? loadScript(SDK_URL_MAP[env].url) : Promise.reject(new Error('未找到对应的sdk')) : Promise.resolve(); getWebviewApi(); }; var webviewSdkready = false; @@ -152,7 +160,7 @@ type = extraData[0]; extraData = extraData.slice(1); } - var data = extraData[0]; + var data = extraData[0] || {}; if (type !== 'getEnv') { var currentCallbackId = ++callbackId; callbacks[currentCallbackId] = function (err, res) { @@ -179,9 +187,15 @@ window.parent.postMessage && window.parent.postMessage(JSON.stringify(postParams), '*'); } } else { - data({ + var result = { webapp: true - }); + }; + if (window.ReactNativeWebView) { + result = { + reactNative: true + }; + } + data(result); } } var getWebviewApi = function getWebviewApi() { @@ -207,7 +221,8 @@ wx: ['checkJSApi', 'chooseImage', 'previewImage', 'uploadImage', 'downloadImage', 'getLocalImgData', 'startRecord', 'stopRecord', 'onVoiceRecordEnd', 'playVoice', 'pauseVoice', 'stopVoice', 'onVoicePlayEnd', 'uploadVoice', 'downloadVoice', 'translateVoice', 'getNetworkType', 'openLocation', 'getLocation', 'startSearchBeacons', 'stopSearchBeacons', 'onSearchBeacons', 'scanQRCode', 'chooseCard', 'addCard', 'openCard'], my: ['navigateTo', 'navigateBack', 'switchTab', 'reLaunch', 'redirectTo', 'chooseImage', 'previewImage', 'getLocation', 'openLocation', 'alert', 'showLoading', 'hideLoading', 'getNetworkType', 'startShare', 'tradePay', 'postMessage', 'onMessage', 'getEnv'], swan: ['makePhoneCall', 'setClipboardData', 'getNetworkType', 'openLocation', 'getLocation', 'chooseLocation', 'chooseImage', 'previewImage', 'openShare', 'navigateToSmartProgram'], - web: ['navigateTo', 'navigateBack', 'switchTab', 'reLaunch', 'redirectTo', 'getEnv', 'postMessage', 'getLoadError', 'getLocation', 'invoke'], + web: ['navigateTo', 'navigateBack', 'switchTab', 'reLaunch', 'redirectTo', 'getEnv', 'postMessage', 'getLocation', 'invoke'], + rn: ['navigateTo', 'navigateBack', 'switchTab', 'reLaunch', 'redirectTo', 'getEnv', 'postMessage', 'getLocation', 'invoke'], tt: [] }; var multiApi = multiApiMap[env] || {}; @@ -229,7 +244,7 @@ for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { args[_key3] = arguments[_key3]; } - if (env === 'web') { + if (env === 'web' || env === 'rn') { postMessage.apply(void 0, [item].concat(args)); } else if (env === 'wx') { runWebviewApiMethod(function () { diff --git a/packages/webview-bridge/dist/webviewbridge.min.js b/packages/webview-bridge/dist/webviewbridge.min.js index 7ac85a8e78..a8a6c7b985 100644 --- a/packages/webview-bridge/dist/webviewbridge.min.js +++ b/packages/webview-bridge/dist/webviewbridge.min.js @@ -3,4 +3,4 @@ * (c) 2024 @mpxjs team * @license Apache */ -var e,o;e=this,o=function(){"use strict";var e,o,a,t,n=Object.assign({wx:{url:"https://res.wx.qq.com/open/js/jweixin-1.3.2.js"},qq:{url:"https://qqq.gtimg.cn/miniprogram/webview_jssdk/qqjssdk-1.0.0.js"},my:{url:"https://appx/web-view.min.js"},swan:{url:"https://b.bdstatic.com/searchbox/icms/searchbox/js/swan-2.0.4.js"},tt:{url:"https://lf3-cdn-tos.bytegoofy.com/obj/goofy/developer/jssdk/jssdk-1.2.1.js"}},window.sdkUrlMap),i=null,r=0,c=(a=location.href,(t=/mpx_webview_id=(\d+)/g.exec(a))&&t[1]&&(o=+t[1]),o),s={},d=navigator.userAgent;d.indexOf("AlipayClient")>-1&&d.indexOf("MiniProgram")>-1?i="my":d.toLowerCase().indexOf("miniprogram")>-1?i=d.indexOf("QQ")>-1?"qq":"wx":d.indexOf("swan/")>-1?i="swan":d.indexOf("toutiao")>-1?i="tt":(i="web",window.addEventListener("message",(function(e){var o=e.data,a=o;try{"string"==typeof o&&(a=JSON.parse(o))}catch(e){}var t=a,n=t.callbackId,i=t.error,r=t.result;void 0!==n&&s[n]&&(i?s[n](i):s[n](null,r),delete s[n])}),!1));var g=!1;function w(o){g?o():e.then((function(){g=!0,o()}))}var p={config:function(e){"wx"===i?w((function(){window.wx&&window.wx.config(e)})):console.warn("\u975e\u5fae\u4fe1\u73af\u5883\u4e0d\u9700\u8981\u914d\u7f6econfig")}};function l(e){for(var o=arguments.length,a=new Array(o>1?o-1:0),t=1;t1&&void 0!==arguments[1]?arguments[1]:{},a=o.time,t=void 0===a?5e3:a,n=o.crossOrigin,i=void 0!==n&&n;function r(){return new Promise((function(o,a){var t=document.createElement("script");t.type="text/javascript",t.async="async",i&&(t.crossOrigin="anonymous"),t.onload=t.onreadystatechange=function(){this.readyState&&!/^(loaded|complete)$/.test(this.readyState)||(o(),t.onload=t.onreadystatechange=null)},t.onerror=function(){a(new Error("load ".concat(e," error"))),t.onerror=null},t.src=e,document.getElementsByTagName("head")[0].appendChild(t)}))}function c(){return new Promise((function(o,a){setTimeout((function(){a(new Error("load ".concat(e," timeout")))}),t)}))}return Promise.race([r(),c()])}(n[i].url):Promise.reject(new Error("\u672a\u627e\u5230\u5bf9\u5e94\u7684sdk")):Promise.resolve(),m(),p},"object"==typeof exports&&"undefined"!=typeof module?module.exports=o():"function"==typeof define&&define.amd?define(o):(e=e||self).mpx=o(); \ No newline at end of file +var e,a;e=this,a=function(){"use strict";var e,a,o,t,n=Object.assign({wx:{url:"https://res.wx.qq.com/open/js/jweixin-1.3.2.js"},qq:{url:"https://qqq.gtimg.cn/miniprogram/webview_jssdk/qqjssdk-1.0.0.js"},my:{url:"https://appx/web-view.min.js"},swan:{url:"https://b.bdstatic.com/searchbox/icms/searchbox/js/swan-2.0.4.js"},tt:{url:"https://lf3-cdn-tos.bytegoofy.com/obj/goofy/developer/jssdk/jssdk-1.2.1.js"}},window.sdkUrlMap),i=null,r=0,c=(o=location.href,(t=/mpx_webview_id=(\d+)/g.exec(o))&&t[1]&&(a=+t[1]),a),s={},d=function(e){var a=e.callbackId,o=e.error,t=e.result;void 0!==a&&s[a]&&(o?s[a](o):s[a](null,t),delete s[a])},w=navigator.userAgent;w.indexOf("AlipayClient")>-1&&w.indexOf("MiniProgram")>-1?i="my":w.toLowerCase().indexOf("miniprogram")>-1?i=w.indexOf("QQ")>-1?"qq":"wx":w.indexOf("swan/")>-1?i="swan":w.indexOf("toutiao")>-1&&(i="tt"),window.ReactNativeWebView?(i="rn",window.mpxWebviewMessageCallback=d):(i="web",window.addEventListener("message",(function(e){var a=e.data,o=a;try{"string"==typeof a&&(o=JSON.parse(a))}catch(e){}d(o)}),!1));var g=!1;function p(a){g?a():e.then((function(){g=!0,a()}))}var l={config:function(e){"wx"===i?p((function(){window.wx&&window.wx.config(e)})):console.warn("\u975e\u5fae\u4fe1\u73af\u5883\u4e0d\u9700\u8981\u914d\u7f6econfig")}};function v(e){for(var a=arguments.length,o=new Array(a>1?a-1:0),t=1;t1&&void 0!==arguments[1]?arguments[1]:{},o=a.time,t=void 0===o?5e3:o,n=a.crossOrigin,i=void 0!==n&&n;function r(){return new Promise((function(a,o){var t=document.createElement("script");t.type="text/javascript",t.async="async",i&&(t.crossOrigin="anonymous"),t.onload=t.onreadystatechange=function(){this.readyState&&!/^(loaded|complete)$/.test(this.readyState)||(a(),t.onload=t.onreadystatechange=null)},t.onerror=function(){o(new Error("load ".concat(e," error"))),t.onerror=null},t.src=e,document.getElementsByTagName("head")[0].appendChild(t)}))}function c(){return new Promise((function(a,o){setTimeout((function(){o(new Error("load ".concat(e," timeout")))}),t)}))}return Promise.race([r(),c()])}(n[i].url):Promise.reject(new Error("\u672a\u627e\u5230\u5bf9\u5e94\u7684sdk")):Promise.resolve(),m(),l},"object"==typeof exports&&"undefined"!=typeof module?module.exports=a():"function"==typeof define&&define.amd?define(a):(e=e||self).mpx=a(); \ No newline at end of file diff --git a/packages/webview-bridge/src/index.js b/packages/webview-bridge/src/index.js index 054b022d22..5d11aded12 100644 --- a/packages/webview-bridge/src/index.js +++ b/packages/webview-bridge/src/index.js @@ -31,6 +31,32 @@ let env = null let callbackId = 0 const clientUid = getMpxWebViewId() const callbacks = {} + +const runCallback = (msgData) => { + const { callbackId, error, result } = msgData + if (callbackId !== undefined && callbacks[callbackId]) { + if (error) { + callbacks[callbackId](error) + } else { + callbacks[callbackId](null, result) + } + delete callbacks[callbackId] + } +} + +const eventListener = (event) => { + // 接收web-view的回调 + const data = event.data + let msgData = data + try { + if (typeof data === 'string') { + msgData = JSON.parse(data) + } + } catch (e) { + } + runCallback(msgData) +} + // 环境判断逻辑 const systemUA = navigator.userAgent if (systemUA.indexOf('AlipayClient') > -1 && systemUA.indexOf('MiniProgram') > -1) { @@ -41,32 +67,16 @@ if (systemUA.indexOf('AlipayClient') > -1 && systemUA.indexOf('MiniProgram') > - env = 'swan' } else if (systemUA.indexOf('toutiao') > -1) { env = 'tt' +} if (window.ReactNativeWebView) { + env = 'rn' + window.mpxWebviewMessageCallback = runCallback } else { env = 'web' - window.addEventListener('message', (event) => { - // 接收web-view的回调 - const data = event.data - let msgData = data - try { - if (typeof data === 'string') { - msgData = JSON.parse(data) - } - } catch (e) { - } - const { callbackId, error, result } = msgData - if (callbackId !== undefined && callbacks[callbackId]) { - if (error) { - callbacks[callbackId](error) - } else { - callbacks[callbackId](null, result) - } - delete callbacks[callbackId] - } - }, false) + window.addEventListener('message', eventListener, false) } const initWebviewBridge = () => { - sdkReady = env !== 'web' ? SDK_URL_MAP[env].url ? loadScript(SDK_URL_MAP[env].url) : Promise.reject(new Error('未找到对应的sdk')) : Promise.resolve() + sdkReady = (env !== 'web' && env !== 'rn') ? SDK_URL_MAP[env].url ? loadScript(SDK_URL_MAP[env].url) : Promise.reject(new Error('未找到对应的sdk')) : Promise.resolve() getWebviewApi() } @@ -101,7 +111,7 @@ function postMessage (type, ...extraData) { type = extraData[0] extraData = extraData.slice(1) } - const data = extraData[0] + const data = extraData[0] || {} if (type !== 'getEnv') { const currentCallbackId = ++callbackId callbacks[currentCallbackId] = (err, res) => { @@ -128,9 +138,15 @@ function postMessage (type, ...extraData) { window.parent.postMessage && window.parent.postMessage(JSON.stringify(postParams), '*') } } else { - data({ + let result = { webapp: true - }) + } + if (window.ReactNativeWebView) { + result = { + reactNative: true + } + } + data(result) } } @@ -263,7 +279,17 @@ const getWebviewApi = () => { 'redirectTo', 'getEnv', 'postMessage', - 'getLoadError', + 'getLocation', + 'invoke' + ], + rn: [ + 'navigateTo', + 'navigateBack', + 'switchTab', + 'reLaunch', + 'redirectTo', + 'getEnv', + 'postMessage', 'getLocation', 'invoke' ], @@ -281,7 +307,7 @@ const getWebviewApi = () => { }) singleApi.forEach((item) => { webviewBridge[item] = (...args) => { - if (env === 'web') { + if (env === 'web' || env === 'rn') { postMessage(item, ...args) } else if (env === 'wx') { runWebviewApiMethod(() => { From cd18eb86611a569cbf2f9b4f095021890e794055 Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Wed, 25 Dec 2024 21:15:13 +0800 Subject: [PATCH 04/32] =?UTF-8?q?fix=20webview=E8=B7=B3=E8=BD=AC=E6=8F=90?= =?UTF-8?q?=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../runtime/components/react/mpx-web-view.tsx | 46 +++++++++++++++++-- .../components/react/types/global.d.ts | 4 +- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx index 787c94a3d8..25a826e35f 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx @@ -1,4 +1,4 @@ -import { forwardRef, JSX, useRef, useContext, useMemo, createElement } from 'react' +import { forwardRef, JSX, useRef, useContext, useMemo, createElement, useCallback, useEffect } from 'react' import { warn, getFocusedNavigation, isFunction } from '@mpxjs/utils' import { Portal } from '@ant-design/react-native' import { getCustomEvent } from './getInnerListeners' @@ -6,8 +6,9 @@ import { promisify, redirectTo, navigateTo, navigateBack, reLaunch, switchTab } import { WebView } from 'react-native-webview' import useNodesRef, { HandlerRef } from './useNodesRef' import { getCurrentPage, extendObject } from './utils' -import { WebViewNavigationEvent, WebViewErrorEvent, WebViewMessageEvent, WebViewNavigation } from 'react-native-webview/lib/WebViewTypes' +import { WebViewNavigationEvent, WebViewErrorEvent, WebViewMessageEvent, WebViewNavigation, WebViewProgressEvent } from 'react-native-webview/lib/WebViewTypes' import { RouteContext } from './context' +import { BackHandler } from 'react-native' type OnMessageCallbackEvent = { detail: { @@ -56,6 +57,35 @@ const _WebView = forwardRef, WebViewProps>((pr top: 0 as number, bottom: 0 as number } + const canGoBack = useRef(false) + + const onAndroidBackPress = useCallback(() => { + if (canGoBack.current) { + webViewRef.current?.goBack() + return true + } + return false + }, [canGoBack]) + + const beforeRemoveHandle = useCallback((e: Event) => { + if (canGoBack.current) { + webViewRef.current?.goBack() + e.preventDefault() + } + }, [canGoBack]) + + const navigation = getFocusedNavigation() + + navigation?.addListener('beforeRemove', beforeRemoveHandle) + + useEffect(() => { + if (__mpx_mode__ === 'android') { + BackHandler.addEventListener('hardwareBackPress', onAndroidBackPress) + return () => { + BackHandler.removeEventListener('hardwareBackPress', onAndroidBackPress) + } + } + }, []) useNodesRef(props, ref, webViewRef, { style: defaultWebViewStyle @@ -119,9 +149,16 @@ const _WebView = forwardRef, WebViewProps>((pr } const _changeUrl = function (navState: WebViewNavigation) { if (navState.navigationType) { // navigationType这个事件在页面开始加载时和页面加载完成时都会被触发所以判断这个避免其他无效触发执行该逻辑 + canGoBack.current = navState.canGoBack currentPage.__webViewUrl = navState.url } } + + const _onLoadProgress = function (event: WebViewProgressEvent) { + if (__mpx_mode__ === 'android') { + canGoBack.current = event.nativeEvent.canGoBack + } + } const _message = function (res: WebViewMessageEvent) { let data: MessageData = {} let asyncCallback @@ -141,7 +178,6 @@ const _WebView = forwardRef, WebViewProps>((pr { // case下不允许直接声明,包个块解决该问题 const title = postData._documentTitle if (title) { - const navigation = getFocusedNavigation() navigation && navigation.setOptions({ title }) } } @@ -226,7 +262,9 @@ const _WebView = forwardRef, WebViewProps>((pr javaScriptEnabled: true, onNavigationStateChange: _changeUrl, onMessage: _message, - injectedJavaScript: injectedJavaScript + injectedJavaScript: injectedJavaScript, + onLoadProgress: _onLoadProgress, + allowsBackForwardNavigationGestures: true }, events))) }) diff --git a/packages/webpack-plugin/lib/runtime/components/react/types/global.d.ts b/packages/webpack-plugin/lib/runtime/components/react/types/global.d.ts index 052fc1ac56..c3ce06a094 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/types/global.d.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/types/global.d.ts @@ -1,3 +1,4 @@ +declare let __mpx_mode__: 'wx' | 'ali' | 'swan' | 'qq' | 'tt' | 'web' | 'dd' | 'qa' | 'jd' | 'android' | 'ios' declare module '@mpxjs/utils' { export function isEmptyObject (obj: Object): boolean export function isFunction (fn: unknown): boolean @@ -20,7 +21,8 @@ declare module '@mpxjs/utils' { left: number right: number }, - setOptions: (params: Record) => void + setOptions: (params: Record) => void, + addListener: (eventName: string, callback: (e: Event) => void) => void } | undefined } From b46c42aafc5384a829a0aa8c709c5df1e12582bb Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Sat, 28 Dec 2024 14:01:30 +0800 Subject: [PATCH 05/32] =?UTF-8?q?=E5=BC=80=E5=8F=91=E5=AE=8Cportal=20add?= =?UTF-8?q?=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/action-sheet/rnActionSheet.jsx | 5 +- .../src/platform/api/modal/rnModal.jsx | 2 +- .../src/platform/api/system/rnSystem.js | 1 + .../src/platform/api/toast/rnToast.jsx | 2 +- .../webpack-plugin/lib/react/processScript.js | 3 +- .../lib/runtime/components/react/context.ts | 18 ++ .../components/react/locale-provider.tsx | 28 ++++ .../runtime/components/react/mpx-portal.tsx | 28 ++++ .../react/mpx-portal/portal-consumer.tsx | 25 +++ .../react/mpx-portal/portal-host.tsx | 158 ++++++++++++++++++ .../react/mpx-portal/portal-manager.tsx | 68 ++++++++ .../runtime/components/react/mpx-provider.tsx | 52 ++++++ .../components/react/mpx-root-portal.tsx | 2 +- .../runtime/components/react/mpx-web-view.tsx | 29 ++-- .../components/react/types/global.d.ts | 3 + 15 files changed, 405 insertions(+), 19 deletions(-) create mode 100644 packages/webpack-plugin/lib/runtime/components/react/locale-provider.tsx create mode 100644 packages/webpack-plugin/lib/runtime/components/react/mpx-portal.tsx create mode 100644 packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx create mode 100644 packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx create mode 100644 packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-manager.tsx create mode 100644 packages/webpack-plugin/lib/runtime/components/react/mpx-provider.tsx diff --git a/packages/api-proxy/src/platform/api/action-sheet/rnActionSheet.jsx b/packages/api-proxy/src/platform/api/action-sheet/rnActionSheet.jsx index 5d3f33df96..68fa09de33 100644 --- a/packages/api-proxy/src/platform/api/action-sheet/rnActionSheet.jsx +++ b/packages/api-proxy/src/platform/api/action-sheet/rnActionSheet.jsx @@ -1,12 +1,13 @@ import { View, Text, StyleSheet } from 'react-native' import { successHandle, failHandle } from '../../../common/js' -import { Portal } from '@ant-design/react-native' +import Portal from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal' import { getWindowInfo } from '../system/rnSystem' import Animated, { useSharedValue, useAnimatedStyle, withTiming } from 'react-native-reanimated' + function showActionSheet (options = {}) { const { alertText, itemList = [], itemColor = '#000000', success, fail, complete } = options const windowInfo = getWindowInfo() @@ -77,6 +78,8 @@ function showActionSheet (options = {}) { const remove = function () { if (actionSheetKey) { slideOut() + Portal.remove(actionSheetKey) + actionSheetKey = null setTimeout(() => { Portal.remove(actionSheetKey) actionSheetKey = null diff --git a/packages/api-proxy/src/platform/api/modal/rnModal.jsx b/packages/api-proxy/src/platform/api/modal/rnModal.jsx index afc8ec1251..3c5ba8ac7c 100644 --- a/packages/api-proxy/src/platform/api/modal/rnModal.jsx +++ b/packages/api-proxy/src/platform/api/modal/rnModal.jsx @@ -1,6 +1,6 @@ import { View, Dimensions, Text, StyleSheet, TouchableOpacity, ScrollView, TextInput } from 'react-native' import { successHandle, failHandle } from '../../../common/js' -import { Portal } from '@ant-design/react-native' +import Portal from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal' const { width, height } = Dimensions.get('window') const showModal = function (options = {}) { const { diff --git a/packages/api-proxy/src/platform/api/system/rnSystem.js b/packages/api-proxy/src/platform/api/system/rnSystem.js index acf3965ed9..67c51e8605 100644 --- a/packages/api-proxy/src/platform/api/system/rnSystem.js +++ b/packages/api-proxy/src/platform/api/system/rnSystem.js @@ -10,6 +10,7 @@ const getWindowInfo = function () { const insets = Object.assign(initialWindowMetricsInset, navigationInsets) let safeArea = {} const { top = 0, bottom = 0, left = 0, right = 0 } = insets + const screenHeight = __mpx_mode__ === 'ios' ? dimensionsScreen.height : dimensionsScreen.height - bottom // 解决安卓开启屏幕内三建导航安卓把安全区计算进去后产生的影响 const screenWidth = __mpx_mode__ === 'ios' ? dimensionsScreen.width : dimensionsScreen.width - right const layout = navigation.layout || {} diff --git a/packages/api-proxy/src/platform/api/toast/rnToast.jsx b/packages/api-proxy/src/platform/api/toast/rnToast.jsx index 45f98fa9e6..368ba60c3a 100644 --- a/packages/api-proxy/src/platform/api/toast/rnToast.jsx +++ b/packages/api-proxy/src/platform/api/toast/rnToast.jsx @@ -1,6 +1,6 @@ import { View, Text, Image, StyleSheet, ActivityIndicator, Dimensions } from 'react-native' import { successHandle, failHandle } from '../../../common/js' -import { Portal } from '@ant-design/react-native' +import Portal from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal' let toastKey let isLoadingShow diff --git a/packages/webpack-plugin/lib/react/processScript.js b/packages/webpack-plugin/lib/react/processScript.js index 7ad2a9913f..f68bd6079f 100644 --- a/packages/webpack-plugin/lib/react/processScript.js +++ b/packages/webpack-plugin/lib/react/processScript.js @@ -26,7 +26,8 @@ module.exports = function (script, { import { getComponent } from ${stringifyRequest(loaderContext, optionProcessorPath)} import { NavigationContainer, StackActions } from '@react-navigation/native' import { createStackNavigator } from '@react-navigation/stack' -import { Provider } from '@ant-design/react-native' +// import Provider from '@ant-design/react-native' +import Provider from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-provider' import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context' import { GestureHandlerRootView } from 'react-native-gesture-handler' diff --git a/packages/webpack-plugin/lib/runtime/components/react/context.ts b/packages/webpack-plugin/lib/runtime/components/react/context.ts index 5bb14fc15d..03690e4802 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/context.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/context.ts @@ -33,6 +33,20 @@ export interface IntersectionObserver { } } +export interface PortalManagerContextValue { + mount: (key: number, children: React.ReactNode) => void + update: (key: number, children: React.ReactNode) => void + unmount: (key: number) => void, + portals: Array<{key: number, children: React.ReactNode}> +} + +export interface PortalContextValue { + mount: (children: React.ReactNode) => number| undefined + update: (key: number, children: React.ReactNode) => void + unmount: (key: number) => void + manager?: PortalManagerContextValue +} + export const MovableAreaContext = createContext({ width: 0, height: 0 }) export const FormContext = createContext(null) @@ -52,3 +66,7 @@ export const IntersectionObserverContext = createContext(null) export const KeyboardAvoidContext = createContext(null) + +export const PortalContext = createContext(null as any) + +export const PortalManagerContext = createContext(null) diff --git a/packages/webpack-plugin/lib/runtime/components/react/locale-provider.tsx b/packages/webpack-plugin/lib/runtime/components/react/locale-provider.tsx new file mode 100644 index 0000000000..630288fe20 --- /dev/null +++ b/packages/webpack-plugin/lib/runtime/components/react/locale-provider.tsx @@ -0,0 +1,28 @@ +import React, { createContext } from 'react' + +export type LocaleContextProps = { + antLocale?: any +} +export interface LocaleProviderProps { + children?: React.ReactNode, + locale: any +} + +export const LocaleContext = createContext< + LocaleContextProps | undefined +>(undefined) + +const LocaleProvider = (props :LocaleProviderProps): JSX.Element => { + const locale = React.useMemo(() => { + return { antLocale: { exist: true } } + }, []) + return ( + + {props.children} + + ) +} + +LocaleProvider.displayName = 'LocaleProvider' + +export default React.memo(LocaleProvider) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal.tsx new file mode 100644 index 0000000000..79aa6b2399 --- /dev/null +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal.tsx @@ -0,0 +1,28 @@ +import React from 'react' +import { PortalContext, PortalContextValue } from './context' +import PortalConsumer from './mpx-portal/portal-consumer' +import PortalHost, { portal } from './mpx-portal/portal-host' + +export type PortalProps = { + /** + * Content of the `Portal`. + */ + children?: React.ReactNode + manager: PortalContextValue +} + +const Portal = ({ children }:PortalProps): JSX.Element => { + return ( + + {(manager) => ( + {children} + )} + + ) +} + +Portal.Host = PortalHost +Portal.add = portal.add +Portal.remove = portal.remove + +export default Portal diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx new file mode 100644 index 0000000000..3af2dcfda4 --- /dev/null +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx @@ -0,0 +1,25 @@ +import React, { useEffect, useRef } from 'react' +import { PortalContextValue } from '../context' + +export type PortalConsumerProps = { + manager: PortalContextValue + children?: React.ReactNode +} +const PortalConsumer = ({ manager, children } :PortalConsumerProps): JSX.Element | null => { + const keyRef = useRef(null) + useEffect(() => { + if (!manager) { + throw new Error( + 'Looks like you forgot to wrap your root component with `Provider` component from `@ant-design/react-native`.\n\n' + ) + } + keyRef.current = manager.mount(children) + manager.update(keyRef.current, children) + return () => { + manager.unmount(keyRef.current) + } + }, []) + return null +} + +export default PortalConsumer diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx new file mode 100644 index 0000000000..51ff956fd7 --- /dev/null +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx @@ -0,0 +1,158 @@ +import React, { useEffect, useRef, useState } from 'react' +import { + View, + DeviceEventEmitter, + EventSubscription, + NativeEventEmitter, + StyleSheet +} from 'react-native' +import PortalManager from './portal-manager' +import { getFocusedNavigation } from '@mpxjs/utils' +import { PortalManagerContextValue, PortalContext } from '../context' + +export type PortalHostProps = { + children: React.ReactNode +} + +type addIdsMapsType = { + [key: number]: number[] +} + +export type Operation = + | { type: 'mount'; key: number; children: React.ReactNode } + | { type: 'update'; key: number; children: React.ReactNode } + | { type: 'unmount'; key: number } + +// events +const addType = 'MPX_RN_ADD_PORTAL' +const removeType = 'MPX_RN_REMOVE_PORTAL' +// fix react native web does not support DeviceEventEmitter +const TopViewEventEmitter = DeviceEventEmitter || new NativeEventEmitter() + +const styles = StyleSheet.create({ + container: { + flex: 1 + } +}) + +class PortalGuard { + private nextKey = 10000 + add = (e: React.ReactNode) => { + const key = this.nextKey++ + TopViewEventEmitter.emit(addType, e, key) + return key + } + + remove = (key: number) => { + TopViewEventEmitter.emit(removeType, key) + } +} +/** + * portal + */ +export const portal = new PortalGuard() + +const PortalHost = ({ children } :PortalHostProps): JSX.Element => { + const _nextKey = useRef(0) + const _queue = useRef([]) + const _addType = useRef(null) + const _removeType = useRef(null) + const manager = useRef(null) + let currentPageId: number | undefined + const _mount = (children: React.ReactNode, _key?: number) => { + const navigation = getFocusedNavigation() + const pageId = navigation?.pageId + if (pageId !== currentPageId) { + return + } + const key = _key || _nextKey.current++ + if (manager.current) { + manager.current.mount(key, children) + } else { + _queue.current.push({ type: 'mount', key, children }) + } + return key + } + + const _unmount = (key: number) => { + const navigation = getFocusedNavigation() + const pageId = navigation?.pageId + if (pageId !== currentPageId) { + return + } + if (manager.current) { + manager.current.unmount(key) + } else { + _queue.current.push({ type: 'unmount', key }) + } + } + + const _update = (key: number, children: React.ReactNode) => { + const navigation = getFocusedNavigation() + const pageId = navigation?.pageId + if (pageId !== currentPageId) { + return + } + if (manager.current) { + manager.current.update(key, children) + } else { + const op: Operation = { type: 'mount', key, children } + const index = _queue.current.findIndex( + (o) => o.type === 'mount' || (o.type === 'update' && o.key === key) + ) + + if (index > -1) { + _queue.current[index] = op + } else { + _queue.current.push(op) + } + } + } + + useEffect(() => { + const navigation = getFocusedNavigation() + currentPageId = navigation?.pageId + _addType.current = TopViewEventEmitter.addListener(addType, _mount) + _removeType.current = TopViewEventEmitter.addListener( + removeType, + _unmount + ) + return () => { + while (_queue.current.length && manager.current) { + const action = _queue.current.pop() + if (!action) { + continue + } + // tslint:disable-next-line:switch-default + switch (action.type) { + case 'mount': + manager.current?.mount(action.key, action.children) + break + case 'update': + manager.current?.update(action.key, action.children) + break + case 'unmount': + manager.current?.unmount(action.key) + break + } + } + } + }, []) + return ( + + {/* Need collapsable=false here to clip the elevations, otherwise they appear above Portal components */} + + {children} + + + + ) +} + +export default PortalHost diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-manager.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-manager.tsx new file mode 100644 index 0000000000..ce93ff2d4b --- /dev/null +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-manager.tsx @@ -0,0 +1,68 @@ +import React, { useState, useCallback, forwardRef, ForwardedRef, useImperativeHandle, useEffect, useContext, ReactElement } from 'react' +import { View, StyleSheet } from 'react-native' +import { getFocusedNavigation } from '@mpxjs/utils' + +export type State = { + portals: Array<{ + key: number + children: React.ReactNode + }> +} + +type PortalManagerProps = { +} + +const _PortalManager = forwardRef((props: PortalManagerProps, ref:ForwardedRef): ReactElement => { + const [state, setState] = useState({ + portals: [] + }) + + const mount = useCallback((key: number, children: React.ReactNode) => { + setState((prevState) => ({ + portals: [...prevState.portals, { key, children }] + })) + }, [state]) + + const update = useCallback((key: number, children: React.ReactNode) => { + setState({ + portals: state.portals.map((item) => { + if (item.key === key) { + return { ...item, children } + } + return item + }) + }) + }, [state]) + + const unmount = useCallback((key: number) => { + setState((prevState) => ({ + portals: prevState.portals.filter((item) => item.key !== key) + })) + }, []) + + useImperativeHandle(ref, () => ({ + mount, + update, + unmount, + portals: state.portals + })) + useEffect(() => { + console.log('State updated:', state.portals) + }, [state.portals]) + console.log(state.portals, '在return的state.portals') + return ( + <> + {state.portals.map(({ key, children }, i) => ( + + {children} + + ))} + + ) +}) + +export default _PortalManager diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-provider.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-provider.tsx new file mode 100644 index 0000000000..ea8febe3b2 --- /dev/null +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-provider.tsx @@ -0,0 +1,52 @@ +import React from 'react' +import LocaleProvider, { LocaleContextProps } from './locale-provider' +import Portal from './mpx-portal' +import { extendObject } from './utils' +// import { Theme, ThemeProvider } from '../style' + +export type Theme = typeof defaultTheme & { [key: string]: any } + +export interface ProviderProps { + locale?: LocaleContextProps + theme?: Partial + children: React.ReactNode +} +const defaultTheme = { + color_text_base: '#000000', // 基本 + color_text_base_inverse: '#ffffff', // 基本 _ 反色 + color_text_secondary: '#a4a9b0', // 辅助色 + color_text_placeholder: '#bbbbbb', // 文本框提示 + color_text_disabled: '#bbbbbb', // 失效 + color_text_caption: '#888888', // 辅助描述 + color_text_paragraph: '#333333', // 段落 + color_error: '#ff4d4f', // 错误(form validate) + color_warning: '#faad14', // 警告 + color_success: '#52c41a', + color_primary: '#1677ff' +} +export const ThemeContext = React.createContext(defaultTheme) + +export type PartialTheme = Partial + +export interface ThemeProviderProps { + value?: PartialTheme + children?: React.ReactNode +} + +const ThemeProvider = (props: ThemeProviderProps) => { + const { value, children } = props + const theme = React.useMemo(() => (extendObject({}, defaultTheme, value)), [value]) + return {children} +} + +const Provider = ({ locale, theme, children }:ProviderProps): JSX.Element => { + return ( + + + {children} + + + ) +} + +export default Provider diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-root-portal.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-root-portal.tsx index 7c5d53769d..39ad317e29 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-root-portal.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-root-portal.tsx @@ -2,7 +2,7 @@ * ✔ enable */ import { ReactNode, createElement, Fragment } from 'react' -import { Portal } from '@ant-design/react-native' +import Portal from './mpx-portal' import { warn } from '@mpxjs/utils' interface RootPortalProps { enable?: boolean diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx index 25a826e35f..9bddab5860 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx @@ -1,6 +1,6 @@ import { forwardRef, JSX, useRef, useContext, useMemo, createElement, useCallback, useEffect } from 'react' import { warn, getFocusedNavigation, isFunction } from '@mpxjs/utils' -import { Portal } from '@ant-design/react-native' +import Portal from './mpx-portal' import { getCustomEvent } from './getInnerListeners' import { promisify, redirectTo, navigateTo, navigateBack, reLaunch, switchTab } from '@mpxjs/api-proxy' import { WebView } from 'react-native-webview' @@ -60,17 +60,17 @@ const _WebView = forwardRef, WebViewProps>((pr const canGoBack = useRef(false) const onAndroidBackPress = useCallback(() => { - if (canGoBack.current) { - webViewRef.current?.goBack() - return true - } - return false + if (canGoBack.current) { + webViewRef.current?.goBack() + return true + } + return false }, [canGoBack]) const beforeRemoveHandle = useCallback((e: Event) => { if (canGoBack.current) { - webViewRef.current?.goBack() - e.preventDefault() + webViewRef.current?.goBack() + e.preventDefault() } }, [canGoBack]) @@ -80,10 +80,11 @@ const _WebView = forwardRef, WebViewProps>((pr useEffect(() => { if (__mpx_mode__ === 'android') { - BackHandler.addEventListener('hardwareBackPress', onAndroidBackPress) - return () => { - BackHandler.removeEventListener('hardwareBackPress', onAndroidBackPress) - } + BackHandler.addEventListener('hardwareBackPress', onAndroidBackPress) + return () => { + BackHandler.removeEventListener('hardwareBackPress', onAndroidBackPress) + navigation?.removeListener('beforeRemove', beforeRemoveHandle) + } } }, []) @@ -141,7 +142,7 @@ const _WebView = forwardRef, WebViewProps>((pr } true; ` - const sendMessage = function(params: string) { + const sendMessage = function (params: string) { return ` window.mpxWebviewMessageCallback(${params}) true; @@ -156,7 +157,7 @@ const _WebView = forwardRef, WebViewProps>((pr const _onLoadProgress = function (event: WebViewProgressEvent) { if (__mpx_mode__ === 'android') { - canGoBack.current = event.nativeEvent.canGoBack + canGoBack.current = event.nativeEvent.canGoBack } } const _message = function (res: WebViewMessageEvent) { diff --git a/packages/webpack-plugin/lib/runtime/components/react/types/global.d.ts b/packages/webpack-plugin/lib/runtime/components/react/types/global.d.ts index c3ce06a094..bfa839b5c5 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/types/global.d.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/types/global.d.ts @@ -23,6 +23,9 @@ declare module '@mpxjs/utils' { }, setOptions: (params: Record) => void, addListener: (eventName: string, callback: (e: Event) => void) => void + removeListener: (eventName: string, callback: (e: Event) => void) => void + dispatch: (eventName: string) => void + pageId: number } | undefined } From fb45c2891c28cd3504fd103ca0f5329c152555fe Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Sat, 28 Dec 2024 14:35:03 +0800 Subject: [PATCH 06/32] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=B8=80=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api-proxy/src/platform/api/route/index.ios.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/api-proxy/src/platform/api/route/index.ios.js b/packages/api-proxy/src/platform/api/route/index.ios.js index 406de71602..c144318760 100644 --- a/packages/api-proxy/src/platform/api/route/index.ios.js +++ b/packages/api-proxy/src/platform/api/route/index.ios.js @@ -71,8 +71,12 @@ function redirectTo (options = {}) { } } } - +let pending = false function navigateBack (options = {}) { + if (pending) { + return + } + pending = true const navigation = Object.values(global.__mpxPagesMap || {})[0]?.[1] const navigationHelper = global.__navigationHelper if (navigation && navigationHelper) { @@ -80,16 +84,19 @@ function navigateBack (options = {}) { const routeLength = navigation.getState().routes.length if (delta >= routeLength && global.__mpx?.config.rnConfig.onAppBack?.(delta - routeLength + 1)) { nextTick(() => { + pending = false const res = { errMsg: 'navigateBack:ok' } successHandle(res, options.success, options.complete) }) } else { navigation.pop(delta) navigationHelper.lastSuccessCallback = () => { + pending = false const res = { errMsg: 'navigateBack:ok' } successHandle(res, options.success, options.complete) } navigationHelper.lastFailCallback = (msg) => { + pending = false const res = { errMsg: `navigateBack:fail ${msg}` } failHandle(res, options.fail, options.complete) } From 486c65caa33cd5c691af731e0a9884730898f488 Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Sat, 28 Dec 2024 20:14:45 +0800 Subject: [PATCH 07/32] =?UTF-8?q?=E8=A1=A5=E5=85=85webview=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E5=A4=B1=E8=B4=A5=E5=85=9C=E5=BA=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/runtime/components/react/context.ts | 6 +- .../runtime/components/react/mpx-portal.tsx | 3 +- .../react/mpx-portal/portal-consumer.tsx | 9 +- .../react/mpx-portal/portal-host.tsx | 14 +-- .../react/mpx-portal/portal-manager.tsx | 11 +- .../runtime/components/react/mpx-web-view.tsx | 101 ++++++++++++++---- 6 files changed, 104 insertions(+), 40 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/context.ts b/packages/webpack-plugin/lib/runtime/components/react/context.ts index 03690e4802..9fa338adff 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/context.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/context.ts @@ -41,9 +41,9 @@ export interface PortalManagerContextValue { } export interface PortalContextValue { - mount: (children: React.ReactNode) => number| undefined - update: (key: number, children: React.ReactNode) => void - unmount: (key: number) => void + mount: (children: React.ReactNode, key?: number, pageId?: number|null) => number| undefined + update: (key: number, children: React.ReactNode, pageId?: number|null) => void + unmount: (key: number, pageId?: number|null) => void manager?: PortalManagerContextValue } diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal.tsx index 79aa6b2399..307c4b60bd 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal.tsx @@ -8,7 +8,8 @@ export type PortalProps = { * Content of the `Portal`. */ children?: React.ReactNode - manager: PortalContextValue + key?: string + manager?: PortalContextValue } const Portal = ({ children }:PortalProps): JSX.Element => { diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx index 3af2dcfda4..bb462d1183 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx @@ -1,5 +1,6 @@ import React, { useEffect, useRef } from 'react' import { PortalContextValue } from '../context' +import { getFocusedNavigation } from '@mpxjs/utils' export type PortalConsumerProps = { manager: PortalContextValue @@ -13,10 +14,12 @@ const PortalConsumer = ({ manager, children } :PortalConsumerProps): JSX.Element 'Looks like you forgot to wrap your root component with `Provider` component from `@ant-design/react-native`.\n\n' ) } - keyRef.current = manager.mount(children) - manager.update(keyRef.current, children) + const navigation = getFocusedNavigation() + const curPageId = navigation?.pageId + keyRef.current = manager.mount(children, undefined, curPageId) + manager.update(keyRef.current, children, curPageId) return () => { - manager.unmount(keyRef.current) + manager.unmount(keyRef.current, curPageId) } }, []) return null diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx index 51ff956fd7..6169ab852e 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef, useState } from 'react' +import React, { useEffect, useRef } from 'react' import { View, DeviceEventEmitter, @@ -59,10 +59,10 @@ const PortalHost = ({ children } :PortalHostProps): JSX.Element => { const _removeType = useRef(null) const manager = useRef(null) let currentPageId: number | undefined - const _mount = (children: React.ReactNode, _key?: number) => { + const _mount = (children: React.ReactNode, _key?: number, curPageId?: number) => { const navigation = getFocusedNavigation() const pageId = navigation?.pageId - if (pageId !== currentPageId) { + if (pageId !== (curPageId ?? currentPageId)) { return } const key = _key || _nextKey.current++ @@ -74,10 +74,10 @@ const PortalHost = ({ children } :PortalHostProps): JSX.Element => { return key } - const _unmount = (key: number) => { + const _unmount = (key: number, curPageId?: number) => { const navigation = getFocusedNavigation() const pageId = navigation?.pageId - if (pageId !== currentPageId) { + if (pageId !== (curPageId ?? currentPageId)) { return } if (manager.current) { @@ -87,10 +87,10 @@ const PortalHost = ({ children } :PortalHostProps): JSX.Element => { } } - const _update = (key: number, children: React.ReactNode) => { + const _update = (key: number, children: React.ReactNode, curPageId?: number) => { const navigation = getFocusedNavigation() const pageId = navigation?.pageId - if (pageId !== currentPageId) { + if (pageId !== (curPageId ?? currentPageId)) { return } if (manager.current) { diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-manager.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-manager.tsx index ce93ff2d4b..a12e4ac151 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-manager.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-manager.tsx @@ -24,14 +24,14 @@ const _PortalManager = forwardRef((props: PortalManagerProps, ref:ForwardedRef { - setState({ - portals: state.portals.map((item) => { + setState((prevState) => ({ + portals: prevState.portals.map((item) => { if (item.key === key) { return { ...item, children } } return item }) - }) + })) }, [state]) const unmount = useCallback((key: number) => { @@ -46,10 +46,7 @@ const _PortalManager = forwardRef((props: PortalManagerProps, ref:ForwardedRef { - console.log('State updated:', state.portals) - }, [state.portals]) - console.log(state.portals, '在return的state.portals') + return ( <> {state.portals.map(({ key, children }, i) => ( diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx index 9bddab5860..34d96e02af 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx @@ -1,4 +1,4 @@ -import { forwardRef, JSX, useRef, useContext, useMemo, createElement, useCallback, useEffect } from 'react' +import { forwardRef, useRef, useContext, useMemo, useState, createElement, useCallback, useEffect } from 'react' import { warn, getFocusedNavigation, isFunction } from '@mpxjs/utils' import Portal from './mpx-portal' import { getCustomEvent } from './getInnerListeners' @@ -8,7 +8,7 @@ import useNodesRef, { HandlerRef } from './useNodesRef' import { getCurrentPage, extendObject } from './utils' import { WebViewNavigationEvent, WebViewErrorEvent, WebViewMessageEvent, WebViewNavigation, WebViewProgressEvent } from 'react-native-webview/lib/WebViewTypes' import { RouteContext } from './context' -import { BackHandler } from 'react-native' +import { BackHandler, StyleSheet, View, Text } from 'react-native' type OnMessageCallbackEvent = { detail: { @@ -41,13 +41,60 @@ type MessageData = { callbackId?: number } +type LanguageCode = 'zh-CN' | 'en-US'; // 支持的语言代码 + +interface ErrorText { + text: string; + button: string; +} + +type ErrorTextMap = Record + +const styles = StyleSheet.create({ + loadErrorContext: { + display: 'flex', + alignItems: 'center' + }, + loadErrorText: { + fontSize: 12, + color: '#666666', + paddingTop: '40%', + paddingBottom: 20, + paddingLeft: '10%', + paddingRight: '10%', + textAlign: 'center' + }, + loadErrorButton: { + color: '#666666', + textAlign: 'center', + padding: 10, + borderColor: '#666666', + borderStyle: 'solid', + borderWidth: StyleSheet.hairlineWidth, + borderRadius: 10 + } +}) + const _WebView = forwardRef, WebViewProps>((props, ref): JSX.Element | null => { const { src, bindmessage, bindload, binderror } = props const mpx = global.__mpx + const errorText: ErrorTextMap = { + 'zh-CN': { + text: '网络不可用,请检查网络设置', + button: '重新加载' + }, + 'en-US': { + text: 'The network is not available. Please check the network settings', + button: 'Reload' + } + } + const currentErrorText = errorText[(mpx.i18n.locale as LanguageCode) || 'zh-CN'] + if (props.style) { warn('The web-view component does not support the style prop.') } const pageId = useContext(RouteContext) + const [pageLoadErr, setPageLoadErr] = useState(false) const currentPage = useMemo(() => getCurrentPage(pageId), [pageId]) const webViewRef = useRef(null) const defaultWebViewStyle = { @@ -107,6 +154,7 @@ const _WebView = forwardRef, WebViewProps>((pr bindload(result) } const _error = function (res: WebViewErrorEvent) { + setPageLoadErr(true) const result = { type: 'error', timeStamp: res.timeStamp, @@ -114,7 +162,11 @@ const _WebView = forwardRef, WebViewProps>((pr src: '' } } - binderror(result) + binderror && binderror(result) + } + + const _reload = function () { + setPageLoadErr(false) } const injectedJavaScript = ` if (window.ReactNativeWebView && window.ReactNativeWebView.postMessage) { @@ -250,23 +302,34 @@ const _WebView = forwardRef, WebViewProps>((pr onLoad: _load }) } - if (binderror) { - extendObject(events, { - onError: _error - }) - } - return createElement(Portal, null, createElement(WebView, extendObject({ - style: defaultWebViewStyle, - source: { uri: src }, - ref: webViewRef, - javaScriptEnabled: true, - onNavigationStateChange: _changeUrl, - onMessage: _message, - injectedJavaScript: injectedJavaScript, - onLoadProgress: _onLoadProgress, - allowsBackForwardNavigationGestures: true - }, events))) + extendObject(events, { + onError: _error + }) + + return ( + + {pageLoadErr + ? ( + + {currentErrorText.text} + {currentErrorText.button} + + ) + : ()} + + ) }) _WebView.displayName = 'MpxWebview' From 83e264c3fa1918d6ee4eb46d1d17096a2f055b74 Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Sun, 29 Dec 2024 16:16:09 +0800 Subject: [PATCH 08/32] =?UTF-8?q?=E8=A1=A5=E5=85=85picker=E5=85=9C?= =?UTF-8?q?=E5=BA=95=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/react/locale-provider.tsx | 69 +++++++++++++++++-- .../components/react/mpx-picker/time.tsx | 3 +- .../react/mpx-portal/portal-consumer.tsx | 4 +- 3 files changed, 66 insertions(+), 10 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/locale-provider.tsx b/packages/webpack-plugin/lib/runtime/components/react/locale-provider.tsx index 630288fe20..ea1ab07326 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/locale-provider.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/locale-provider.tsx @@ -1,11 +1,64 @@ -import React, { createContext } from 'react' +import React, { createContext, useMemo, memo } from 'react' +import { extendObject } from './utils' +interface Locale { + /** zh_CN */ + locale: string + DatePicker: { + /** 确定 */ + okText: string + /** 取消 */ + dismissText: string + /** 请选择 */ + extra: string + DatePickerLocale: { + /** 年 */ + year: string + /** 月 */ + month: string + /** 日 */ + day: string + /** 时 */ + hour: string + /** 分 */ + minute: string + /** 上午 */ + am: string + /** 下午 */ + pm: string + } + } + DatePickerView: { + /** 年 */ + year: string + /** 月 */ + month: string + /** 日 */ + day: string + /** 时 */ + hour: string + /** 分 */ + minute: string + /** 上午 */ + am: string + /** 下午 */ + pm: string + } + Picker: { + /** 确定 */ + okText: string + /** 取消 */ + dismissText: string + /** 请选择 */ + extra: string + } +} export type LocaleContextProps = { - antLocale?: any + antLocale: Partial } export interface LocaleProviderProps { children?: React.ReactNode, - locale: any + locale?: LocaleContextProps } export const LocaleContext = createContext< @@ -13,9 +66,11 @@ export const LocaleContext = createContext< >(undefined) const LocaleProvider = (props :LocaleProviderProps): JSX.Element => { - const locale = React.useMemo(() => { - return { antLocale: { exist: true } } - }, []) + const locale = useMemo(() => { + return { + antLocale: extendObject({}, props.locale, { exist: true }) + } + }, [props.locale]) return ( {props.children} @@ -25,4 +80,4 @@ const LocaleProvider = (props :LocaleProviderProps): JSX.Element => { LocaleProvider.displayName = 'LocaleProvider' -export default React.memo(LocaleProvider) +export default memo(LocaleProvider) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker/time.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker/time.tsx index 8d2bd04188..1780aa3894 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker/time.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker/time.tsx @@ -1,5 +1,6 @@ import { View, Text, Modal, TouchableWithoutFeedback } from 'react-native' -import { PickerView, Portal } from '@ant-design/react-native' +import Portal from '../mpx-portal' +import { PickerView } from '@ant-design/react-native' import React, { forwardRef, useState, useRef, useEffect } from 'react' import useNodesRef, { HandlerRef } from '../useNodesRef' // 引入辅助函数 import { TimeProps } from './type' diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx index bb462d1183..7f72d9240f 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx @@ -11,7 +11,7 @@ const PortalConsumer = ({ manager, children } :PortalConsumerProps): JSX.Element useEffect(() => { if (!manager) { throw new Error( - 'Looks like you forgot to wrap your root component with `Provider` component from `@ant-design/react-native`.\n\n' + 'Looks like you forgot to wrap your root component with `Provider` component from `@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal`.\n\n' ) } const navigation = getFocusedNavigation() @@ -21,7 +21,7 @@ const PortalConsumer = ({ manager, children } :PortalConsumerProps): JSX.Element return () => { manager.unmount(keyRef.current, curPageId) } - }, []) + }, [children]) return null } From 2437c1cd0eb90c369673d12dd9e76cc8fa8e185e Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Mon, 30 Dec 2024 11:16:45 +0800 Subject: [PATCH 09/32] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=89=80=E6=9C=89?= =?UTF-8?q?=E8=BF=9E=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/platform/api/route/index.ios.js | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/packages/api-proxy/src/platform/api/route/index.ios.js b/packages/api-proxy/src/platform/api/route/index.ios.js index c144318760..bef60e9733 100644 --- a/packages/api-proxy/src/platform/api/route/index.ios.js +++ b/packages/api-proxy/src/platform/api/route/index.ios.js @@ -34,7 +34,12 @@ function resolvePath (relative, base) { return stack.join('/') } +let toPending = false function navigateTo (options = {}) { + if (toPending) { + return + } + toPending = true const navigation = Object.values(global.__mpxPagesMap || {})[0]?.[1] const navigationHelper = global.__navigationHelper if (navigation && navigationHelper) { @@ -43,17 +48,23 @@ function navigateTo (options = {}) { const finalPath = resolvePath(path, basePath).slice(1) navigation.push(finalPath, queryObj) navigationHelper.lastSuccessCallback = () => { + toPending = false const res = { errMsg: 'navigateTo:ok' } successHandle(res, options.success, options.complete) } navigationHelper.lastFailCallback = (msg) => { + toPending = false const res = { errMsg: `navigateTo:fail ${msg}` } failHandle(res, options.fail, options.complete) } } } - +let redirectPending = false function redirectTo (options = {}) { + if (redirectPending) { + return + } + redirectPending = true const navigation = Object.values(global.__mpxPagesMap || {})[0]?.[1] const navigationHelper = global.__navigationHelper if (navigation && navigationHelper) { @@ -62,21 +73,23 @@ function redirectTo (options = {}) { const finalPath = resolvePath(path, basePath).slice(1) navigation.replace(finalPath, queryObj) navigationHelper.lastSuccessCallback = () => { + redirectPending = false const res = { errMsg: 'redirectTo:ok' } successHandle(res, options.success, options.complete) } navigationHelper.lastFailCallback = (msg) => { + redirectPending = false const res = { errMsg: `redirectTo:fail ${msg}` } failHandle(res, options.fail, options.complete) } } } -let pending = false +let backPending = false function navigateBack (options = {}) { - if (pending) { + if (backPending) { return } - pending = true + backPending = true const navigation = Object.values(global.__mpxPagesMap || {})[0]?.[1] const navigationHelper = global.__navigationHelper if (navigation && navigationHelper) { @@ -84,27 +97,31 @@ function navigateBack (options = {}) { const routeLength = navigation.getState().routes.length if (delta >= routeLength && global.__mpx?.config.rnConfig.onAppBack?.(delta - routeLength + 1)) { nextTick(() => { - pending = false + backPending = false const res = { errMsg: 'navigateBack:ok' } successHandle(res, options.success, options.complete) }) } else { navigation.pop(delta) navigationHelper.lastSuccessCallback = () => { - pending = false + backPending = false const res = { errMsg: 'navigateBack:ok' } successHandle(res, options.success, options.complete) } navigationHelper.lastFailCallback = (msg) => { - pending = false + backPending = false const res = { errMsg: `navigateBack:fail ${msg}` } failHandle(res, options.fail, options.complete) } } } } - +let reLaunchPending = false function reLaunch (options = {}) { + if (reLaunchPending) { + return + } + reLaunchPending = true const navigation = Object.values(global.__mpxPagesMap || {})[0]?.[1] const navigationHelper = global.__navigationHelper if (navigation && navigationHelper) { @@ -121,10 +138,12 @@ function reLaunch (options = {}) { ] }) navigationHelper.lastSuccessCallback = () => { + reLaunchPending = false const res = { errMsg: 'redirectTo:ok' } successHandle(res, options.success, options.complete) } navigationHelper.lastFailCallback = (msg) => { + reLaunchPending = false const res = { errMsg: `redirectTo:fail ${msg}` } failHandle(res, options.fail, options.complete) } From 971d5dbd7904ae44f4114229d589aa1ef857af38 Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Mon, 30 Dec 2024 11:46:33 +0800 Subject: [PATCH 10/32] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=BC=96=E5=86=99?= =?UTF-8?q?=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/webpack-plugin/lib/react/processScript.js | 1 - .../runtime/components/react/locale-provider.tsx | 4 ++-- .../lib/runtime/components/react/mpx-portal.tsx | 4 ++-- .../react/mpx-portal/portal-consumer.tsx | 4 ++-- .../components/react/mpx-portal/portal-host.tsx | 14 +++++++------- .../components/react/mpx-portal/portal-manager.tsx | 9 ++++----- .../lib/runtime/components/react/mpx-provider.tsx | 9 ++++----- 7 files changed, 21 insertions(+), 24 deletions(-) diff --git a/packages/webpack-plugin/lib/react/processScript.js b/packages/webpack-plugin/lib/react/processScript.js index f68bd6079f..bcea57b65b 100644 --- a/packages/webpack-plugin/lib/react/processScript.js +++ b/packages/webpack-plugin/lib/react/processScript.js @@ -26,7 +26,6 @@ module.exports = function (script, { import { getComponent } from ${stringifyRequest(loaderContext, optionProcessorPath)} import { NavigationContainer, StackActions } from '@react-navigation/native' import { createStackNavigator } from '@react-navigation/stack' -// import Provider from '@ant-design/react-native' import Provider from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-provider' import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context' import { GestureHandlerRootView } from 'react-native-gesture-handler' diff --git a/packages/webpack-plugin/lib/runtime/components/react/locale-provider.tsx b/packages/webpack-plugin/lib/runtime/components/react/locale-provider.tsx index ea1ab07326..5866936bf2 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/locale-provider.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/locale-provider.tsx @@ -1,4 +1,4 @@ -import React, { createContext, useMemo, memo } from 'react' +import { createContext, useMemo, memo, ReactNode } from 'react' import { extendObject } from './utils' interface Locale { @@ -57,7 +57,7 @@ export type LocaleContextProps = { antLocale: Partial } export interface LocaleProviderProps { - children?: React.ReactNode, + children?: ReactNode, locale?: LocaleContextProps } diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal.tsx index 307c4b60bd..89312d0773 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import { ReactNode } from 'react' import { PortalContext, PortalContextValue } from './context' import PortalConsumer from './mpx-portal/portal-consumer' import PortalHost, { portal } from './mpx-portal/portal-host' @@ -7,7 +7,7 @@ export type PortalProps = { /** * Content of the `Portal`. */ - children?: React.ReactNode + children?: ReactNode key?: string manager?: PortalContextValue } diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx index 7f72d9240f..c7f1a000cb 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx @@ -1,10 +1,10 @@ -import React, { useEffect, useRef } from 'react' +import { useEffect, useRef, ReactNode } from 'react' import { PortalContextValue } from '../context' import { getFocusedNavigation } from '@mpxjs/utils' export type PortalConsumerProps = { manager: PortalContextValue - children?: React.ReactNode + children?: ReactNode } const PortalConsumer = ({ manager, children } :PortalConsumerProps): JSX.Element | null => { const keyRef = useRef(null) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx index 6169ab852e..ec807578a4 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef } from 'react' +import { useEffect, useRef, ReactNode } from 'react' import { View, DeviceEventEmitter, @@ -11,7 +11,7 @@ import { getFocusedNavigation } from '@mpxjs/utils' import { PortalManagerContextValue, PortalContext } from '../context' export type PortalHostProps = { - children: React.ReactNode + children: ReactNode } type addIdsMapsType = { @@ -19,8 +19,8 @@ type addIdsMapsType = { } export type Operation = - | { type: 'mount'; key: number; children: React.ReactNode } - | { type: 'update'; key: number; children: React.ReactNode } + | { type: 'mount'; key: number; children: ReactNode } + | { type: 'update'; key: number; children: ReactNode } | { type: 'unmount'; key: number } // events @@ -37,7 +37,7 @@ const styles = StyleSheet.create({ class PortalGuard { private nextKey = 10000 - add = (e: React.ReactNode) => { + add = (e: ReactNode) => { const key = this.nextKey++ TopViewEventEmitter.emit(addType, e, key) return key @@ -59,7 +59,7 @@ const PortalHost = ({ children } :PortalHostProps): JSX.Element => { const _removeType = useRef(null) const manager = useRef(null) let currentPageId: number | undefined - const _mount = (children: React.ReactNode, _key?: number, curPageId?: number) => { + const _mount = (children: ReactNode, _key?: number, curPageId?: number) => { const navigation = getFocusedNavigation() const pageId = navigation?.pageId if (pageId !== (curPageId ?? currentPageId)) { @@ -87,7 +87,7 @@ const PortalHost = ({ children } :PortalHostProps): JSX.Element => { } } - const _update = (key: number, children: React.ReactNode, curPageId?: number) => { + const _update = (key: number, children: ReactNode, curPageId?: number) => { const navigation = getFocusedNavigation() const pageId = navigation?.pageId if (pageId !== (curPageId ?? currentPageId)) { diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-manager.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-manager.tsx index a12e4ac151..fd2f2b1b12 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-manager.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-manager.tsx @@ -1,11 +1,10 @@ -import React, { useState, useCallback, forwardRef, ForwardedRef, useImperativeHandle, useEffect, useContext, ReactElement } from 'react' +import { useState, useCallback, forwardRef, ForwardedRef, useImperativeHandle, ReactNode, ReactElement } from 'react' import { View, StyleSheet } from 'react-native' -import { getFocusedNavigation } from '@mpxjs/utils' export type State = { portals: Array<{ key: number - children: React.ReactNode + children: ReactNode }> } @@ -17,13 +16,13 @@ const _PortalManager = forwardRef((props: PortalManagerProps, ref:ForwardedRef { + const mount = useCallback((key: number, children: ReactNode) => { setState((prevState) => ({ portals: [...prevState.portals, { key, children }] })) }, [state]) - const update = useCallback((key: number, children: React.ReactNode) => { + const update = useCallback((key: number, children: ReactNode) => { setState((prevState) => ({ portals: prevState.portals.map((item) => { if (item.key === key) { diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-provider.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-provider.tsx index ea8febe3b2..8b66f21063 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-provider.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-provider.tsx @@ -1,15 +1,14 @@ -import React from 'react' +import { ReactNode, createContext, useMemo } from 'react' import LocaleProvider, { LocaleContextProps } from './locale-provider' import Portal from './mpx-portal' import { extendObject } from './utils' -// import { Theme, ThemeProvider } from '../style' export type Theme = typeof defaultTheme & { [key: string]: any } export interface ProviderProps { locale?: LocaleContextProps theme?: Partial - children: React.ReactNode + children: ReactNode } const defaultTheme = { color_text_base: '#000000', // 基本 @@ -24,7 +23,7 @@ const defaultTheme = { color_success: '#52c41a', color_primary: '#1677ff' } -export const ThemeContext = React.createContext(defaultTheme) +export const ThemeContext = createContext(defaultTheme) export type PartialTheme = Partial @@ -35,7 +34,7 @@ export interface ThemeProviderProps { const ThemeProvider = (props: ThemeProviderProps) => { const { value, children } = props - const theme = React.useMemo(() => (extendObject({}, defaultTheme, value)), [value]) + const theme = useMemo(() => (extendObject({}, defaultTheme, value)), [value]) return {children} } From 5c03178172c022ae69923ad6e61ad918456c0e2a Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Mon, 30 Dec 2024 11:55:37 +0800 Subject: [PATCH 11/32] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E5=90=88=E5=B9=B6?= =?UTF-8?q?=E5=86=B2=E7=AA=81=E4=B8=A2=E5=A4=B1=E7=9A=84=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/runtime/components/react/context.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/context.ts b/packages/webpack-plugin/lib/runtime/components/react/context.ts index 9fa338adff..f7d2231fb3 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/context.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/context.ts @@ -32,7 +32,6 @@ export interface IntersectionObserver { throttleMeasure: () => void } } - export interface PortalManagerContextValue { mount: (key: number, children: React.ReactNode) => void update: (key: number, children: React.ReactNode) => void @@ -47,6 +46,10 @@ export interface PortalContextValue { manager?: PortalManagerContextValue } +export interface ScrollViewContextValue { + gestureRef: React.RefObject | null +} + export const MovableAreaContext = createContext({ width: 0, height: 0 }) export const FormContext = createContext(null) @@ -65,8 +68,12 @@ export const IntersectionObserverContext = createContext(null) +export const SwiperContext = createContext({}) + export const KeyboardAvoidContext = createContext(null) +export const ScrollViewContext = createContext({ gestureRef: null }) + export const PortalContext = createContext(null as any) export const PortalManagerContext = createContext(null) From fbacf3fb881494408004663dac51a2258ab025a0 Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Mon, 30 Dec 2024 16:53:34 +0800 Subject: [PATCH 12/32] =?UTF-8?q?=E4=BF=AE=E6=AD=A3consumer=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E5=A4=84=E7=90=86=E6=9C=89=E9=97=AE=E9=A2=98=E7=9A=84?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/react/mpx-portal/portal-consumer.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx index c7f1a000cb..5ab6ecea27 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx @@ -8,6 +8,11 @@ export type PortalConsumerProps = { } const PortalConsumer = ({ manager, children } :PortalConsumerProps): JSX.Element | null => { const keyRef = useRef(null) + useEffect(() => { + const navigation = getFocusedNavigation() + const curPageId = navigation?.pageId + manager.update(keyRef.current, children, curPageId) + }, [children]) useEffect(() => { if (!manager) { throw new Error( @@ -17,11 +22,10 @@ const PortalConsumer = ({ manager, children } :PortalConsumerProps): JSX.Element const navigation = getFocusedNavigation() const curPageId = navigation?.pageId keyRef.current = manager.mount(children, undefined, curPageId) - manager.update(keyRef.current, children, curPageId) return () => { manager.unmount(keyRef.current, curPageId) } - }, [children]) + }, []) return null } From abd29dedb24e8a948089adb5ac17e2d501b831d8 Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Mon, 30 Dec 2024 21:39:05 +0800 Subject: [PATCH 13/32] =?UTF-8?q?=E4=BF=AE=E6=94=B9router=E8=B7=B3?= =?UTF-8?q?=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/platform/api/route/index.ios.js | 20 +++++++++++-------- .../platform/patch/getDefaultOptions.ios.js | 7 +++++++ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/packages/api-proxy/src/platform/api/route/index.ios.js b/packages/api-proxy/src/platform/api/route/index.ios.js index bef60e9733..2288feeb3c 100644 --- a/packages/api-proxy/src/platform/api/route/index.ios.js +++ b/packages/api-proxy/src/platform/api/route/index.ios.js @@ -48,15 +48,16 @@ function navigateTo (options = {}) { const finalPath = resolvePath(path, basePath).slice(1) navigation.push(finalPath, queryObj) navigationHelper.lastSuccessCallback = () => { - toPending = false const res = { errMsg: 'navigateTo:ok' } successHandle(res, options.success, options.complete) } navigationHelper.lastFailCallback = (msg) => { - toPending = false const res = { errMsg: `navigateTo:fail ${msg}` } failHandle(res, options.fail, options.complete) } + navigationHelper.transitionEndCallback = () => { + toPending = false + } } } let redirectPending = false @@ -73,15 +74,16 @@ function redirectTo (options = {}) { const finalPath = resolvePath(path, basePath).slice(1) navigation.replace(finalPath, queryObj) navigationHelper.lastSuccessCallback = () => { - redirectPending = false const res = { errMsg: 'redirectTo:ok' } successHandle(res, options.success, options.complete) } navigationHelper.lastFailCallback = (msg) => { - redirectPending = false const res = { errMsg: `redirectTo:fail ${msg}` } failHandle(res, options.fail, options.complete) } + navigationHelper.transitionEndCallback = () => { + redirectPending = false + } } } let backPending = false @@ -104,15 +106,16 @@ function navigateBack (options = {}) { } else { navigation.pop(delta) navigationHelper.lastSuccessCallback = () => { - backPending = false const res = { errMsg: 'navigateBack:ok' } successHandle(res, options.success, options.complete) } navigationHelper.lastFailCallback = (msg) => { - backPending = false const res = { errMsg: `navigateBack:fail ${msg}` } failHandle(res, options.fail, options.complete) } + navigationHelper.transitionEndCallback = () => { + backPending = false + } } } } @@ -138,15 +141,16 @@ function reLaunch (options = {}) { ] }) navigationHelper.lastSuccessCallback = () => { - reLaunchPending = false const res = { errMsg: 'redirectTo:ok' } successHandle(res, options.success, options.complete) } navigationHelper.lastFailCallback = (msg) => { - reLaunchPending = false const res = { errMsg: `redirectTo:fail ${msg}` } failHandle(res, options.fail, options.complete) } + navigationHelper.transitionEndCallback = () => { + reLaunchPending = false + } } } diff --git a/packages/core/src/platform/patch/getDefaultOptions.ios.js b/packages/core/src/platform/patch/getDefaultOptions.ios.js index 1f44e346f6..5d447fab34 100644 --- a/packages/core/src/platform/patch/getDefaultOptions.ios.js +++ b/packages/core/src/platform/patch/getDefaultOptions.ios.js @@ -367,6 +367,12 @@ function usePageStatus (navigation, pageId) { const blurSubscription = navigation.addListener('blur', () => { pageStatusMap[pageId] = 'hide' }) + const transitionEndSubscription = navigation.addListener('transitionEnd', () => { + if (global.__navigationHelper.transitionEndCallback) { + global.__navigationHelper.transitionEndCallback() + global.__navigationHelper.transitionEndCallback = null + } + }) const unWatchAppFocusedState = watch(global.__mpxAppFocusedState, (value) => { pageStatusMap[pageId] = value }) @@ -375,6 +381,7 @@ function usePageStatus (navigation, pageId) { focusSubscription() blurSubscription() unWatchAppFocusedState() + transitionEndSubscription() del(pageStatusMap, pageId) } }, [navigation]) From 9596940baeaf519b136e4810f93d23eee50710b8 Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Mon, 30 Dec 2024 23:20:18 +0800 Subject: [PATCH 14/32] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E8=BF=94=E5=9B=9E?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api-proxy/src/platform/api/route/index.ios.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/api-proxy/src/platform/api/route/index.ios.js b/packages/api-proxy/src/platform/api/route/index.ios.js index 2288feeb3c..d500753d30 100644 --- a/packages/api-proxy/src/platform/api/route/index.ios.js +++ b/packages/api-proxy/src/platform/api/route/index.ios.js @@ -113,9 +113,9 @@ function navigateBack (options = {}) { const res = { errMsg: `navigateBack:fail ${msg}` } failHandle(res, options.fail, options.complete) } - navigationHelper.transitionEndCallback = () => { - backPending = false - } + } + navigationHelper.transitionEndCallback = () => { + backPending = false } } } From 1ecbb575d35532ef68efbd5774abc12cd0cb784a Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Tue, 31 Dec 2024 18:35:44 +0800 Subject: [PATCH 15/32] =?UTF-8?q?=E8=A1=A5=E5=85=85getWindowInfo=E5=A3=B0?= =?UTF-8?q?=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api-proxy/@types/index.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/api-proxy/@types/index.d.ts b/packages/api-proxy/@types/index.d.ts index 733c2cb306..9b51d43ccc 100644 --- a/packages/api-proxy/@types/index.d.ts +++ b/packages/api-proxy/@types/index.d.ts @@ -98,6 +98,7 @@ export const clearStorage: WechatMiniprogram.Wx['clearStorage'] export const clearStorageSync: WechatMiniprogram.Wx['clearStorageSync'] export const getSystemInfo: WechatMiniprogram.Wx['getSystemInfo'] export const getSystemInfoSync: WechatMiniprogram.Wx['getSystemInfoSync'] +export const getWindowInfo: WechatMiniprogram.Wx['getWindowInfo'] export const setTabBarItem: WechatMiniprogram.Wx['setTabBarItem'] export const setTabBarStyle: WechatMiniprogram.Wx['setTabBarStyle'] export const showTabBar: WechatMiniprogram.Wx['showTabBar'] From 5ba850bb56b2bab62e8e881c20011f3f5d321877 Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Thu, 2 Jan 2025 11:13:46 +0800 Subject: [PATCH 16/32] =?UTF-8?q?=E6=8B=89webview=20addlistener=E9=BD=90?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E5=86=99=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/runtime/components/react/mpx-web-view.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx index 34d96e02af..d99905a706 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx @@ -29,6 +29,7 @@ interface WebViewProps { binderror?: (event: CommonCallbackEvent) => void [x: string]: any } +type Listener = (type: string, callback: (e: Event) => void) => () => void interface PayloadData { [x: string]: any @@ -123,14 +124,18 @@ const _WebView = forwardRef, WebViewProps>((pr const navigation = getFocusedNavigation() - navigation?.addListener('beforeRemove', beforeRemoveHandle) - useEffect(() => { if (__mpx_mode__ === 'android') { BackHandler.addEventListener('hardwareBackPress', onAndroidBackPress) - return () => { + } + const addListener: Listener = navigation?.addListener.bind(navigation) + const beforeRemoveSubscription = addListener?.('beforeRemove', beforeRemoveHandle) + return () => { + if (__mpx_mode__ === 'android') { BackHandler.removeEventListener('hardwareBackPress', onAndroidBackPress) - navigation?.removeListener('beforeRemove', beforeRemoveHandle) + } + if (isFunction(beforeRemoveSubscription)) { + beforeRemoveSubscription() } } }, []) From 23bd943768f4798449978e3dd285c47dc3db6c99 Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Thu, 2 Jan 2025 19:24:12 +0800 Subject: [PATCH 17/32] =?UTF-8?q?=E8=A7=A3=E5=86=B3actionsheet=E5=9B=9E?= =?UTF-8?q?=E8=B0=83=E8=B7=B3=E8=BD=AC=E7=9A=84=E6=83=85=E5=86=B5actionact?= =?UTF-8?q?ion=E6=9C=AA=E8=A2=AB=E5=88=A0=E9=99=A4=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/runtime/components/react/mpx-portal/portal-host.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx index ec807578a4..26b173f0b7 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx @@ -77,7 +77,7 @@ const PortalHost = ({ children } :PortalHostProps): JSX.Element => { const _unmount = (key: number, curPageId?: number) => { const navigation = getFocusedNavigation() const pageId = navigation?.pageId - if (pageId !== (curPageId ?? currentPageId)) { + if (pageId && pageId !== (curPageId ?? currentPageId)) { // 过滤掉获取不到pageid的情况,这样避免删不掉的情况 return } if (manager.current) { From 0a68b71962aac2dfc4ea371463bf25b85147cbac Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Thu, 2 Jan 2025 19:35:06 +0800 Subject: [PATCH 18/32] =?UTF-8?q?=E5=B9=B2=E6=8E=89=E5=85=9C=E5=BA=95?= =?UTF-8?q?=E6=B8=B2=E6=9F=93=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../react/mpx-portal/portal-host.tsx | 36 ++----------------- 1 file changed, 3 insertions(+), 33 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx index 26b173f0b7..5517787c14 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx @@ -68,8 +68,6 @@ const PortalHost = ({ children } :PortalHostProps): JSX.Element => { const key = _key || _nextKey.current++ if (manager.current) { manager.current.mount(key, children) - } else { - _queue.current.push({ type: 'mount', key, children }) } return key } @@ -82,8 +80,6 @@ const PortalHost = ({ children } :PortalHostProps): JSX.Element => { } if (manager.current) { manager.current.unmount(key) - } else { - _queue.current.push({ type: 'unmount', key }) } } @@ -95,17 +91,6 @@ const PortalHost = ({ children } :PortalHostProps): JSX.Element => { } if (manager.current) { manager.current.update(key, children) - } else { - const op: Operation = { type: 'mount', key, children } - const index = _queue.current.findIndex( - (o) => o.type === 'mount' || (o.type === 'update' && o.key === key) - ) - - if (index > -1) { - _queue.current[index] = op - } else { - _queue.current.push(op) - } } } @@ -117,25 +102,10 @@ const PortalHost = ({ children } :PortalHostProps): JSX.Element => { removeType, _unmount ) + return () => { - while (_queue.current.length && manager.current) { - const action = _queue.current.pop() - if (!action) { - continue - } - // tslint:disable-next-line:switch-default - switch (action.type) { - case 'mount': - manager.current?.mount(action.key, action.children) - break - case 'update': - manager.current?.update(action.key, action.children) - break - case 'unmount': - manager.current?.unmount(action.key) - break - } - } + _addType.current?.remove() + _removeType.current?.remove() } }, []) return ( From 896ca0081b600b3573e7ffbafe380dd5021c5eb3 Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Thu, 2 Jan 2025 21:02:56 +0800 Subject: [PATCH 19/32] =?UTF-8?q?fix=E8=B7=AF=E7=94=B1=E8=B7=B3=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/platform/api/route/index.ios.js | 37 +++++++++++++------ .../platform/patch/getDefaultOptions.ios.js | 8 +++- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/packages/api-proxy/src/platform/api/route/index.ios.js b/packages/api-proxy/src/platform/api/route/index.ios.js index d500753d30..72d1fdf974 100644 --- a/packages/api-proxy/src/platform/api/route/index.ios.js +++ b/packages/api-proxy/src/platform/api/route/index.ios.js @@ -35,13 +35,25 @@ function resolvePath (relative, base) { } let toPending = false +let redirectPending = false +let backPending = false +let reLaunchPending = false + +const navigationHelper = global.__navigationHelper +const transitionEndCallback = function (callback) { + if (!navigationHelper.transitionEndCallback) { + navigationHelper.transitionEndCallback = [] + } + navigationHelper.transitionEndCallback.push(callback) +} + function navigateTo (options = {}) { if (toPending) { return } toPending = true - const navigation = Object.values(global.__mpxPagesMap || {})[0]?.[1] const navigationHelper = global.__navigationHelper + const navigation = Object.values(global.__mpxPagesMap || {})[0]?.[1] if (navigation && navigationHelper) { const { path, queryObj } = parseUrl(options.url) const basePath = getBasePath(navigation) @@ -52,15 +64,15 @@ function navigateTo (options = {}) { successHandle(res, options.success, options.complete) } navigationHelper.lastFailCallback = (msg) => { + toPending = false const res = { errMsg: `navigateTo:fail ${msg}` } failHandle(res, options.fail, options.complete) } - navigationHelper.transitionEndCallback = () => { + transitionEndCallback(() => { toPending = false - } + }) } } -let redirectPending = false function redirectTo (options = {}) { if (redirectPending) { return @@ -78,15 +90,15 @@ function redirectTo (options = {}) { successHandle(res, options.success, options.complete) } navigationHelper.lastFailCallback = (msg) => { + redirectPending = false const res = { errMsg: `redirectTo:fail ${msg}` } failHandle(res, options.fail, options.complete) } - navigationHelper.transitionEndCallback = () => { + transitionEndCallback(() => { redirectPending = false - } + }) } } -let backPending = false function navigateBack (options = {}) { if (backPending) { return @@ -110,16 +122,16 @@ function navigateBack (options = {}) { successHandle(res, options.success, options.complete) } navigationHelper.lastFailCallback = (msg) => { + backPending = false const res = { errMsg: `navigateBack:fail ${msg}` } failHandle(res, options.fail, options.complete) } } - navigationHelper.transitionEndCallback = () => { + transitionEndCallback(() => { backPending = false - } + }) } } -let reLaunchPending = false function reLaunch (options = {}) { if (reLaunchPending) { return @@ -145,12 +157,13 @@ function reLaunch (options = {}) { successHandle(res, options.success, options.complete) } navigationHelper.lastFailCallback = (msg) => { + reLaunchPending = false const res = { errMsg: `redirectTo:fail ${msg}` } failHandle(res, options.fail, options.complete) } - navigationHelper.transitionEndCallback = () => { + transitionEndCallback(() => { reLaunchPending = false - } + }) } } diff --git a/packages/core/src/platform/patch/getDefaultOptions.ios.js b/packages/core/src/platform/patch/getDefaultOptions.ios.js index 5d447fab34..39c2f6521c 100644 --- a/packages/core/src/platform/patch/getDefaultOptions.ios.js +++ b/packages/core/src/platform/patch/getDefaultOptions.ios.js @@ -368,8 +368,12 @@ function usePageStatus (navigation, pageId) { pageStatusMap[pageId] = 'hide' }) const transitionEndSubscription = navigation.addListener('transitionEnd', () => { - if (global.__navigationHelper.transitionEndCallback) { - global.__navigationHelper.transitionEndCallback() + if (global.__navigationHelper.transitionEndCallback?.length) { + global.__navigationHelper.transitionEndCallback.forEach((callback) => { + if (isFunction(callback)) { + callback() + } + }) global.__navigationHelper.transitionEndCallback = null } }) From 366c837c59a6bb6ff7bd06c21d4d53ee401a0ed8 Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Mon, 6 Jan 2025 10:43:05 +0800 Subject: [PATCH 20/32] =?UTF-8?q?=E4=BF=AE=E6=94=B9navigation=E8=B0=83?= =?UTF-8?q?=E7=94=A8=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/runtime/components/react/mpx-web-view.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx index d99905a706..4edb61c9f3 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx @@ -1,11 +1,12 @@ -import { forwardRef, useRef, useContext, useMemo, useState, createElement, useCallback, useEffect } from 'react' -import { warn, getFocusedNavigation, isFunction } from '@mpxjs/utils' +import { forwardRef, useRef, useContext, useMemo, useState, useCallback, useEffect } from 'react' +import { warn, isFunction } from '@mpxjs/utils' import Portal from './mpx-portal' import { getCustomEvent } from './getInnerListeners' import { promisify, redirectTo, navigateTo, navigateBack, reLaunch, switchTab } from '@mpxjs/api-proxy' import { WebView } from 'react-native-webview' import useNodesRef, { HandlerRef } from './useNodesRef' import { getCurrentPage, extendObject } from './utils' +import { useNavigation } from '@react-navigation/native' import { WebViewNavigationEvent, WebViewErrorEvent, WebViewMessageEvent, WebViewNavigation, WebViewProgressEvent } from 'react-native-webview/lib/WebViewTypes' import { RouteContext } from './context' import { BackHandler, StyleSheet, View, Text } from 'react-native' @@ -122,7 +123,7 @@ const _WebView = forwardRef, WebViewProps>((pr } }, [canGoBack]) - const navigation = getFocusedNavigation() + const navigation = useNavigation() useEffect(() => { if (__mpx_mode__ === 'android') { From 75872f949ecc3c8a2a0f3c3505fc240c6f4f369e Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Tue, 7 Jan 2025 20:38:42 +0800 Subject: [PATCH 21/32] =?UTF-8?q?=E4=BF=AE=E6=94=B9toast=E4=BD=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api-proxy/src/platform/api/toast/rnToast.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/api-proxy/src/platform/api/toast/rnToast.jsx b/packages/api-proxy/src/platform/api/toast/rnToast.jsx index 368ba60c3a..ba5dcbe624 100644 --- a/packages/api-proxy/src/platform/api/toast/rnToast.jsx +++ b/packages/api-proxy/src/platform/api/toast/rnToast.jsx @@ -6,7 +6,7 @@ let toastKey let isLoadingShow const dimensionsScreen = Dimensions.get('screen') const screenHeight = dimensionsScreen.height -const contentTop = parseInt(screenHeight * 0.35) +const contentTop = parseInt(screenHeight * 0.4) let tId // show duration 计时id const styles = StyleSheet.create({ toastContent: { From 1fe518d551958ba5dd4746305087df5ddf6bd26e Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Wed, 8 Jan 2025 15:23:06 +0800 Subject: [PATCH 22/32] =?UTF-8?q?fix=20toast=E4=B8=8D=E6=B6=88=E5=A4=B1?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/runtime/components/react/mpx-portal/portal-host.tsx | 5 ----- .../lib/runtime/components/react/types/global.d.ts | 4 ++++ 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx index 5517787c14..46b0681f02 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx @@ -73,11 +73,6 @@ const PortalHost = ({ children } :PortalHostProps): JSX.Element => { } const _unmount = (key: number, curPageId?: number) => { - const navigation = getFocusedNavigation() - const pageId = navigation?.pageId - if (pageId && pageId !== (curPageId ?? currentPageId)) { // 过滤掉获取不到pageid的情况,这样避免删不掉的情况 - return - } if (manager.current) { manager.current.unmount(key) } diff --git a/packages/webpack-plugin/lib/runtime/components/react/types/global.d.ts b/packages/webpack-plugin/lib/runtime/components/react/types/global.d.ts index bfa839b5c5..d9502c9b9d 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/types/global.d.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/types/global.d.ts @@ -32,3 +32,7 @@ declare module '@mpxjs/utils' { declare let global: { __formatValue (value: string): string | number } & Record + +declare module '@react-navigation/native' { + export function useNavigation (): Record +} From 411f00db4ae68b181a4e34bd8d7b40f0e85af7b9 Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Thu, 9 Jan 2025 12:11:30 +0800 Subject: [PATCH 23/32] =?UTF-8?q?fix=20back=E9=80=BB=E8=BE=91=E4=BB=A5?= =?UTF-8?q?=E5=8F=8Aactionsheet=E8=B6=85=E8=BF=876=E8=BF=99=E4=B8=AA?= =?UTF-8?q?=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/platform/api/action-sheet/rnActionSheet.jsx | 7 +++++++ .../lib/runtime/components/react/mpx-web-view.tsx | 8 ++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/api-proxy/src/platform/api/action-sheet/rnActionSheet.jsx b/packages/api-proxy/src/platform/api/action-sheet/rnActionSheet.jsx index 68fa09de33..bd878df8e3 100644 --- a/packages/api-proxy/src/platform/api/action-sheet/rnActionSheet.jsx +++ b/packages/api-proxy/src/platform/api/action-sheet/rnActionSheet.jsx @@ -10,6 +10,13 @@ import Animated, { function showActionSheet (options = {}) { const { alertText, itemList = [], itemColor = '#000000', success, fail, complete } = options + if (itemList.length > 6) { + const result = { + errMsg: 'showActionSheet:fail parameter error: itemList should not be large than 6' + } + failHandle(result, fail, complete) + return + } const windowInfo = getWindowInfo() const bottom = windowInfo.screenHeight - windowInfo.safeArea.bottom let actionSheetKey = null diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx index 4edb61c9f3..3abf018686 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx @@ -107,6 +107,7 @@ const _WebView = forwardRef, WebViewProps>((pr bottom: 0 as number } const canGoBack = useRef(false) + const isNavigateBack = useRef(false) const onAndroidBackPress = useCallback(() => { if (canGoBack.current) { @@ -117,10 +118,11 @@ const _WebView = forwardRef, WebViewProps>((pr }, [canGoBack]) const beforeRemoveHandle = useCallback((e: Event) => { - if (canGoBack.current) { + if (canGoBack.current && !isNavigateBack.current) { webViewRef.current?.goBack() e.preventDefault() } + isNavigateBack.current = false }, [canGoBack]) const navigation = useNavigation() @@ -157,7 +159,7 @@ const _WebView = forwardRef, WebViewProps>((pr src: res.nativeEvent?.url } } - bindload(result) + bindload && bindload(result) } const _error = function (res: WebViewErrorEvent) { setPageLoadErr(true) @@ -200,6 +202,7 @@ const _WebView = forwardRef, WebViewProps>((pr } true; ` + const sendMessage = function (params: string) { return ` window.mpxWebviewMessageCallback(${params}) @@ -255,6 +258,7 @@ const _WebView = forwardRef, WebViewProps>((pr asyncCallback = navObj.navigateTo(...params) break case 'navigateBack': + isNavigateBack.current = true asyncCallback = navObj.navigateBack(...params) break case 'redirectTo': From 77c7ec89d99511d7ca226f0d862fc8d05883e2ef Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Thu, 9 Jan 2025 17:03:21 +0800 Subject: [PATCH 24/32] =?UTF-8?q?=E6=94=AF=E6=8C=81portal.update=E8=83=BD?= =?UTF-8?q?=E5=8A=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/platform/api/route/index.ios.js | 34 ++++++------------- .../lib/runtime/components/react/context.ts | 2 +- .../runtime/components/react/mpx-portal.tsx | 1 + .../react/mpx-portal/portal-consumer.tsx | 2 +- .../react/mpx-portal/portal-host.tsx | 19 +++++++---- 5 files changed, 25 insertions(+), 33 deletions(-) diff --git a/packages/api-proxy/src/platform/api/route/index.ios.js b/packages/api-proxy/src/platform/api/route/index.ios.js index 72d1fdf974..4777668960 100644 --- a/packages/api-proxy/src/platform/api/route/index.ios.js +++ b/packages/api-proxy/src/platform/api/route/index.ios.js @@ -35,18 +35,6 @@ function resolvePath (relative, base) { } let toPending = false -let redirectPending = false -let backPending = false -let reLaunchPending = false - -const navigationHelper = global.__navigationHelper -const transitionEndCallback = function (callback) { - if (!navigationHelper.transitionEndCallback) { - navigationHelper.transitionEndCallback = [] - } - navigationHelper.transitionEndCallback.push(callback) -} - function navigateTo (options = {}) { if (toPending) { return @@ -60,6 +48,7 @@ function navigateTo (options = {}) { const finalPath = resolvePath(path, basePath).slice(1) navigation.push(finalPath, queryObj) navigationHelper.lastSuccessCallback = () => { + toPending = false const res = { errMsg: 'navigateTo:ok' } successHandle(res, options.success, options.complete) } @@ -68,11 +57,10 @@ function navigateTo (options = {}) { const res = { errMsg: `navigateTo:fail ${msg}` } failHandle(res, options.fail, options.complete) } - transitionEndCallback(() => { - toPending = false - }) } } + +let redirectPending = false function redirectTo (options = {}) { if (redirectPending) { return @@ -86,6 +74,7 @@ function redirectTo (options = {}) { const finalPath = resolvePath(path, basePath).slice(1) navigation.replace(finalPath, queryObj) navigationHelper.lastSuccessCallback = () => { + redirectPending = false const res = { errMsg: 'redirectTo:ok' } successHandle(res, options.success, options.complete) } @@ -94,11 +83,10 @@ function redirectTo (options = {}) { const res = { errMsg: `redirectTo:fail ${msg}` } failHandle(res, options.fail, options.complete) } - transitionEndCallback(() => { - redirectPending = false - }) } } + +let backPending = false function navigateBack (options = {}) { if (backPending) { return @@ -118,6 +106,7 @@ function navigateBack (options = {}) { } else { navigation.pop(delta) navigationHelper.lastSuccessCallback = () => { + backPending = false const res = { errMsg: 'navigateBack:ok' } successHandle(res, options.success, options.complete) } @@ -127,11 +116,10 @@ function navigateBack (options = {}) { failHandle(res, options.fail, options.complete) } } - transitionEndCallback(() => { - backPending = false - }) } } + +let reLaunchPending = false function reLaunch (options = {}) { if (reLaunchPending) { return @@ -153,6 +141,7 @@ function reLaunch (options = {}) { ] }) navigationHelper.lastSuccessCallback = () => { + reLaunchPending = false const res = { errMsg: 'redirectTo:ok' } successHandle(res, options.success, options.complete) } @@ -161,9 +150,6 @@ function reLaunch (options = {}) { const res = { errMsg: `redirectTo:fail ${msg}` } failHandle(res, options.fail, options.complete) } - transitionEndCallback(() => { - reLaunchPending = false - }) } } diff --git a/packages/webpack-plugin/lib/runtime/components/react/context.ts b/packages/webpack-plugin/lib/runtime/components/react/context.ts index f7d2231fb3..6b9a527aa0 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/context.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/context.ts @@ -42,7 +42,7 @@ export interface PortalManagerContextValue { export interface PortalContextValue { mount: (children: React.ReactNode, key?: number, pageId?: number|null) => number| undefined update: (key: number, children: React.ReactNode, pageId?: number|null) => void - unmount: (key: number, pageId?: number|null) => void + unmount: (key: number) => void manager?: PortalManagerContextValue } diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal.tsx index 89312d0773..54e5bb5009 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal.tsx @@ -25,5 +25,6 @@ const Portal = ({ children }:PortalProps): JSX.Element => { Portal.Host = PortalHost Portal.add = portal.add Portal.remove = portal.remove +Portal.update = portal.update export default Portal diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx index 5ab6ecea27..c741f17ebd 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx @@ -23,7 +23,7 @@ const PortalConsumer = ({ manager, children } :PortalConsumerProps): JSX.Element const curPageId = navigation?.pageId keyRef.current = manager.mount(children, undefined, curPageId) return () => { - manager.unmount(keyRef.current, curPageId) + manager.unmount(keyRef.current) } }, []) return null diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx index 46b0681f02..39a7d1b01d 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx @@ -26,6 +26,7 @@ export type Operation = // events const addType = 'MPX_RN_ADD_PORTAL' const removeType = 'MPX_RN_REMOVE_PORTAL' +const updateType = 'MPX_RN_UPDATE_PORTAL' // fix react native web does not support DeviceEventEmitter const TopViewEventEmitter = DeviceEventEmitter || new NativeEventEmitter() @@ -46,6 +47,10 @@ class PortalGuard { remove = (key: number) => { TopViewEventEmitter.emit(removeType, key) } + + update = (key: number, e: ReactNode) => { + TopViewEventEmitter.emit(updateType, key, e) + } } /** * portal @@ -57,6 +62,7 @@ const PortalHost = ({ children } :PortalHostProps): JSX.Element => { const _queue = useRef([]) const _addType = useRef(null) const _removeType = useRef(null) + const _updateType = useRef(null) const manager = useRef(null) let currentPageId: number | undefined const _mount = (children: ReactNode, _key?: number, curPageId?: number) => { @@ -72,13 +78,13 @@ const PortalHost = ({ children } :PortalHostProps): JSX.Element => { return key } - const _unmount = (key: number, curPageId?: number) => { + const _unmount = (key: number) => { if (manager.current) { manager.current.unmount(key) } } - const _update = (key: number, children: ReactNode, curPageId?: number) => { + const _update = (key: number, children?: ReactNode, curPageId?: number) => { const navigation = getFocusedNavigation() const pageId = navigation?.pageId if (pageId !== (curPageId ?? currentPageId)) { @@ -93,14 +99,14 @@ const PortalHost = ({ children } :PortalHostProps): JSX.Element => { const navigation = getFocusedNavigation() currentPageId = navigation?.pageId _addType.current = TopViewEventEmitter.addListener(addType, _mount) - _removeType.current = TopViewEventEmitter.addListener( - removeType, - _unmount - ) + _removeType.current = TopViewEventEmitter.addListener(removeType, _unmount) + _updateType.current = TopViewEventEmitter.addListener(updateType, _update) + return () => { _addType.current?.remove() _removeType.current?.remove() + _updateType.current?.remove() } }, []) return ( @@ -111,7 +117,6 @@ const PortalHost = ({ children } :PortalHostProps): JSX.Element => { unmount: _unmount }} > - {/* Need collapsable=false here to clip the elevations, otherwise they appear above Portal components */} {children} From 6e569d256cfeb3cdb07d2404b88b6a5fef324f28 Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Thu, 9 Jan 2025 17:20:41 +0800 Subject: [PATCH 25/32] =?UTF-8?q?=E5=B9=B2=E6=8E=89=E6=97=A0=E7=94=A8?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/src/platform/patch/getDefaultOptions.ios.js | 11 ----------- .../components/react/mpx-portal/portal-host.tsx | 1 - 2 files changed, 12 deletions(-) diff --git a/packages/core/src/platform/patch/getDefaultOptions.ios.js b/packages/core/src/platform/patch/getDefaultOptions.ios.js index 39c2f6521c..1f44e346f6 100644 --- a/packages/core/src/platform/patch/getDefaultOptions.ios.js +++ b/packages/core/src/platform/patch/getDefaultOptions.ios.js @@ -367,16 +367,6 @@ function usePageStatus (navigation, pageId) { const blurSubscription = navigation.addListener('blur', () => { pageStatusMap[pageId] = 'hide' }) - const transitionEndSubscription = navigation.addListener('transitionEnd', () => { - if (global.__navigationHelper.transitionEndCallback?.length) { - global.__navigationHelper.transitionEndCallback.forEach((callback) => { - if (isFunction(callback)) { - callback() - } - }) - global.__navigationHelper.transitionEndCallback = null - } - }) const unWatchAppFocusedState = watch(global.__mpxAppFocusedState, (value) => { pageStatusMap[pageId] = value }) @@ -385,7 +375,6 @@ function usePageStatus (navigation, pageId) { focusSubscription() blurSubscription() unWatchAppFocusedState() - transitionEndSubscription() del(pageStatusMap, pageId) } }, [navigation]) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx index 39a7d1b01d..bcdf7b7e76 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx @@ -101,7 +101,6 @@ const PortalHost = ({ children } :PortalHostProps): JSX.Element => { _addType.current = TopViewEventEmitter.addListener(addType, _mount) _removeType.current = TopViewEventEmitter.addListener(removeType, _unmount) _updateType.current = TopViewEventEmitter.addListener(updateType, _update) - return () => { _addType.current?.remove() From 64055e2dcd4d4cfbee722de6e570c41a9b6ba950 Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Thu, 9 Jan 2025 21:31:52 +0800 Subject: [PATCH 26/32] =?UTF-8?q?=E5=8E=BB=E6=8E=89=E6=97=A0=E7=94=A8?= =?UTF-8?q?=E7=9A=84provider,=E4=BF=AE=E6=94=B9portal=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/action-sheet/rnActionSheet.jsx | 2 +- .../src/platform/api/modal/rnModal.jsx | 2 +- .../src/platform/api/toast/rnToast.jsx | 2 +- .../webpack-plugin/lib/react/processScript.js | 4 +- .../lib/runtime/components/react/context.ts | 4 +- .../components/react/locale-provider.tsx | 83 ------------------- .../components/react/mpx-picker/time.tsx | 2 +- .../{mpx-portal.tsx => mpx-portal/index.tsx} | 6 +- .../react/mpx-portal/portal-consumer.tsx | 11 +-- .../react/mpx-portal/portal-host.tsx | 28 +++---- .../react/mpx-portal/portal-manager.tsx | 3 +- .../runtime/components/react/mpx-provider.tsx | 51 ------------ .../components/react/mpx-root-portal.tsx | 2 +- .../runtime/components/react/mpx-web-view.tsx | 2 +- 14 files changed, 31 insertions(+), 171 deletions(-) delete mode 100644 packages/webpack-plugin/lib/runtime/components/react/locale-provider.tsx rename packages/webpack-plugin/lib/runtime/components/react/{mpx-portal.tsx => mpx-portal/index.tsx} (75%) delete mode 100644 packages/webpack-plugin/lib/runtime/components/react/mpx-provider.tsx diff --git a/packages/api-proxy/src/platform/api/action-sheet/rnActionSheet.jsx b/packages/api-proxy/src/platform/api/action-sheet/rnActionSheet.jsx index bd878df8e3..9bb81896aa 100644 --- a/packages/api-proxy/src/platform/api/action-sheet/rnActionSheet.jsx +++ b/packages/api-proxy/src/platform/api/action-sheet/rnActionSheet.jsx @@ -1,6 +1,6 @@ import { View, Text, StyleSheet } from 'react-native' import { successHandle, failHandle } from '../../../common/js' -import Portal from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal' +import Portal from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal/index' import { getWindowInfo } from '../system/rnSystem' import Animated, { useSharedValue, diff --git a/packages/api-proxy/src/platform/api/modal/rnModal.jsx b/packages/api-proxy/src/platform/api/modal/rnModal.jsx index 3c5ba8ac7c..eaee365bbc 100644 --- a/packages/api-proxy/src/platform/api/modal/rnModal.jsx +++ b/packages/api-proxy/src/platform/api/modal/rnModal.jsx @@ -1,6 +1,6 @@ import { View, Dimensions, Text, StyleSheet, TouchableOpacity, ScrollView, TextInput } from 'react-native' import { successHandle, failHandle } from '../../../common/js' -import Portal from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal' +import Portal from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal/index' const { width, height } = Dimensions.get('window') const showModal = function (options = {}) { const { diff --git a/packages/api-proxy/src/platform/api/toast/rnToast.jsx b/packages/api-proxy/src/platform/api/toast/rnToast.jsx index ba5dcbe624..6942f186d2 100644 --- a/packages/api-proxy/src/platform/api/toast/rnToast.jsx +++ b/packages/api-proxy/src/platform/api/toast/rnToast.jsx @@ -1,6 +1,6 @@ import { View, Text, Image, StyleSheet, ActivityIndicator, Dimensions } from 'react-native' import { successHandle, failHandle } from '../../../common/js' -import Portal from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal' +import Portal from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal/index' let toastKey let isLoadingShow diff --git a/packages/webpack-plugin/lib/react/processScript.js b/packages/webpack-plugin/lib/react/processScript.js index bcea57b65b..b068021b67 100644 --- a/packages/webpack-plugin/lib/react/processScript.js +++ b/packages/webpack-plugin/lib/react/processScript.js @@ -26,7 +26,7 @@ module.exports = function (script, { import { getComponent } from ${stringifyRequest(loaderContext, optionProcessorPath)} import { NavigationContainer, StackActions } from '@react-navigation/native' import { createStackNavigator } from '@react-navigation/stack' -import Provider from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-provider' +import PortalHost from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal/portal-host' import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context' import { GestureHandlerRootView } from 'react-native-gesture-handler' @@ -35,7 +35,7 @@ global.__navigationHelper = { createStackNavigator: createStackNavigator, StackActions: StackActions, GestureHandlerRootView: GestureHandlerRootView, - Provider: Provider, + Provider: PortalHost, SafeAreaProvider: SafeAreaProvider, useSafeAreaInsets: useSafeAreaInsets }\n` diff --git a/packages/webpack-plugin/lib/runtime/components/react/context.ts b/packages/webpack-plugin/lib/runtime/components/react/context.ts index 6b9a527aa0..0bc4d57cd9 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/context.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/context.ts @@ -40,8 +40,8 @@ export interface PortalManagerContextValue { } export interface PortalContextValue { - mount: (children: React.ReactNode, key?: number, pageId?: number|null) => number| undefined - update: (key: number, children: React.ReactNode, pageId?: number|null) => void + mount: (children: React.ReactNode, key?: number) => number| undefined + update: (key: number, children: React.ReactNode) => void unmount: (key: number) => void manager?: PortalManagerContextValue } diff --git a/packages/webpack-plugin/lib/runtime/components/react/locale-provider.tsx b/packages/webpack-plugin/lib/runtime/components/react/locale-provider.tsx deleted file mode 100644 index 5866936bf2..0000000000 --- a/packages/webpack-plugin/lib/runtime/components/react/locale-provider.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { createContext, useMemo, memo, ReactNode } from 'react' -import { extendObject } from './utils' - -interface Locale { - /** zh_CN */ - locale: string - DatePicker: { - /** 确定 */ - okText: string - /** 取消 */ - dismissText: string - /** 请选择 */ - extra: string - DatePickerLocale: { - /** 年 */ - year: string - /** 月 */ - month: string - /** 日 */ - day: string - /** 时 */ - hour: string - /** 分 */ - minute: string - /** 上午 */ - am: string - /** 下午 */ - pm: string - } - } - DatePickerView: { - /** 年 */ - year: string - /** 月 */ - month: string - /** 日 */ - day: string - /** 时 */ - hour: string - /** 分 */ - minute: string - /** 上午 */ - am: string - /** 下午 */ - pm: string - } - Picker: { - /** 确定 */ - okText: string - /** 取消 */ - dismissText: string - /** 请选择 */ - extra: string - } -} -export type LocaleContextProps = { - antLocale: Partial -} -export interface LocaleProviderProps { - children?: ReactNode, - locale?: LocaleContextProps -} - -export const LocaleContext = createContext< - LocaleContextProps | undefined ->(undefined) - -const LocaleProvider = (props :LocaleProviderProps): JSX.Element => { - const locale = useMemo(() => { - return { - antLocale: extendObject({}, props.locale, { exist: true }) - } - }, [props.locale]) - return ( - - {props.children} - - ) -} - -LocaleProvider.displayName = 'LocaleProvider' - -export default memo(LocaleProvider) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker/time.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker/time.tsx index 1780aa3894..7ef78f3c9e 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker/time.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker/time.tsx @@ -1,5 +1,5 @@ import { View, Text, Modal, TouchableWithoutFeedback } from 'react-native' -import Portal from '../mpx-portal' +import Portal from '../mpx-portal/index' import { PickerView } from '@ant-design/react-native' import React, { forwardRef, useState, useRef, useEffect } from 'react' import useNodesRef, { HandlerRef } from '../useNodesRef' // 引入辅助函数 diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/index.tsx similarity index 75% rename from packages/webpack-plugin/lib/runtime/components/react/mpx-portal.tsx rename to packages/webpack-plugin/lib/runtime/components/react/mpx-portal/index.tsx index 54e5bb5009..02a82a0d65 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/index.tsx @@ -1,7 +1,7 @@ import { ReactNode } from 'react' -import { PortalContext, PortalContextValue } from './context' -import PortalConsumer from './mpx-portal/portal-consumer' -import PortalHost, { portal } from './mpx-portal/portal-host' +import { PortalContext, PortalContextValue } from '../context' +import PortalConsumer from './portal-consumer' +import PortalHost, { portal } from './portal-host' export type PortalProps = { /** diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx index c741f17ebd..ac0ca186d8 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx @@ -1,6 +1,5 @@ import { useEffect, useRef, ReactNode } from 'react' import { PortalContextValue } from '../context' -import { getFocusedNavigation } from '@mpxjs/utils' export type PortalConsumerProps = { manager: PortalContextValue @@ -9,19 +8,15 @@ export type PortalConsumerProps = { const PortalConsumer = ({ manager, children } :PortalConsumerProps): JSX.Element | null => { const keyRef = useRef(null) useEffect(() => { - const navigation = getFocusedNavigation() - const curPageId = navigation?.pageId - manager.update(keyRef.current, children, curPageId) + manager.update(keyRef.current, children) }, [children]) useEffect(() => { if (!manager) { throw new Error( - 'Looks like you forgot to wrap your root component with `Provider` component from `@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal`.\n\n' + 'Looks like you forgot to wrap your root component with `Provider` component from `@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal/index`.\n\n' ) } - const navigation = getFocusedNavigation() - const curPageId = navigation?.pageId - keyRef.current = manager.mount(children, undefined, curPageId) + keyRef.current = manager.mount(children) return () => { manager.unmount(keyRef.current) } diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx index bcdf7b7e76..81134e372a 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx @@ -7,7 +7,7 @@ import { StyleSheet } from 'react-native' import PortalManager from './portal-manager' -import { getFocusedNavigation } from '@mpxjs/utils' +import { useNavigation } from '@react-navigation/native' import { PortalManagerContextValue, PortalContext } from '../context' export type PortalHostProps = { @@ -59,16 +59,13 @@ export const portal = new PortalGuard() const PortalHost = ({ children } :PortalHostProps): JSX.Element => { const _nextKey = useRef(0) - const _queue = useRef([]) const _addType = useRef(null) const _removeType = useRef(null) const _updateType = useRef(null) const manager = useRef(null) - let currentPageId: number | undefined - const _mount = (children: ReactNode, _key?: number, curPageId?: number) => { - const navigation = getFocusedNavigation() - const pageId = navigation?.pageId - if (pageId !== (curPageId ?? currentPageId)) { + const focusState = useRef(false) + const _mount = (children: ReactNode, _key?: number) => { + if (!focusState.current) { return } const key = _key || _nextKey.current++ @@ -85,27 +82,28 @@ const PortalHost = ({ children } :PortalHostProps): JSX.Element => { } const _update = (key: number, children?: ReactNode, curPageId?: number) => { - const navigation = getFocusedNavigation() - const pageId = navigation?.pageId - if (pageId !== (curPageId ?? currentPageId)) { - return - } if (manager.current) { manager.current.update(key, children) } } - + const navigation = useNavigation() useEffect(() => { - const navigation = getFocusedNavigation() - currentPageId = navigation?.pageId _addType.current = TopViewEventEmitter.addListener(addType, _mount) _removeType.current = TopViewEventEmitter.addListener(removeType, _unmount) _updateType.current = TopViewEventEmitter.addListener(updateType, _update) + const focusSubscription = navigation.addListener('focus', () => { + focusState.current = true + }) + const blurSubscription = navigation.addListener('blur', () => { + focusState.current = false + }) return () => { _addType.current?.remove() _removeType.current?.remove() _updateType.current?.remove() + focusSubscription() + blurSubscription() } }, []) return ( diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-manager.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-manager.tsx index fd2f2b1b12..fe51c8d270 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-manager.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-manager.tsx @@ -1,5 +1,6 @@ import { useState, useCallback, forwardRef, ForwardedRef, useImperativeHandle, ReactNode, ReactElement } from 'react' import { View, StyleSheet } from 'react-native' +import { extendObject } from '@mpxjs/utils' export type State = { portals: Array<{ @@ -26,7 +27,7 @@ const _PortalManager = forwardRef((props: PortalManagerProps, ref:ForwardedRef ({ portals: prevState.portals.map((item) => { if (item.key === key) { - return { ...item, children } + return extendObject({}, item, { children }) } return item }) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-provider.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-provider.tsx deleted file mode 100644 index 8b66f21063..0000000000 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-provider.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { ReactNode, createContext, useMemo } from 'react' -import LocaleProvider, { LocaleContextProps } from './locale-provider' -import Portal from './mpx-portal' -import { extendObject } from './utils' - -export type Theme = typeof defaultTheme & { [key: string]: any } - -export interface ProviderProps { - locale?: LocaleContextProps - theme?: Partial - children: ReactNode -} -const defaultTheme = { - color_text_base: '#000000', // 基本 - color_text_base_inverse: '#ffffff', // 基本 _ 反色 - color_text_secondary: '#a4a9b0', // 辅助色 - color_text_placeholder: '#bbbbbb', // 文本框提示 - color_text_disabled: '#bbbbbb', // 失效 - color_text_caption: '#888888', // 辅助描述 - color_text_paragraph: '#333333', // 段落 - color_error: '#ff4d4f', // 错误(form validate) - color_warning: '#faad14', // 警告 - color_success: '#52c41a', - color_primary: '#1677ff' -} -export const ThemeContext = createContext(defaultTheme) - -export type PartialTheme = Partial - -export interface ThemeProviderProps { - value?: PartialTheme - children?: React.ReactNode -} - -const ThemeProvider = (props: ThemeProviderProps) => { - const { value, children } = props - const theme = useMemo(() => (extendObject({}, defaultTheme, value)), [value]) - return {children} -} - -const Provider = ({ locale, theme, children }:ProviderProps): JSX.Element => { - return ( - - - {children} - - - ) -} - -export default Provider diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-root-portal.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-root-portal.tsx index 39ad317e29..4050cbd1d1 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-root-portal.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-root-portal.tsx @@ -2,7 +2,7 @@ * ✔ enable */ import { ReactNode, createElement, Fragment } from 'react' -import Portal from './mpx-portal' +import Portal from './mpx-portal/index' import { warn } from '@mpxjs/utils' interface RootPortalProps { enable?: boolean diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx index 3abf018686..648a6ca828 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-web-view.tsx @@ -1,6 +1,6 @@ import { forwardRef, useRef, useContext, useMemo, useState, useCallback, useEffect } from 'react' import { warn, isFunction } from '@mpxjs/utils' -import Portal from './mpx-portal' +import Portal from './mpx-portal/index' import { getCustomEvent } from './getInnerListeners' import { promisify, redirectTo, navigateTo, navigateBack, reLaunch, switchTab } from '@mpxjs/api-proxy' import { WebView } from 'react-native-webview' From 10a9fd3d15b2f4f0ddfbac27e3f567f99f8c5cad Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Thu, 9 Jan 2025 21:33:17 +0800 Subject: [PATCH 27/32] =?UTF-8?q?=E4=BF=AE=E6=94=B9extendObject=E5=BC=95?= =?UTF-8?q?=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/runtime/components/react/mpx-portal/portal-manager.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-manager.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-manager.tsx index fe51c8d270..1c7f33b00c 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-manager.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-manager.tsx @@ -1,6 +1,6 @@ import { useState, useCallback, forwardRef, ForwardedRef, useImperativeHandle, ReactNode, ReactElement } from 'react' import { View, StyleSheet } from 'react-native' -import { extendObject } from '@mpxjs/utils' +import { extendObject } from '../utils' export type State = { portals: Array<{ From 5c6bdc6130f5340e5bcf9473d9997c131352c8b4 Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Mon, 13 Jan 2025 17:53:35 +0800 Subject: [PATCH 28/32] =?UTF-8?q?=E8=A1=A5=E5=85=85=E9=BB=91=E5=90=8D?= =?UTF-8?q?=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api-proxy/src/common/js/promisify.js | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/packages/api-proxy/src/common/js/promisify.js b/packages/api-proxy/src/common/js/promisify.js index 3f19af391a..0453284440 100644 --- a/packages/api-proxy/src/common/js/promisify.js +++ b/packages/api-proxy/src/common/js/promisify.js @@ -14,24 +14,37 @@ const blackList = [ 'stopBackgroundAudio', 'showNavigationBarLoading', 'hideNavigationBarLoading', - 'createAnimation', - 'createAnimationVideo', - 'createSelectorQuery', - 'createIntersectionObserver', 'getPerformance', 'hideKeyboard', 'stopPullDownRefresh', - 'createWorker', 'pageScrollTo', 'reportAnalytics', 'getMenuButtonBoundingClientRect', 'reportMonitor', - 'createOffscreenCanvas', 'reportEvent', 'connectSocket', 'base64ToArrayBuffer', + 'arrayBufferToBase64', 'getDeviceInfo', - 'getWindowInfo' + 'getWindowInfo', + 'getAppBaseInfo', + 'getAppAuthorizeSetting', + 'getApiCategory', + 'postMessageToReferrerPage', + 'postMessageToReferrerMiniProgram', + 'reportPerformance', + 'getPerformance', + 'preDownloadSubpackage', + 'router', + 'nextTick', + 'checkIsPictureInPictureActive', + 'worklet', + 'revokeBufferURL', + 'reportEvent', + 'getExptInfoSync', + 'reserveChannelsLive', + 'getNFCAdapter', + 'isVKSupport' ] function getMapFromList (list) { @@ -55,7 +68,7 @@ function promisify (listObj, whiteList, customBlackList) { } else { return !(blackListMap[key] || // 特别指定的方法 /^get\w*Manager$/.test(key) || // 获取manager的api - /^create\w*Context$/.test(key) || // 创建上下文相关api + /^create(?!BLEConnection|BLEPeripheralServer)\w*$/.test(key) || // 创建上下文相关api /^(on|off)/.test(key) || // 以 on* 或 off开头的方法 /\w+Sync$/.test(key) // 以Sync结尾的方法 ) From 638bc95a26ecc8ff3ab583f9d758fca3ea8f5c90 Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Tue, 14 Jan 2025 21:27:07 +0800 Subject: [PATCH 29/32] =?UTF-8?q?=E4=BF=AE=E6=94=B9router=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/platform/api/route/index.ios.js | 71 +++++++++---------- 1 file changed, 34 insertions(+), 37 deletions(-) diff --git a/packages/api-proxy/src/platform/api/route/index.ios.js b/packages/api-proxy/src/platform/api/route/index.ios.js index 4777668960..b8a0a9b93c 100644 --- a/packages/api-proxy/src/platform/api/route/index.ios.js +++ b/packages/api-proxy/src/platform/api/route/index.ios.js @@ -33,14 +33,26 @@ function resolvePath (relative, base) { } return stack.join('/') } +function isLock(navigationHelper, type, options) { + if (navigationHelper.lastSuccessCallback && navigationHelper.lastFailCallback) { + const res = { errMsg: `${type}:fail the previous routing event didn't complete` } + failHandle(res, options.fail, options.complete) + return true + } + setTimeout(() => { + if (navigationHelper.lastFailCallback) { + navigationHelper.lastFailCallback('timeout') + navigationHelper.lastFailCallback = null + } + }, 350) + return false +} -let toPending = false -function navigateTo (options = {}) { - if (toPending) { +function navigateTo (options = {}) { + const navigationHelper = global.__navigationHelper + if (isLock(navigationHelper, 'navigateTo', options)) { return } - toPending = true - const navigationHelper = global.__navigationHelper const navigation = Object.values(global.__mpxPagesMap || {})[0]?.[1] if (navigation && navigationHelper) { const { path, queryObj } = parseUrl(options.url) @@ -48,85 +60,72 @@ function navigateTo (options = {}) { const finalPath = resolvePath(path, basePath).slice(1) navigation.push(finalPath, queryObj) navigationHelper.lastSuccessCallback = () => { - toPending = false const res = { errMsg: 'navigateTo:ok' } successHandle(res, options.success, options.complete) } navigationHelper.lastFailCallback = (msg) => { - toPending = false const res = { errMsg: `navigateTo:fail ${msg}` } failHandle(res, options.fail, options.complete) } } } -let redirectPending = false function redirectTo (options = {}) { - if (redirectPending) { - return - } - redirectPending = true const navigation = Object.values(global.__mpxPagesMap || {})[0]?.[1] const navigationHelper = global.__navigationHelper + if (isLock(navigationHelper, 'redirectTo', options)) { + return + } if (navigation && navigationHelper) { const { path, queryObj } = parseUrl(options.url) const basePath = getBasePath(navigation) const finalPath = resolvePath(path, basePath).slice(1) navigation.replace(finalPath, queryObj) navigationHelper.lastSuccessCallback = () => { - redirectPending = false const res = { errMsg: 'redirectTo:ok' } successHandle(res, options.success, options.complete) } navigationHelper.lastFailCallback = (msg) => { - redirectPending = false const res = { errMsg: `redirectTo:fail ${msg}` } failHandle(res, options.fail, options.complete) } } } -let backPending = false function navigateBack (options = {}) { - if (backPending) { - return - } - backPending = true const navigation = Object.values(global.__mpxPagesMap || {})[0]?.[1] const navigationHelper = global.__navigationHelper + if (isLock(navigationHelper, 'navigateBack', options)) { + return + } if (navigation && navigationHelper) { const delta = options.delta || 1 const routeLength = navigation.getState().routes.length + navigationHelper.lastSuccessCallback = () => { + const res = { errMsg: 'navigateBack:ok' } + successHandle(res, options.success, options.complete) + } + navigationHelper.lastFailCallback = (msg) => { + const res = { errMsg: `navigateBack:fail ${msg}` } + failHandle(res, options.fail, options.complete) + } if (delta >= routeLength && global.__mpx?.config.rnConfig.onAppBack?.(delta - routeLength + 1)) { nextTick(() => { - backPending = false const res = { errMsg: 'navigateBack:ok' } successHandle(res, options.success, options.complete) }) } else { navigation.pop(delta) - navigationHelper.lastSuccessCallback = () => { - backPending = false - const res = { errMsg: 'navigateBack:ok' } - successHandle(res, options.success, options.complete) - } - navigationHelper.lastFailCallback = (msg) => { - backPending = false - const res = { errMsg: `navigateBack:fail ${msg}` } - failHandle(res, options.fail, options.complete) - } } } } -let reLaunchPending = false function reLaunch (options = {}) { - if (reLaunchPending) { - return - } - reLaunchPending = true const navigation = Object.values(global.__mpxPagesMap || {})[0]?.[1] const navigationHelper = global.__navigationHelper + if (isLock(navigationHelper, 'reLaunch', options)) { + return + } if (navigation && navigationHelper) { const { path, queryObj } = parseUrl(options.url) const basePath = getBasePath(navigation) @@ -141,12 +140,10 @@ function reLaunch (options = {}) { ] }) navigationHelper.lastSuccessCallback = () => { - reLaunchPending = false const res = { errMsg: 'redirectTo:ok' } successHandle(res, options.success, options.complete) } navigationHelper.lastFailCallback = (msg) => { - reLaunchPending = false const res = { errMsg: `redirectTo:fail ${msg}` } failHandle(res, options.fail, options.complete) } From 34f1e0a6191d1acec1440d9d62b460e7746d7b0b Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Tue, 21 Jan 2025 11:23:31 +0800 Subject: [PATCH 30/32] =?UTF-8?q?=E6=94=B9=E9=80=A0portal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api-proxy/src/common/js/utils.js | 12 ++- .../api/action-sheet/rnActionSheet.jsx | 14 +++- .../src/platform/api/modal/rnModal.jsx | 13 ++- .../src/platform/api/route/index.ios.js | 4 +- .../src/platform/api/toast/rnToast.jsx | 13 ++- .../platform/patch/getDefaultOptions.ios.js | 5 +- .../lib/runtime/components/react/context.ts | 2 +- .../components/react/mpx-portal/index.tsx | 6 +- .../react/mpx-portal/portal-host.tsx | 84 ++++++++++++++----- 9 files changed, 118 insertions(+), 35 deletions(-) diff --git a/packages/api-proxy/src/common/js/utils.js b/packages/api-proxy/src/common/js/utils.js index 44ce3bfb63..d1bf81606d 100644 --- a/packages/api-proxy/src/common/js/utils.js +++ b/packages/api-proxy/src/common/js/utils.js @@ -1,4 +1,5 @@ import { hasOwn, noop, getEnvObj, getFocusedNavigation } from '@mpxjs/utils' +import { getCurrentInstance } from '@mpxjs/core' /** * @@ -87,6 +88,14 @@ function failHandle (result, fail, complete) { typeof complete === 'function' && complete(result) } +function getPageId () { + const navigation = getFocusedNavigation() + const currentInstance = getCurrentInstance() + console.log(currentInstance, currentInstance?.getPageId, navigation, navigation?.pageId) + const id = currentInstance?.getPageId || navigation?.pageId || null + return id +} + const ENV_OBJ = getEnvObj() export { @@ -101,5 +110,6 @@ export { defineUnsupportedProps, successHandle, failHandle, - getFocusedNavigation + getFocusedNavigation, + getPageId } diff --git a/packages/api-proxy/src/platform/api/action-sheet/rnActionSheet.jsx b/packages/api-proxy/src/platform/api/action-sheet/rnActionSheet.jsx index 9bb81896aa..50c8fd2fc6 100644 --- a/packages/api-proxy/src/platform/api/action-sheet/rnActionSheet.jsx +++ b/packages/api-proxy/src/platform/api/action-sheet/rnActionSheet.jsx @@ -1,5 +1,5 @@ import { View, Text, StyleSheet } from 'react-native' -import { successHandle, failHandle } from '../../../common/js' +import { successHandle, failHandle, getPageId, error } from '../../../common/js' import Portal from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal/index' import { getWindowInfo } from '../system/rnSystem' import Animated, { @@ -9,7 +9,16 @@ import Animated, { } from 'react-native-reanimated' function showActionSheet (options = {}) { + const id = getPageId() const { alertText, itemList = [], itemColor = '#000000', success, fail, complete } = options + if (id === null) { + error('showActionSheet cannot be invoked outside the mpx life cycle in React Native environments') + const result = { + errMsg: 'showActionSheet:fail cannot be invoked outside the mpx life cycle in React Native environments' + } + failHandle(result, fail, complete) + return + } if (itemList.length > 6) { const result = { errMsg: 'showActionSheet:fail parameter error: itemList should not be large than 6' @@ -116,8 +125,7 @@ function showActionSheet (options = {}) { ) } - - actionSheetKey = Portal.add() + actionSheetKey = Portal.add(, id) } export { diff --git a/packages/api-proxy/src/platform/api/modal/rnModal.jsx b/packages/api-proxy/src/platform/api/modal/rnModal.jsx index eaee365bbc..a82eb7d07a 100644 --- a/packages/api-proxy/src/platform/api/modal/rnModal.jsx +++ b/packages/api-proxy/src/platform/api/modal/rnModal.jsx @@ -1,8 +1,9 @@ import { View, Dimensions, Text, StyleSheet, TouchableOpacity, ScrollView, TextInput } from 'react-native' -import { successHandle, failHandle } from '../../../common/js' +import { successHandle, failHandle, getPageId, error } from '../../../common/js' import Portal from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal/index' const { width, height } = Dimensions.get('window') const showModal = function (options = {}) { + const id = getPageId() const { title, content, @@ -17,6 +18,14 @@ const showModal = function (options = {}) { fail, complete } = options + if (id === null) { + error('showModal cannot be invoked outside the mpx life cycle in React Native environments') + const result = { + errMsg: 'showModal:fail cannot be invoked outside the mpx life cycle in React Native environments' + } + failHandle(result, fail, complete) + return + } const modalWidth = width * 0.8 const styles = StyleSheet.create({ modalTask: { @@ -156,7 +165,7 @@ const showModal = function (options = {}) { try { - modalKey = Portal.add(ModalView) + modalKey = Portal.add(ModalView, id) } catch (e) { const result = { errMsg: `showModal:fail invalid ${e}` diff --git a/packages/api-proxy/src/platform/api/route/index.ios.js b/packages/api-proxy/src/platform/api/route/index.ios.js index b8a0a9b93c..653f87713b 100644 --- a/packages/api-proxy/src/platform/api/route/index.ios.js +++ b/packages/api-proxy/src/platform/api/route/index.ios.js @@ -33,7 +33,7 @@ function resolvePath (relative, base) { } return stack.join('/') } -function isLock(navigationHelper, type, options) { +function isLock (navigationHelper, type, options) { if (navigationHelper.lastSuccessCallback && navigationHelper.lastFailCallback) { const res = { errMsg: `${type}:fail the previous routing event didn't complete` } failHandle(res, options.fail, options.complete) @@ -48,7 +48,7 @@ function isLock(navigationHelper, type, options) { return false } -function navigateTo (options = {}) { +function navigateTo (options = {}) { const navigationHelper = global.__navigationHelper if (isLock(navigationHelper, 'navigateTo', options)) { return diff --git a/packages/api-proxy/src/platform/api/toast/rnToast.jsx b/packages/api-proxy/src/platform/api/toast/rnToast.jsx index 6942f186d2..2909255154 100644 --- a/packages/api-proxy/src/platform/api/toast/rnToast.jsx +++ b/packages/api-proxy/src/platform/api/toast/rnToast.jsx @@ -1,5 +1,5 @@ import { View, Text, Image, StyleSheet, ActivityIndicator, Dimensions } from 'react-native' -import { successHandle, failHandle } from '../../../common/js' +import { successHandle, failHandle, getPageId, error } from '../../../common/js' import Portal from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal/index' let toastKey @@ -55,7 +55,16 @@ const styles = StyleSheet.create({ }) function showToast (options = {}) { + const id = getPageId() const { title, icon = 'success', image, duration = 1500, mask = false, success, fail, complete, isLoading } = options + if (id === null) { + error('showToast cannot be invoked outside the mpx life cycle in React Native environments') + const result = { + errMsg: 'showToast:fail cannot be invoked outside the mpx life cycle in React Native environments' + } + failHandle(result, fail, complete) + return + } let ToastView const successPng = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFwAAABcAgMAAACegTrLAAAADFBMVEUAAAD////R0dHj4+PcME2AAAAAAXRSTlMAQObYZgAAANNJREFUSMft1FEKwkAMBNBGyBFyH4/gR8f+ehSPLtiyQ+wM2C8Rmp+WQN90N8tOZ/1/4SbbYfpp+sBVMqafgGQKs+FvkjF8GR6aj4M8NB+GT8OX5i+GT8OX4aH5MHwZHpoPw+c3fA0xGl/zYBrP92o8vwVwF3NO8tySjXmINQLAs/WX9S8bP9UGJUZSi8MAuUqsj75payPxuWn1BmrjewCCfAvA4Fmjvx8HQL4HiJEHeRmg5k2+B8gTFeRFwL7NzREB/sCKwsG7KN2VSb7XMp31i3oBatNdEForTOoAAAAASUVORK5CYII=' const errorPng = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFwAAABcCAYAAADj79JYAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAXKADAAQAAAABAAAAXAAAAABhCMkfAAAHyUlEQVR4Ae2d74tVRRjHd7PU1IxerAX542IQQkXZDzMjN/JFSRgmJogkQS8l6c2+8a3gH2DQuyAMI2qpMMR6k6IgpoVRBL2ouGoYuRAUpJW22+dbc5e93XPmzpwzc8657jzw5dw7M+eZ5/mcc8+dmXv27NBQskQgEUgEEoFEIBFIBBKBRCARSAQSgUQgJIHhkM5i+ZqamlqA74XoJjQX6b3sMvoLXUW/Dw8P632jrXHAgXsDxJaiNeg+1EIr0Ai6GQm4JBNs6QqaQOdQG32NTqMfOQiTbBtjjQBuIK+Cykb0JFqNbkc3oiJ2jZ1+RmfRMXQEfdsE+LUCB/RiQGxCW9E6tATFsEs4PYnG0UeA/y1GJy4+awEO6OUE9xzagda6BBqwzSl8HUSHAH8+oN/muQL0IvQq+gHVbYpBsSxqHqkAEZHYKDqGmmaKaTRAis1wQTLz0Rj6BTXVFJtinN8MagWjIIGl6G00iZpuilGxakg6eEbgK9FJNGimmFfGIq5JRnAj4AdxqiHYY8Gdx3eomMdNDsF7Cz4sJNAHiPJDpNnhINs5gt/M0PHLkEkEBQ5sQdaZ/XDIIGf40gTmLXTGlD3C9kUUa8L0Ob63Al3wm2XAXoaOo1j2HY4FuMtUhlQXy5TTsq5O635DQPPQO7Eyxu819FJenqozbdhEMeU2L69/n/JQX5qv0KnWQ2KZLiUnLM5VpzaxTLkpx9JWGjhHfj1R7EFzSkeT7+APqrQMm2eqU5tYptz2mFxL9VEKOAHcQu970W2loui/c2cdPK+l1sfVJqYpx70m58L9lAJOry+jJwr37r6j1sX1a0+eqa7o2nmez6xy5aqcC1th4BxpfXPvRkGHljmZCKbtS0t1VQBXrrtN7jmh2osLA8et1rNbdvfBagXTtrCkuiqAK6EWUu6FrBBwjvCt9LYTVXF2K7EmAVfOOw0DxeZlhYDTw7OoZxLi1bNfYwFvwiWlE7VyFwNv8wbOkVXyGpdWdXYrKfVpG4WoTm2qMuW+1bDw6tMbON7vRo979RKmcb9reJhe3L2IgVh4WRHgG+kh1mKRLfh+lxTbvjHqxEAsvMwLuPkIbfDqIVxjTW7yzFaXt0+I8g2+lxUv4ER4J7o/RKQFfCyw7GOrs+xWukosxMTZfIE/imfdEVWH2aDa6mLGKhZi4my+wO/F8xxn72Eb2qDa6sJG0e1NLMTE2ZyBc63SUKjl7Dl8w4UWl7Y6y25BqlqGjZMzZ+B401mkW9TqMttZbKuLHa+YOPfvC3wkdvQW/7akbHUWl0GqxMS5fx/gWgK1TT6CRG9xYkvKVmdxGaRKTGxLx12d+ADXWNc2+ehyHOGN7Tptq4sQSpdLMXGeB/gA11lUJ/B+ayldFCp8IybOnzAf4BXmkNmVbgrtWTAzZXVe6jKDzSv0AX4ZJ3/mOaqgPO+jW/elTkzExsl8gOuX8TqB54HNOxBOAAI0EhPbHQVdXfgAv8qeMW9F6Aos441GAlnfISpzHiVk+C1bJCZi42Q+wPWxmXDyGqeRwOYBzyqPE0WvVzGJckmR0/O9/VVW0tRLipiEB84dpFM4bleGt7cjXTayxrsqq/OS0jZseiPOKPG5pGj3r9DfGX6qKBLUrOGf10wvcKBiISbO5gv8Mzz/5Ow9bEMthead4XUtGYuFmDibL/CLeA76FwHOkf63Dp/15aiyuoCLhZg4mxdwrlX6CB119h62ocDekeFSZVkHIqNp8KKjhomzYy/gxuvHbC859xCuoWLdxVR++s/6zOtdlBfJo2xkYiAWXtazNtFvb5LUDTfvouf7tY1U/wV+PzG+n2b7UKR++rn9gAbbOMP15Apn875bSR0AXX84tRl5HzDnyPIbCnBdkDtRaYg87gtbOxf9KB5m3zNyMEtNuYuBtxUCzpH9lZ4OIB3p2WbK+YBh4J17IeCml0Ns2949Dv4Oylm5F7LCwDnCF+hxP6rjLNdyqPOSaCEy2Tsp1/0m9+wWfUoLAzd+32B7ok8fIas1IngTPWOk116jBNqXMeWqnOszRizrUVXPQnmdvqZnlXqNVFaFKUf9iWQpK3uGD/HxOk4E+5BmoTFN/g/T33Q/5rVGC9NlkQKQ/30m11JdlAZuen+Nrcbm16spN+VY2oIA58jrd70xdLJ0RPkOdCnZzsd6eonWvN5O+fRlJn/3wjXKaczkWNhJZ8egM0UA3IXj99DqTgeBt5P407LCQeN3B9ttKMiJY3zO3JzlzQvA/n5mYZnXQYErEKAL9vuohWKZwMtigZbvNtoCbEEPZsEDNgFuIcLTwaLsdaS4g8c+oxvFHhz2DP/hX3KmL0cxH1iD+yimmHUL8uAZgS9F6TF6VR46gKcHRVYJvNMX4EeRHjvaNFNMo504r6stic36h/0GHxa6nCGA15eSHoGhcfRal30CtjmFL43ja3mcdS3AO/AAv5jXm5AelrAOLUEx7BJONWPUFH32PbD9/0QBrzH1KqS/XX8KafI0grx/c2UfmZZsJ5AmLZ+iIyj9SwIg9BjwtS6yDK1B96AWWoF0APRnJ3ON2Pz7I4R+iLiCBPgcaqNvkCYvF5iIxV5JpBt3q/WS4homB2EBbRci3V8o4Hov012rAq77swfi38oQZ7JEIBFIBBKBRCARSAQSgUQgEUgEEoFEICCBfwDKNlghU/F3VgAAAABJRU5ErkJggg==' @@ -101,7 +110,7 @@ function showToast (options = {}) { if (toastKey) { Portal.remove(toastKey) } - toastKey = Portal.add(ToastView) + toastKey = Portal.add(ToastView, id) if (!isLoading) { tId = setTimeout(() => { Portal.remove(toastKey) diff --git a/packages/core/src/platform/patch/getDefaultOptions.ios.js b/packages/core/src/platform/patch/getDefaultOptions.ios.js index 0189823b53..7745dc25e9 100644 --- a/packages/core/src/platform/patch/getDefaultOptions.ios.js +++ b/packages/core/src/platform/patch/getDefaultOptions.ios.js @@ -489,7 +489,6 @@ export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) { const currentPageId = useMemo(() => ++pageId, []) const intersectionObservers = useRef({}) usePageStatus(navigation, currentPageId) - useLayoutEffect(() => { const isCustom = pageConfig.navigationStyle === 'custom' navigation.setOptions({ @@ -566,7 +565,9 @@ export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) { value: intersectionObservers.current }, createElement(Provider, - null, + { + pageId: currentPageId + }, createElement(defaultOptions, { navigation, diff --git a/packages/webpack-plugin/lib/runtime/components/react/context.ts b/packages/webpack-plugin/lib/runtime/components/react/context.ts index 0bc4d57cd9..5355012d96 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/context.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/context.ts @@ -40,7 +40,7 @@ export interface PortalManagerContextValue { } export interface PortalContextValue { - mount: (children: React.ReactNode, key?: number) => number| undefined + mount: (children: React.ReactNode, key?: number, id?: number) => number| undefined update: (key: number, children: React.ReactNode) => void unmount: (key: number) => void manager?: PortalManagerContextValue diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/index.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/index.tsx index 02a82a0d65..d8bf1354a5 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/index.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/index.tsx @@ -1,6 +1,7 @@ import { ReactNode } from 'react' import { PortalContext, PortalContextValue } from '../context' import PortalConsumer from './portal-consumer' +import { useNavigation } from '@react-navigation/native' import PortalHost, { portal } from './portal-host' export type PortalProps = { @@ -9,10 +10,13 @@ export type PortalProps = { */ children?: ReactNode key?: string - manager?: PortalContextValue + manager?: PortalContextValue, + pageId?: number } const Portal = ({ children }:PortalProps): JSX.Element => { + const navigation = useNavigation() + const pageId = navigation?.pageId return ( {(manager) => ( diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx index 81134e372a..724dfca79f 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, ReactNode } from 'react' +import { useEffect, useRef, ReactNode, useMemo } from 'react' import { View, DeviceEventEmitter, @@ -11,7 +11,8 @@ import { useNavigation } from '@react-navigation/native' import { PortalManagerContextValue, PortalContext } from '../context' export type PortalHostProps = { - children: ReactNode + children: ReactNode, + pageId: number } type addIdsMapsType = { @@ -38,9 +39,9 @@ const styles = StyleSheet.create({ class PortalGuard { private nextKey = 10000 - add = (e: ReactNode) => { + add = (e: ReactNode, id: number) => { const key = this.nextKey++ - TopViewEventEmitter.emit(addType, e, key) + TopViewEventEmitter.emit(addType, e, key, id) return key } @@ -57,17 +58,48 @@ class PortalGuard { */ export const portal = new PortalGuard() -const PortalHost = ({ children } :PortalHostProps): JSX.Element => { +const PortalHost = ({ children, pageId } :PortalHostProps): JSX.Element => { + const isMounted = useRef(false) const _nextKey = useRef(0) const _addType = useRef(null) const _removeType = useRef(null) const _updateType = useRef(null) const manager = useRef(null) - const focusState = useRef(false) - const _mount = (children: ReactNode, _key?: number) => { - if (!focusState.current) { - return + const queue = useRef>([]) + const _mount = (children: ReactNode, _key?: number, id?: number) => { + if (id !== pageId) return + const key = _key || _nextKey.current++ + if (!isMounted.current) { + queue.current.push({ type: 'mount', key, children }) + } else if (manager.current) { + manager.current.mount(key, children) + } + return key + } + + const _unmount = (key: number) => { + if (!isMounted.current) { + queue.current.push({ type: 'unmount', key, children }) + } else if (manager.current) { + manager.current.unmount(key) + } + } + + const _update = (key: number, children?: ReactNode) => { + if (!isMounted.current) { + const operation = { type: 'mount', key, children } + const index = queue.current.findIndex((q) => q.key === key) + if (index > -1) { + queue.current[index] = operation + } else { + queue.current.push(operation) + } + } else if (manager.current) { + manager.current.update(key, children) } + } + + const mount = (children: ReactNode, _key?: number) => { const key = _key || _nextKey.current++ if (manager.current) { manager.current.mount(key, children) @@ -75,27 +107,38 @@ const PortalHost = ({ children } :PortalHostProps): JSX.Element => { return key } - const _unmount = (key: number) => { + const unmount = (key: number) => { if (manager.current) { manager.current.unmount(key) } } - const _update = (key: number, children?: ReactNode, curPageId?: number) => { + const update = (key: number, children?: ReactNode) => { if (manager.current) { manager.current.update(key, children) } } - const navigation = useNavigation() - useEffect(() => { + + useMemo(() => { _addType.current = TopViewEventEmitter.addListener(addType, _mount) _removeType.current = TopViewEventEmitter.addListener(removeType, _unmount) _updateType.current = TopViewEventEmitter.addListener(updateType, _update) + }, []) + const navigation = useNavigation() + useEffect(() => { + while (queue.current.length && manager.current) { + const operation = queue.current.shift() + if (!operation) return + switch (operation.type) { + case 'mount': + manager.current.mount(operation.key, operation.children) + break + case 'unmount': + manager.current.unmount(operation.key) + } + } const focusSubscription = navigation.addListener('focus', () => { - focusState.current = true - }) - const blurSubscription = navigation.addListener('blur', () => { - focusState.current = false + isMounted.current = true }) return () => { @@ -103,15 +146,14 @@ const PortalHost = ({ children } :PortalHostProps): JSX.Element => { _removeType.current?.remove() _updateType.current?.remove() focusSubscription() - blurSubscription() } }, []) return ( From 2abcd9cdb9fe1ed7cefcf793744064f68347afa8 Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Tue, 21 Jan 2025 14:03:54 +0800 Subject: [PATCH 31/32] =?UTF-8?q?=E4=BF=AE=E6=94=B9router=20=20lock?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api-proxy/src/platform/api/route/index.ios.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/api-proxy/src/platform/api/route/index.ios.js b/packages/api-proxy/src/platform/api/route/index.ios.js index 653f87713b..04ec39ea47 100644 --- a/packages/api-proxy/src/platform/api/route/index.ios.js +++ b/packages/api-proxy/src/platform/api/route/index.ios.js @@ -40,7 +40,7 @@ function isLock (navigationHelper, type, options) { return true } setTimeout(() => { - if (navigationHelper.lastFailCallback) { + if (navigationHelper.lastSuccessCallback && navigationHelper.lastFailCallback) { navigationHelper.lastFailCallback('timeout') navigationHelper.lastFailCallback = null } From dc28f2f144aaeb6fa72b22f1e562541e1a050767 Mon Sep 17 00:00:00 2001 From: wangcuijuan Date: Tue, 21 Jan 2025 18:59:06 +0800 Subject: [PATCH 32/32] =?UTF-8?q?=E6=94=B9=E9=80=A0=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/platform/api/toast/rnToast.jsx | 41 ++++++----- .../lib/runtime/components/react/context.ts | 11 +-- .../components/react/mpx-portal/index.tsx | 19 ++--- .../react/mpx-portal/portal-consumer.tsx | 7 +- .../react/mpx-portal/portal-host.tsx | 70 +++++++------------ 5 files changed, 55 insertions(+), 93 deletions(-) diff --git a/packages/api-proxy/src/platform/api/toast/rnToast.jsx b/packages/api-proxy/src/platform/api/toast/rnToast.jsx index 2909255154..169d7a0b13 100644 --- a/packages/api-proxy/src/platform/api/toast/rnToast.jsx +++ b/packages/api-proxy/src/platform/api/toast/rnToast.jsx @@ -2,12 +2,10 @@ import { View, Text, Image, StyleSheet, ActivityIndicator, Dimensions } from 're import { successHandle, failHandle, getPageId, error } from '../../../common/js' import Portal from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal/index' -let toastKey let isLoadingShow const dimensionsScreen = Dimensions.get('screen') const screenHeight = dimensionsScreen.height const contentTop = parseInt(screenHeight * 0.4) -let tId // show duration 计时id const styles = StyleSheet.create({ toastContent: { maxWidth: '60%', @@ -53,7 +51,8 @@ const styles = StyleSheet.create({ marginTop: 10 } }) - +const toastMap = new Map() +const timerMap = new Map() function showToast (options = {}) { const id = getPageId() const { title, icon = 'success', image, duration = 1500, mask = false, success, fail, complete, isLoading } = options @@ -65,6 +64,10 @@ function showToast (options = {}) { failHandle(result, fail, complete) return } + + const timeoutId = timerMap.get(id) + clearTimeout(timeoutId) + timerMap.delete(id) let ToastView const successPng = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFwAAABcAgMAAACegTrLAAAADFBMVEUAAAD////R0dHj4+PcME2AAAAAAXRSTlMAQObYZgAAANNJREFUSMft1FEKwkAMBNBGyBFyH4/gR8f+ehSPLtiyQ+wM2C8Rmp+WQN90N8tOZ/1/4SbbYfpp+sBVMqafgGQKs+FvkjF8GR6aj4M8NB+GT8OX5i+GT8OX4aH5MHwZHpoPw+c3fA0xGl/zYBrP92o8vwVwF3NO8tySjXmINQLAs/WX9S8bP9UGJUZSi8MAuUqsj75payPxuWn1BmrjewCCfAvA4Fmjvx8HQL4HiJEHeRmg5k2+B8gTFeRFwL7NzREB/sCKwsG7KN2VSb7XMp31i3oBatNdEForTOoAAAAASUVORK5CYII=' const errorPng = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFwAAABcCAYAAADj79JYAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAXKADAAQAAAABAAAAXAAAAABhCMkfAAAHyUlEQVR4Ae2d74tVRRjHd7PU1IxerAX542IQQkXZDzMjN/JFSRgmJogkQS8l6c2+8a3gH2DQuyAMI2qpMMR6k6IgpoVRBL2ouGoYuRAUpJW22+dbc5e93XPmzpwzc8657jzw5dw7M+eZ5/mcc8+dmXv27NBQskQgEUgEEoFEIBFIBBKBRCARSAQSgUQgJIHhkM5i+ZqamlqA74XoJjQX6b3sMvoLXUW/Dw8P632jrXHAgXsDxJaiNeg+1EIr0Ai6GQm4JBNs6QqaQOdQG32NTqMfOQiTbBtjjQBuIK+Cykb0JFqNbkc3oiJ2jZ1+RmfRMXQEfdsE+LUCB/RiQGxCW9E6tATFsEs4PYnG0UeA/y1GJy4+awEO6OUE9xzagda6BBqwzSl8HUSHAH8+oN/muQL0IvQq+gHVbYpBsSxqHqkAEZHYKDqGmmaKaTRAis1wQTLz0Rj6BTXVFJtinN8MagWjIIGl6G00iZpuilGxakg6eEbgK9FJNGimmFfGIq5JRnAj4AdxqiHYY8Gdx3eomMdNDsF7Cz4sJNAHiPJDpNnhINs5gt/M0PHLkEkEBQ5sQdaZ/XDIIGf40gTmLXTGlD3C9kUUa8L0Ob63Al3wm2XAXoaOo1j2HY4FuMtUhlQXy5TTsq5O635DQPPQO7Eyxu819FJenqozbdhEMeU2L69/n/JQX5qv0KnWQ2KZLiUnLM5VpzaxTLkpx9JWGjhHfj1R7EFzSkeT7+APqrQMm2eqU5tYptz2mFxL9VEKOAHcQu970W2loui/c2cdPK+l1sfVJqYpx70m58L9lAJOry+jJwr37r6j1sX1a0+eqa7o2nmez6xy5aqcC1th4BxpfXPvRkGHljmZCKbtS0t1VQBXrrtN7jmh2osLA8et1rNbdvfBagXTtrCkuiqAK6EWUu6FrBBwjvCt9LYTVXF2K7EmAVfOOw0DxeZlhYDTw7OoZxLi1bNfYwFvwiWlE7VyFwNv8wbOkVXyGpdWdXYrKfVpG4WoTm2qMuW+1bDw6tMbON7vRo979RKmcb9reJhe3L2IgVh4WRHgG+kh1mKRLfh+lxTbvjHqxEAsvMwLuPkIbfDqIVxjTW7yzFaXt0+I8g2+lxUv4ER4J7o/RKQFfCyw7GOrs+xWukosxMTZfIE/imfdEVWH2aDa6mLGKhZi4my+wO/F8xxn72Eb2qDa6sJG0e1NLMTE2ZyBc63SUKjl7Dl8w4UWl7Y6y25BqlqGjZMzZ+B401mkW9TqMttZbKuLHa+YOPfvC3wkdvQW/7akbHUWl0GqxMS5fx/gWgK1TT6CRG9xYkvKVmdxGaRKTGxLx12d+ADXWNc2+ehyHOGN7Tptq4sQSpdLMXGeB/gA11lUJ/B+ayldFCp8IybOnzAf4BXmkNmVbgrtWTAzZXVe6jKDzSv0AX4ZJ3/mOaqgPO+jW/elTkzExsl8gOuX8TqB54HNOxBOAAI0EhPbHQVdXfgAv8qeMW9F6Aos441GAlnfISpzHiVk+C1bJCZi42Q+wPWxmXDyGqeRwOYBzyqPE0WvVzGJckmR0/O9/VVW0tRLipiEB84dpFM4bleGt7cjXTayxrsqq/OS0jZseiPOKPG5pGj3r9DfGX6qKBLUrOGf10wvcKBiISbO5gv8Mzz/5Ow9bEMthead4XUtGYuFmDibL/CLeA76FwHOkf63Dp/15aiyuoCLhZg4mxdwrlX6CB119h62ocDekeFSZVkHIqNp8KKjhomzYy/gxuvHbC859xCuoWLdxVR++s/6zOtdlBfJo2xkYiAWXtazNtFvb5LUDTfvouf7tY1U/wV+PzG+n2b7UKR++rn9gAbbOMP15Apn875bSR0AXX84tRl5HzDnyPIbCnBdkDtRaYg87gtbOxf9KB5m3zNyMEtNuYuBtxUCzpH9lZ4OIB3p2WbK+YBh4J17IeCml0Ns2949Dv4Oylm5F7LCwDnCF+hxP6rjLNdyqPOSaCEy2Tsp1/0m9+wWfUoLAzd+32B7ok8fIas1IngTPWOk116jBNqXMeWqnOszRizrUVXPQnmdvqZnlXqNVFaFKUf9iWQpK3uGD/HxOk4E+5BmoTFN/g/T33Q/5rVGC9NlkQKQ/30m11JdlAZuen+Nrcbm16spN+VY2oIA58jrd70xdLJ0RPkOdCnZzsd6eonWvN5O+fRlJn/3wjXKaczkWNhJZ8egM0UA3IXj99DqTgeBt5P407LCQeN3B9ttKMiJY3zO3JzlzQvA/n5mYZnXQYErEKAL9vuohWKZwMtigZbvNtoCbEEPZsEDNgFuIcLTwaLsdaS4g8c+oxvFHhz2DP/hX3KmL0cxH1iD+yimmHUL8uAZgS9F6TF6VR46gKcHRVYJvNMX4EeRHjvaNFNMo504r6stic36h/0GHxa6nCGA15eSHoGhcfRal30CtjmFL43ja3mcdS3AO/AAv5jXm5AelrAOLUEx7BJONWPUFH32PbD9/0QBrzH1KqS/XX8KafI0grx/c2UfmZZsJ5AmLZ+iIyj9SwIg9BjwtS6yDK1B96AWWoF0APRnJ3ON2Pz7I4R+iLiCBPgcaqNvkCYvF5iIxV5JpBt3q/WS4homB2EBbRci3V8o4Hov012rAq77swfi38oQZ7JEIBFIBBKBRCARSAQSgUQgEUgEEoFEICCBfwDKNlghU/F3VgAAAABJRU5ErkJggg==' @@ -74,10 +77,6 @@ function showToast (options = {}) { } const pointerEvents = mask ? 'auto' : 'none' isLoadingShow = isLoading - if (tId) { - clearTimeout(tId) - } - tId = null if (image || icon === 'success' || icon === 'error') { ToastView = @@ -107,15 +106,17 @@ function showToast (options = {}) { } try { - if (toastKey) { - Portal.remove(toastKey) + if (toastMap.get(id)) { + Portal.remove(toastMap.get(id)) + toastMap.delete(id) } - toastKey = Portal.add(ToastView, id) + const toastKey = Portal.add(ToastView, id) + toastMap.set(id, toastKey) if (!isLoading) { - tId = setTimeout(() => { - Portal.remove(toastKey) - toastKey = null + const timeoutId = setTimeout(() => { + Portal.remove(toastMap.get(id)) }, duration) + timerMap.set(id, timeoutId) } const result = { errMsg: 'showToast:ok' @@ -130,15 +131,16 @@ function showToast (options = {}) { } function hideToast(options = {}) { + const id = getPageId() const { noConflict = false, success, fail, complete } = options if (isLoadingShow && noConflict) { return } try { - if (toastKey) { - Portal.remove(toastKey) - toastKey = null + if (toastMap.get(id)) { + Portal.remove(toastMap.get(id)) + toastMap.delete(id) } const result = { errMsg: 'hideToast:ok' @@ -175,15 +177,16 @@ function showLoading (options = {}) { } function hideLoading (options = {}) { + const id = getPageId() const { noConflict = false, success, fail, complete } = options if (!isLoadingShow && noConflict) { return } isLoadingShow = false try { - if (toastKey) { - Portal.remove(toastKey) - toastKey = null + if (toastMap.get(id)) { + Portal.remove(toastMap.get(id)) + toastMap.delete(id) } const result = { errMsg: 'hideLoading:ok' diff --git a/packages/webpack-plugin/lib/runtime/components/react/context.ts b/packages/webpack-plugin/lib/runtime/components/react/context.ts index 5355012d96..341fccbb90 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/context.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/context.ts @@ -32,18 +32,11 @@ export interface IntersectionObserver { throttleMeasure: () => void } } -export interface PortalManagerContextValue { - mount: (key: number, children: React.ReactNode) => void - update: (key: number, children: React.ReactNode) => void - unmount: (key: number) => void, - portals: Array<{key: number, children: React.ReactNode}> -} export interface PortalContextValue { - mount: (children: React.ReactNode, key?: number, id?: number) => number| undefined + mount: (children: React.ReactNode, key?: number | null, id?: number| null) => number| undefined update: (key: number, children: React.ReactNode) => void unmount: (key: number) => void - manager?: PortalManagerContextValue } export interface ScrollViewContextValue { @@ -75,5 +68,3 @@ export const KeyboardAvoidContext = createContext({ gestureRef: null }) export const PortalContext = createContext(null as any) - -export const PortalManagerContext = createContext(null) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/index.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/index.tsx index d8bf1354a5..7ce1cde8be 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/index.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/index.tsx @@ -1,7 +1,6 @@ -import { ReactNode } from 'react' -import { PortalContext, PortalContextValue } from '../context' +import { ReactNode, useContext } from 'react' +import { PortalContext } from '../context' import PortalConsumer from './portal-consumer' -import { useNavigation } from '@react-navigation/native' import PortalHost, { portal } from './portal-host' export type PortalProps = { @@ -9,21 +8,11 @@ export type PortalProps = { * Content of the `Portal`. */ children?: ReactNode - key?: string - manager?: PortalContextValue, - pageId?: number } const Portal = ({ children }:PortalProps): JSX.Element => { - const navigation = useNavigation() - const pageId = navigation?.pageId - return ( - - {(manager) => ( - {children} - )} - - ) + const manager = useContext(PortalContext) + return {children} } Portal.Host = PortalHost diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx index ac0ca186d8..4159387825 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-consumer.tsx @@ -1,5 +1,5 @@ -import { useEffect, useRef, ReactNode } from 'react' -import { PortalContextValue } from '../context' +import { useEffect, useRef, ReactNode, useContext } from 'react' +import { PortalContextValue, RouteContext } from '../context' export type PortalConsumerProps = { manager: PortalContextValue @@ -7,6 +7,7 @@ export type PortalConsumerProps = { } const PortalConsumer = ({ manager, children } :PortalConsumerProps): JSX.Element | null => { const keyRef = useRef(null) + const pageId = useContext(RouteContext) useEffect(() => { manager.update(keyRef.current, children) }, [children]) @@ -16,7 +17,7 @@ const PortalConsumer = ({ manager, children } :PortalConsumerProps): JSX.Element 'Looks like you forgot to wrap your root component with `Provider` component from `@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal/index`.\n\n' ) } - keyRef.current = manager.mount(children) + keyRef.current = manager.mount(children, null, pageId) return () => { manager.unmount(keyRef.current) } diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx index 724dfca79f..eb18039f49 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-portal/portal-host.tsx @@ -7,16 +7,17 @@ import { StyleSheet } from 'react-native' import PortalManager from './portal-manager' -import { useNavigation } from '@react-navigation/native' -import { PortalManagerContextValue, PortalContext } from '../context' +import { PortalContext } from '../context' -export type PortalHostProps = { +type PortalHostProps = { children: ReactNode, pageId: number } -type addIdsMapsType = { - [key: number]: number[] +interface PortalManagerContextValue { + mount: (key: number, children: React.ReactNode) => void + update: (key: number, children: React.ReactNode) => void + unmount: (key: number) => void } export type Operation = @@ -66,43 +67,13 @@ const PortalHost = ({ children, pageId } :PortalHostProps): JSX.Element => { const _updateType = useRef(null) const manager = useRef(null) const queue = useRef>([]) - const _mount = (children: ReactNode, _key?: number, id?: number) => { + const mount = (children: ReactNode, _key?: number, id?: number) => { if (id !== pageId) return - const key = _key || _nextKey.current++ - if (!isMounted.current) { - queue.current.push({ type: 'mount', key, children }) - } else if (manager.current) { - manager.current.mount(key, children) - } - return key - } - - const _unmount = (key: number) => { - if (!isMounted.current) { - queue.current.push({ type: 'unmount', key, children }) - } else if (manager.current) { - manager.current.unmount(key) - } - } - - const _update = (key: number, children?: ReactNode) => { - if (!isMounted.current) { - const operation = { type: 'mount', key, children } - const index = queue.current.findIndex((q) => q.key === key) - if (index > -1) { - queue.current[index] = operation - } else { - queue.current.push(operation) - } - } else if (manager.current) { - manager.current.update(key, children) - } - } - - const mount = (children: ReactNode, _key?: number) => { const key = _key || _nextKey.current++ if (manager.current) { manager.current.mount(key, children) + } else { + queue.current.push({ type: 'mount', key, children }) } return key } @@ -110,22 +81,32 @@ const PortalHost = ({ children, pageId } :PortalHostProps): JSX.Element => { const unmount = (key: number) => { if (manager.current) { manager.current.unmount(key) + } else { + queue.current.push({ type: 'unmount', key, children }) } } const update = (key: number, children?: ReactNode) => { if (manager.current) { manager.current.update(key, children) + } else { + const operation = { type: 'mount', key, children } + const index = queue.current.findIndex((q) => q.type === 'mount' && q.key === key) + if (index > -1) { + queue.current[index] = operation + } else { + queue.current.push(operation) + } } } useMemo(() => { - _addType.current = TopViewEventEmitter.addListener(addType, _mount) - _removeType.current = TopViewEventEmitter.addListener(removeType, _unmount) - _updateType.current = TopViewEventEmitter.addListener(updateType, _update) + _addType.current = TopViewEventEmitter.addListener(addType, mount) + _removeType.current = TopViewEventEmitter.addListener(removeType, unmount) + _updateType.current = TopViewEventEmitter.addListener(updateType, update) }, []) - const navigation = useNavigation() useEffect(() => { + isMounted.current = true while (queue.current.length && manager.current) { const operation = queue.current.shift() if (!operation) return @@ -135,17 +116,14 @@ const PortalHost = ({ children, pageId } :PortalHostProps): JSX.Element => { break case 'unmount': manager.current.unmount(operation.key) + break } } - const focusSubscription = navigation.addListener('focus', () => { - isMounted.current = true - }) return () => { _addType.current?.remove() _removeType.current?.remove() _updateType.current?.remove() - focusSubscription() } }, []) return (