 joywayerandClaude Opus 4.7
|
57d4d93781
|
SplashOverlay 抽公共组件 + 进度条配色统一
大厅 / 子游戏之前各有一份 private SplashOverlay:lobby 版含错误态 UI(拉远端
配置失败 splash 上提示重试 / 前往设置);sub-game 版只有 image + label +
progressView,配色规则不同(width 0.45 vs 0.7,progressTintColor 不设)。
抽到 Source/WebView/SplashOverlay.swift 共用组件:
- 含完整加载 UI + 错误态 UI(sub-game 不调 showError,但保留兜底)
- 进度条 progressTintColor = .black,trackTintColor = .systemGray5(实色浅灰)
之前 lobby 用 .systemBlue + label.withAlphaComponent(0.15) 半透明轨道,
黑色填充推过半透明轨道时视觉上像"颜色逐渐加深";改实色轨道后是干脆的
"黑滑过灰",无加深错觉
- progressView.setProgress(animated: true) 保留补间,让快下载也能看到平滑增长
WebContainer / SubGame 删除各自的 private SplashOverlay 类定义,引用同一份。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-23 21:34:25 +08:00 |
|
 joywayerandClaude Opus 4.7
|
e50dfb75af
|
子游戏跳转链路修复 + H5 音频统一可用
修复后子游戏 SwitchOverGameData 跳转完全可用,行为对齐 daoqi msext:
【zip URL 解析】H5 字段 gamedownloadurl 实为 gameid(msext gameController.m:544
注释"游戏ID")。RemoteConfigClient 新增 lastParsed 缓存 + current() getter;
SubGameViewController.resolveBoot 做两次 VersionResolver.resolve:
- lobby 视角(gameId=bc.gameId)→ ResolvedVersion 传 AppDataWriter 算
app_appversion 审核标志(msext result_state 等价)
- sub-game 视角(gameId=H5 传的 gameid)→ 真实 game_zip 下载 URL
之前 SubGameDownloader 直接把 token 当 URL 用,URLSession 报 -1002
unsupported URL;AppDataWriter resolvedVersion 始终 nil 导致子游戏 app_appversion
永远 0、丢失审核切换能力。
【push 前停大厅背景音】AppCoordinator.showSubGame 节流通过后、push 前调
AudioPlayer.stopAllBackground(msext NewRootVC.m:549-552 等价)。
【H5 音频】
- AVAudioSession.setCategory(.playback) 在 AppDelegate 启动期设置,让原生
+ WKWebView 内 <audio> 都不受静音键影响(msext RootVC.m:1302 等多处等价)
- WKWebViewConfiguration.mediaTypesRequiringUserActionForPlayback = [],子游戏
push 后立即 loadFileURL 自播背景音不再被 WebKit 静默拦截
- LocalAudioHandler.register 增 assetsRoot 参数,srcIsloop 按容器各自的 H5
根目录拼 assets/wav/{src}(msext gameController.m:364 等价,大厅 / 子游戏
音频文件在各自 zip 内不重叠)。修前所有路径都指向大厅根,子游戏读不到自己的
音频文件,AVAudioPlayer 报 OSStatus 2003334207 (wht?
kAudioFileUnsupportedFileTypeError)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-23 21:33:46 +08:00 |
|
 joywayerandClaude Opus 4.7
|
be09e6e697
|
splash 进度条宽度从屏幕 45% 加长到 70%
原 0.45 倍宽度在横屏中央显得偏短,下载进度反馈不够明显。加长到 0.7 倍
更接近常见 app 启动进度条视觉。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-23 07:29:57 +08:00 |
|
 joywayerandClaude Opus 4.7
|
1247c14351
|
splash 启动图改 scaleAspectFill 全屏覆盖(与 WebView 同款撤销黑边)
接上一 commit f4307cc(WebView 全屏铺满):splash 启动图也应保持一致策略,
否则启动瞬间(iOS 系统 LaunchScreen)和过渡期(SplashOverlay)仍会显示
左右大黑边,与 WebView 全屏的视觉不连续。
3 处 contentMode 从 scaleAspectFit 改为 scaleAspectFill:
- LaunchScreen.storyboard(iOS 启动瞬间)
- WebContainerViewController 内 SplashOverlay.imageView
- SubGameViewController 内 SplashOverlay.imageView
scaleAspectFill 行为:素材 1136×640(16:9)在现代 iPhone 横屏(19.5:9)
上高度撑满,宽度按比例放大→左右轻微裁切。启动图左右是空白底色,logo
+ 文字主体居中可见,裁切无影响。
等价 daoqi msext LaunchImage Asset Catalog 时代「启动图覆盖整屏」体感。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-23 07:26:15 +08:00 |
|
 joywayerandClaude Opus 4.7
