Skip to content

Commit

Permalink
feat: add notifications on focus mode beginning
Browse files Browse the repository at this point in the history
  • Loading branch information
a11rew committed Nov 19, 2023
1 parent ec63e46 commit b3b9639
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 9 deletions.
4 changes: 4 additions & 0 deletions Calendar Focus Sync.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
4907ED502AA5C0EF000A566E /* CalendarSync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4907ED4F2AA5C0EF000A566E /* CalendarSync.swift */; };
490D80C72ADB151800E361C1 /* calendar-focus-sync.shortcut in Resources */ = {isa = PBXBuildFile; fileRef = 490D80C62ADB151800E361C1 /* calendar-focus-sync.shortcut */; };
491C43832AFC3452000168EA /* SyncOrchestrator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 491C43822AFC3452000168EA /* SyncOrchestrator.swift */; };
4963BE852B0964DF0004FA7D /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4963BE842B0964DF0004FA7D /* Notifications.swift */; };
496FB0CD2AF6638900FE2BE8 /* SyncOrchestratorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 496FB0CC2AF6638900FE2BE8 /* SyncOrchestratorTests.swift */; };
496FB0CF2AF6640000FE2BE8 /* UserPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 496FB0CE2AF6640000FE2BE8 /* UserPreferences.swift */; };
49A566782AED6B5B0083D89A /* ShortcutInstallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49A566772AED6B5B0083D89A /* ShortcutInstallView.swift */; };
Expand Down Expand Up @@ -49,6 +50,7 @@
490D80C62ADB151800E361C1 /* calendar-focus-sync.shortcut */ = {isa = PBXFileReference; lastKnownFileType = file; path = "calendar-focus-sync.shortcut"; sourceTree = "<group>"; };
490D80C82ADB1E0A00E361C1 /* Calendar Focus SyncRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Calendar Focus SyncRelease.entitlements"; sourceTree = "<group>"; };
491C43822AFC3452000168EA /* SyncOrchestrator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncOrchestrator.swift; sourceTree = "<group>"; };
4963BE842B0964DF0004FA7D /* Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifications.swift; sourceTree = "<group>"; };
496FB0CC2AF6638900FE2BE8 /* SyncOrchestratorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncOrchestratorTests.swift; sourceTree = "<group>"; };
496FB0CE2AF6640000FE2BE8 /* UserPreferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPreferences.swift; sourceTree = "<group>"; };
49A566772AED6B5B0083D89A /* ShortcutInstallView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutInstallView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -101,6 +103,7 @@
4907ED4F2AA5C0EF000A566E /* CalendarSync.swift */,
49B74B292AA8640E00C6A853 /* SyncOrchestrator.swift */,
49B74B2E2AADEB4200C6A853 /* Focus.swift */,
4963BE842B0964DF0004FA7D /* Notifications.swift */,
);
path = Services;
sourceTree = "<group>";
Expand Down Expand Up @@ -337,6 +340,7 @@
49F388FE2AEAF9D600F3D9BF /* UpcomingEventsView.swift in Sources */,
49B74B2F2AADEB4200C6A853 /* Focus.swift in Sources */,
49AB0DBA2AA3AAA10047CC2E /* SettingsView.swift in Sources */,
4963BE852B0964DF0004FA7D /* Notifications.swift in Sources */,
49AB0D8D2AA3A8C80047CC2E /* App.swift in Sources */,
49B74B352AB1E05200C6A853 /* Shell.swift in Sources */,
49AB0DBC2AA4837E0047CC2E /* Constants.swift in Sources */,
Expand Down
4 changes: 3 additions & 1 deletion Calendar Focus Sync/App.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,15 @@ struct AppMain: App {
}

