Files
Qiu Yuzhou 34caebf322 feat: add GUI-scoped subscription automatic updates (#1516)
* feat: add GUI-scoped subscription automatic updates

* fix: drain newly due subscription updates during a batch

* style: use segmented subscription update pickers
2026-10-25 20:21:06 +08:00

80 lines
2.8 KiB
Swift

import Foundation
/// 订阅源记录:持久不透明的订阅 UUID + 固定分组
/// 身份 + 订阅 URL + 最近刷新状态。URL 随服务器库整体加密;
/// 编辑 URL 保留订阅与固定分组身份。
struct SubscriptionRecord: Codable, Equatable, Sendable, Identifiable {
/// 订阅源身份;删除后新建即新身份(GLOSSARY.md「Subscription」)。
let id: NodeID
/// 固定订阅分组的稳定身份(客户端所有,跨刷新与 URL 编辑不变)。
let groupID: NodeID
var url: String
var status: SubscriptionRefreshStatus
/// 最近成功提交的白名单资料;成功响应省略时清除,失败或取消保留。
var information: SubscriptionInformation?
/// 独立于最近尝试状态,旧文件缺失时不猜测。
var lastSucceededAt: Date?
/// Optional envelope keeps existing encrypted documents compatible and disabled.
var autoUpdateState: SubscriptionAutoUpdate?
var autoUpdate: SubscriptionAutoUpdate {
get { autoUpdateState ?? SubscriptionAutoUpdate() }
set { autoUpdateState = newValue }
}
}
/// 最近一次刷新结果。失败只保存白名单事实,不保存订阅 URL、秘密、凭据引用
/// 或任意错误 description(均需脱敏)。
enum SubscriptionRefreshStatus: Codable, Equatable, Sendable {
case never
// swiftlint:disable:next identifier_name
case succeeded(at: Date)
// swiftlint:disable:next identifier_name
case failed(at: Date, failure: SubscriptionRefreshFailure)
}
/// 配置目录持久化文档:结构树、订阅源记录与有序收藏一次原子落盘,
/// 保证「订阅记录 ↔ 固定分组子树」跨文档一致。
struct CatalogDocument: Equatable, Sendable {
var catalog: ConfigurationCatalog
var subscriptions: [SubscriptionRecord]
var favoriteIDs: [NodeID]
init(
catalog: ConfigurationCatalog = ConfigurationCatalog(),
subscriptions: [SubscriptionRecord] = [],
favoriteIDs: [NodeID] = []
) {
self.catalog = catalog
self.subscriptions = subscriptions
self.favoriteIDs = favoriteIDs
}
}
/// 已解析为目录形态的订阅快照:身份已按订阅作用域限定,记录携带凭据值。
/// `root` 是固定分组的内容来源;固定分组自身身份由应用入口的 `groupID`
/// 决定(客户端所有),`root.id` 不参与挂载。
struct CatalogSubscriptionSnapshot: Equatable, Sendable {
struct ServerLeaf: Equatable, Sendable {
let id: NodeID
var fields: ServerFields
}
/// 有序子引用:顺序即快照(远端权威)的用户可见顺序。
enum Child: Equatable, Sendable {
case group(Group)
case server(ServerLeaf)
}
struct Group: Equatable, Sendable {
let id: NodeID
let name: String
var children: [Child]
}
/// 固定分组显示名:远端根分组名,扩展缺失时为 URL host。
let name: String
let root: Group
}