|
f4307ccc80
|
WebView 改全屏铺满(与 daoqi msext 同款),撤销 16:9 letterbox
用户反馈:现代 iPhone 横屏(19.5:9 比例)下 16:9 letterbox 出现明显
左右大黑边,与原项目视觉不一致。
调研 daoqi msext NewRootVC.m:256:实际用 CGRectMake(0, 0, DEVW, DEVH)
直接全屏 WKWebView,没有任何 letterbox。H5 viewport 自身处理设计稿
(1280×720)→ 设备实际宽高的伸缩。msext 上线多年用户已习惯此视觉。
改 WebContainerViewController + SubGameViewController 的 setupBridgedWebView:
- 去掉 16:9 aspect 约束 + 居中 + width/height max/fill 优先级方案
- 改为 top/bottom/leading/trailing 四 edge 直接撑满 view
文件头注释同步:从「16:9 letterbox」改为「全屏铺满」。
原 Design §6.3.5 的 letterbox 决策已被现网实测推翻,未来如某些 H5
页面需要恢复 letterbox 再单独抽 LetterboxLayout(YAGNI)。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-23 07:23:34 +08:00 |
|
 joywayerandClaude Opus 4.7
|
4ca4459ad5
|
启动期错误态精确分类:NWPath 区分真断网/权限拒,按钮语义动态切换
用户反馈:之前的文案统一映射 NSURLError code,没区分"真没网"和"权限
被拒"——两类 iOS 14+ 上都报 -1009,但 NWPath.unsatisfiedReason 里有
wifiDenied / cellularDenied 标记可精确区分。点击重试时也没有针对权限
被拒场景做特殊处理(重发请求被同样的权限决定拒绝,原地循环)。
改动:
1. **NWPath snapshot 精确分类**
- 新增 BootErrorKind: noNetworkAccess / wifiDenied / cellularDenied /
networkTimeout / networkCannotReach / localResourceMissing / unknown
- 新增 currentPathSnapshot() async:临时 NWPathMonitor 等首次 update
拿当前 path
- classifyBootError 接 path 参数:先看 NWPath.unsatisfiedReason
(iOS 14.2+)区分权限拒/真断网;fallback 才按 NSURLError code
- runBootPipeline 的 catch 内 await snapshot,传入精确 kind
2. **按钮语义按 kind 切换**(同一个按钮承担双语义,UX 一致)
- 权限拒(wifiDenied / cellularDenied)→ 按钮显示"前往设置",
点击跳 UIApplication.openSettingsURLString
- 其他(noNetworkAccess / timeout / unreachable / localResource)→
按钮显示"重试",点击重跑 runBootPipeline
- SplashOverlay.showError 加 actionTitle 参数,通过 UIButton.Configuration
动态改 title
3. **didBecomeActive 自动重试**(行业最佳做法)
- 权限拒分支跳设置时挂 UIApplication.didBecomeActiveNotification 监听
- 用户在设置改完权限切回 app → 自动 runBootPipeline → 进入大厅
无需用户再点任何按钮
- 一次性 observer:触发后自动取消,避免重复
- runBootPipeline 入口同时 stop NWPathMonitor + stop settingsReturnObserver
保证多次重试干净
文案精确化:
- noNetworkAccess:"当前未联网 / 请检查 Wi-Fi 或蜂窝数据后重试"
- wifiDenied:"未授权使用无线局域网 / 请前往 iOS 设置 → 本应用 → 打开
「无线数据」,授权后将自动重试"
- cellularDenied:"未授权使用蜂窝数据 / 请前往 iOS 设置 → 本应用 → 打开
「无线数据」,或连接 Wi-Fi 后重试"
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-23 07:19:50 +08:00 |
|
 joywayerandClaude Opus 4.7
|
0e446df97a
|
splash 进度条配色:systemBlue 填充 + .label 15% alpha 轨道
跟错误态"重试"主按钮 systemBlue 视觉一致;轨道用 .label 系统语义色
的 15% alpha,浅深模式自适应(浅模式淡灰、深模式淡白),在浅色启动图
上比默认 UIProgressView 灰条更显眼。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-23 07:13:13 +08:00 |
|
 joywayerandClaude Opus 4.7
