Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
67 changes: 67 additions & 0 deletions Timer/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
self.addBadgeToDock(controller: controller)

UNUserNotificationCenter.current().delegate = self
self.registerNotificationCategories()
Task {
do {
try await UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound])
Expand Down Expand Up @@ -161,6 +162,23 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
completionHandler([.banner, .sound])
}

nonisolated func userNotificationCenter(
_: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
let actionIdentifier = response.actionIdentifier
let controllerIdentifier = response.notification.request.content.userInfo[
MVNotificationIdentifiers.controllerIdentifierKey
] as? String

Task { @MainActor in
guard let appDelegate = NSApplication.shared.delegate as? AppDelegate else { return }
appDelegate.handleNotificationAction(actionIdentifier, controllerIdentifier: controllerIdentifier)
}
Comment thread
mookwoo marked this conversation as resolved.
Outdated

completionHandler()
}
Comment thread
mookwoo marked this conversation as resolved.
Outdated

func addBadgeToDock(controller: MVTimerController) {
if self.currentlyInDock != controller {
self.removeBadgeFromDock()
Expand Down Expand Up @@ -199,6 +217,55 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
self.staysOnTop = UserDefaults.standard.bool(forKey: MVUserDefaultsKeys.staysOnTop)
}

private func handleNotificationAction(_ actionIdentifier: String, controllerIdentifier: String?) {
guard let controllerIdentifier,
let controller = self.controllers.first(where: { $0.identifier == controllerIdentifier }) else { return }
Comment thread
mookwoo marked this conversation as resolved.
Outdated

switch actionIdentifier {
case MVNotificationIdentifiers.restartTimerActionIdentifier:
controller.restartLastTimer()

case MVNotificationIdentifiers.addFiveMinutesActionIdentifier:
controller.addTime(seconds: 5 * 60)
Comment thread
mookwoo marked this conversation as resolved.
Outdated

case MVNotificationIdentifiers.stopTimerActionIdentifier:
controller.resetTimer()
Comment thread
mookwoo marked this conversation as resolved.
Outdated

case UNNotificationDefaultActionIdentifier:
controller.window?.makeKeyAndOrderFront(nil)
NSApplication.shared.activate(ignoringOtherApps: true)

default:
break
}
}

private func registerNotificationCategories() {
let restartAction = UNNotificationAction(
identifier: MVNotificationIdentifiers.restartTimerActionIdentifier,
title: "Restart",
options: []
)
let addFiveMinutesAction = UNNotificationAction(
identifier: MVNotificationIdentifiers.addFiveMinutesActionIdentifier,
title: "+5 min",
options: []
)
let stopAction = UNNotificationAction(
identifier: MVNotificationIdentifiers.stopTimerActionIdentifier,
title: "Stop",
options: [.destructive]
)
let category = UNNotificationCategory(
identifier: MVNotificationIdentifiers.timerCompleteCategoryIdentifier,
actions: [restartAction, addFiveMinutesAction, stopAction],
intentIdentifiers: [],
options: []
)

UNUserNotificationCenter.current().setNotificationCategories([category])
}

private func observeNotifications() {
self.notificationTasks.append(
Task { [weak self] in
Expand Down
22 changes: 22 additions & 0 deletions Timer/MVTimerController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import AVFoundation
import UserNotifications

final class MVTimerController: NSWindowController {
let identifier = UUID().uuidString

private weak var dockMenuItem: NSMenuItem?
let clockView = MVClockView()

Expand Down Expand Up @@ -73,6 +75,8 @@ final class MVTimerController: NSWindowController {
private func handleClockTimer() {
let content = UNMutableNotificationContent()
content.title = "It's time! 🕘"
content.categoryIdentifier = MVNotificationIdentifiers.timerCompleteCategoryIdentifier
content.userInfo = [MVNotificationIdentifiers.controllerIdentifierKey: self.identifier]

let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
UNUserNotificationCenter.current().add(request)
Expand Down Expand Up @@ -100,4 +104,22 @@ final class MVTimerController: NSWindowController {
self.soundURL = nil
}
}

func restartLastTimer() {
guard let seconds = self.clockView.lastTimerSeconds, seconds > 0 else { return }
self.clockView.startTimer(seconds: seconds)
}

func addTime(seconds: CGFloat) {
let currentSeconds = self.clockView.timerTask != nil || self.clockView.paused ? self.clockView.seconds : 0
self.clockView.startTimer(seconds: currentSeconds + seconds)
}

func resetTimer() {
self.clockView.paused = false
self.clockView.stop()
self.clockView.seconds = 0
self.clockView.updateTimerTime()
self.clockView.inputSeconds = false
}
}
8 changes: 8 additions & 0 deletions Timer/MVUserDefaultsKeys.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,11 @@ enum MVUserDefaultsKeys {
static let staysOnTop = "staysOnTop"
static let soundIndex = "soundIndex"
}

enum MVNotificationIdentifiers {
static let timerCompleteCategoryIdentifier = "timerComplete"
static let controllerIdentifierKey = "controllerIdentifier"
static let restartTimerActionIdentifier = "restartTimer"
static let addFiveMinutesActionIdentifier = "addFiveMinutes"
static let stopTimerActionIdentifier = "stopTimer"
}
Comment thread
mookwoo marked this conversation as resolved.