general: basic cleanup

This commit is contained in:
Michael Freno
2026-01-17 09:09:09 -05:00
parent 03ab6160d2
commit a528a549b9
8 changed files with 259 additions and 298 deletions

View File

@@ -28,11 +28,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
self.windowManager = WindowManager.shared
super.init()
// Setup window close observers
setupWindowCloseObservers()
}
/// Initializer for testing with injectable dependencies
init(serviceContainer: ServiceContainer, windowManager: WindowManaging) {
self.serviceContainer = serviceContainer
self.windowManager = windowManager
@@ -40,16 +37,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
}
func applicationDidFinishLaunching(_ notification: Notification) {
// Set activation policy to hide dock icon
NSApplication.shared.setActivationPolicy(.accessory)
logInfo("🚀 Application did finish launching")
timerEngine = serviceContainer.timerEngine
serviceContainer.setupSmartModeServices()
// Initialize update manager after onboarding is complete
if settingsManager.settings.hasCompletedOnboarding {
updateManager = UpdateManager.shared
@@ -62,24 +55,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
if settingsManager.settings.hasCompletedOnboarding {
startTimers()
}
// DEBUG: Auto-start eye tracking test mode if launch argument is present
#if DEBUG
if CommandLine.arguments.contains("--debug-eye-tracking") {
NSLog("🔬 DEBUG: Auto-starting eye tracking test mode")
Task { @MainActor in
// Enable enforce mode if not already
if !settingsManager.settings.enforcementMode {
settingsManager.settings.enforcementMode = true
}
// Start test mode after a brief delay
try? await Task.sleep(nanoseconds: 1_000_000_000) // 1 second
NSLog("🔬 DEBUG: Starting test mode now...")
await EnforceModeService.shared.startTestMode()
NSLog("🔬 DEBUG: Test mode started")
}
}
#endif
}
// Note: Smart mode setup is now handled by ServiceContainer
@@ -105,7 +80,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
func onboardingCompleted() {
startTimers()
// Start update checks after onboarding
if updateManager == nil {
updateManager = UpdateManager.shared
}
@@ -249,24 +223,4 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
}
}
private func setupWindowCloseObservers() {
NotificationCenter.default.addObserver(
self,
selector: #selector(settingsWindowDidClose),
name: Notification.Name("SettingsWindowDidClose"),
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(onboardingWindowDidClose),
name: Notification.Name("OnboardingWindowDidClose"),
object: nil
)
}
@objc private func settingsWindowDidClose() {}
@objc private func onboardingWindowDidClose() {}
}

View File

@@ -41,7 +41,7 @@ struct GazeApp: App {
}
.windowStyle(.hiddenTitleBar)
.windowResizability(.contentSize)
.defaultSize(width: 700, height: 700)
.defaultSize(width: 1000, height: 700)
.commands {
CommandGroup(replacing: .newItem) {}
}

View File

