From ae0892dcd31621c9273f1e4fe4810367f86fb6ba Mon Sep 17 00:00:00 2001 From: Oleksandr Riasnyi Date: Fri, 7 Aug 2026 22:53:58 +0300 Subject: [PATCH] Add Pomodoro presets and daily count to TimerPane and TimerStore --- Sources/VoidBar/Services/TimerStore.swift | 46 ++++++++++++++++++++--- Sources/VoidBar/UI/TimerPane.swift | 31 ++++++++++++++- 2 files changed, 71 insertions(+), 6 deletions(-) diff --git a/Sources/VoidBar/Services/TimerStore.swift b/Sources/VoidBar/Services/TimerStore.swift index aeea207..21df4e5 100644 --- a/Sources/VoidBar/Services/TimerStore.swift +++ b/Sources/VoidBar/Services/TimerStore.swift @@ -12,19 +12,31 @@ final class TimerStore: ObservableObject { @Published var state: State = .idle @Published var timeRemaining: TimeInterval = 25 * 60 + @Published var selectedDuration: TimeInterval = 25 * 60 + @Published var completedToday: Int = 0 - let defaultDuration: TimeInterval = 25 * 60 private var timer: Timer? + private var lastCompletionDate: Date? var formattedTime: String { let minutes = Int(timeRemaining) / 60 let seconds = Int(timeRemaining) % 60 return String(format: "%02d:%02d", minutes, seconds) } + + init() { + let defaults = UserDefaults.standard + completedToday = defaults.integer(forKey: "pomodoroCompletedToday") + if let lastDate = defaults.object(forKey: "pomodoroLastDate") as? Date { + lastCompletionDate = lastDate + } + checkNewDay() + } func start() { + checkNewDay() if state == .idle { - timeRemaining = defaultDuration + timeRemaining = selectedDuration } state = .running timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in @@ -44,7 +56,14 @@ final class TimerStore: ObservableObject { state = .idle timer?.invalidate() timer = nil - timeRemaining = defaultDuration + timeRemaining = selectedDuration + } + + func selectDuration(_ duration: TimeInterval) { + selectedDuration = duration + if state == .idle { + timeRemaining = duration + } } private func tick() { @@ -57,11 +76,28 @@ final class TimerStore: ObservableObject { } private func finish() { + checkNewDay() + completedToday += 1 + lastCompletionDate = Date() + + let defaults = UserDefaults.standard + defaults.set(completedToday, forKey: "pomodoroCompletedToday") + defaults.set(lastCompletionDate, forKey: "pomodoroLastDate") + reset() let notification = NSUserNotification() notification.title = "Pomodoro Finished" - notification.informativeText = "Time to take a break!" - notification.soundName = NSUserNotificationDefaultSoundName + notification.informativeText = "Time to take a break! You have completed \(completedToday) today." + notification.soundName = "Glass" NSUserNotificationCenter.default.deliver(notification) } + + private func checkNewDay() { + guard let lastDate = lastCompletionDate else { return } + if !Calendar.current.isDateInToday(lastDate) { + completedToday = 0 + let defaults = UserDefaults.standard + defaults.set(0, forKey: "pomodoroCompletedToday") + } + } } diff --git a/Sources/VoidBar/UI/TimerPane.swift b/Sources/VoidBar/UI/TimerPane.swift index 3f44668..661dcb6 100644 --- a/Sources/VoidBar/UI/TimerPane.swift +++ b/Sources/VoidBar/UI/TimerPane.swift @@ -2,12 +2,33 @@ import SwiftUI struct TimerPane: View { @ObservedObject var timer: TimerStore + + let presets: [TimeInterval] = [5 * 60, 10 * 60, 25 * 60, 50 * 60] + let presetLabels = ["5m", "10m", "25m", "50m"] var body: some View { - VStack(spacing: 20) { + VStack(spacing: 16) { Text(timer.formattedTime) .font(.system(size: 48, weight: .semibold).monospacedDigit()) .foregroundStyle(.white) + + HStack(spacing: 8) { + ForEach(0.. 0 { + Text("🍅 Completed today: \(timer.completedToday)") + .font(.system(size: 11, weight: .medium)) + .foregroundStyle(Theme.tertiary) + .padding(.top, 4) + } } .frame(maxWidth: .infinity, maxHeight: .infinity) }