feat(macOS): 作业集、实时开放关卡与掉落材料显示、关卡数据热更新 - #92
Open
Gotit-zhx wants to merge 3 commits into
Open
Conversation
对齐 Windows GUI,为 macOS 端补充以下能力: - 刷理智的关卡下拉改为数据驱动:根据当前开放情况动态展示, 关闭的关卡置灰、过期活动关加删除线,并支持「隐藏未开放关卡」。 - 关卡提示补充可刷取的掉落材料(item id 经 item_index 转为材料名, 纯文字说明直接透传)。 - 解析 gui/StageActivityV2.json(V2 schema),合并常驻资源本与活动关。 - 关卡数据热更新:新增 MaaApiService(主备地址 + ETag/Last-Modified 协商 + 本地缓存),运行时按游戏日(服务器 04:00 切换)定时刷新, 替换原 OTAFetcher。 - 新增 GameCalendar 处理各服务器的游戏日时区换算,资源本周开放 判断改用游戏日星期,避免跨午夜/跨服误判。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
对齐 Windows GUI 的作业集能力: - 自动战斗页新增「单个作业 / 作业集」切换。 - 支持从 PRTS 作业集神秘代码批量导入,逐份下载并校验, 作业下载到独立目录,不与单个作业列表混淆。 - 作业集支持勾选、逐项突袭开关、拖动排序、滑动/工具栏删除, 并持久化到用户目录。 - 选中作业集条目时右侧详情面板预览该作业文档。 - 全局选项:自动编队、信赖干员、自动使用理智药、助战干员。 - 运行时把勾选项打包为 copilot_list 参数交由核心顺序执行, 导航关卡名经 tile-pos 解析为游戏内代号(如 act50side_01 → TD-1)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Hey - 我发现了 1 个问题,并且给了一些整体性的反馈:
- GameCalendar 的 API 声明为
yjDay(for:channel:)、yjWeekday(for:channel:)和yjHourMinute(for:channel:),但所有调用处使用的是GameCalendar.yjDay(channel:)/yjWeekday(channel:)/yjHourMinute(channel:),这样是无法编译的;请统一外部参数标签(或者添加重载),让调用方与函数签名保持一致。 copilotList属性在每次变更时都会通过didSet { saveCopilotList() }进行持久化,这意味着每次勾选/拖动复选框都会在主线程执行文件 IO;建议对写入进行防抖处理,或将saveCopilotList()放到后台队列执行,以避免在编辑大型列表时出现 UI 卡顿。
给 AI Agent 的提示
Please address the comments from this code review:
## Overall Comments
- GameCalendar’s APIs are declared as `yjDay(for:channel:)`, `yjWeekday(for:channel:)`, and `yjHourMinute(for:channel:)`, but all call sites use `GameCalendar.yjDay(channel:)` / `yjWeekday(channel:)` / `yjHourMinute(channel:)`, which won’t compile; align the external parameter labels (or add overloads) so the callers match the function signatures.
- The `copilotList` property persists on every mutation via `didSet { saveCopilotList() }`, which means file IO on the main thread for each checkbox toggle/reorder; consider debouncing writes or offloading `saveCopilotList()` to a background queue to avoid UI jank when editing large lists.
## Individual Comments
### Comment 1
<location path="MeoAsstMac/Model/MAAStage.swift" line_range="88-97" />
<code_context>
+ var isResourceCollection = false
+
+ /// Activity currently open: started and not yet expired.
+ var beingOpen: Bool { !notOpenYet && !isExpired }
+
+ var isExpired: Bool { Date() >= expireTime }
+
+ var notOpenYet: Bool { Date() <= startTime }
+
+ init(
</code_context>
<issue_to_address>
**suggestion:** 避免在计算开启/过期标记时多次调用 `Date()`,以防止边界时间上的不一致。
这三个计算属性各自独立调用了一次 `Date()`,因此在边界时间附近,它们可能会因为几毫秒的差异而产生矛盾。建议重构为只捕获一次 `let now = Date()`(例如通过 `func status(at date: Date = Date())`),并基于这一共享值推导出这三个布尔值。
```suggestion
var isResourceCollection = false
/// Activity currently open: started and not yet expired.
var beingOpen: Bool { status().beingOpen }
var isExpired: Bool { status().isExpired }
var notOpenYet: Bool { status().notOpenYet }
/// Compute open/expired status at a specific point in time.
/// Using a shared `date` avoids inconsistencies around boundary times.
func status(at date: Date = Date()) -> (beingOpen: Bool, isExpired: Bool, notOpenYet: Bool) {
let isExpired = date >= expireTime
let notOpenYet = date <= startTime
let beingOpen = !notOpenYet && !isExpired
return (beingOpen, isExpired, notOpenYet)
}
init(
```
</issue_to_address>帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进评审质量。
Original comment in English
Hey - I've found 1 issue, and left some high level feedback:
- GameCalendar’s APIs are declared as
yjDay(for:channel:),yjWeekday(for:channel:), andyjHourMinute(for:channel:), but all call sites useGameCalendar.yjDay(channel:)/yjWeekday(channel:)/yjHourMinute(channel:), which won’t compile; align the external parameter labels (or add overloads) so the callers match the function signatures. - The
copilotListproperty persists on every mutation viadidSet { saveCopilotList() }, which means file IO on the main thread for each checkbox toggle/reorder; consider debouncing writes or offloadingsaveCopilotList()to a background queue to avoid UI jank when editing large lists.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- GameCalendar’s APIs are declared as `yjDay(for:channel:)`, `yjWeekday(for:channel:)`, and `yjHourMinute(for:channel:)`, but all call sites use `GameCalendar.yjDay(channel:)` / `yjWeekday(channel:)` / `yjHourMinute(channel:)`, which won’t compile; align the external parameter labels (or add overloads) so the callers match the function signatures.
- The `copilotList` property persists on every mutation via `didSet { saveCopilotList() }`, which means file IO on the main thread for each checkbox toggle/reorder; consider debouncing writes or offloading `saveCopilotList()` to a background queue to avoid UI jank when editing large lists.
## Individual Comments
### Comment 1
<location path="MeoAsstMac/Model/MAAStage.swift" line_range="88-97" />
<code_context>
+ var isResourceCollection = false
+
+ /// Activity currently open: started and not yet expired.
+ var beingOpen: Bool { !notOpenYet && !isExpired }
+
+ var isExpired: Bool { Date() >= expireTime }
+
+ var notOpenYet: Bool { Date() <= startTime }
+
+ init(
</code_context>
<issue_to_address>
**suggestion:** Avoid multiple `Date()` calls when computing open/expired flags to prevent edge inconsistencies.
These three computed properties each call `Date()` independently, so at boundary times they can disagree by a few milliseconds. Refactor to capture `let now = Date()` once (for example via `func status(at date: Date = Date())`) and derive all three booleans from that shared value.
```suggestion
var isResourceCollection = false
/// Activity currently open: started and not yet expired.
var beingOpen: Bool { status().beingOpen }
var isExpired: Bool { status().isExpired }
var notOpenYet: Bool { status().notOpenYet }
/// Compute open/expired status at a specific point in time.
/// Using a shared `date` avoids inconsistencies around boundary times.
func status(at date: Date = Date()) -> (beingOpen: Bool, isExpired: Bool, notOpenYet: Bool) {
let isExpired = date >= expireTime
let notOpenYet = date <= startTime
let beingOpen = !notOpenYet && !isExpired
return (beingOpen, isExpired, notOpenYet)
}
init(
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
- StageActivityInfo 新增 status(at:),单次捕获 Date() 推导 beingOpen/isExpired/notOpenYet,避免边界时间下三者不一致; isStageOpen 也复用同一快照。 - saveCopilotList 的写盘 IO 移到后台串行队列,避免勾选/排序 作业集时在主线程同步写文件造成 UI 卡顿。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Author
|
感谢 review,已逐条处理:
后两条修改见 commit |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
简介
将 Windows GUI 已有但 macOS 端缺失的几项功能移植到 macOS,并尽量与 Windows 保持一致的逻辑与维护成本。
改动内容
1. 刷理智实时显示开放关卡 + 掉落材料
item_index转材料名,纯文字说明透传)。gui/StageActivityV2.json(V2 schema),合并常驻资源本与活动关。2. 关卡数据热更新
MaaApiService:主备地址 + ETag/Last-Modified 协商 + 本地缓存,替换原OTAFetcher。GameCalendar处理各服务器游戏日时区换算,资源本周开放判断改用游戏日星期,避免跨午夜/跨服误判。3. 作业集(Copilot List)
copilot_list参数交核心顺序执行,导航关卡名经 tile-pos 解析为游戏内代号(如act50side_01→TD-1)。测试
🤖 Generated with Claude Code
Summary by Sourcery
将 Windows GUI 的相关功能移植到 macOS:新增作业集列表支持、可热更新数据的动态关卡活动处理,以及与全新基于 MaaApiService 的 OTA 流水线打通的更丰富关卡选择体验。
New Features:
Enhancements:
Original summary in English
Summary by Sourcery
Port Windows GUI functionality to macOS by adding copilot list support, dynamic stage activity handling with hot-updated data, and a richer stage selection experience wired to a new MaaApiService-backed OTA pipeline.
New Features:
Enhancements: