mirror of
https://github.com/kuaifan/dootask.git
synced 2026-04-23 10:18:41 +00:00
feat(mobile): 兼容新 Expo 壳(dootask_expo UA)
配合 dootask-app 仓库的 Expo 迁移(见 docs/migration-eeui-to-expo.md 的 Phase 5),
让服务端和前端同时识别旧 EEUI 壳与新 Expo 壳的 User-Agent,并让 eeui.js 的同步返回
方法在 Expo 壳下优先读取 injectedJS 启动时写入的 __EXPO_INIT_DATA__ / __EXPO_VARIATES__
缓存,避免原本同步 API 变成 Promise 后破坏调用方。
后端:
- Base::isEEUIApp():同时匹配 kuaifan_eeui / dootask_expo
- UserDevice:android_(kuaifan_eeui|dootask_expo) 正则捕获标识段,版本号按实际段名取
- IndexController PDF 预览:浏览器分类兼容 android_dootask_expo / ios_dootask_expo
- SystemController::prefetch:$isApp 同时接受两种 UA
- resources/views/download.blade.php:/eeui|dootask_expo/i
前端:
- app.js:
- isEEUIApp 正则新增 dootask_expo
- $preload 等待条件改为 requireModuleJs 可用 OR window.__EXPO_BRIDGE_READY__,
避免 Expo 壳下等 15 秒超时
- eeui.js:以下几个同步 getter 在 Expo 壳下先读 window.__EXPO_* 再回落到原生:
- eeuiAppVersion / eeuiAppLocalVersion → __EXPO_INIT_DATA__.version
- eeuiAppGetPageInfo → __EXPO_INIT_DATA__.pageInfo
- eeuiAppGetThemeName → __EXPO_INIT_DATA__.themeName
- eeuiAppKeyboardStatus → __EXPO_INIT_DATA__.keyboardVisible
- eeuiAppGetVariate → __EXPO_VARIATES__[key]
- eeuiAppGetCachesString → __EXPO_CACHES__[key](RN 侧后续要同步 broadcast)
旧 EEUI 壳不受影响:只读缓存不存在时自动回落到原有 $A.eeuiModule() 调用,
行为与改动前一致。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
fbd1c829a1
commit
84f225f3f3
@ -1557,7 +1557,8 @@ class SystemController extends AbstractController
|
||||
{
|
||||
$userAgent = strtolower(Request::server('HTTP_USER_AGENT'));
|
||||
$isMain = str_contains($userAgent, 'maintaskwindow');
|
||||
$isApp = str_contains($userAgent, 'kuaifan_eeui');
|
||||
$isApp = str_contains($userAgent, 'kuaifan_eeui')
|
||||
|| str_contains($userAgent, 'dootask_expo');
|
||||
$version = Base::getVersion();
|
||||
$array = [];
|
||||
|
||||
|
||||
@ -457,12 +457,16 @@ class IndexController extends InvokeController
|
||||
'button' => Doo::translate('点击下载'),
|
||||
]);
|
||||
}
|
||||
// 浏览器类型
|
||||
// 浏览器类型(兼容旧 EEUI 与新 Expo 壳)
|
||||
$browser = 'none';
|
||||
if (str_contains($userAgent, 'chrome') || str_contains($userAgent, 'android_kuaifan_eeui')) {
|
||||
$browser = str_contains($userAgent, 'android_kuaifan_eeui') ? 'android-mobile' : 'chrome-desktop';
|
||||
} elseif (str_contains($userAgent, 'safari') || str_contains($userAgent, 'ios_kuaifan_eeui')) {
|
||||
$browser = str_contains($userAgent, 'ios_kuaifan_eeui') ? 'safari-mobile' : 'safari-desktop';
|
||||
$isAndroidApp = str_contains($userAgent, 'android_kuaifan_eeui')
|
||||
|| str_contains($userAgent, 'android_dootask_expo');
|
||||
$isIosApp = str_contains($userAgent, 'ios_kuaifan_eeui')
|
||||
|| str_contains($userAgent, 'ios_dootask_expo');
|
||||
if (str_contains($userAgent, 'chrome') || $isAndroidApp) {
|
||||
$browser = $isAndroidApp ? 'android-mobile' : 'chrome-desktop';
|
||||
} elseif (str_contains($userAgent, 'safari') || $isIosApp) {
|
||||
$browser = $isIosApp ? 'safari-mobile' : 'safari-desktop';
|
||||
}
|
||||
// electron 直接在线预览查看
|
||||
if (str_contains($userAgent, 'electron') || str_contains($browser, 'desktop')) {
|
||||
|
||||
@ -129,8 +129,8 @@ class UserDevice extends AbstractModel
|
||||
}
|
||||
}
|
||||
|
||||
if (preg_match("/android_kuaifan_eeui/i", $ua)) {
|
||||
// Android 客户端
|
||||
if (preg_match("/android_(kuaifan_eeui|dootask_expo)/i", $ua, $m)) {
|
||||
// Android 客户端(兼容旧 EEUI 与新 Expo 壳)
|
||||
$result['app_type'] = 'Android';
|
||||
if ($dd->getBrandName() && $dd->getModel()) {
|
||||
// 厂商+型号
|
||||
@ -145,9 +145,9 @@ class UserDevice extends AbstractModel
|
||||
// 平板
|
||||
$result['app_name'] = 'Phablet';
|
||||
}
|
||||
$result['app_version'] = self::getAfterVersion($ua, 'kuaifan_eeui/');
|
||||
} elseif (preg_match("/ios_kuaifan_eeui/i", $ua)) {
|
||||
// iOS 客户端
|
||||
$result['app_version'] = self::getAfterVersion($ua, $m[1] . '/');
|
||||
} elseif (preg_match("/ios_(kuaifan_eeui|dootask_expo)/i", $ua, $m)) {
|
||||
// iOS 客户端(兼容旧 EEUI 与新 Expo 壳)
|
||||
$result['app_type'] = 'iOS';
|
||||
if (preg_match("/(macintosh|ipad)/i", $ua)) {
|
||||
// iPad
|
||||
@ -156,7 +156,7 @@ class UserDevice extends AbstractModel
|
||||
// iPhone
|
||||
$result['app_name'] = 'iPhone';
|
||||
}
|
||||
$result['app_version'] = self::getAfterVersion($ua, 'kuaifan_eeui/');
|
||||
$result['app_version'] = self::getAfterVersion($ua, $m[1] . '/');
|
||||
} elseif (preg_match("/dootask/i", $ua)) {
|
||||
// DooTask 客户端
|
||||
$result['app_type'] = $osInfo['name'];
|
||||
|
||||
@ -1841,13 +1841,14 @@ class Base
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是App移动端
|
||||
* 是否是App移动端(兼容旧 EEUI 壳与新 Expo 壳)
|
||||
* @return bool
|
||||
*/
|
||||
public static function isEEUIApp()
|
||||
{
|
||||
$userAgent = strtolower(Request::server('HTTP_USER_AGENT'));
|
||||
return str_contains($userAgent, 'kuaifan_eeui');
|
||||
return str_contains($userAgent, 'kuaifan_eeui')
|
||||
|| str_contains($userAgent, 'dootask_expo');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
7
resources/assets/js/app.js
vendored
7
resources/assets/js/app.js
vendored
@ -1,5 +1,5 @@
|
||||
const isElectron = !!(window && window.process && window.process.type && window.electron);
|
||||
const isEEUIApp = window && window.navigator && /eeui/i.test(window.navigator.userAgent);
|
||||
const isEEUIApp = window && window.navigator && /eeui|dootask_expo/i.test(window.navigator.userAgent);
|
||||
const isSoftware = isElectron || isEEUIApp;
|
||||
|
||||
document.getElementById("app")?.setAttribute("data-preload", "false");
|
||||
@ -325,14 +325,15 @@ const $preload = async () => {
|
||||
document.getElementById("app")?.setAttribute("data-preload", "true")
|
||||
|
||||
if ($A.isEEUIApp) {
|
||||
// 同时等待旧 EEUI 的 requireModuleJs 与新 Expo 壳注入的 __EXPO_BRIDGE_READY__
|
||||
const requireTime = new Date().getTime();
|
||||
while (typeof requireModuleJs !== "function") {
|
||||
while (typeof requireModuleJs !== "function" && !window.__EXPO_BRIDGE_READY__) {
|
||||
await new Promise(resolve => setTimeout(resolve, 200));
|
||||
if (new Date().getTime() - requireTime > 15 * 1000) {
|
||||
break
|
||||
}
|
||||
}
|
||||
if (typeof requireModuleJs !== "function") {
|
||||
if (typeof requireModuleJs !== "function" && !window.__EXPO_BRIDGE_READY__) {
|
||||
const errorTip = $A.L("加载失败,请重启软件")
|
||||
const errorView = document.querySelector(".app-view-loading")
|
||||
if (errorView) {
|
||||
|
||||
33
resources/assets/js/functions/eeui.js
vendored
33
resources/assets/js/functions/eeui.js
vendored
@ -35,14 +35,14 @@ import {languageName} from "../language";
|
||||
})
|
||||
},
|
||||
|
||||
// 获取eeui版本号
|
||||
// 获取eeui版本号(Expo 壳下改为读取启动时注入的 __EXPO_INIT_DATA__,兼容旧 EEUI)
|
||||
eeuiAppVersion() {
|
||||
return $A.eeuiModule()?.getVersion();
|
||||
return window.__EXPO_INIT_DATA__?.version ?? $A.eeuiModule()?.getVersion();
|
||||
},
|
||||
|
||||
// 获取本地软件版本号
|
||||
eeuiAppLocalVersion() {
|
||||
return $A.eeuiModule()?.getLocalVersion();
|
||||
return window.__EXPO_INIT_DATA__?.version ?? $A.eeuiModule()?.getLocalVersion();
|
||||
},
|
||||
|
||||
// Alert
|
||||
@ -61,8 +61,12 @@ import {languageName} from "../language";
|
||||
return $A.eeuiModule()?.rewriteUrl(val);
|
||||
},
|
||||
|
||||
// 获取页面信息
|
||||
// 获取页面信息(Expo 壳在 injectedJS 启动时写入 __EXPO_INIT_DATA__.pageInfo)
|
||||
eeuiAppGetPageInfo(pageName) {
|
||||
const cached = window.__EXPO_INIT_DATA__?.pageInfo;
|
||||
if (cached) {
|
||||
return pageName ? { ...cached, pageName } : cached;
|
||||
}
|
||||
return $A.eeuiModule()?.getPageInfo(pageName || "");
|
||||
},
|
||||
|
||||
@ -152,13 +156,16 @@ import {languageName} from "../language";
|
||||
$A.eeuiModule()?.checkUpdate();
|
||||
},
|
||||
|
||||
// 获取主题名称 light|dark
|
||||
// 获取主题名称 light|dark(Expo 壳:启动时注入 + 系统变更推送更新 __EXPO_INIT_DATA__.themeName)
|
||||
eeuiAppGetThemeName() {
|
||||
return $A.eeuiModule()?.getThemeName();
|
||||
return window.__EXPO_INIT_DATA__?.themeName ?? $A.eeuiModule()?.getThemeName();
|
||||
},
|
||||
|
||||
// 判断软键盘是否可见
|
||||
// 判断软键盘是否可见(Expo 壳:keyboardDidShow/Hide 会同步更新 __EXPO_INIT_DATA__.keyboardVisible)
|
||||
eeuiAppKeyboardStatus() {
|
||||
if (window.__EXPO_INIT_DATA__ && typeof window.__EXPO_INIT_DATA__.keyboardVisible === "boolean") {
|
||||
return window.__EXPO_INIT_DATA__.keyboardVisible;
|
||||
}
|
||||
return $A.eeuiModule()?.keyboardStatus();
|
||||
},
|
||||
|
||||
@ -167,8 +174,12 @@ import {languageName} from "../language";
|
||||
$A.eeuiModule()?.setVariate(key, value);
|
||||
},
|
||||
|
||||
// 获取全局变量
|
||||
// 获取全局变量(Expo 壳:setVariate 时 RN 会 broadcast 到所有 WebView 的 __EXPO_VARIATES__)
|
||||
eeuiAppGetVariate(key, defaultVal = "") {
|
||||
const cache = window.__EXPO_VARIATES__;
|
||||
if (cache && Object.prototype.hasOwnProperty.call(cache, key)) {
|
||||
return cache[key];
|
||||
}
|
||||
return $A.eeuiModule()?.getVariate(key, defaultVal);
|
||||
},
|
||||
|
||||
@ -177,8 +188,12 @@ import {languageName} from "../language";
|
||||
$A.eeuiModule()?.setCachesString(key, value, expired);
|
||||
},
|
||||
|
||||
// 获取缓存数据
|
||||
// 获取缓存数据(Expo 壳:若 __EXPO_CACHES__ 已 hydrate 就同步读取,否则回落到原生桥)
|
||||
eeuiAppGetCachesString(key, defaultVal = "") {
|
||||
const cache = window.__EXPO_CACHES__;
|
||||
if (cache && Object.prototype.hasOwnProperty.call(cache, key)) {
|
||||
return cache[key];
|
||||
}
|
||||
return $A.eeuiModule()?.getCachesString(key, defaultVal);
|
||||
},
|
||||
|
||||
|
||||
@ -78,7 +78,7 @@
|
||||
document.body.classList.add("dark");
|
||||
}
|
||||
//
|
||||
const isEEUIApp = window && window.navigator && /eeui/i.test(window.navigator.userAgent);
|
||||
const isEEUIApp = window && window.navigator && /eeui|dootask_expo/i.test(window.navigator.userAgent);
|
||||
if (isEEUIApp) {
|
||||
document.querySelector(".link").addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user