@@ -5,9 +5,9 @@
// Fullscreen overlay view for eye tracking calibration targets.
//
import SwiftUI
import Combine
import AVFoundation
import Combine
import SwiftUI
struct CalibrationOverlayView: View {
@StateObject private var calibrationManager = CalibrationManager.shared
@@ -35,7 +35,9 @@ struct CalibrationOverlayView: View {
startingCameraView
} else if calibrationManager.isCalibrating {
calibrationContentView(screenSize: geometry.size)
} else if viewModel.calibrationStarted && calibrationManager.calibrationData.isComplete {
} else if viewModel.calibrationStarted
&& calibrationManager.calibrationData.isComplete
{
// Only show completion if we started calibration this session AND it completed
completionView
} else if viewModel.calibrationStarted {
@@ -45,10 +47,12 @@ struct CalibrationOverlayView: View {
}
}
.task {
await viewModel.startCamera(eyeTrackingService: eyeTrackingService, calibrationManager: calibrationManager)
await viewModel.startCamera(
eyeTrackingService: eyeTrackingService, calibrationManager: calibrationManager)
}
.onDisappear {
viewModel.cleanup(eyeTrackingService: eyeTrackingService, calibrationManager: calibrationManager)
viewModel.cleanup(
eyeTrackingService: eyeTrackingService, calibrationManager: calibrationManager)
}
.onChange(of: calibrationManager.currentStep) { oldStep, newStep in
if newStep != nil && oldStep != newStep {
@@ -200,14 +204,16 @@ struct CalibrationOverlayView: View {
.stroke(Color.green, lineWidth: 4)
.frame(width: 90, height: 90)
.rotationEffect(.degrees(-90))
.animation(.linear(duration: 0.1), value: calibrationManager.samplesCollected)
.animation(
.linear(duration: 0.1), value: calibrationManager.samplesCollected)
}
// Inner circle
Circle()
.fill(calibrationManager.isCollectingSamples ? Color.green : Color.blue)
.frame(width: 60, height: 60)
.animation(.easeInOut(duration: 0.3), value: calibrationManager.isCollectingSamples)
.animation(
.easeInOut(duration: 0.3), value: calibrationManager.isCollectingSamples)
// Countdown number or collecting indicator
if viewModel.isCountingDown && viewModel.countdownValue > 0 {
@@ -260,7 +266,8 @@ struct CalibrationOverlayView: View {
private var cancelButton: some View {
Button {
viewModel.cleanup(eyeTrackingService: eyeTrackingService, calibrationManager: calibrationManager)
viewModel.cleanup(
eyeTrackingService: eyeTrackingService, calibrationManager: calibrationManager)
onDismiss()
} label: {
HStack(spacing: 6) {
@@ -362,7 +369,9 @@ class CalibrationOverlayViewModel: ObservableObject {
private var lastFaceDetectedTime: Date = .distantPast
private let faceDetectionDebounce: TimeInterval = 0.5 // 500ms debounce
func startCamera(eyeTrackingService: EyeTrackingService, calibrationManager: CalibrationManager) async {
func startCamera(eyeTrackingService: EyeTrackingService, calibrationManager: CalibrationManager)
async
{
do {
try await eyeTrackingService.startEyeTracking()
cameraStarted = true
@@ -370,8 +379,7 @@ class CalibrationOverlayViewModel: ObservableObject {
// Set up debounced face detection
setupFaceDetectionObserver(eyeTrackingService: eyeTrackingService)
// Small delay to let camera stabilize
try? await Task.sleep(nanoseconds: 500_000_000)
try? await Task.sleep(for: .seconds(0.5))
// Reset any previous calibration data before starting fresh
calibrationManager.resetForNewCalibration()
@@ -446,4 +454,3 @@ class CalibrationOverlayViewModel: ObservableObject {
#Preview {
CalibrationOverlayView(onDismiss: {})
}

View File

@@ -83,7 +83,9 @@ final class OnboardingWindowPresenter {
window.titlebarAppearsTransparent = true
window.center()
window.isReleasedWhenClosed = true
window.collectionBehavior = [.managed, .participatesInCycle, .moveToActiveSpace, .fullScreenAuxiliary]
window.collectionBehavior = [
.managed, .participatesInCycle, .moveToActiveSpace, .fullScreenAuxiliary,
]
window.contentView = NSHostingView(
rootView: OnboardingContainerView(settingsManager: settingsManager)

View File

@@ -6,6 +6,7 @@
//
import XCTest
@testable import Gaze
@MainActor
@@ -30,20 +31,16 @@ final class AppDelegateTestabilityTests: XCTestCase {
func testWindowManagerReceivesReminderEvents() async throws {
let appDelegate = testEnv.createAppDelegate()
// Simulate app launch
let notification = Notification(name: NSApplication.didFinishLaunchingNotification)
appDelegate.applicationDidFinishLaunching(notification)
// Give time for setup
try await Task.sleep(nanoseconds: 100_000_000) // 100ms
try await Task.sleep(for: .milliseconds(100))
// Trigger a reminder through timer engine
if let timerEngine = appDelegate.timerEngine {
let timerId = TimerIdentifier.builtIn(.blink)
timerEngine.triggerReminder(for: timerId)
// Give time for reminder to propagate
try await Task.sleep(nanoseconds: 100_000_000) // 100ms
try await Task.sleep(for: .milliseconds(100))
// Verify window manager received the show command
XCTAssertTrue(testEnv.windowManager.didPerformOperation(.showSubtleReminder))
@@ -58,8 +55,7 @@ final class AppDelegateTestabilityTests: XCTestCase {
// Change a setting
testEnv.settingsManager.settings.lookAwayTimer.enabled = false
// Give time for observation
try await Task.sleep(nanoseconds: 50_000_000) // 50ms
try await Task.sleep(for: .milliseconds(50))
// Verify the change propagated
XCTAssertFalse(testEnv.settingsManager.settings.lookAwayTimer.enabled)
@@ -73,7 +69,8 @@ final class AppDelegateTestabilityTests: XCTestCase {
// Give time for async dispatch
let expectation = XCTestExpectation(description: "Settings opened")
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
XCTAssertTrue(self.testEnv.windowManager.didPerformOperation(.showSettings(initialTab: 2)))
XCTAssertTrue(
self.testEnv.windowManager.didPerformOperation(.showSettings(initialTab: 2)))
expectation.fulfill()
}

View File

@@ -7,6 +7,7 @@
import Combine
import XCTest
@testable import Gaze
@MainActor
@@ -159,8 +160,7 @@ final class TimerEngineTests: XCTestCase {
timerEngine.triggerReminder(for: firstTimer)
// Give time for async operations
try await Task.sleep(nanoseconds: 50_000_000)
try await Task.sleep(for: .milliseconds(50))
XCTAssertNotNil(timerEngine.activeReminder)
}

View File

@@ -5,8 +5,11 @@
// Test helpers and utilities for unit testing.
//
// MARK: - Import Statement for Combine
import Combine
import Foundation
import XCTest
@testable import Gaze
// MARK: - Enhanced MockSettingsManager
@@ -25,11 +28,12 @@ final class EnhancedMockSettingsManager: SettingsProviding {
}
@ObservationIgnored
private let timerConfigKeyPaths: [TimerType: WritableKeyPath<AppSettings, TimerConfiguration>] = [
.lookAway: \.lookAwayTimer,
.blink: \.blinkTimer,
.posture: \.postureTimer,
]
private let timerConfigKeyPaths: [TimerType: WritableKeyPath<AppSettings, TimerConfiguration>] =
[
.lookAway: \.lookAwayTimer,
.blink: \.blinkTimer,
.posture: \.postureTimer,
]
// Track method calls for verification
@ObservationIgnored
@@ -245,7 +249,7 @@ extension XCTestCase {
XCTFail(message)
return
}
try await Task.sleep(nanoseconds: 10_000_000) // 10ms
try await Task.sleep(for: .milliseconds(100))
}
}
@@ -269,6 +273,3 @@ extension XCTestCase {
cancellable?.cancel()
}
}
// MARK: - Import Statement for Combine
import Combine

View File

@@ -7,6 +7,7 @@
import Combine
import XCTest
@testable import Gaze
@MainActor
@@ -106,8 +107,7 @@ final class TimerEngineTestabilityTests: XCTestCase {
let timerId = TimerIdentifier.builtIn(.lookAway)
timerEngine.triggerReminder(for: timerId)
// Give time for publisher to fire
try await Task.sleep(nanoseconds: 10_000_000)
try await Task.sleep(for: .milliseconds(10))
XCTAssertNotNil(receivedReminder)
}