fixed
This commit is contained in:
@@ -15,6 +15,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
|||||||
private let serviceContainer: ServiceContainer
|
private let serviceContainer: ServiceContainer
|
||||||
private let windowManager: WindowManaging
|
private let windowManager: WindowManaging
|
||||||
private var updateManager: UpdateManager?
|
private var updateManager: UpdateManager?
|
||||||
|
private var systemSleepManager: SystemSleepManager?
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
private var hasStartedTimers = false
|
private var hasStartedTimers = false
|
||||||
|
|
||||||
@@ -46,6 +47,11 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
timerEngine = serviceContainer.timerEngine
|
timerEngine = serviceContainer.timerEngine
|
||||||
|
systemSleepManager = SystemSleepManager(
|
||||||
|
timerEngine: timerEngine,
|
||||||
|
settingsManager: settingsManager
|
||||||
|
)
|
||||||
|
systemSleepManager?.startObserving()
|
||||||
|
|
||||||
serviceContainer.setupSmartModeServices()
|
serviceContainer.setupSmartModeServices()
|
||||||
|
|
||||||
@@ -54,8 +60,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
|||||||
updateManager = UpdateManager.shared
|
updateManager = UpdateManager.shared
|
||||||
}
|
}
|
||||||
|
|
||||||
setupLifecycleObservers()
|
|
||||||
|
|
||||||
observeSettingsChanges()
|
observeSettingsChanges()
|
||||||
|
|
||||||
if settingsManager.settings.hasCompletedOnboarding {
|
if settingsManager.settings.hasCompletedOnboarding {
|
||||||
@@ -129,34 +133,13 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
|||||||
func applicationWillTerminate(_ notification: Notification) {
|
func applicationWillTerminate(_ notification: Notification) {
|
||||||
logInfo(" applicationWill terminate")
|
logInfo(" applicationWill terminate")
|
||||||
settingsManager.saveImmediately()
|
settingsManager.saveImmediately()
|
||||||
|
stopLifecycleObservers()
|
||||||
timerEngine?.stop()
|
timerEngine?.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupLifecycleObservers() {
|
private func stopLifecycleObservers() {
|
||||||
NSWorkspace.shared.notificationCenter.addObserver(
|
systemSleepManager?.stopObserving()
|
||||||
self,
|
systemSleepManager = nil
|
||||||
selector: #selector(systemWillSleep),
|
|
||||||
name: NSWorkspace.willSleepNotification,
|
|
||||||
object: nil
|
|
||||||
)
|
|
||||||
|
|
||||||
NSWorkspace.shared.notificationCenter.addObserver(
|
|
||||||
self,
|
|
||||||
selector: #selector(systemDidWake),
|
|
||||||
name: NSWorkspace.didWakeNotification,
|
|
||||||
object: nil
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func systemWillSleep() {
|
|
||||||
logInfo("System will sleep")
|
|
||||||
timerEngine?.handleSystemSleep()
|
|
||||||
settingsManager.saveImmediately()
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func systemDidWake() {
|
|
||||||
logInfo("System did wake")
|
|
||||||
timerEngine?.handleSystemWake()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func observeReminderEvents() {
|
private func observeReminderEvents() {
|
||||||
|
|||||||
@@ -60,12 +60,6 @@ protocol TimerEngineProviding: AnyObject, ObservableObject {
|
|||||||
/// Checks if a timer is currently paused
|
/// Checks if a timer is currently paused
|
||||||
func isTimerPaused(_ identifier: TimerIdentifier) -> Bool
|
func isTimerPaused(_ identifier: TimerIdentifier) -> Bool
|
||||||
|
|
||||||
/// Handles system sleep event
|
|
||||||
func handleSystemSleep()
|
|
||||||
|
|
||||||
/// Handles system wake event
|
|
||||||
func handleSystemWake()
|
|
||||||
|
|
||||||
/// Sets up smart mode with fullscreen and idle detection services
|
/// Sets up smart mode with fullscreen and idle detection services
|
||||||
func setupSmartMode(
|
func setupSmartMode(
|
||||||
fullscreenService: FullscreenDetectionService?,
|
fullscreenService: FullscreenDetectionService?,
|
||||||
|
|||||||
63
Gaze/Services/SystemSleepManager.swift
Normal file
63
Gaze/Services/SystemSleepManager.swift
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
//
|
||||||
|
// SystemSleepManager.swift
|
||||||
|
// Gaze
|
||||||
|
//
|
||||||
|
// Coordinates system sleep/wake handling.
|
||||||
|
//
|
||||||
|
|
||||||
|
import AppKit
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
final class SystemSleepManager {
|
||||||
|
private let settingsManager: any SettingsProviding
|
||||||
|
private weak var timerEngine: (any TimerEngineProviding)?
|
||||||
|
private var observers: [NSObjectProtocol] = []
|
||||||
|
|
||||||
|
init(timerEngine: (any TimerEngineProviding)?, settingsManager: any SettingsProviding) {
|
||||||
|
self.timerEngine = timerEngine
|
||||||
|
self.settingsManager = settingsManager
|
||||||
|
}
|
||||||
|
|
||||||
|
func startObserving() {
|
||||||
|
guard observers.isEmpty else { return }
|
||||||
|
|
||||||
|
let center = NSWorkspace.shared.notificationCenter
|
||||||
|
let willSleep = center.addObserver(
|
||||||
|
forName: NSWorkspace.willSleepNotification,
|
||||||
|
object: nil,
|
||||||
|
queue: .main
|
||||||
|
) { [weak self] _ in
|
||||||
|
self?.handleSystemWillSleep()
|
||||||
|
}
|
||||||
|
|
||||||
|
let didWake = center.addObserver(
|
||||||
|
forName: NSWorkspace.didWakeNotification,
|
||||||
|
object: nil,
|
||||||
|
queue: .main
|
||||||
|
) { [weak self] _ in
|
||||||
|
self?.handleSystemDidWake()
|
||||||
|
}
|
||||||
|
|
||||||
|
observers = [willSleep, didWake]
|
||||||
|
}
|
||||||
|
|
||||||
|
func stopObserving() {
|
||||||
|
let center = NSWorkspace.shared.notificationCenter
|
||||||
|
for observer in observers {
|
||||||
|
center.removeObserver(observer)
|
||||||
|
}
|
||||||
|
observers.removeAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleSystemWillSleep() {
|
||||||
|
logInfo("System will sleep")
|
||||||
|
timerEngine?.stop()
|
||||||
|
settingsManager.saveImmediately()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleSystemDidWake() {
|
||||||
|
logInfo("System did wake")
|
||||||
|
timerEngine?.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -146,7 +146,8 @@ class TimerEngine: ObservableObject {
|
|||||||
let config = settingsProvider.timerConfiguration(for: type)
|
let config = settingsProvider.timerConfiguration(for: type)
|
||||||
return config.intervalSeconds
|
return config.intervalSeconds
|
||||||
case .user(let id):
|
case .user(let id):
|
||||||
guard let userTimer = settingsProvider.settings.userTimers.first(where: { $0.id == id }) else {
|
guard let userTimer = settingsProvider.settings.userTimers.first(where: { $0.id == id })
|
||||||
|
else {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return userTimer.intervalMinutes * 60
|
return userTimer.intervalMinutes * 60
|
||||||
@@ -183,7 +184,8 @@ class TimerEngine: ObservableObject {
|
|||||||
secondsRemaining: updatedState.remainingSeconds
|
secondsRemaining: updatedState.remainingSeconds
|
||||||
) {
|
) {
|
||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
await reminderService.prepareEnforceMode(secondsRemaining: updatedState.remainingSeconds)
|
await reminderService.prepareEnforceMode(
|
||||||
|
secondsRemaining: updatedState.remainingSeconds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,22 +217,6 @@ class TimerEngine: ObservableObject {
|
|||||||
return stateManager.isTimerPaused(identifier)
|
return stateManager.isTimerPaused(identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
// System sleep/wake handling is now managed by SystemSleepManager
|
|
||||||
// This method is kept for compatibility but will be removed in future versions
|
|
||||||
/// Handles system sleep event - deprecated
|
|
||||||
@available(*, deprecated, message: "Use SystemSleepManager instead")
|
|
||||||
func handleSystemSleep() {
|
|
||||||
logDebug("System going to sleep (deprecated)")
|
|
||||||
// This functionality has been moved to SystemSleepManager
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handles system wake event - deprecated
|
|
||||||
@available(*, deprecated, message: "Use SystemSleepManager instead")
|
|
||||||
func handleSystemWake() {
|
|
||||||
logDebug("System waking up (deprecated)")
|
|
||||||
// This functionality has been moved to SystemSleepManager
|
|
||||||
}
|
|
||||||
|
|
||||||
private func timerConfigurations() -> [TimerIdentifier: TimerConfiguration] {
|
private func timerConfigurations() -> [TimerIdentifier: TimerConfiguration] {
|
||||||
var configurations: [TimerIdentifier: TimerConfiguration] = [:]
|
var configurations: [TimerIdentifier: TimerConfiguration] = [:]
|
||||||
for timerType in TimerType.allCases {
|
for timerType in TimerType.allCases {
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ struct AdditionalModifiersView: View {
|
|||||||
)
|
)
|
||||||
|
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
SetupHeader(icon: "slider.horizontal.3", title: "Additional Options", color: .purple)
|
SetupHeader(
|
||||||
|
icon: "slider.horizontal.3", title: "Additional Options", color: .purple)
|
||||||
|
|
||||||
Text("Optional features to enhance your experience")
|
Text("Optional features to enhance your experience")
|
||||||
.font(isCompact ? .subheadline : .title3)
|
.font(isCompact ? .subheadline : .title3)
|
||||||
@@ -77,7 +78,7 @@ struct AdditionalModifiersView: View {
|
|||||||
HStack(spacing: isCompact ? 10 : 16) {
|
HStack(spacing: isCompact ? 10 : 16) {
|
||||||
cardIndicator(index: 0, icon: "video.fill", label: "Enforce")
|
cardIndicator(index: 0, icon: "video.fill", label: "Enforce")
|
||||||
cardIndicator(index: 1, icon: "brain.fill", label: "Smart")
|
cardIndicator(index: 1, icon: "brain.fill", label: "Smart")
|
||||||
}
|
}.padding(.all, 20)
|
||||||
|
|
||||||
Button(action: { swapCards() }) {
|
Button(action: { swapCards() }) {
|
||||||
Image(systemName: "chevron.right")
|
Image(systemName: "chevron.right")
|
||||||
@@ -211,7 +212,11 @@ struct AdditionalModifiersView: View {
|
|||||||
private var enforceModeContent: some View {
|
private var enforceModeContent: some View {
|
||||||
VStack(spacing: isCompact ? 10 : 16) {
|
VStack(spacing: isCompact ? 10 : 16) {
|
||||||
Image(systemName: "video.fill")
|
Image(systemName: "video.fill")
|
||||||
.font(.system(size: isCompact ? AdaptiveLayout.Font.cardIconSmall : AdaptiveLayout.Font.cardIcon))
|
.font(
|
||||||
|
.system(
|
||||||
|
size: isCompact
|
||||||
|
? AdaptiveLayout.Font.cardIconSmall : AdaptiveLayout.Font.cardIcon)
|
||||||
|
)
|
||||||
.foregroundStyle(Color.accentColor)
|
.foregroundStyle(Color.accentColor)
|
||||||
|
|
||||||
Text("Enforce Mode")
|
Text("Enforce Mode")
|
||||||
@@ -292,7 +297,11 @@ struct AdditionalModifiersView: View {
|
|||||||
private var smartModeContent: some View {
|
private var smartModeContent: some View {
|
||||||
VStack(spacing: isCompact ? 10 : 16) {
|
VStack(spacing: isCompact ? 10 : 16) {
|
||||||
Image(systemName: "brain.fill")
|
Image(systemName: "brain.fill")
|
||||||
.font(.system(size: isCompact ? AdaptiveLayout.Font.cardIconSmall : AdaptiveLayout.Font.cardIcon))
|
.font(
|
||||||
|
.system(
|
||||||
|
size: isCompact
|
||||||
|
? AdaptiveLayout.Font.cardIconSmall : AdaptiveLayout.Font.cardIcon)
|
||||||
|
)
|
||||||
.foregroundStyle(.purple)
|
.foregroundStyle(.purple)
|
||||||
|
|
||||||
Text("Smart Mode")
|
Text("Smart Mode")
|
||||||
|
|||||||
@@ -132,7 +132,8 @@ final class MenuBarGuideOverlayPresenter {
|
|||||||
queue: .main
|
queue: .main
|
||||||
) { [weak self] notification in
|
) { [weak self] notification in
|
||||||
guard let window = notification.object as? NSWindow,
|
guard let window = notification.object as? NSWindow,
|
||||||
window.identifier == WindowIdentifiers.onboarding else {
|
window.identifier == WindowIdentifiers.onboarding
|
||||||
|
else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,8 +177,8 @@ struct MenuBarGuideOverlayView: View {
|
|||||||
$0.identifier == WindowIdentifiers.onboarding
|
$0.identifier == WindowIdentifiers.onboarding
|
||||||
}) {
|
}) {
|
||||||
let windowFrame = onboardingWindow.frame
|
let windowFrame = onboardingWindow.frame
|
||||||
let textRightX = windowFrame.midX + 210
|
let textRightX = windowFrame.midX
|
||||||
let textY = screenFrame.maxY - windowFrame.maxY + 505
|
let textY = screenFrame.maxY - windowFrame.maxY + 305
|
||||||
return CGPoint(x: textRightX, y: textY)
|
return CGPoint(x: textRightX, y: textY)
|
||||||
}
|
}
|
||||||
return CGPoint(x: screenSize.width * 0.5, y: screenSize.height * 0.45)
|
return CGPoint(x: screenSize.width * 0.5, y: screenSize.height * 0.45)
|
||||||
@@ -195,7 +196,6 @@ struct HandDrawnArrowShape: Shape {
|
|||||||
// This creates a more playful, hand-drawn feel
|
// This creates a more playful, hand-drawn feel
|
||||||
|
|
||||||
let dx = end.x - start.x
|
let dx = end.x - start.x
|
||||||
let dy = end.y - start.y
|
|
||||||
|
|
||||||
// First control point: go DOWN and slightly toward target
|
// First control point: go DOWN and slightly toward target
|
||||||
let ctrl1 = CGPoint(
|
let ctrl1 = CGPoint(
|
||||||
@@ -203,23 +203,14 @@ struct HandDrawnArrowShape: Shape {
|
|||||||
y: start.y + 120 // Go DOWN first
|
y: start.y + 120 // Go DOWN first
|
||||||
)
|
)
|
||||||
|
|
||||||
// Second control point: curve back up toward target
|
|
||||||
let ctrl2 = CGPoint(
|
let ctrl2 = CGPoint(
|
||||||
x: start.x + dx * 0.6,
|
x: start.x + dx * 0.6,
|
||||||
y: start.y + 80
|
y: start.y + 80
|
||||||
)
|
)
|
||||||
|
|
||||||
// Third control point: approach target from below-ish
|
|
||||||
let ctrl3 = CGPoint(
|
|
||||||
x: end.x - dx * 0.15,
|
|
||||||
y: end.y + 60
|
|
||||||
)
|
|
||||||
|
|
||||||
// Add slight hand-drawn wobble
|
|
||||||
let wobble: CGFloat = 2.5
|
let wobble: CGFloat = 2.5
|
||||||
let wobbledCtrl1 = CGPoint(x: ctrl1.x + wobble, y: ctrl1.y - wobble)
|
let wobbledCtrl1 = CGPoint(x: ctrl1.x + wobble, y: ctrl1.y - wobble)
|
||||||
let wobbledCtrl2 = CGPoint(x: ctrl2.x - wobble, y: ctrl2.y + wobble)
|
let wobbledCtrl2 = CGPoint(x: ctrl2.x - wobble, y: ctrl2.y + wobble)
|
||||||
let wobbledCtrl3 = CGPoint(x: ctrl3.x + wobble * 0.5, y: ctrl3.y - wobble)
|
|
||||||
|
|
||||||
path.move(to: start)
|
path.move(to: start)
|
||||||
path.addCurve(to: end, control1: wobbledCtrl1, control2: wobbledCtrl2)
|
path.addCurve(to: end, control1: wobbledCtrl1, control2: wobbledCtrl2)
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ struct OnboardingContainerView: View {
|
|||||||
.tag(0)
|
.tag(0)
|
||||||
.tabItem { Image(systemName: "hand.wave.fill") }
|
.tabItem { Image(systemName: "hand.wave.fill") }
|
||||||
|
|
||||||
MenuBarWelcomeView()
|
MenuBarTargetView()
|
||||||
.tag(1)
|
.tag(1)
|
||||||
.tabItem { Image(systemName: "menubar.rectangle") }
|
.tabItem { Image(systemName: "menubar.rectangle") }
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// MenuBarWelcomeView.swift
|
// MenuBarTargetView.swift
|
||||||
// Gaze
|
// Gaze
|
||||||
//
|
//
|
||||||
// Created by Mike Freno on 1/17/26.
|
// Created by Mike Freno on 1/17/26.
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct MenuBarWelcomeView: View {
|
struct MenuBarTargetView: View {
|
||||||
@Environment(\.isCompactLayout) private var isCompact
|
@Environment(\.isCompactLayout) private var isCompact
|
||||||
|
|
||||||
private var iconSize: CGFloat {
|
private var iconSize: CGFloat {
|
||||||
@@ -64,5 +64,5 @@ struct MenuBarWelcomeView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#Preview("Menu Bar Welcome") {
|
#Preview("Menu Bar Welcome") {
|
||||||
MenuBarWelcomeView()
|
MenuBarTargetView()
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user