fixed
This commit is contained in:
@@ -15,6 +15,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
||||
private let serviceContainer: ServiceContainer
|
||||
private let windowManager: WindowManaging
|
||||
private var updateManager: UpdateManager?
|
||||
private var systemSleepManager: SystemSleepManager?
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
private var hasStartedTimers = false
|
||||
|
||||
@@ -46,6 +47,11 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
||||
}
|
||||
|
||||
timerEngine = serviceContainer.timerEngine
|
||||
systemSleepManager = SystemSleepManager(
|
||||
timerEngine: timerEngine,
|
||||
settingsManager: settingsManager
|
||||
)
|
||||
systemSleepManager?.startObserving()
|
||||
|
||||
serviceContainer.setupSmartModeServices()
|
||||
|
||||
@@ -54,8 +60,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
||||
updateManager = UpdateManager.shared
|
||||
}
|
||||
|
||||
setupLifecycleObservers()
|
||||
|
||||
observeSettingsChanges()
|
||||
|
||||
if settingsManager.settings.hasCompletedOnboarding {
|
||||
@@ -129,34 +133,13 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
||||
func applicationWillTerminate(_ notification: Notification) {
|
||||
logInfo(" applicationWill terminate")
|
||||
settingsManager.saveImmediately()
|
||||
stopLifecycleObservers()
|
||||
timerEngine?.stop()
|
||||
}
|
||||
|
||||
private func setupLifecycleObservers() {
|
||||
NSWorkspace.shared.notificationCenter.addObserver(
|
||||
self,
|
||||
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 stopLifecycleObservers() {
|
||||
systemSleepManager?.stopObserving()
|
||||
systemSleepManager = nil
|
||||
}
|
||||
|
||||
private func observeReminderEvents() {
|
||||
|
||||
@@ -60,12 +60,6 @@ protocol TimerEngineProviding: AnyObject, ObservableObject {
|
||||
/// Checks if a timer is currently paused
|
||||
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
|
||||
func setupSmartMode(
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -138,7 +138,7 @@ class TimerEngine: ObservableObject {
|
||||
let intervalSeconds = getTimerInterval(for: identifier)
|
||||
stateManager.resetTimer(identifier: identifier, intervalSeconds: intervalSeconds)
|
||||
}
|
||||
|
||||
|
||||
/// Unified way to get interval for any timer type
|
||||
private func getTimerInterval(for identifier: TimerIdentifier) -> Int {
|
||||
switch identifier {
|
||||
@@ -146,7 +146,8 @@ class TimerEngine: ObservableObject {
|
||||
let config = settingsProvider.timerConfiguration(for: type)
|
||||
return config.intervalSeconds
|
||||
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 userTimer.intervalMinutes * 60
|
||||
@@ -183,7 +184,8 @@ class TimerEngine: ObservableObject {
|
||||
secondsRemaining: updatedState.remainingSeconds
|
||||
) {
|
||||
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)
|
||||
}
|
||||
|
||||
// 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] {
|
||||
var configurations: [TimerIdentifier: TimerConfiguration] = [:]
|
||||
for timerType in TimerType.allCases {
|
||||
|
||||
@@ -21,7 +21,7 @@ struct AdditionalModifiersView: View {
|
||||
GeometryReader { geometry in
|
||||
let availableWidth = geometry.size.width - 80 // Account for padding
|
||||
let availableHeight = geometry.size.height - 200 // Account for header and nav
|
||||
|
||||
|
||||
let cardWidth = min(
|
||||
max(availableWidth * 0.85, AdaptiveLayout.Card.minWidth),
|
||||
AdaptiveLayout.Card.maxWidth
|
||||
@@ -30,9 +30,10 @@ struct AdditionalModifiersView: View {
|
||||
max(availableHeight * 0.75, AdaptiveLayout.Card.minHeight),
|
||||
AdaptiveLayout.Card.maxHeight
|
||||
)
|
||||
|
||||
|
||||
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")
|
||||
.font(isCompact ? .subheadline : .title3)
|
||||
@@ -77,7 +78,7 @@ struct AdditionalModifiersView: View {
|
||||
HStack(spacing: isCompact ? 10 : 16) {
|
||||
cardIndicator(index: 0, icon: "video.fill", label: "Enforce")
|
||||
cardIndicator(index: 1, icon: "brain.fill", label: "Smart")
|
||||
}
|
||||
}.padding(.all, 20)
|
||||
|
||||
Button(action: { swapCards() }) {
|
||||
Image(systemName: "chevron.right")
|
||||
@@ -211,7 +212,11 @@ struct AdditionalModifiersView: View {
|
||||
private var enforceModeContent: some View {
|
||||
VStack(spacing: isCompact ? 10 : 16) {
|
||||
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)
|
||||
|
||||
Text("Enforce Mode")
|
||||
@@ -292,7 +297,11 @@ struct AdditionalModifiersView: View {
|
||||
private var smartModeContent: some View {
|
||||
VStack(spacing: isCompact ? 10 : 16) {
|
||||
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)
|
||||
|
||||
Text("Smart Mode")
|
||||
|
||||
@@ -118,13 +118,13 @@ final class MenuBarGuideOverlayPresenter {
|
||||
let overlayView = MenuBarGuideOverlayView()
|
||||
window.contentView = NSHostingView(rootView: overlayView)
|
||||
}
|
||||
|
||||
|
||||
func setupOnboardingWindowObserver() {
|
||||
// Remove any existing observer to prevent duplicates
|
||||
if let observer = onboardingWindowObserver {
|
||||
NotificationCenter.default.removeObserver(observer)
|
||||
}
|
||||
|
||||
|
||||
// Add observer for when the onboarding window is closed
|
||||
onboardingWindowObserver = NotificationCenter.default.addObserver(
|
||||
forName: NSWindow.willCloseNotification,
|
||||
@@ -132,10 +132,11 @@ final class MenuBarGuideOverlayPresenter {
|
||||
queue: .main
|
||||
) { [weak self] notification in
|
||||
guard let window = notification.object as? NSWindow,
|
||||
window.identifier == WindowIdentifiers.onboarding else {
|
||||
window.identifier == WindowIdentifiers.onboarding
|
||||
else {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Hide the overlay when onboarding window closes
|
||||
self?.hide()
|
||||
}
|
||||
@@ -176,8 +177,8 @@ struct MenuBarGuideOverlayView: View {
|
||||
$0.identifier == WindowIdentifiers.onboarding
|
||||
}) {
|
||||
let windowFrame = onboardingWindow.frame
|
||||
let textRightX = windowFrame.midX + 210
|
||||
let textY = screenFrame.maxY - windowFrame.maxY + 505
|
||||
let textRightX = windowFrame.midX
|
||||
let textY = screenFrame.maxY - windowFrame.maxY + 305
|
||||
return CGPoint(x: textRightX, y: textY)
|
||||
}
|
||||
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
|
||||
|
||||
let dx = end.x - start.x
|
||||
let dy = end.y - start.y
|
||||
|
||||
// First control point: go DOWN and slightly toward target
|
||||
let ctrl1 = CGPoint(
|
||||
@@ -203,23 +203,14 @@ struct HandDrawnArrowShape: Shape {
|
||||
y: start.y + 120 // Go DOWN first
|
||||
)
|
||||
|
||||
// Second control point: curve back up toward target
|
||||
let ctrl2 = CGPoint(
|
||||
x: start.x + dx * 0.6,
|
||||
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 wobbledCtrl1 = CGPoint(x: ctrl1.x + wobble, y: ctrl1.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.addCurve(to: end, control1: wobbledCtrl1, control2: wobbledCtrl2)
|
||||
|
||||
@@ -136,7 +136,7 @@ struct OnboardingContainerView: View {
|
||||
.tag(0)
|
||||
.tabItem { Image(systemName: "hand.wave.fill") }
|
||||
|
||||
MenuBarWelcomeView()
|
||||
MenuBarTargetView()
|
||||
.tag(1)
|
||||
.tabItem { Image(systemName: "menubar.rectangle") }
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// MenuBarWelcomeView.swift
|
||||
// MenuBarTargetView.swift
|
||||
// Gaze
|
||||
//
|
||||
// Created by Mike Freno on 1/17/26.
|
||||
@@ -7,21 +7,21 @@
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct MenuBarWelcomeView: View {
|
||||
struct MenuBarTargetView: View {
|
||||
@Environment(\.isCompactLayout) private var isCompact
|
||||
|
||||
|
||||
private var iconSize: CGFloat {
|
||||
isCompact ? AdaptiveLayout.Font.heroIconSmall : AdaptiveLayout.Font.heroIcon
|
||||
}
|
||||
|
||||
|
||||
private var titleSize: CGFloat {
|
||||
isCompact ? AdaptiveLayout.Font.heroTitleSmall : AdaptiveLayout.Font.heroTitle
|
||||
}
|
||||
|
||||
|
||||
private var spacing: CGFloat {
|
||||
isCompact ? AdaptiveLayout.Spacing.compact : AdaptiveLayout.Spacing.standard
|
||||
}
|
||||
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: spacing * 1.5) {
|
||||
Spacer()
|
||||
@@ -64,5 +64,5 @@ struct MenuBarWelcomeView: View {
|
||||
}
|
||||
|
||||
#Preview("Menu Bar Welcome") {
|
||||
MenuBarWelcomeView()
|
||||
MenuBarTargetView()
|
||||
}
|
||||
Reference in New Issue
Block a user