|
34f76e8824
|
启动期错误态 UX 调整:浅背景深色字,仅保留"重试"按钮
按用户反馈精简错误态 UI:
- splash 启动图是浅色背景(米白底 + logo + 蓝色文字),白色文字在上面
看不清。错误态显示时改为 backgroundColor = .systemBackground(浅模式
白、深模式黑),同时隐藏启动图 imageView,文字用 .label / .secondaryLabel
系统语义色确保两种 appearance 下都对比清晰
- 去掉"前往系统设置"次按钮:仅保留蓝色填充主按钮"重试"
- "无法连接服务器"文案删去"或前往系统设置"尾句
"权限弹窗"诉求的实际实现:
重试按钮直接重跑 runBootPipeline → RemoteConfigClient.fetch →
URLSession.dataTask 重新发起请求。这正是 Apple 推荐的"让网络请求
自然触发权限弹窗"模式:
- 首次启动若系统网络权限弹窗未响应/被拒,重新发请求会再次触发系统
对权限状态的评估,可能复弹
- 用户在 iOS 隐私管理里改了权限后回 app,点重试就能成功
- NWPathMonitor 也在静默监听,切到正常网络后无需点击即自动重试
不需要额外的"手动申请权限" API 代码(iOS 没有公开的 API 强制弹起
被拒绝的网络权限弹窗);现有「重新发请求」即是教科书做法。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-23 07:10:23 +08:00 |
|
 joywayerandClaude Opus 4.7
|
5731588d33
|
启动期错误处理 v2:撤回降级,splash 错误态 + NWPathMonitor 自动重试
撤回上一 commit 74f6925 的"远端 config 失败就降级进大厅"路径——错过升级
判定 = 用户用旧 IPA 不被强制升级 = 业务风险。新策略:
**远端 config 失败 → 停留 splash 显示错误态**(不弹 modal alert / 不暴露原始 NSError)
行业主流方案:
1. SplashOverlay 扩展错误态 UI(隐藏 label / progress,显示 title +
message + "重试" 主按钮 + "前往系统设置" 次按钮),通过 onRetry /
onOpenSettings closure 与 controller 解耦
2. WebContainerViewController.presentBootError 按 BootErrorKind 分类给出
友好文案(networkOffline / networkTimeout / networkCannotReach /
localResourceMissing / unknown)
3. 启动期独立 NWPathMonitor,仅在"从无网变有网"时静默自动重试一次
(初始 nil 状态不触发,避免死循环;用户切到 Wi-Fi 后无需手动操作)
4. runBootPipeline 重新进入时 stopBootRetryWaiting + splash.hideError
保证多次重试干净(防止 monitor 泄漏)
进大厅的必要条件(恢复严格):
- ResourceUnzipper.ensureReady 成功(本地 H5 zip 就位)
- RemoteConfigClient.fetch 拿到正确 config(升级判定可执行)
- 本地 appVersion >= 远端(否则走 IPA 升级 modal alert)
- showmessage 为空(否则走运营 modal alert)
- LobbyZipUpgrader 完成(含跳过升级 / 真升级两路径)
业务流程信号(operationalMessage / ipaUpgradeRequired)继续用 modal alert
(这两类不是错误,是产品决策的弹窗永停);技术错误(网络/解压/写文件等)
统一进 splash 错误态 + 自动重试。
raw error 仍 print 到 Xcode console(含 NSURLErrorDomain code 等开发期
排查信息),用户视角永不暴露技术细节。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-23 06:47:04 +08:00 |
|
 joywayerandClaude Opus 4.7
