From f0a116ccf7669e181a8dafc47bc8d643e2c735f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E7=BF=94=E5=AE=87?= Date: Wed, 20 May 2026 09:49:17 +0800 Subject: [PATCH] =?UTF-8?q?feat(clockout):=20weekend=20"=E4=BB=8A=E5=A4=A9?= =?UTF-8?q?=E4=B8=8D=E4=B8=8A=E7=8F=AD"=20mode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Saturday/Sunday previously kept showing the workday countdown even though no one's working — UX surfaced friction where there was nothing to count. Now ClockoutView switches to a purple weekend-hero card with "🌴 今天是周末 · 去做你想做的事" copy. Implementation: - WorkerStore.isWeekend computed (Sun=1, Sat=7) - ClockoutView.body branches: weekendHero + weekendTipText for weekend, original countdown+progress+controls for weekday - Clockout celebration logic still runs in the background — if someone does work Saturday and crosses their configured clockout hour, the next-weekday view will see the dedupe key updated; no spurious extra fire Co-Authored-By: Claude Opus 4.7 (1M context) --- Sources/engine/WorkerStore.swift | 8 +++ Sources/ui/ClockoutView.swift | 99 ++++++++++++++++++++++++++------ 2 files changed, 90 insertions(+), 17 deletions(-) diff --git a/Sources/engine/WorkerStore.swift b/Sources/engine/WorkerStore.swift index 172fe8d..80a6402 100644 --- a/Sources/engine/WorkerStore.swift +++ b/Sources/engine/WorkerStore.swift @@ -576,6 +576,14 @@ final class WorkerStore: ObservableObject { /// no observable "zero" frame — celebrating the "下班" moment was /// impossible. Now: hit zero, hold zero until midnight, restart at /// new day's wall-time countdown. + /// True iff today is Saturday or Sunday (local time). Used by + /// ClockoutView to switch to a "今天不上班" mode without forcing + /// the user to fiddle with their clockout time on the weekend. + var isWeekend: Bool { + let weekday = calendar.component(.weekday, from: Date()) + return weekday == 1 || weekday == 7 // Sun = 1, Sat = 7 + } + var clockoutRemainingSec: Int { let now = Date() var comps = calendar.dateComponents([.year, .month, .day], from: now) diff --git a/Sources/ui/ClockoutView.swift b/Sources/ui/ClockoutView.swift index f81a365..64e2d0d 100644 --- a/Sources/ui/ClockoutView.swift +++ b/Sources/ui/ClockoutView.swift @@ -16,23 +16,22 @@ struct ClockoutView: View { var body: some View { ZStack(alignment: .top) { VStack(spacing: 18) { - statusBadge - - countdownDisplay - - progressBar - - controls - - Divider() - .background(WorkerTheme.overlay08) - .padding(.horizontal, 24) - - timeRow - - tipText - - Spacer(minLength: 0) + if store.isWeekend { + weekendHero + weekendTipText + Spacer(minLength: 0) + } else { + statusBadge + countdownDisplay + progressBar + controls + Divider() + .background(WorkerTheme.overlay08) + .padding(.horizontal, 24) + timeRow + tipText + Spacer(minLength: 0) + } } .padding(.top, 8) @@ -275,6 +274,72 @@ struct ClockoutView: View { ) } + // MARK: - Weekend mode + + /// Weekend-mode hero — replaces the countdown card with a purple + /// "today's a weekend" panel. The clockout countdown still works + /// in the background (anyone working Saturday's edge case), but + /// the default UI no longer rubs in the fact that there's + /// theoretically a clockout time on a day no one's working. + private var weekendHero: some View { + VStack(spacing: 8) { + HStack(spacing: 8) { + Circle() + .fill(WorkerTheme.weekendPurple) + .frame(width: 8, height: 8) + .shadow(color: WorkerTheme.weekendPurple.opacity(0.7), radius: 4) + Text("今天不上班") + .font(.system(size: 12.5, weight: .semibold)) + .foregroundColor(WorkerTheme.fg85) + } + .padding(.horizontal, 12) + .frame(height: 26) + .background(Capsule().fill(WorkerTheme.overlay06)) + + Text("🌴") + .font(.system(size: 64)) + .padding(.top, 8) + + Text("今天是周末") + .font(.system(size: 22, weight: .bold)) + .foregroundColor(WorkerTheme.fgPrimary) + + Text("去做你想做的事") + .font(.system(size: 13)) + .foregroundColor(WorkerTheme.fg55) + } + .frame(maxWidth: .infinity) + .padding(.vertical, 28) + .background( + RoundedRectangle(cornerRadius: 14) + .fill( + LinearGradient( + colors: [ + WorkerTheme.weekendPurple.opacity(0.18), + WorkerTheme.weekendPurple.opacity(0.04) + ], + startPoint: .topLeading, endPoint: .bottomTrailing + ) + ) + .overlay( + RoundedRectangle(cornerRadius: 14) + .stroke(WorkerTheme.weekendPurple.opacity(0.35), lineWidth: 0.5) + ) + ) + .padding(.horizontal, 16) + .padding(.top, 12) + } + + private var weekendTipText: some View { + Text("如果你周末也上班,记得在 周末 tab 取消休息时调整设置。\n(暂时还没做这个 toggle — 反正下周一会自动恢复倒计时。)") + .font(.system(size: 11)) + .foregroundColor(WorkerTheme.fg45) + .padding(.horizontal, 20) + .padding(.top, 8) + .multilineTextAlignment(.center) + .frame(maxWidth: .infinity) + } + private var tipText: some View { Text("到点会有通知 + 一段 Glass 音效庆祝下班\n每日只触发一次,重启插件不会重复响铃。") .font(.system(size: 11))