feat(账号/待办): 日历本区分日历与待办类型,待办标签两级判定 #6
用户要求两点:
1. 添加/修改账号时,在颜色前面显示图标区分「日历本 / 待办本」
2. 没勾选任何待办本 → 待办标签不显示;选了待办本但没数据 → 也不显示
## 协议层:supported-calendar-component-set
CalDAV 用这个属性声明集合支持哪些组件:
<C:supported-calendar-component-set>
<C:comp name="VEVENT"/> ← 纯日历本
<C:comp name="VTODO"/> ← 纯待办本
</C:supported-calendar-component-set>
DavClient 改动:
- PROPFIND 请求加该属性(两处:propfindColors / propfindCalendars)
- 新增 `parseComps()` 归一成四类:event / todo / both / **unknown**
- `DavColorEntry` 与 `DavCalendarEntry` 都加 comps 字段
⭐ 为什么保留 unknown:
很多服务器(含自研实现)压根不返回这个属性。若武断判成"不是待办本",
用户原本能用的日历会被错误分类。unknown 一律按**日历**对待,保持兼容。
## 存储
DavAccount 增加 `calendarComps: string[]`,与 calendarHrefs 一一对应。
JSON 存储天然支持;旧数据读出来是空数组 → 全部 unknown → 视为日历。
## 界面
EditCalendarRow 在颜色圆点**前面**加类型图标,用户先看类型再看颜色:
· 待办本 → ✓ 配成功色
· 混合本 → ▦✓
· 日历/unknown → ▦
## 待办标签两级判定
1. `hasTodoBook()`:被选中的日历本里存在 comps 为 todo/both 的
—— 一个都没选 ⇒ 必然没待办,直接不显示
2. `todos.length > 0`
—— 选了待办本但没数据,也不显示
比只看条数更准:没勾待办本时,同步可能拉到残留数据,
只看条数会误显示;第一级在"还没同步"时就能给出确定答案,不会闪一下。
## 待确认(需真机日志)
服务器是否真的返回 supported-calendar-component-set。
诊断日志已加:`日历本组件类型 <名称> → <类型>`。
若全是 unknown,说明服务器没声明 ⇒ 图标会退化成日历图标,
此时需要改用其它判据(如按本内是否含 VTODO 探测)。
This commit is contained in:
@@ -18,6 +18,15 @@ export class DavColorEntry {
|
||||
color: string = ''; // 规范化后的 #RRGGBB,可能为空
|
||||
writable: boolean = true; // current-user-privilege 是否含 write 权限(默认可写)
|
||||
privilegeKnown: boolean = false; // 服务器是否返回了 current-user-privilege-set(未返回时需要写探测)
|
||||
/**
|
||||
* ⭐ COMP_SET 该集合支持的组件类型(来自 supported-calendar-component-set)。
|
||||
* 'event' = 纯日历本(只有 VEVENT)
|
||||
* 'todo' = 纯待办本(只有 VTODO)
|
||||
* 'both' = 混合本(两者都有)
|
||||
* 'unknown' = 服务器未声明该属性(老实现常见)→ 按日历对待,保持兼容
|
||||
*/
|
||||
comps: string = 'unknown';
|
||||
|
||||
}
|
||||
|
||||
/** 日历本集合条目("添加/编辑账号"页列日历本用) */
|
||||
@@ -25,6 +34,8 @@ export class DavCalendarEntry {
|
||||
href: string = ''; // 完整 URL(已补全域名)
|
||||
displayName: string = ''; // 显示名(服务器未给 displayname 时回退为路径末段)
|
||||
color: string = ''; // 规范化后的 #RRGGBB,可能为空
|
||||
/** ⭐ COMP_SET 'event' 纯日历 / 'todo' 纯待办 / 'both' 混合 / 'unknown' 服务器未声明 */
|
||||
comps: string = 'unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,6 +72,8 @@ export class DavClient {
|
||||
'xmlns:ical="http://apple.com/ns/ical/"><d:prop>' +
|
||||
'<d:resourcetype/><cs:getcolor/><ical:calendar-color/>' +
|
||||
'<d:current-user-privilege-set/>' +
|
||||
// ⭐ COMP_SET 让服务器告诉我们这本是日历还是待办
|
||||
'<c:supported-calendar-component-set xmlns:c="urn:ietf:params:xml:ns:caldav"/>' +
|
||||
'</d:prop></d:propfind>';
|
||||
const httpRequest = http.createHttp();
|
||||
try {
|
||||
@@ -104,6 +117,7 @@ export class DavClient {
|
||||
color = DavClient.normalizeHex(DavClient.extractTag(block, 'calendar-color'));
|
||||
}
|
||||
entry.color = color;
|
||||
entry.comps = DavClient.parseComps(block);
|
||||
// 写权限:current-user-privilege-set 存在且不含任何 write 权限 → 只读
|
||||
const privSet: string = DavClient.extractTag(block, 'current-user-privilege-set');
|
||||
if (privSet !== '') {
|
||||
@@ -123,6 +137,33 @@ export class DavClient {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ⭐ COMP_SET 解析 supported-calendar-component-set,归一成四类:
|
||||
* event / todo / both / unknown
|
||||
*
|
||||
* 为什么 unknown 要单独存在:很多服务器(含自研实现)压根不返回这个属性。
|
||||
* 这时不能武断判成"不是待办本",否则用户原本能用的日历会被当成待办、
|
||||
* 或者反过来把纯日历标成待办 —— 保守按"日历"对待,不破坏既有行为。
|
||||
*/
|
||||
static parseComps(block: string): string {
|
||||
const seg: string = DavClient.extractTag(block, 'supported-calendar-component-set');
|
||||
if (seg === '') {
|
||||
return 'unknown';
|
||||
}
|
||||
const event: boolean = /name\s*=\s*["']VEVENT["']/i.test(seg);
|
||||
const todo: boolean = /name\s*=\s*["']VTODO["']/i.test(seg);
|
||||
if (event && todo) {
|
||||
return 'both';
|
||||
}
|
||||
if (todo) {
|
||||
return 'todo';
|
||||
}
|
||||
if (event) {
|
||||
return 'event';
|
||||
}
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
* PROPFIND 列出某账号下所有「日历集合」(resourcetype 含 calendar)。
|
||||
*
|
||||
@@ -136,6 +177,7 @@ export class DavClient {
|
||||
'<d:propfind xmlns:d="DAV:" xmlns:cs="http://calendarserver.org/ns/" ' +
|
||||
'xmlns:ical="http://apple.com/ns/ical/"><d:prop>' +
|
||||
'<d:displayname/><d:resourcetype/><cs:getcolor/><ical:calendar-color/>' +
|
||||
'<c:supported-calendar-component-set xmlns:c="urn:ietf:params:xml:ns:caldav"/>' +
|
||||
'</d:prop></d:propfind>';
|
||||
const httpRequest = http.createHttp();
|
||||
const out = new DavCalendarDiscovery();
|
||||
@@ -197,6 +239,9 @@ export class DavClient {
|
||||
entry.href = href.startsWith('http') ? href : origin + href;
|
||||
entry.displayName = name;
|
||||
entry.color = color;
|
||||
entry.comps = DavClient.parseComps(block);
|
||||
// COMPS_LOG 诊断:确认服务器有没有声明组件类型
|
||||
LogUtil.write(`日历本组件类型 ${entry.displayName} → ${entry.comps}`);
|
||||
out.entries.push(entry);
|
||||
}
|
||||
return out;
|
||||
|
||||
Reference in New Issue
Block a user