@MainActor
private final class AppDelegate: NSObject, NSApplicationDelegate {
final class AppDelegate: NSObject, NSApplicationDelegate {
var window: NSWindow?

func applicationDidFinishLaunching(_ notification: Notification) {
// Listen for window becoming key (i.e., active)
NotificationCenter.default.addObserver(self, selector: #selector(windowDidBecomeKey(notification:)), name: NSWindow.didBecomeKeyNotification, object: nil)

notificationCenter.delegate = self

Task {
await SyncOrchestrator.shared.go()
}
Expand Down
44 changes: 44 additions & 0 deletions Calendar Focus Sync/Services/Notifications.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import Foundation
import UserNotifications

let notificationCenter = UNUserNotificationCenter.current()

@discardableResult
func requestNotificationPermissions() async throws -> Bool {
let status = await notificationCenter.notificationSettings()
var isGranted = false

switch status.authorizationStatus {
case .authorized:
isGranted = true
default:
isGranted = try await notificationCenter.requestAuthorization(options: [.alert, .badge, .sound])
}

return isGranted
}

func sendFocusBeginningNotification(event: CalendarEvent) {
let content = UNMutableNotificationContent()
content.title = "Focus Mode Activated"
content.body = "Your event \(event.title) is starting soon. Focus up!"
content.interruptionLevel = .timeSensitive

content.sound = UNNotificationSound.default

let request = UNNotificationRequest(identifier: event.id, content: content, trigger: nil)

notificationCenter.add(request) { error in
if error != nil {
print(error as Any)
}
}
}

extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.list, .banner, .sound, .badge])
}
}
4 changes: 4 additions & 0 deletions Calendar Focus Sync/Services/SyncOrchestrator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ class SyncOrchestrator {
activeFocusModeTimers.removeValue(forKey: event.id)
}
let timer = Timer.scheduledTimer(withTimeInterval: triggerDate.timeIntervalSinceNow, repeats: false, block: { _ in
if self.userPreferences.notificationsAccessGranted {
sendFocusBeginningNotification(event: event)
}

enableFocusMode(duration: eventDuration)
}
)
Expand Down
9 changes: 9 additions & 0 deletions Calendar Focus Sync/State/UserPreferences.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ class UserPreferences: ObservableObject {
}
}
}

@Published var notificationsAccessGranted: Bool {
didSet {
defaults.set(notificationsAccessGranted, forKey: "notificationsAccessGranted")
}
}


@Published var selectedPriorTimeBuffer: Int {
didSet {
defaults.set(selectedPriorTimeBuffer, forKey: "selectedPriorTimeBuffer")
Expand All @@ -27,5 +35,6 @@ class UserPreferences: ObservableObject {
init() {
self.nativeCalendarAccessGranted = EKEventStore.authorizationStatus(for: .event) == .fullAccess
self.selectedPriorTimeBuffer = defaults.integer(forKey: "selectedPriorTimeBuffer") != 0 ? defaults.integer(forKey: "selectedPriorTimeBuffer") : 5
self.notificationsAccessGranted = defaults.bool(forKey: "notificationsAccessGranted")
}
}
36 changes: 28 additions & 8 deletions Calendar Focus Sync/Views/SettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,19 @@ struct SettingsView: View {
Text("Preferences")

VStack {
HStack {
Text("Enter Focus Mode how long before")
Spacer()
Picker("", selection: $userPreferences.selectedPriorTimeBuffer) {
Text("1 minute").tag(TimeBefore.one_minute.rawValue)
Text("2 minutes").tag(TimeBefore.two_minutes.rawValue)
Text("5 minutes").tag(TimeBefore.five_minutes.rawValue)
Text("10 minutes").tag(TimeBefore.ten_minutes.rawValue)
}.frame(maxWidth: 140)
}

Divider()

HStack {
Text("Launch at login")
Spacer()
Expand All @@ -69,14 +82,21 @@ struct SettingsView: View {
Divider()

HStack {
Text("Enter Focus Mode how long before")
Text("Notifications when Focus Mode begins")
Spacer()
Picker("", selection: $userPreferences.selectedPriorTimeBuffer) {
Text("1 minute").tag(TimeBefore.one_minute.rawValue)
Text("2 minutes").tag(TimeBefore.two_minutes.rawValue)
Text("5 minutes").tag(TimeBefore.five_minutes.rawValue)
Text("10 minutes").tag(TimeBefore.ten_minutes.rawValue)
}.frame(maxWidth: 140)
Toggle("", isOn: $userPreferences.notificationsAccessGranted).toggleStyle(.switch).onChange(of: userPreferences.notificationsAccessGranted, initial: false) {
(_, newValue) in

if newValue {
Task {
do {
userPreferences.notificationsAccessGranted = try await requestNotificationPermissions()
} catch {
userPreferences.notificationsAccessGranted = false
}
}
}
}
}
}
.padding(8)
Expand Down Expand Up @@ -105,7 +125,7 @@ struct SettingsViewPreview: PreviewProvider {
static var previews: some View {
let preferences = UserPreferences()
let appState = AppState()

SettingsView()
.environmentObject(preferences)
.environmentObject(appState)
Expand Down

0 comments on commit b3b9639

Please sign in to comment.