refactor(calendar): 移除月历干支显示与开关
This commit is contained in:
1 parent
222b2baa89
commit
3aae19a4c6
14 files changed
+24
-34
No files matched your search
+1
-1
@@ -55,7 +55,7 @@ GitHub Actions 只在分支 push 且 `server/**` 发生变化时构建并发布
|
||||
有 Developer ID Application 证书时,Taskfile 会自动使用钥匙串中第一个证书的唯一指纹;否则由构建脚本逐层补齐 ad-hoc 签名与 entitlement。临时签名没有团队身份,不能加载启用团队验证的 Sparkle 动态库,因此仅此本地/CI 路径关闭 Hardened Runtime;正式 Developer ID 构建仍强制启用,且临时签名构建不能在线安装或操作特权辅助工具。
|
||||
桌面小组件与主应用通过 `group.work.12306.xstats` App Group 共享只读展示摘要。
|
||||
菜单栏日历在所有应用语言下均提供“日期与星期”“日期与农历”“农历”“自定义”显示方式;日期与农历按应用语言呈现,切换语言保留用户选择。自定义可自由组合 y/M/d 字段与分隔符,年份可省略,输入时显示预览并提示无效格式,独立于月历的星期开关;支持农历字号与对比度、可选悬停展开,按分钟刷新,休眠或关闭功能后停止。日程和到期提醒默认关闭,通过明确的授权按钮请求 EventKit 权限,只在日历面板可见时查询当前 42 格日期范围;只读展示,不修改日程,也不向 Widget、设置备份或服务端传递内容。列表 ID 仅保存在本机,显示偏好参与设置备份。节假日覆盖年份来自内置 Tyme 数据并随应用更新;假期倒计时与最多 3 天的请假建议只基于已公布的中国调休与双休安排,跨到未知年份时停止推算。
|
||||
日历首次初始化按 macOS 地区设置决定默认值,不按设备销售地或应用语言判断:中国大陆(CN)默认启用农历、星期、大陆节假日与调休、传统与公历节日、节气、黄历及假期概览;其他地区(含台湾、香港、澳门)默认只启用星期。干支、三伏/梅雨/数九、藏历与回历各地区均默认关闭。用户可自行启用或关闭,首次默认值保存后不随语言、地区变化重置;已有本机偏好及旧备份保持原选择。每周起始日首次采用 macOS 系统日历设置并保存,支持星期日到星期六,手动选择及备份恢复优先;后续系统设置变化不覆盖已保存的值,主应用月历与小组件使用同一周起始日。大陆节假日数据不能用于推断其他地区的法定安排。单日及整月小组件遵循日历选项,未启用大陆节假日时“明天上班吗”提示“仅支持中国大陆节假日”;黄历关闭后不再计算宜忌摘要。
|
||||
日历首次初始化按 macOS 地区设置决定默认值,不按设备销售地或应用语言判断:中国大陆(CN)默认启用农历、星期、大陆节假日与调休、传统与公历节日、节气、黄历及假期概览;其他地区(含台湾、香港、澳门)默认只启用星期。三伏/梅雨/数九、藏历与回历各地区均默认关闭。月历不再显示干支,也不提供对应开关;年月日干支只在启用黄历后的日期详情中显示,旧偏好与备份中的干支选项被忽略,其他选项保留。用户可自行启用或关闭,首次默认值保存后不随语言、地区变化重置;已有本机偏好及旧备份保持原选择。每周起始日首次采用 macOS 系统日历设置并保存,支持星期日到星期六,手动选择及备份恢复优先;后续系统设置变化不覆盖已保存的值,主应用月历与小组件使用同一周起始日。大陆节假日数据不能用于推断其他地区的法定安排。单日及整月小组件遵循日历选项,未启用大陆节假日时“明天上班吗”提示“仅支持中国大陆节假日”;黄历关闭后不再计算宜忌摘要。
|
||||
月历使用固定六行布局,日期 ID 在生成日期时计算一次。面板打开时测量初始高度,后续翻月、详情与日程内容变化在内部滚动,不从内容几何回调反向调整窗口,避免窗口高度与懒布局估算形成反馈循环。
|
||||
月历计算仅缓存最近一个月份(包含周起始日和时区),供实际面板、高度测量和重复打开共用;翻月替换缓存,避免浏览历史导致内存累积。日程日期标记在工作 actor 内随查询结果生成,界面不再逐格扫描日程列表;连续 EventKit 通知按 300 毫秒窗口合并,关闭面板时取消待执行刷新。
|
||||
用于正式签名的 Apple Developer 团队须为主应用和 Widget 扩展启用同一个 App Group;
|
||||
|
||||
@@ -18,7 +18,6 @@ extension Translations {
|
||||
中国大陆节假日与调休 Mainland China holidays and makeup workdays
|
||||
传统与公历节日 Festivals
|
||||
二十四节气 Solar terms
|
||||
干支 Sexagenary cycle
|
||||
三伏天 Dog days
|
||||
梅雨天 Plum-rain days
|
||||
三伏·梅雨·数九 Fu · Plum rain · Nines
|
||||
@@ -108,7 +107,6 @@ extension Translations {
|
||||
小寒 Minor Cold
|
||||
大寒 Major Cold
|
||||
返回月历 Back to calendar
|
||||
控制月历格中的日干支;黄历详情始终显示年月日干支 Show the day cycle in month cells; almanac details always include year, month and day cycles
|
||||
宜 Favorable
|
||||
忌 Avoid
|
||||
纳音 Na Yin
|
||||
|
||||
@@ -1317,7 +1317,6 @@ Apple 智能 Apple Intelligence
|
||||
中国大陆节假日与调休 العطلات وأيام العمل التعويضية في البر الرئيسي للصين
|
||||
传统与公历节日 المناسبات التقليدية والميلادية
|
||||
二十四节气 الفصول الشمسية الأربعة والعشرون
|
||||
干支 الدورة الستينية
|
||||
三伏天 أيام القيظ
|
||||
梅雨天 أيام أمطار البرقوق التقليدية
|
||||
三伏·梅雨·数九 فو · ميو · التسعات
|
||||
@@ -1393,7 +1392,6 @@ Apple 智能 Apple Intelligence
|
||||
小寒 البرد الصغير
|
||||
大寒 البرد الكبير
|
||||
返回月历 العودة إلى التقويم
|
||||
控制月历格中的日干支;黄历详情始终显示年月日干支 عرض دورة اليوم في خلايا الشهر؛ تتضمن تفاصيل التقويم التقليدي دائمًا دورات السنة والشهر واليوم
|
||||
宜 مستحب
|
||||
忌 يُتجنّب
|
||||
纳音 نا يين
|
||||
|
||||
|
@@ -1317,7 +1317,6 @@ Apple 智能 Apple Intelligence
|
||||
中国大陆节假日与调休 Feiertage und Ausgleichsarbeitstage in Festlandchina
|
||||
传统与公历节日 Traditionelle und gregorianische Feste
|
||||
二十四节气 24 Sonnenperioden
|
||||
干支 Sechzigjahreszyklus
|
||||
三伏天 Hundstage
|
||||
梅雨天 Traditionelle Pflaumenregenzeit
|
||||
三伏·梅雨·数九 Fu · Meiyu · Neuner
|
||||
@@ -1393,7 +1392,6 @@ Apple 智能 Apple Intelligence
|
||||
小寒 Kleine Kälte
|
||||
大寒 Große Kälte
|
||||
返回月历 Zurück zum Kalender
|
||||
控制月历格中的日干支;黄历详情始终显示年月日干支 Tageszyklus in Monatszellen anzeigen; Almanachdetails enthalten immer Jahres-, Monats- und Tageszyklen
|
||||
宜 Günstig
|
||||
忌 Meiden
|
||||
纳音 Na Yin
|
||||
|
||||
|
@@ -1317,7 +1317,6 @@ Apple 智能 Apple Intelligence
|
||||
中国大陆节假日与调休 Festivos y días laborales compensatorios de China continental
|
||||
传统与公历节日 Fiestas tradicionales y gregorianas
|
||||
二十四节气 24 términos solares
|
||||
干支 Ciclo sexagenario
|
||||
三伏天 Días caniculares
|
||||
梅雨天 Lluvias de ciruela tradicionales
|
||||
三伏·梅雨·数九 Fu · Meiyu · Nueves
|
||||
@@ -1393,7 +1392,6 @@ Apple 智能 Apple Intelligence
|
||||
小寒 Frío menor
|
||||
大寒 Frío mayor
|
||||
返回月历 Volver al calendario
|
||||
控制月历格中的日干支;黄历详情始终显示年月日干支 Muestra el ciclo diario en las celdas; el almanaque siempre incluye los ciclos del año, mes y día
|
||||
宜 Favorable
|
||||
忌 Evitar
|
||||
纳音 Na Yin
|
||||
|
||||
|
@@ -1317,7 +1317,6 @@ Apple 智能 Apple Intelligence
|
||||
中国大陆节假日与调休 Jours fériés et jours de travail compensatoires en Chine continentale
|
||||
传统与公历节日 Fêtes traditionnelles et grégoriennes
|
||||
二十四节气 24 périodes solaires
|
||||
干支 Cycle sexagésimal
|
||||
三伏天 Jours caniculaires
|
||||
梅雨天 Pluies des prunes traditionnelles
|
||||
三伏·梅雨·数九 Fu · Meiyu · Neuvaines
|
||||
@@ -1393,7 +1392,6 @@ Apple 智能 Apple Intelligence
|
||||
小寒 Petit froid
|
||||
大寒 Grand froid
|
||||
返回月历 Retour au calendrier
|
||||
控制月历格中的日干支;黄历详情始终显示年月日干支 Affiche le cycle du jour dans les cases ; l’almanach inclut toujours les cycles de l’année, du mois et du jour
|
||||
宜 Favorable
|
||||
忌 À éviter
|
||||
纳音 Na Yin
|
||||
|
||||
|
@@ -1317,7 +1317,6 @@ Apple 智能 Apple Intelligence
|
||||
中国大陆节假日与调休 中国本土の祝日・振替出勤日
|
||||
传统与公历节日 伝統と西暦の祝祭日
|
||||
二十四节气 二十四節気
|
||||
干支 干支
|
||||
三伏天 三伏
|
||||
梅雨天 入梅・出梅
|
||||
三伏·梅雨·数九 三伏・梅雨・数九
|
||||
@@ -1393,7 +1392,6 @@ Apple 智能 Apple Intelligence
|
||||
小寒 小寒
|
||||
大寒 大寒
|
||||
返回月历 月表示に戻る
|
||||
控制月历格中的日干支;黄历详情始终显示年月日干支 月表示の日干支を設定します。暦の詳細には年・月・日の干支を常に表示します
|
||||
宜 宜
|
||||
忌 忌
|
||||
纳音 納音
|
||||
|
||||
|
@@ -1317,7 +1317,6 @@ Apple 智能 Apple Intelligence
|
||||
中国大陆节假日与调休 중국 본토 공휴일 및 대체 근무일
|
||||
传统与公历节日 전통 및 양력 기념일
|
||||
二十四节气 24절기
|
||||
干支 간지
|
||||
三伏天 삼복
|
||||
梅雨天 입매·출매
|
||||
三伏·梅雨·数九 삼복·매우·구구
|
||||
@@ -1393,7 +1392,6 @@ Apple 智能 Apple Intelligence
|
||||
小寒 소한
|
||||
大寒 대한
|
||||
返回月历 월 달력으로 돌아가기
|
||||
控制月历格中的日干支;黄历详情始终显示年月日干支 월 달력 칸의 일진 표시를 설정합니다. 황력 상세에는 연·월·일 간지가 항상 표시됩니다
|
||||
宜 길
|
||||
忌 기
|
||||
纳音 납음
|
||||
|
||||
|
@@ -1318,7 +1318,6 @@ Apple 智能 Apple Intelligence
|
||||
中国大陆节假日与调休 中國大陸節假日與調休
|
||||
传统与公历节日 傳統與公曆節日
|
||||
二十四节气 二十四節氣
|
||||
干支 干支
|
||||
三伏天 三伏天
|
||||
梅雨天 梅雨天
|
||||
三伏·梅雨·数九 三伏·梅雨·數九
|
||||
@@ -1394,7 +1393,6 @@ Apple 智能 Apple Intelligence
|
||||
小寒 小寒
|
||||
大寒 大寒
|
||||
返回月历 返回月曆
|
||||
控制月历格中的日干支;黄历详情始终显示年月日干支 控制月曆格中的日干支;黃曆詳情始終顯示年月日干支
|
||||
宜 宜
|
||||
忌 忌
|
||||
纳音 納音
|
||||
|
||||
|
@@ -22,8 +22,6 @@ struct CalendarAlmanacView: View {
|
||||
}
|
||||
if let almanac {
|
||||
Text(almanac.ganzhiSummary).font(.system(size: 13)).foregroundStyle(.secondary)
|
||||
} else if features.contains(.ganzhi) {
|
||||
Text(day.ganzhiSummary).font(.system(size: 13)).foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
if let almanac {
|
||||
|
||||
@@ -9,7 +9,7 @@ import WidgetData
|
||||
|
||||
/// 公历是月历的坐标;其余信息可独立隐藏,设置以稳定 rawValue 保存和同步。
|
||||
public enum CalendarFeature: String, CaseIterable, Identifiable, Sendable {
|
||||
case lunar, weekdays, holidays, festivals, solarTerms, ganzhi, seasonal, tibetan, hijri
|
||||
case lunar, weekdays, holidays, festivals, solarTerms, seasonal, tibetan, hijri
|
||||
public var id: String { rawValue }
|
||||
public static var defaults: Set<Self> { defaults(for: .current) }
|
||||
|
||||
@@ -25,7 +25,6 @@ public enum CalendarFeature: String, CaseIterable, Identifiable, Sendable {
|
||||
case .holidays: tr("中国大陆节假日与调休")
|
||||
case .festivals: tr("传统与公历节日")
|
||||
case .solarTerms: tr("二十四节气")
|
||||
case .ganzhi: tr("干支")
|
||||
case .seasonal: tr("三伏·梅雨·数九")
|
||||
case .tibetan: tr("藏历")
|
||||
case .hijri: tr("回历")
|
||||
@@ -34,6 +33,7 @@ public enum CalendarFeature: String, CaseIterable, Identifiable, Sendable {
|
||||
|
||||
/// 旧偏好和备份里的两个独立开关合并为一个;任一项曾开启就保留用户的显示意愿。
|
||||
static func restored(from rawValues: [String]) -> Set<Self> {
|
||||
// 已移除的月历干支(ganzhi)与未知选项一起忽略,不影响其他已保存的选择。
|
||||
var features = Set(rawValues.compactMap(Self.init(rawValue:)))
|
||||
if rawValues.contains("dogDays") || rawValues.contains("plumRain") { features.insert(.seasonal) }
|
||||
return features
|
||||
@@ -65,8 +65,6 @@ struct CalendarDay: Identifiable, Equatable {
|
||||
let weekday: Int
|
||||
let lunarLabel: String
|
||||
let lunarSummary: String
|
||||
let ganzhiDay: String
|
||||
let ganzhiSummary: String
|
||||
let festivals: [String]
|
||||
let solarTerm: String?
|
||||
let holiday: Holiday?
|
||||
@@ -173,7 +171,6 @@ enum CalendarEngine {
|
||||
let calendar = gregorian(timeZone: timeZone)
|
||||
guard let date = calendar.date(from: DateComponents(year: year, month: month, day: day, hour: 12)) else { return nil }
|
||||
let lunar = solar.getLunarDay()
|
||||
let cycle = solar.getSixtyCycleDay()
|
||||
let term = solar.termDay
|
||||
let festivals = [lunar.festival?.getName(), solar.festival?.getName()].compactMap { $0 }
|
||||
let dog = solar.dogDay
|
||||
@@ -187,8 +184,7 @@ enum CalendarEngine {
|
||||
return CalendarDay(date: date, year: year, month: month, day: day,
|
||||
weekday: calendar.component(.weekday, from: date),
|
||||
lunarLabel: lunar.day == 1 ? lunar.lunarMonth.getName() : lunar.getName(),
|
||||
lunarSummary: lunar.description, ganzhiDay: cycle.sixtyCycle.getName(),
|
||||
ganzhiSummary: "\(cycle.year.getName())年 \(cycle.month.getName())月 \(cycle.sixtyCycle.getName())日",
|
||||
lunarSummary: lunar.description,
|
||||
festivals: festivals, solarTerm: term.dayIndex == 0 ? term.solarTerm.getName() : nil,
|
||||
holiday: solar.legalHoliday.map { .init(name: $0.name, isWork: $0.isWork) },
|
||||
dogDays: dog?.description, plumRain: plumRain?.description,
|
||||
|
||||
@@ -349,9 +349,6 @@ private struct CalendarDayCell: View {
|
||||
weight: preferences.strongerLunarText ? .semibold : .regular))
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.75)
|
||||
if features.contains(.ganzhi) {
|
||||
Text(day.ganzhiDay).font(.system(size: sizing.cellHeight < 50 ? 8 : 9)).opacity(0.75)
|
||||
}
|
||||
}
|
||||
.foregroundStyle(foreground)
|
||||
.frame(maxWidth: .infinity)
|
||||
|
||||
@@ -56,7 +56,7 @@ struct CalendarOptionsPopover: View {
|
||||
}
|
||||
ForEach(CalendarFeature.allCases) { feature in
|
||||
GroupRow {
|
||||
SettingRow(title: feature.title, subtitle: feature == .ganzhi ? tr("控制月历格中的日干支;黄历详情始终显示年月日干支") : nil) {
|
||||
SettingRow(title: feature.title) {
|
||||
DSToggle(isOn: Binding(get: { settings.calendarFeatures.contains(feature) }, set: { enabled in
|
||||
if enabled { settings.calendarFeatures.insert(feature) }
|
||||
else { settings.calendarFeatures.remove(feature) }
|
||||
|
||||
@@ -183,6 +183,21 @@ struct CalendarTests {
|
||||
#expect(CalendarEngine.selection(old, in: CalendarMonth(year: 2024, month: 4), timeZone: newZone)?.id == "2024-04-01")
|
||||
}
|
||||
|
||||
@Test func retiredGanzhiSettingPreservesOtherCalendarChoices() throws {
|
||||
let name = "CalendarRetiredGanzhiTests.\(UUID())"
|
||||
let defaults = try #require(UserDefaults(suiteName: name))
|
||||
defer { defaults.removePersistentDomain(forName: name) }
|
||||
defaults.set(["ganzhi", "lunar", "holidays"], forKey: "calendarFeatures")
|
||||
let settings = AppSettings(defaults: defaults)
|
||||
#expect(!CalendarFeature.allCases.map(\.rawValue).contains("ganzhi"))
|
||||
#expect(settings.calendarFeatures == [.lunar, .holidays])
|
||||
var backup = SettingsDocument()
|
||||
backup.calendarFeatures = ["ganzhi", "hijri"]
|
||||
settings.apply(backup)
|
||||
#expect(settings.calendarFeatures == [.hijri])
|
||||
#expect(settings.exportDocument().calendarFeatures == ["hijri"])
|
||||
}
|
||||
|
||||
@Test func preferencesPersistAndRoundTripWhileOldBackupsPreserveThem() throws {
|
||||
let name = "CalendarTests.\(UUID().uuidString)"
|
||||
let defaults = try #require(UserDefaults(suiteName: name))
|
||||
@@ -191,19 +206,19 @@ struct CalendarTests {
|
||||
#expect(settings.calendarEnabled == false)
|
||||
#expect(settings.calendarFeatures == CalendarFeature.defaults)
|
||||
settings.calendarEnabled = true
|
||||
settings.calendarFeatures = [.hijri, .ganzhi]
|
||||
settings.calendarFeatures = [.hijri, .lunar]
|
||||
settings.calendarFirstWeekday = 1
|
||||
let restored = AppSettings(defaults: defaults)
|
||||
#expect(restored.calendarEnabled)
|
||||
#expect(restored.calendarFeatures == [.hijri, .ganzhi])
|
||||
#expect(restored.calendarFeatures == [.hijri, .lunar])
|
||||
#expect(restored.calendarFirstWeekday == 1)
|
||||
let doc = try JSONDecoder().decode(SettingsDocument.self, from: JSONEncoder().encode(settings.exportDocument()))
|
||||
restored.calendarFeatures = []
|
||||
restored.apply(doc)
|
||||
#expect(restored.calendarFeatures == [.hijri, .ganzhi])
|
||||
#expect(restored.calendarFeatures == [.hijri, .lunar])
|
||||
restored.apply(SettingsDocument())
|
||||
#expect(restored.calendarEnabled)
|
||||
#expect(restored.calendarFeatures == [.hijri, .ganzhi])
|
||||
#expect(restored.calendarFeatures == [.hijri, .lunar])
|
||||
var invalid = SettingsDocument()
|
||||
invalid.calendarFeatures = ["hijri", "future-feature"]
|
||||
invalid.calendarFirstWeekday = 99
|
||||
|
||||
Reference in new issue
Block a user