|
74f6925be3
|
启动期错误处理 UX 改造:优雅降级 + 友好文案,不暴露技术细节
修复用户反馈的体验问题:网络权限未同意 / 长期不响应权限弹窗时,
启动会弹出"启动失败" + 原始 NSError("Error Domain=NSURLErrorDomain
Code=-1009 ...大量技术细节..."),用户被吓懵。
按行业主流做法(参考微信/支付宝/网易云音乐启动期错误处理)改两件事:
1. **优雅降级:远端 config 失败 ≠ 启动失败**
runBootPipelineSteps 把"拉远端 config + 升级判定"整段包 do/catch:
- 业务流程信号(operationalMessage / ipaUpgradeRequired)正常抛出弹窗
- 网络 / 解析 / H5 zip 下载任何失败 → 用 LocalVersionReader 构造
fallback ResolvedVersion,跳过升级判定,直接加载本地 H5 大厅
- splash 文案区分在线 "加载大厅..." / 离线 "离线加载..."
- raw error 仍 print 到 Xcode console 便于排查
理论依据:ResourceUnzipper.ensureReady 之后本地 H5 zip 已就绪,
远端 config 仅用于升级判定,失败时本地大厅完全可用。
2. **友好文案 + 多按钮 alert(showRetryableAlert)**
- 新增 BootErrorKind 分类:剥洋葱看 NSURLError code 映射到
networkOffline (-1009) / networkTimeout (-1001) / networkCannotReach
(-1003/-1004/-1005/-1009/-1018/-1019/-1020) / localResourceMissing /
unknown
- 每类配友好标题 + 正文("网络未连接"/"网络较慢"/"暂时无法连接服务器"
而非 raw "Error Domain=...")
- 按钮组合按类别:网络类含"去设置"(跳 UIApplication.openSettingsURLString)
+ 重试 + 稍后再说;本地资源类只重试 + 稍后再说
- "重试" 不强制("稍后再说" 让用户自由退出,避免单按钮卡死)
结果:用户在断网 / 未授权 Wi-Fi / 测试域名不可达等场景下,splash 多停
约 7s(3 次重试 + 退避)后自动降级进入大厅,**不弹任何 alert**。仅当
本地资源也失效(极罕见)时才弹友好提示,永不暴露原始 NSError。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-23 03:12:33 +08:00 |
|
 joywayerandClaude Opus 4.7
|
5f3a435180
|
Phase 6 commit C:backgameData pop + getWebdata 反向 callback
- 新增 Source/Bridge/Handlers/BackGameDataHandler.swift(仅 SubGameViewController 注册):
- 调 AudioPlayer.stopAllBackground 停背景音
- AppCoordinator.popSubGame(returningData:) → popViewController + 发 .subGameDidReturn
- data 兼容 string / object(非字符串走 JSONSerialization 序列化透传)
- 字面 cb "backgameData"(msext gameController.m:691-709 等价,
WXApi 清理跳过 — 微信 SDK 待 Phase 4.E)
- AudioPlayer.stopAllBackground:无条件停背景音(msext 不看 type 直接 nil 行为)
- SubGameViewController.registerBridgeHandlers:挂上 BackGameDataHandler,
注释更新 exitRoom/getVideoinfo/createRoom 推迟到 Phase 8
- WebContainerViewController:在 setupExternalSubscriptions 挂 .subGameDidReturn
观察者 → bridge.call("getWebdata", .string(data)),teardown 时 removeObserver
(生命周期与 battery/network/appservice 同一对,子游戏栈顶时大厅已 teardown
避免双发)
- Plan §5.6.3 / 6.5 / 6.6 / §8 进度已勾选
至此 SwitchOverGameData → push 子游戏 → backgameData → pop + getWebdata
完整链路接通;子游戏视频房间(exitRoom/getVideoinfo/createRoom)留待 Phase 8。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-22 21:05:23 +08:00 |
|
 joywayerandClaude Opus 4.7
|
68cf7140d7
|
Phase 6/7 stub:SwitchOverGameData + OpenurlTitleData
补齐 H5 桥 §3.1 [15][17]两个 stub handler,让 H5 调子游戏切换 / 弹层
入口时不再报 "no handler registered",避免业务流程卡住。完整流程留
Phase 6 / Phase 7 实施。
新增:
- Source/Bridge/Handlers/SwitchOverGameHandler.swift §3.1 [17]stub
cb "SwitchOverGameData"(驼峰首字母大写沿用 msext 字面)
完整实现需 AppCoordinator + SubGameViewController + SubGameDownloader
(Phase 6.4 落地)
- Source/Bridge/Handlers/OpenurlTitleDataHandler.swift §3.1 [15]stub
cb "OpenurlTitleData"
完整实现需 OverlayViewController + window.settings polyfill + 3s 节流
(Phase 7 落地);注意契约硬约束 "title " 末尾空格已在注释提示
WebContainer.registerBridgeHandlers 接入。
至此 H5 桥 §3.1 大厅可调的全部 16 项 handler(除 backgameData / createRoom
等子游戏专属 §H 4 项)都已注册(含完整实现 / stub 两种状态),
H5 启动 / 业务期任何 handler 调用都不会报 "no handler"。
BuildProject 通过
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-22 20:14:31 +08:00 |
|
 joywayerandClaude Opus 4.7
|
aa80aec18c
|
Phase 4.A:登录+分享 stub + QQ 改走 URL Scheme 不依 SDK
按用户约定调整 Phase 4 方向:微信继续走 SDK(等项目方提供),QQ 分享
改用 URL Scheme 直接调起 QQ app,**不依赖 SDK**。
调研 daoqi/msext/Class/Utils/QQShareManager.m 发现已有完整的 URL Scheme
fallback 路径:
- line 14: __has_include(<TencentOpenAPI/QQApiInterface.h>) 条件编译
- line 231 注释: "Fallback: 使用 URL Scheme (mqqapi://) - 无需 SDK,
直接调起 (Legacy)"
- mqqapi://share/to_fri / mqqapi://share/to_qzone / mqqopensdkfriend://share
等 schema 实现见 line 733/786/789
新增 handler stub(让 H5 启动 / 业务期调登录 / 分享不报 "no handler"):
- Source/Bridge/Handlers/AccreditLoginHandler.swift
accreditlogin cb "Response from accreditlogin"
完整实现需 Phase 4.B 微信 SDK + WeChatManager.authorize() + WeChatAuth.exchangeForUser
+ bridge.call("sharelogin", 7 字段含大写 Province) 反向 callback
- Source/Bridge/Handlers/FriendsShareHandler.swift
friendsSharetypeUrlToptitleDescript cb "sharefriend"
完整实现需 ShareCenter 策略分发 + sharesuccess 反向 callback
WebContainer.registerBridgeHandlers 接入。
文档调整:
- Plan §2.3 阻塞表:QQ OpenSDK 标"不需要",改 URL Scheme 路径
- Plan §5 Phase 4 前置:去掉 QQ OpenSDK 一行
- Plan §5 Phase 4.2:完整改写为 URL Scheme 方案,含 LSApplicationQueriesSchemes
清单 + URL 构造样例 + "调起即 success" 乐观策略说明
- Plan §8 进度追踪:4.2 QQ SDK Vendor 标"不需要"
- Plan §6.4 里程碑加 3 行(前两轮 commit hash 补 + 本次)
- CLAUDE.md 依赖管理段加 "不接入 QQ SDK" 约定(与"不引入 CocoaPods"
并列的负面清单)
- Verification-Checklist Phase 4 章节:E.19/E.20 stub + K/L 待 4.B/4.C
完整后填充验证项
BuildProject 通过
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-22 19:19:23 +08:00 |
|
 joywayerandClaude Opus 4.7
|
0602b9d4b6
|
Phase 3.A:srcIsloop 本地音频 + 3.B/3.C/3.D handler stub
3.A 本地音频完整落地(与 msext NewRootVC initJSdata 等价):
Source/Audio/AudioPlayer.swift(@MainActor 状态机):
- playOnce(url) 单次按钮音;buttonPlayers 数组保活池避免多次点击
互相打断(duration + 0.5s 后清理)
- loopBackground(url, type:) 循环背景音;numberOfLoops = -1 +
backgroundType 记录用于同名停止
- stopBackground(type:) 同名校验后才停止(msext 同款语义)
Source/Bridge/Handlers/LocalAudioHandler.swift:
- 注册 srcIsloop handler(@MainActor enum)
- 入参 src + isloop,按 0/1/-1 分支调 AudioPlayer.shared 对应方法
- audioFileURL 路径:{Caches}/{gamedir}/{gamestart}/assets/wav/{src}
与 msext NewRootVC 路径约定 1:1(H5 zip 内 assets/wav 子目录)
- audioFileURL 标 nonisolated 因 SandboxPaths 是 nonisolated 静态
- cb 字面 "Response from srcIsloop" 严格保持
Source/Bridge/Handlers/RemoteAudioHandler.swift(3.B/3.C/3.D stub):
- prepareaudio + mediaTypeAudio 仅 cb 维持契约,无真实业务
- 待 Phase 3.B(opencore-amr .a 接入)+ 3.C(七牛 SPM + token)+
3.D(远端播放)完整后升级
- 按 CLAUDE.md 原则 A:H5 调啥就得有人接,即使是 stub
WebContainerViewController.registerBridgeHandlers 接入:
- LocalAudioHandler.register(完整实现)
- RemoteAudioHandler.register(stub)
Plan §5 Phase 3.1 勾选;3.2 真机验证留待 H5 提供音频文件后做。
Verification-Checklist.md 新增 Phase 3 章节:
- H. 本地音频(E.14-E.16 srcIsloop 三个 isloop 值)
- I. prepareaudio / mediaTypeAudio stub(E.17 E.18)
BuildProject 通过
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-22 19:11:34 +08:00 |
|
 joywayerandClaude Opus 4.7
|
09f071e38f
|
Phase 2.C:ExternalSubscriptions 生命周期管理(防双发预备)
按 Design §2.4.2 把 battery / network / appservice 三组外部事件源的订阅
挂钩与 VC 生命周期配对:viewWillAppear → setup,viewWillDisappear →
teardown。配套约束:未来 Phase 6 子游戏 push 上来时大厅自动 teardown,
避免桥事件双发(栈深 ≤ 2,§2.4.3)。
WebContainerViewController 改造:
- 新增 setupExternalSubscriptions():
* 幂等 start 3 个 monitor(NetworkMonitor / BatteryMonitor /
AppLifecycleObserver)
* 设 BatteryMonitor.onChange → evaluateJavaScript("window.app_getbattery=N")
+ bridge.call("getBattery", "%.2f")
* 设 NetworkMonitor.onChange → evaluateJavaScript("window.app_getnetwork=N")
+ bridge.call("getnetwork", "1"/"2"/"3")
* 设 AppLifecycleObserver.onBackground/Foreground → bridge.call("appservice",
"1"/"2")
- 新增 teardownExternalSubscriptions():
* 解绑 onChange / on{Background,Foreground} = nil
* 释放对 bridge/webView 的 closure 引用避免子游戏 push 后双发
* monitor 不停(保持后台 currentXxx 状态新鲜),下次 setup 直接 resume
- viewWillAppear / viewWillDisappear override 调 setup / teardown 配对
- writeAppDataFiles 精简:只写首次值 + 启动 NetworkMonitor/BatteryMonitor
(需要 currentXxx 读首次值);AppLifecycleObserver 启动挪到 setup
至此 Phase 2 全部完成(2.A 8 个 handler + 2.B 5 项反向 callback + 2.C
生命周期管理)。
BuildProject 通过
Plan §5 Phase 2.13 勾选
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-22 19:02:35 +08:00 |
|
 joywayerandClaude Opus 4.7
|
4986f36967
|
业务期 app_getbattery / app_getnetwork 改为 evaluateJavaScript 重新赋值
按用户指示调整变化时机制:
启动期(保持):loadFileURL 之前写 4 个 app_*.js 文件,H5 同步
<script src> 引入读到实际值
业务期(调整):变化时**不重写文件**,改为
webView.evaluateJavaScript("window.app_getbattery=N;") 直接重新赋值
全局变量。原因:H5 已加载完后 <script src> 不会再 fetch,重写文件
对当前页面无影响(仅影响下次 reload),改 evaluateJavaScript 即时
生效 + 无文件 IO 开销。
与 §3.2 反向 callback 双轨同时执行:
- evaluateJavaScript 让 H5 业务期同步读 app_xxx 永远拿到最新值
- bridge.call 让 H5 注册的 callback handler 收到事件主动响应
WebContainerViewController.writeAppDataFiles 改动:
- BatteryMonitor.onChange:Task @MainActor await
webView.evaluateJavaScript("window.app_getbattery=N;") + bridge.call
- NetworkMonitor.onChange:同款 evaluateJavaScript + bridge.call
- AppLifecycleObserver:appservice 无对应 app_* 全局变量,只走 bridge.call
文档同步:
- Design §7.5.2 改为「两阶段」表述:启动期写文件 + 业务期 evaluateJavaScript
- Design §7.5.4 接入点代码示例同步
- Contract §4.2 app_battery.js / app_network.js 子节更新时机段
- Contract §4.2 修订记录加一行
- Plan §6.4 里程碑加一行 + 前两轮 commit hash 补充
BuildProject 通过
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-22 18:50:53 +08:00 |
|
 joywayerandClaude Opus 4.7
|
23b3a1fdad
|
Phase 2.B:3 项反向 callback 落地(getBattery / getnetwork / appservice)
完成大厅业务期实时状态推送给 H5 的事件驱动链路。每次变化时同时(1)
重写对应 app_*.js 文件供下次 reload,(2)bridge.call 反向推送给当前
页 H5 — 双轨与原 msext 行为等价。
新增 Source/Resource/BatteryMonitor.swift:
- @MainActor public final class BatteryMonitor,shared 单例 + 幂等 start()
- 监听 UIDevice.batteryLevelDidChangeNotification + 启动期读首次值
- currentLevel 同步快照(max(level, 0) 兜底模拟器 -1)+ onChange 回调
- notification block 内 MainActor.assumeIsolated 安全跨到 main actor
新增 Source/Resource/AppLifecycleObserver.swift:
- @MainActor public final class AppLifecycleObserver,shared 单例 + 幂等 start()
- 监听 didEnterBackgroundNotification + willEnterForegroundNotification
- onBackground / onForeground 双钩子;assumeIsolated 同上
WebContainerViewController.writeAppDataFiles 接入(替换 inline addObserver):
- 3 个 monitor 启动统一聚合(NetworkMonitor + BatteryMonitor + AppLifecycle)
- BatteryMonitor.onChange:writeBattery + bridge.call("getBattery", "%.2f")
- NetworkMonitor.onChange:writeNetwork + bridge.call("getnetwork", "1"/"2"/"3")
- AppLifecycle.onBackground/Foreground:bridge.call("appservice", "1"/"2")
- 首次值改读 BatteryMonitor.currentLevel 而非 UIDevice 原始值
至此 Phase 2.B 全部完成(5 项反向 callback:getphoneinfo / getBattery /
getnetwork / appservice / shakeEnd 全部接好)。
剩 Phase 2.C ExternalSubscriptions 生命周期管理(防双发,栈深 ≥ 2 时
下层不发桥事件)。
BuildProject 通过
Plan §5 Phase 2.B 全部勾选
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-22 18:45:03 +08:00 |
|
 joywayerandClaude Opus 4.7
|
d38ff0f8c4
|
Phase 2.A:大厅 8 个简单 handler 落地 + BridgeData 加 nonisolated
闭合 Phase 1.17 联调最后一公里(解决 Xcode 日志 4 个 "no handler registered"
+ 提前落地 4 个无外部依赖的简单 handler)。
新增 7 个 handler 模块(Source/Bridge/Handlers/):
- ClipboardHandler.swift §3.1 [13][14]剪贴板 UIPasteboard.general
- ShakeHandler.swift §3.1 [7][8][9]摇一摇 + §3.2 [5]shakeEnd 反向
canShake / canVoice 状态 + motionEnded 转发钩子
- VoicePlayingHandler.swift §3.1 [6]actor VoiceCenter.shared 开关位
- DeviceInfoHandler.swift §3.1 [21]+ §3.2 [1]getphoneInfo 大写 I 入、
getphoneinfo 小写 i 出反向 callback
DeviceInfoSnapshot 6 字段(IDFA 用 idfv 兜底,
CLAUDE.md 不接 ATT/IDFA)
- BrowserHandler.swift §3.1 [16]UIApplication.open async(Task @MainActor
异步打开,闭包先 cb 不等 open 完成)
- OpenSaomaHandler.swift §3.1 [22]空 stub(契约要求注册)
- StartLocationHandler.swift §3.1 [20]Phase 5 完整实现,Phase 2 stub
扩展 VibratorHandler.swift:
- §3.1 [10]vibrator(原有)+ [11]repeatvibrator(msext RootVC.m:1820 沿用:
实际单次 vibrate 不真循环)+ [12]canclevibrator
BridgeProtocol.swift BridgeData 加 nonisolated:
- 项目默认 SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor 让 BridgeData 跨 actor
访问被拦;asString / asDouble / asInt / asBool / asArray / asObject /
subscript 全部标 nonisolated(纯值类型本就该是 isolation-free)
- 影响所有 @Sendable async handler 闭包内的 data?.asXxx 访问
WebContainerViewController 接入:
- 加 private let shakeHandler = ShakeHandler() 持有
- registerBridgeHandlers() 注册 8 个 handler 模块
- 加 canBecomeFirstResponder = true + viewDidAppear becomeFirstResponder
- 加 motionEnded(_:with:) 转发到 shakeHandler.handleMotionEnded
(motion 检测必需 first responder,否则不触发)
BuildProject 通过。重装 app 后 H5 启动应不再报 "no handler registered" 错误。
Plan §5 Phase 2.A 全部勾选(含 startlocation bonus stub + BridgeData
nonisolated 重构)。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-22 18:38:00 +08:00 |
|
 joywayerandClaude Opus 4.7
|
829cc8f825
|
代码层落地 AppDataWriter + NetworkMonitor + WebContainer 接入
按 Design §7.5 蓝图落地,让 H5 启动时能从沙盒读到实际渠道/启动/设备
值,闭合 Phase 1.17 联调"app_gameconfig 不正确"现象。
新增 Source/WebView/AppDataWriter.swift:
- public struct AppDataWriter(@MainActor)
- ContainerRole enum:.lobby / .subGame(name:, dir:)
- writeInitial():写 app_data.js(12 项)+ app_gamesname.js(暂只写当前
gameStart 一项,Phase 6 子游戏完整时扩为扫描已装列表)
- writeBattery(level:):写 app_battery.js(var app_getbattery=N;)
- writeNetwork(code:):写 app_network.js(var app_getnetwork=N;)
- 字面严格对齐 msext:
* 字符串单引号包裹 'value'
* 数值无引号(version=1 / Launchtype=0 / getwifisignalLevel=1)
* app_gamesname 用 "var app_gamesname=new Array(...)"(var 后两空格)
* 大小写硬约束:Launchtype L 大、getwifisignalLevel wifi 小 + signal/Level 区分
- escape 转义反斜杠 / 单引号 / 换行(避免渠道字段含单引号导致 H5 JS 解析错)
- Logger debug 输出文件路径 + 每个 key=value 一行,便于 §7.5.8 联调对账
新增 Source/Resource/NetworkMonitor.swift:
- @MainActor public final class NetworkMonitor
- NWPathMonitor 最简 wrap:currentCode 同步快照(默认 2 WiFi,启动后被
首次 path 更新)+ onChange MainActor 回调
- shared 单例 + 幂等 start()
WebContainer.runBootPipelineSteps 接入:
- switch outcome 前提取 let resolved: ResolvedVersion,避免变量作用域
限制 case 内
- 新增 writeAppDataFiles(resolved:):启动 NetworkMonitor + 开 battery
监控 + 写 4 个文件首次值 + 挂 addObserver(battery) + onChange(network)
- 调用位置:step 5 LobbyZipUpgrader 之后、step 6 loadFileURL 之前
(与 msext NewRootVC.initJSdata 等价时序)
BuildProject 通过
Plan §6.4 里程碑加一行。
Phase 1.17 联调验证:启动后 H5 console 应读到实际 app_channel /
app_gameconfig 等值,不再是 H5 zip 包内默认占位。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-22 12:44:45 +08:00 |
|
 joywayerandClaude Opus 4.7
|
6c943e2924
|
Phase 1.15:VibratorHandler 单次振动 handler
新增 ylgamehall/Source/Bridge/Handlers/VibratorHandler.swift:
- 无状态 public enum VibratorHandler,static func register(on:)
- 入参忽略(参考原 msext RootVC.m:1816-1818:time 参数实际不读取)
- 副作用:AudioServicesPlaySystemSound(kSystemSoundID_Vibrate)
- responseCallback:.string("vibrator"),与契约 §3.1 [10]一致
- 模拟器无振动硬件、调用静默忽略;真机才能感知
WebContainerViewController.viewDidLoad 新增 registerBridgeHandlers()
单一聚合点,Phase 2+ 在此追加更多 handler,便于发现 / 调试。
repeatvibrator / canclevibrator 严格守住 Plan §5 Phase 2.2 范围,不
提前实现。
BuildProject 通过
Plan 进度已勾选(§5 Phase 1.15 + §8)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-22 04:02:33 +08:00 |
|
 joywayerandClaude Opus 4.7
|
0ecfa4fb69
|
修复 SplashImage 方向:素材逆时针旋转 90° 改为真正横屏
源素材 docs/res/Res/Default-568h@2x~iphone.png 是 CgBI PNG,物理 640×1136
但画面内容是"横躺"在竖向容器里 —— msext 旧 LaunchImage 系统依赖 device
orientation 自动旋转,现代 LaunchScreen.storyboard 没有这个魔法,直接显示
会看到躺倒的画面(用户反馈:启动图的横竖方向错误)。
处理:
- 用 sips -r -90 -s format png 一次性把素材逆时针旋转 90° 输出为标准
PNG(顺便去掉 CgBI 优化标志),物理像素 1136×640,覆盖到
ylgamehall/Assets.xcassets/SplashImage.imageset/SplashImage@2x.png
- LaunchScreen.storyboard 中 <image> 的 width/height 从 640/1136 改为
1136/640;UIImageView 的 contentMode 从 scaleAspectFill 改为
scaleAspectFit。aspectFit 在比 16:9 更宽的现代横屏 iPhone(如 16 Pro
≈2.17:1)上左右补黑边(黑底无视觉违和),保画面完整不裁切 logo
- WebContainer 的 SplashOverlay 用同款 scaleAspectFit,避免启动 → 容器
视觉过渡时出现裁切方式跳变
- BuildProject 通过
Plan 进度已勾选(§5 Phase 1.14.b + §8 同步修订)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-22 03:10:53 +08:00 |
|
 joywayerandClaude Opus 4.7
|
f795957eb1
|
Phase 1.14.e:webView didFinish 后 0.3s 淡出 splash
把 WKNavigationDelegate.didFinish 里的 splash.isHidden = true 换成
UIView.animate(withDuration: 0.3) 渐变 alpha=0 + completion 内
removeFromSuperview,避免渲染瞬间硬切,也让 view 层级在大厅起来后
完全干净(无空持有的 splash 占位)。
BuildProject 通过
Plan 进度已勾选(§5 Phase 1.14.e + §8)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-22 02:51:25 +08:00 |
|
 joywayerandClaude Opus 4.7
|
f247b81d6d
|
Phase 1.14.d:WebContainerViewController(16:9 letterbox + Splash + 启动流水线)
新增 ylgamehall/Source/WebView/WebContainerViewController.swift:
- 持有 BridgedWebView(间接持 BridgeBus)+ 文件内私有 SplashOverlay
- 16:9 letterbox 布局:required(aspect 16:9 + width/height ≤ superview)
+ low(width/height = superview) + center{X,Y} 居中。屏幕比 < 16:9 → 上下
黑边,> 16:9 → 左右黑边。Auto Layout 自动选短边贴边、长边居中
- SplashOverlay:SplashImage(scaleAspectFill) + UIProgressView + UILabel;
update(text:progress:) 切换状态机
- runBootPipeline 串:ensureReady → "拉取配置中..." → RemoteConfigClient.fetch
→ 分支(.shortText / .parsed → showmessage / IPA 升级 / LobbyZipUpgrader
with onProgress hop 到 MainActor) → "加载大厅..." → loadFileURL(lobbyIndex,
allowingReadAccessTo: lobbyRoot)
- 三种 alert:showBlockingAlert(短文本/showmessage,永停)/
showIPAUpgradeAlert(确定→openURL,永停)/ showFatalAlert(重试→重跑 pipeline)
- WKNavigationDelegate.didFinish 暂用 splash.isHidden = true(1.14.e 改为
0.3s 淡出动画)
- 横屏锁定 + statusBar 显示(与 RootViewController 一致)
不接 SceneDelegate,1.16 才换 rootViewController;本次仅落地容器代码。
BuildProject 通过
Plan 进度已勾选(§5 Phase 1.14.d + §8)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-22 02:50:16 +08:00 |
|