fix: bad fix for multi-window
This commit is contained in:
@@ -18,6 +18,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
||||
private var updateManager: UpdateManager?
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
private var hasStartedTimers = false
|
||||
private var isSettingsWindowOpen = false
|
||||
private var isOnboardingWindowOpen = false
|
||||
|
||||
// Logging manager
|
||||
private let logger = LoggingManager.shared
|
||||
@@ -26,11 +28,14 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
||||
private var settingsManager: any SettingsProviding {
|
||||
serviceContainer.settingsManager
|
||||
}
|
||||
|
||||
|
||||
override init() {
|
||||
self.serviceContainer = ServiceContainer.shared
|
||||
self.windowManager = WindowManager.shared
|
||||
super.init()
|
||||
|
||||
// Setup window close observers
|
||||
setupWindowCloseObservers()
|
||||
}
|
||||
|
||||
/// Initializer for testing with injectable dependencies
|
||||
@@ -39,7 +44,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
||||
self.windowManager = windowManager
|
||||
super.init()
|
||||
}
|
||||
|
||||
|
||||
func applicationDidFinishLaunching(_ notification: Notification) {
|
||||
// Set activation policy to hide dock icon
|
||||
NSApplication.shared.setActivationPolicy(.accessory)
|
||||
@@ -54,6 +59,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
||||
// Setup smart mode services through container
|
||||
serviceContainer.setupSmartModeServices()
|
||||
|
||||
// Check if onboarding needs to be shown automatically
|
||||
if !settingsManager.settings.hasCompletedOnboarding {
|
||||
// Set the flag to indicate we expect an onboarding window
|
||||
isOnboardingWindowOpen = true
|
||||
}
|
||||
|
||||
// Initialize update manager after onboarding is complete
|
||||
if settingsManager.settings.hasCompletedOnboarding {
|
||||
updateManager = UpdateManager.shared
|
||||
@@ -206,7 +217,20 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
||||
}
|
||||
|
||||
func openSettings(tab: Int = 0) {
|
||||
// If settings window is already open, focus it instead of opening new one
|
||||
if isSettingsWindowOpen {
|
||||
// Try to focus existing window
|
||||
DispatchQueue.main.async {
|
||||
NotificationCenter.default.post(
|
||||
name: Notification.Name("SwitchToSettingsTab"),
|
||||
object: tab
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
handleMenuDismissal()
|
||||
isSettingsWindowOpen = true
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
|
||||
guard let self else { return }
|
||||
@@ -215,7 +239,18 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
||||
}
|
||||
|
||||
func openOnboarding() {
|
||||
// If onboarding window is already open, focus it instead of opening new one
|
||||
if isOnboardingWindowOpen {
|
||||
// Try to activate existing window
|
||||
DispatchQueue.main.async {
|
||||
OnboardingWindowPresenter.shared.activateIfPresent()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
handleMenuDismissal()
|
||||
// Explicitly set the flag to true when we're about to show the onboarding window
|
||||
isOnboardingWindowOpen = true
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
|
||||
guard let self else { return }
|
||||
@@ -227,5 +262,32 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
||||
NotificationCenter.default.post(name: Notification.Name("CloseMenuBarPopover"), object: nil)
|
||||
windowManager.dismissOverlayReminder()
|
||||
}
|
||||
|
||||
private func setupWindowCloseObservers() {
|
||||
// Observe settings window closing
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(settingsWindowDidClose),
|
||||
name: Notification.Name("SettingsWindowDidClose"),
|
||||
object: nil
|
||||
)
|
||||
|
||||
// Observe onboarding window closing
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(onboardingWindowDidClose),
|
||||
name: Notification.Name("OnboardingWindowDidClose"),
|
||||
object: nil
|
||||
)
|
||||
}
|
||||
|
||||
@objc private func settingsWindowDidClose() {
|
||||
isSettingsWindowOpen = false
|
||||
}
|
||||
|
||||
@objc private func onboardingWindowDidClose() {
|
||||
// Reset the flag when we receive the close notification
|
||||
isOnboardingWindowOpen = false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -39,8 +39,16 @@ final class OnboardingWindowPresenter {
|
||||
windowController = nil
|
||||
return false
|
||||
}
|
||||
|
||||
// Ensure the window is brought to front and focused properly for menu bar apps
|
||||
window.makeKeyAndOrderFront(nil)
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
|
||||
// Additional focus handling for menu bar applications
|
||||
if let window = windowController?.window {
|
||||
window.makeMain()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -88,6 +96,9 @@ final class OnboardingWindowPresenter {
|
||||
NotificationCenter.default.removeObserver(closeObserver)
|
||||
}
|
||||
self?.closeObserver = nil
|
||||
|
||||
// Notify AppDelegate that onboarding window closed
|
||||
NotificationCenter.default.post(name: Notification.Name("OnboardingWindowDidClose"), object: nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -90,6 +90,9 @@ final class SettingsWindowPresenter {
|
||||
Task { @MainActor [weak self] in
|
||||
self?.windowController = nil
|
||||
self?.removeCloseObserver()
|
||||
|
||||
// Notify AppDelegate that settings window closed
|
||||
NotificationCenter.default.post(name: Notification.Name("SettingsWindowDidClose"), object: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -206,11 +209,10 @@ struct SettingsWindowView: View {
|
||||
|
||||
#if DEBUG
|
||||
private func retriggerOnboarding() {
|
||||
OnboardingWindowPresenter.shared.close()
|
||||
SettingsWindowPresenter.shared.close()
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
|
||||
OnboardingWindowPresenter.shared.show(settingsManager: self.settingsManager)
|
||||
settingsManager.settings.hasCompletedOnboarding = false
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -88,24 +88,27 @@ struct MenuBarContentView: View {
|
||||
.padding(.horizontal)
|
||||
.padding(.top, 8)
|
||||
|
||||
ForEach(getSortedTimerIdentifiers(timerEngine: timerEngine), id: \.self) {
|
||||
ForEach(
|
||||
timerEngine.map { getSortedTimerIdentifiers(timerEngine: $0) } ?? [],
|
||||
id: \.self
|
||||
) {
|
||||
identifier in
|
||||
if timerEngine.timerStates[identifier] != nil {
|
||||
if let engine = timerEngine, engine.timerStates[identifier] != nil {
|
||||
TimerStatusRowWithIndividualControls(
|
||||
identifier: identifier,
|
||||
timerEngine: timerEngine,
|
||||
timerEngine: engine,
|
||||
settingsManager: settingsManager,
|
||||
onSkip: {
|
||||
timerEngine.skipNext(identifier: identifier)
|
||||
engine.skipNext(identifier: identifier)
|
||||
},
|
||||
onDevTrigger: {
|
||||
timerEngine.triggerReminder(for: identifier)
|
||||
engine.triggerReminder(for: identifier)
|
||||
},
|
||||
onTogglePause: { isPaused in
|
||||
if isPaused {
|
||||
timerEngine.pauseTimer(identifier: identifier)
|
||||
engine.pauseTimer(identifier: identifier)
|
||||
} else {
|
||||
timerEngine.resumeTimer(identifier: identifier)
|
||||
engine.resumeTimer(identifier: identifier)
|
||||
}
|
||||
},
|
||||
onTap: {
|
||||
@@ -127,18 +130,21 @@ struct MenuBarContentView: View {
|
||||
// Controls
|
||||
VStack(spacing: 4) {
|
||||
Button(action: {
|
||||
if isAllPaused(timerEngine: timerEngine) {
|
||||
timerEngine.resume()
|
||||
} else {
|
||||
timerEngine.pause()
|
||||
if let engine = timerEngine {
|
||||
if isAllPaused(timerEngine: engine) {
|
||||
engine.resume()
|
||||
} else {
|
||||
engine.pause()
|
||||
}
|
||||
}
|
||||
}) {
|
||||
HStack {
|
||||
Image(
|
||||
systemName: isAllPaused(timerEngine: timerEngine)
|
||||
systemName: timerEngine.map { isAllPaused(timerEngine: $0) }
|
||||
?? false
|
||||
? "play.circle" : "pause.circle")
|
||||
Text(
|
||||
isAllPaused(timerEngine: timerEngine)
|
||||
timerEngine.map { isAllPaused(timerEngine: $0) } ?? false
|
||||
? "Resume All Timers" : "Pause All Timers")
|
||||
Spacer()
|
||||
}
|
||||
@@ -217,14 +223,16 @@ struct MenuBarContentView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func isAllPaused(timerEngine: TimerEngine) -> Bool {
|
||||
private func isAllPaused(timerEngine: TimerEngine?) -> Bool {
|
||||
// Check if all timers are paused
|
||||
let activeStates = timerEngine.timerStates.values.filter { $0.isActive }
|
||||
guard let engine = timerEngine else { return false }
|
||||
let activeStates = engine.timerStates.values.filter { $0.isActive }
|
||||
return !activeStates.isEmpty && activeStates.allSatisfy { $0.isPaused }
|
||||
}
|
||||
|
||||
private func getSortedTimerIdentifiers(timerEngine: TimerEngine) -> [TimerIdentifier] {
|
||||
return timerEngine.timerStates.keys.sorted { id1, id2 in
|
||||
private func getSortedTimerIdentifiers(timerEngine: TimerEngine?) -> [TimerIdentifier] {
|
||||
guard let engine = timerEngine else { return [] }
|
||||
return engine.timerStates.keys.sorted { id1, id2 in
|
||||
// Sort built-in timers before user timers
|
||||
switch (id1, id2) {
|
||||
case (.builtIn(let t1), .builtIn(let t2)):
|
||||
|
||||
Reference in New Issue
Block a user