Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 41 additions & 5 deletions Sources/VoidBar/Services/TimerStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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() {
Expand All @@ -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")
}
}
}
31 changes: 30 additions & 1 deletion Sources/VoidBar/UI/TimerPane.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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..<presets.count, id: \.self) { i in
Button(presetLabels[i]) {
timer.selectDuration(presets[i])
}
.font(.system(size: 11, weight: .medium))
.foregroundStyle(timer.selectedDuration == presets[i] ? .white : Theme.secondary)
.padding(.horizontal, 10)
.padding(.vertical, 4)
.background(
Capsule()
.fill(timer.selectedDuration == presets[i] ? Color.white.opacity(0.15) : Color.clear)
)
.buttonStyle(.plain)
.disabled(timer.state == .running)
}
}

HStack(spacing: 30) {
Button {
Expand All @@ -30,6 +51,14 @@ struct TimerPane: View {
}
.buttonStyle(NotchButtonStyle(size: 50, prominent: true))
}
.padding(.top, 4)

if timer.completedToday > 0 {
Text("🍅 Completed today: \(timer.completedToday)")
.font(.system(size: 11, weight: .medium))
.foregroundStyle(Theme.tertiary)
.padding(.top, 4)
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
Expand Down
Loading