fix(同步): 待办从未被同步 —— 取消勾选后重新勾选也拉不回来 #6
## 现象(用户反馈) 1. 账号设置里取消勾选待办本 → 保存同步 → 待办标签消失(这一步是正常的) 2. 再进去重新勾选 → 保存并同步 → **待办没回来,标签也不显示** ## 根因:VTODO 从来就没被同步过 同步主循环 `syncAccountInner` 全程只处理 VEVENT: - `DavClient.reportEtags(href)`:REPORT 的 comp-filter 只写了 `VEVENT` - `EventDb.queryByCalKey(context, calKey, 'event')`:只查 kind='event' - `EventDb.applyRemote(..., isTodo)`:这个 isTodo 参数**从未传过 true** 而 `DavClient.reportTodos()`(拉 VTODO)虽然早就写好了, **全项目零调用** —— 是一段死代码。 因果链: · 取消勾选待办本 → `pruneStaleHrefs` 清掉不属于已选本的 todo 行 · 重新勾选 → 同步只拉 VEVENT,不拉 VTODO · 本地库没有待办数据 → `loadTodos` 返回空 → 标签不显示 待办功能"能读能改"(勾选完成、设置循环都能用),但**数据来源一直是空的**, 只有以前残留的行在撑着。一旦被清掉就再也不回来。 ## 修法 在同步循环里对每个日历本额外拉一次 VTODO: `reportTodos(href)` → 逐个 GET 原文 → `IcsUtil.parseTodos` → `applyRemote(..., true)` ⭐ 为什么对 unknown 类型的本也拉: 服务器若没声明 supported-calendar-component-set,我们分不清哪个本是待办本。 待办量远小于日程,多拉一次的代价很小;宁可贵一点, 也不能让用户的待办再次"消失"。 ⭐ 失败隔离: 待办同步整段包在 try/catch 里,单本失败只记日志、不影响日程同步。 很多服务器对 VTODO 查询返回 403 或不支持该 REPORT, 不能让这种情况把整个账号同步拖垮。 ## 影响面 `applyRemote` 内部按 `kind` 隔离(existing 只查同 kind), 且自带 removed 处理 ⇒ 不会误删日程行,也不会漏删已消失的待办。
This commit is contained in:
@@ -173,6 +173,36 @@ export class SyncEngine {
|
||||
throw err as Error;
|
||||
}
|
||||
LogUtil.write(`日历本[${i}]「${calName}」REPORT 返回 ${items.length} 个资源`);
|
||||
// ⭐ SYNC_TODOS 同步该本里的待办(VTODO)。
|
||||
// 原来整个同步流程只处理 VEVENT,`DavClient.reportTodos` 写了却没人调用,
|
||||
// 导致待办数据从来不会从服务器拉下来 ——
|
||||
// 用户取消勾选本(触发 pruneStaleHrefs 清行)后再勾回来,待办就永久消失了。
|
||||
// 放在这里(VEVENT 处理之前)单独走一遍,不影响原有日程逻辑。
|
||||
try {
|
||||
const todoItems: RemoteItem[] = await DavClient.reportTodos(href, auth);
|
||||
if (todoItems.length > 0) {
|
||||
const todoRemote: RemoteEvent[] = [];
|
||||
for (const ti of todoItems) {
|
||||
try {
|
||||
const raw: string = await DavClient.getRawStrict(ti.href, auth);
|
||||
const parsed: RemoteEvent[] = IcsUtil.parseTodos(raw);
|
||||
for (const pr of parsed) {
|
||||
pr.etag = ti.etag;
|
||||
todoRemote.push(pr);
|
||||
}
|
||||
} catch (e) {
|
||||
LogUtil.write(`待办 GET 失败(跳过) ${ti.href}`);
|
||||
}
|
||||
}
|
||||
if (todoRemote.length > 0) {
|
||||
await EventDb.applyRemote(context, calKey, href, todoRemote, true);
|
||||
LogUtil.write(`日历本[${i}]「${calName}」待办同步:${todoRemote.length} 条`);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// 待办同步失败不能影响日程同步(很多服务器对 VTODO 查询返回 403/不支持)
|
||||
LogUtil.write(`日历本[${i}]「${calName}」待办同步失败(忽略):${(e as BusinessError).message ?? ''}`);
|
||||
}
|
||||
// 现有行按 uid 分组(同一资源的覆盖实例共享 uid 与 etag)
|
||||
const existingRows: LocalEvent[] = await EventDb.queryByCalKey(context, calKey, 'event');
|
||||
const byUid: Map<string, LocalEvent[]> = new Map();
|
||||
|
||||
Reference in New Issue
Block a user