mirror of
https://github.com/carey314/mio-plugin-worker.git
synced 2026-08-10 07:04:32 +00:00
chore: bump v0.3.0 build 3 + holiday data + cross-day water dedupe fix
Three things squashed into one commit since they're all release-prep:
1. Bump CFBundleVersion 2 → 3, CFBundleShortVersionString 0.2.0 →
0.3.0, WorkerPlugin.version & ExpandedView footer string to match.
Aligns with the 5-feature v0.3.0 ship.
2. fix(water): lastWaterReminderHr was an Int hour (0-23) so a
yesterday-fired 17:00 blocked today's 17:00 reminder. Now stores
"yyyy-MM-dd HH" composite key so dedupe is scoped per-day.
3. Holidays.swift: 2026 entry table re-calibrated per typical
State Council 调休 pattern. Notable changes:
- 春节 starts on 除夕 02-16 (not 初一 02-17) per 2024 policy
- 中秋 + 国庆 merged into 10-day 09-25 ~ 10-04 window (since
2026 中秋 09-25 is 6 days before 国庆 10-01)
- 劳动节 renamed from "五一" for clarity
Disclaimer still in header: verify against 国务院 announcement
when published, edit + re-release if 调休 differs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
51cc5bb0cc
commit
dc0d22700c
@ -15,9 +15,9 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>BNDL</string>
|
<string>BNDL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>0.2.0</string>
|
<string>0.3.0</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>2</string>
|
<string>3</string>
|
||||||
<key>NSPrincipalClass</key>
|
<key>NSPrincipalClass</key>
|
||||||
<string>WorkerPlugin.WorkerPlugin</string>
|
<string>WorkerPlugin.WorkerPlugin</string>
|
||||||
<!--
|
<!--
|
||||||
|
|||||||
@ -13,7 +13,7 @@ final class WorkerPlugin: NSObject, MioPlugin {
|
|||||||
var id: String { "worker" }
|
var id: String { "worker" }
|
||||||
var name: String { "摸鱼侠" }
|
var name: String { "摸鱼侠" }
|
||||||
var icon: String { "fish.fill" }
|
var icon: String { "fish.fill" }
|
||||||
var version: String { "0.2.0" }
|
var version: String { "0.3.0" }
|
||||||
|
|
||||||
func activate() {
|
func activate() {
|
||||||
WorkerDebugLog.write("plugin activate")
|
WorkerDebugLog.write("plugin activate")
|
||||||
|
|||||||
@ -2,15 +2,26 @@
|
|||||||
// Holidays.swift
|
// Holidays.swift
|
||||||
// 摸鱼侠 plugin v0.3.0
|
// 摸鱼侠 plugin v0.3.0
|
||||||
//
|
//
|
||||||
// China State Council statutory holidays 2026. Estimated from prior-
|
// China State Council statutory holidays for 2026. Calibrated to the
|
||||||
// year patterns (公历日期固定假 + 农历节假) — exact dates and
|
// 公历日期 + 农历对应日 + typical 调休 pattern; **the official 国务院
|
||||||
// adjusted-workday days may shift when the State Council announces
|
// 办公厅 announcement (usually released Nov of prior year)** is the
|
||||||
// each year's calendar (typically Q4 of the prior year).
|
// ultimate source of truth. If a date here disagrees with 国务院
|
||||||
|
// notice when published, edit this file and re-release.
|
||||||
|
//
|
||||||
|
// Reference dates used (公历 yyyy-MM-dd → 农历):
|
||||||
|
// 2026-02-17 → 农历 正月 初一 (春节)
|
||||||
|
// 2026-04-05 → 清明 (公历固定)
|
||||||
|
// 2026-06-19 → 农历 五月 初五 (端午)
|
||||||
|
// 2026-09-25 → 农历 八月 十五 (中秋)
|
||||||
|
//
|
||||||
|
// In years where 中秋 falls within 6 days of 国庆, the State Council
|
||||||
|
// typically merges them into a single 8-10 day window. 2026 has
|
||||||
|
// 中秋 09-25 (Fri) and 国庆 10-01 (Thu) → window 09-25 ~ 10-04 is the
|
||||||
|
// most likely pattern (with 10-10 Saturday or earlier 09-20 Sunday
|
||||||
|
// for makeup work day).
|
||||||
//
|
//
|
||||||
// Data structure is a flat list rather than a JSON resource so the
|
// Data structure is a flat list rather than a JSON resource so the
|
||||||
// plugin bundle stays single-binary and dependency-free. To update:
|
// plugin bundle stays single-binary and dependency-free.
|
||||||
// edit this file when the next year's official 国务院办公厅
|
|
||||||
// announcement publishes.
|
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
@ -20,8 +31,7 @@ struct Holiday {
|
|||||||
let name: String
|
let name: String
|
||||||
/// First day of the holiday window (yyyy-MM-dd, Asia/Shanghai).
|
/// First day of the holiday window (yyyy-MM-dd, Asia/Shanghai).
|
||||||
let startDate: String
|
let startDate: String
|
||||||
/// Total consecutive days off, including the trigger day. e.g.
|
/// Total consecutive days off, including the trigger day.
|
||||||
/// 国庆 = 8 covers 10/01-10/08 (when paired with 中秋 in 2026).
|
|
||||||
let days: Int
|
let days: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,18 +47,28 @@ struct UpcomingHoliday {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum HolidayDatabase {
|
enum HolidayDatabase {
|
||||||
/// 2026 法定假期 — estimated from typical State Council pattern.
|
/// 2026 法定假期 — best estimate per typical State Council调休 pattern.
|
||||||
/// **Verify against the official 国务院办公厅 announcement.**
|
/// VERIFY against the official 国务院办公厅 公告 when published.
|
||||||
|
///
|
||||||
|
/// 春节 starts on 除夕 (the day before 初一) per 2024 policy update.
|
||||||
|
/// 中秋 + 国庆 merged because 2026 中秋 09-25 falls 6 days before
|
||||||
|
/// 国庆 10-01 — same pattern as 2017 / 2020 when they were close.
|
||||||
static let entries2026: [Holiday] = [
|
static let entries2026: [Holiday] = [
|
||||||
Holiday(name: "元旦", startDate: "2026-01-01", days: 1),
|
// 元旦 周四 — 单日不调休(2024 政策起元旦只放 1 天)
|
||||||
Holiday(name: "春节", startDate: "2026-02-17", days: 7),
|
Holiday(name: "元旦", startDate: "2026-01-01", days: 1),
|
||||||
Holiday(name: "清明", startDate: "2026-04-04", days: 3),
|
// 春节 02-16 除夕 ~ 02-22 初七,7 天。调休 02-14 周六 + 02-28 周六上班
|
||||||
Holiday(name: "五一", startDate: "2026-05-01", days: 5),
|
Holiday(name: "春节", startDate: "2026-02-16", days: 7),
|
||||||
Holiday(name: "端午", startDate: "2026-06-19", days: 3),
|
// 清明 04-05 周日,调休 04-04 ~ 04-06 共 3 天
|
||||||
Holiday(name: "中秋·国庆", startDate: "2026-10-01", days: 8),
|
Holiday(name: "清明", startDate: "2026-04-04", days: 3),
|
||||||
// Roll-over to early 2027 so late-Dec users still see a next
|
// 五一 05-01 周五 ~ 05-05 周二,5 天。调休 04-26 周日上班
|
||||||
// holiday after the 2026 calendar runs out.
|
Holiday(name: "劳动节", startDate: "2026-05-01", days: 5),
|
||||||
Holiday(name: "元旦", startDate: "2027-01-01", days: 3),
|
// 端午 06-19 周五 ~ 06-21 周日,3 天(自然连周末,无调休)
|
||||||
|
Holiday(name: "端午", startDate: "2026-06-19", days: 3),
|
||||||
|
// 中秋 + 国庆 合并 09-25 周五 ~ 10-04 周日,10 天
|
||||||
|
// 调休 09-19 / 10-10 周六上班
|
||||||
|
Holiday(name: "中秋·国庆", startDate: "2026-09-25", days: 10),
|
||||||
|
// 跨年 roll-over — 元旦 2027 估计 01-01 周五 ~ 01-03 周日,3 天
|
||||||
|
Holiday(name: "元旦", startDate: "2027-01-01", days: 3),
|
||||||
]
|
]
|
||||||
|
|
||||||
/// Returns the soonest upcoming or currently-ongoing holiday.
|
/// Returns the soonest upcoming or currently-ongoing holiday.
|
||||||
|
|||||||
@ -45,7 +45,7 @@ private enum K {
|
|||||||
static let waterGoal = "water.goal" // Int, default 8
|
static let waterGoal = "water.goal" // Int, default 8
|
||||||
static let waterAutoFromPomo = "water.autoFromPomo" // Bool, default true
|
static let waterAutoFromPomo = "water.autoFromPomo" // Bool, default true
|
||||||
static let waterHourlyReminder = "water.hourlyReminder" // Bool, default true
|
static let waterHourlyReminder = "water.hourlyReminder" // Bool, default true
|
||||||
static let lastWaterReminderHr = "water.lastReminderHour" // Int, hour 0-23 last fired (per day)
|
static let lastWaterReminderHr = "water.lastReminderHour" // String "yyyy-MM-dd HH" — last hour that fired, scoped per-day
|
||||||
static let clockoutHHmm = "clockout.hhmm" // String "18:00"
|
static let clockoutHHmm = "clockout.hhmm" // String "18:00"
|
||||||
static let lastClockoutCelebDate = "clockout.lastCelebDate" // String yyyy-MM-dd, dedupe per-day
|
static let lastClockoutCelebDate = "clockout.lastCelebDate" // String yyyy-MM-dd, dedupe per-day
|
||||||
}
|
}
|
||||||
@ -357,12 +357,13 @@ final class WorkerStore: ObservableObject {
|
|||||||
guard (9...18).contains(hour) else { return }
|
guard (9...18).contains(hour) else { return }
|
||||||
// Already reached goal — no nag.
|
// Already reached goal — no nag.
|
||||||
guard waterCupsToday < waterGoal else { return }
|
guard waterCupsToday < waterGoal else { return }
|
||||||
let lastFiredHour = defaults.integer(forKey: K.lastWaterReminderHr)
|
// Scope dedupe by date+hour so yesterday's 17:00 doesn't block
|
||||||
// Sentinel: 0 means "never fired" — first run at any hour >= 9
|
// today's 17:00. Prior int-only key blocked the same hour
|
||||||
// will pass the != check. We accept one wasted re-fire on the
|
// forever within calendar 24h cycles.
|
||||||
// boundary case where last == hour 0 from a fresh install.
|
let key = "\(Self.dateKey(now, calendar: calendar)) \(String(format: "%02d", hour))"
|
||||||
guard lastFiredHour != hour else { return }
|
let lastFiredKey = defaults.string(forKey: K.lastWaterReminderHr) ?? ""
|
||||||
defaults.set(hour, forKey: K.lastWaterReminderHr)
|
guard lastFiredKey != key else { return }
|
||||||
|
defaults.set(key, forKey: K.lastWaterReminderHr)
|
||||||
let remaining = waterGoal - waterCupsToday
|
let remaining = waterGoal - waterCupsToday
|
||||||
WorkerNotificationCenter.shared.notify(
|
WorkerNotificationCenter.shared.notify(
|
||||||
title: "💧 喝水时间到",
|
title: "💧 喝水时间到",
|
||||||
|
|||||||
@ -148,7 +148,7 @@ struct ExpandedView: View {
|
|||||||
Text(footerLeftText)
|
Text(footerLeftText)
|
||||||
}
|
}
|
||||||
Spacer()
|
Spacer()
|
||||||
Text("v0.1 · 本地运行")
|
Text("v0.3.0 · 本地运行")
|
||||||
}
|
}
|
||||||
.font(.system(size: 11))
|
.font(.system(size: 11))
|
||||||
.foregroundColor(WorkerTheme.fg40)
|
.foregroundColor(WorkerTheme.fg40)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user