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
|
||||
@@ -31,6 +33,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
||||
self.serviceContainer = ServiceContainer.shared
|
||||
self.windowManager = WindowManager.shared
|
||||
super.init()
|
||||
|
||||
// Setup window close observers
|
||||
setupWindowCloseObservers()
|
||||
}
|
||||
|
||||
/// Initializer for testing with injectable dependencies
|
||||
@@ -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 }
|
||||
@@ -228,4 +263,31 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
||||
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)):
|
||||
|
||||
21
run
21
run
@@ -102,20 +102,13 @@ launch_app() {
|
||||
if [ -d "$app_path" ]; then
|
||||
echo "🚀 Launching: $app_path"
|
||||
|
||||
if [ "$VERBOSE" = true ]; then
|
||||
echo "📝 Capturing application logs in terminal (Ctrl+C to stop)..."
|
||||
open "$app_path" &
|
||||
sleep 2
|
||||
echo "📝 Capturing application logs (Ctrl+C to stop - won't kill app)..."
|
||||
open "$app_path" &
|
||||
|
||||
echo "Logs from Gaze.app will appear below (Ctrl+C to stop):"
|
||||
echo "================================================================"
|
||||
/usr/bin/log stream --predicate "subsystem contains \"$APP_SUBSYSTEM\"" \
|
||||
--style compact 2>/dev/null
|
||||
echo "================================================================"
|
||||
echo "Application runtime logging stopped."
|
||||
else
|
||||
open "$app_path"
|
||||
fi
|
||||
sleep 2
|
||||
echo "================================================================"
|
||||
/usr/bin/log stream --predicate "subsystem contains \"$APP_SUBSYSTEM\"" \
|
||||
--style compact 2>/dev/null
|
||||
else
|
||||
echo "⚠️ App not found at expected location, trying fallback..."
|
||||
open "$HOME/Library/Developer/Xcode/DerivedData/Gaze-*/Build/Products/Debug/Gaze.app"
|
||||
@@ -205,8 +198,6 @@ done
|
||||
# Default to run if no action specified
|
||||
if [ -z "$ACTION" ]; then
|
||||
ACTION="run"
|
||||
# Default run action is always verbose with full logging
|
||||
VERBOSE=true
|
||||
fi
|
||||
|
||||
# Main execution
|
||||
|
||||
Reference in New Issue
Block a user