general: working through bugs and maintainability

This commit is contained in:
Michael Freno
2026-01-14 21:28:22 -05:00
parent f7d8470a43
commit 25455e714c
10 changed files with 189 additions and 197 deletions

View File

@@ -10,20 +10,24 @@ import Combine
import CoreGraphics
import Foundation
struct FullscreenWindowDescriptor: Equatable {
let ownerPID: pid_t
let layer: Int
let bounds: CGRect
public struct FullscreenWindowDescriptor: Equatable {
public let ownerPID: pid_t
public let layer: Int
public let bounds: CGRect
public init(ownerPID: pid_t, layer: Int, bounds: CGRect) {
self.ownerPID = ownerPID
self.layer = layer
self.bounds = bounds
}
}
@MainActor
protocol FullscreenEnvironmentProviding {
func frontmostProcessIdentifier() -> pid_t?
func windowDescriptors() -> [FullscreenWindowDescriptor]
func screenFrames() -> [CGRect]
}
@MainActor
struct SystemFullscreenEnvironmentProvider: FullscreenEnvironmentProviding {
func frontmostProcessIdentifier() -> pid_t? {
NSWorkspace.shared.frontmostApplication?.processIdentifier
@@ -53,13 +57,13 @@ struct SystemFullscreenEnvironmentProvider: FullscreenEnvironmentProviding {
}
}
func screenFrames() -> [CGRect] {
public func screenFrames() -> [CGRect] {
NSScreen.screens.map(\.frame)
}
}
@MainActor
class FullscreenDetectionService: ObservableObject {
final class FullscreenDetectionService: ObservableObject {
@Published private(set) var isFullscreenActive = false
private var observers: [NSObjectProtocol] = []
@@ -68,11 +72,11 @@ class FullscreenDetectionService: ObservableObject {
private let environmentProvider: FullscreenEnvironmentProviding
init(
permissionManager: ScreenCapturePermissionManaging? = nil,
environmentProvider: FullscreenEnvironmentProviding? = nil
permissionManager: ScreenCapturePermissionManaging = ScreenCapturePermissionManager.shared,
environmentProvider: FullscreenEnvironmentProviding = SystemFullscreenEnvironmentProvider()
) {
self.permissionManager = permissionManager ?? ScreenCapturePermissionManager.shared
self.environmentProvider = environmentProvider ?? SystemFullscreenEnvironmentProvider()
self.permissionManager = permissionManager
self.environmentProvider = environmentProvider
setupObservers()
}

View File

@@ -22,7 +22,7 @@ public enum ScreenCaptureAuthorizationStatus: Equatable {
}
@MainActor
public protocol ScreenCapturePermissionManaging: AnyObject {
protocol ScreenCapturePermissionManaging: AnyObject {
var authorizationStatus: ScreenCaptureAuthorizationStatus { get }
var authorizationStatusPublisher: AnyPublisher<ScreenCaptureAuthorizationStatus, Never> { get }

View File

@@ -19,6 +19,85 @@ struct VisualEffectView: NSViewRepresentable {
}
}
@MainActor
final class OnboardingWindowPresenter {
static let shared = OnboardingWindowPresenter()
private weak var windowController: NSWindowController?
private var closeObserver: NSObjectProtocol?
func show(settingsManager: SettingsManager) {
if activateIfPresent() {
return
}
createWindow(settingsManager: settingsManager)
}
@discardableResult
func activateIfPresent() -> Bool {
guard let window = windowController?.window else {
windowController = nil
return false
}
window.makeKeyAndOrderFront(nil)
NSApp.activate(ignoringOtherApps: true)
return true
}
func close() {
windowController?.close()
windowController = nil
if let closeObserver {
NotificationCenter.default.removeObserver(closeObserver)
self.closeObserver = nil
}
}
private func createWindow(settingsManager: SettingsManager) {
let window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 700, height: 700),
styleMask: [.titled, .closable, .miniaturizable, .fullSizeContentView],
backing: .buffered,
defer: false
)
window.identifier = WindowIdentifiers.onboarding
window.titleVisibility = .hidden
window.titlebarAppearsTransparent = true
window.center()
window.isReleasedWhenClosed = true
window.contentView = NSHostingView(
rootView: OnboardingContainerView(settingsManager: settingsManager)
)
let controller = NSWindowController(window: window)
controller.showWindow(nil)
window.makeKeyAndOrderFront(nil)
NSApp.activate(ignoringOtherApps: true)
windowController = controller
closeObserver.map(NotificationCenter.default.removeObserver)
closeObserver = NotificationCenter.default.addObserver(
forName: NSWindow.willCloseNotification,
object: window,
queue: .main
) { [weak self] _ in
self?.windowController = nil
if let closeObserver = self?.closeObserver {
NotificationCenter.default.removeObserver(closeObserver)
}
self?.closeObserver = nil
}
}
deinit {
if let closeObserver {
NotificationCenter.default.removeObserver(closeObserver)
}
}
}
struct OnboardingContainerView: View {
@ObservedObject var settingsManager: SettingsManager
@State private var currentPage = 0

View File

@@ -17,37 +17,36 @@ struct SettingsWindowView: View {
}
var body: some View {
VStack(spacing: 0) {
NavigationSplitView {
List(SettingsSection.allCases, selection: $selectedSection) { section in
NavigationLink(value: section) {
Label(section.title, systemImage: section.iconName)
ZStack {
VisualEffectView(material: .hudWindow, blendingMode: .behindWindow)
.ignoresSafeArea()
VStack(spacing: 0) {
NavigationSplitView {
List(SettingsSection.allCases, selection: $selectedSection) { section in
NavigationLink(value: section) {
Label(section.title, systemImage: section.iconName)
}
}
.listStyle(.sidebar)
} detail: {
detailView(for: selectedSection)
}
.listStyle(.sidebar)
} detail: {
detailView(for: selectedSection)
}
Divider()
HStack {
#if DEBUG
Button("Retrigger Onboarding") {
retriggerOnboarding()
Divider()
HStack {
Button("Retrigger Onboarding") {
retriggerOnboarding()
}
.buttonStyle(.bordered)
Spacer()
}
.buttonStyle(.bordered)
.padding()
#endif
Spacer()
Button("Close") {
closeWindow()
}
.keyboardShortcut(.escape)
.buttonStyle(.borderedProminent)
}
.padding()
}
#if APPSTORE
.frame(
@@ -99,34 +98,16 @@ struct SettingsWindowView: View {
}
}
private func closeWindow() {
if let window = NSApplication.shared.windows.first(where: { $0.title == "Settings" }) {
window.close()
}
}
#if DEBUG
private func retriggerOnboarding() {
// Get AppDelegate reference first
guard let appDelegate = NSApplication.shared.delegate as? AppDelegate else { return }
OnboardingWindowPresenter.shared.close()
SettingsWindowPresenter.shared.close()
// Step 1: Close any existing onboarding window
if let onboardingWindow = NSApplication.shared.windows.first(where: {
$0.identifier == WindowIdentifiers.onboarding
}) {
onboardingWindow.close()
}
// Step 2: Close settings window
closeWindow()
// Step 3: Reset onboarding state with a delay to ensure settings window is closed
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
self.settingsManager.settings.hasCompletedOnboarding = false
// Step 4: Open onboarding window with another delay to ensure state is saved
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
appDelegate.openOnboarding()
OnboardingWindowPresenter.shared.show(settingsManager: self.settingsManager)
}
}
}

View File

@@ -73,18 +73,6 @@ struct EnforceModeSetupView: View {
.padding()
.glassEffectIfAvailable(GlassStyle.regular, in: .rect(cornerRadius: 12))
#if DEBUG
HStack {
Button("Debug Info") {
showDebugView.toggle()
}
.buttonStyle(.bordered)
Spacer()
}
.padding()
.glassEffectIfAvailable(GlassStyle.regular, in: .rect(cornerRadius: 12))
#endif
cameraStatusView
if enforceModeService.isEnforceModeEnabled {
@@ -97,9 +85,9 @@ struct EnforceModeSetupView: View {
if enforceModeService.isCameraActive && !isTestModeActive {
eyeTrackingStatusView
#if DEBUG
if showDebugView {
debugEyeTrackingView
}
if showDebugView {
debugEyeTrackingView
}
#endif
} else if enforceModeService.isEnforceModeEnabled {
cameraPendingView
@@ -378,17 +366,17 @@ struct EnforceModeSetupView: View {
VStack(alignment: .leading, spacing: 8) {
Text("Face Detected: \(eyeTrackingService.faceDetected ? "Yes" : "No")")
.font(.caption)
Text("Looking at Screen: \(eyeTrackingService.userLookingAtScreen ? "Yes" : "No")")
.font(.caption)
Text("Eyes Closed: \(eyeTrackingService.isEyesClosed ? "Yes" : "No")")
.font(.caption)
if eyeTrackingService.faceDetected {
Text("Yaw: 0.0")
.font(.caption)
Text("Roll: 0.0")
.font(.caption)
}