diff --git a/client/ui/services/windowmanager.go b/client/ui/services/windowmanager.go index 73d1cafb7..ba69a597b 100644 --- a/client/ui/services/windowmanager.go +++ b/client/ui/services/windowmanager.go @@ -119,6 +119,7 @@ type WindowManager struct { ready map[uint]bool showPending map[uint]bool pendingTab map[uint]string + pendingEmits map[uint][]string fallbackTimers map[uint]*time.Timer // recenterOnShow is set only on the minimal-WM/XEmbed path, where the WM neither centers nor // restores position; nil on full desktops so re-centering can't fight a user-moved window. @@ -135,6 +136,7 @@ func NewWindowManager(app *application.App, mainWindow *application.WebviewWindo ready: map[uint]bool{}, showPending: map[uint]bool{}, pendingTab: map[uint]string{}, + pendingEmits: map[uint][]string{}, fallbackTimers: map[uint]*time.Timer{}, } s.watchPainted() @@ -470,6 +472,27 @@ func (s *WindowManager) ShowMain() { s.showWhenReady(s.MainWindow()) } +// ShowMainAndEmit brings the main window forward and emits event once its frontend is ready. +func (s *WindowManager) ShowMainAndEmit(event string) { + w := s.MainWindow() + if w == nil { + return + } + + id := w.ID() + s.mu.Lock() + ready := s.ready[id] + if !ready { + s.pendingEmits[id] = append(s.pendingEmits[id], event) + } + s.mu.Unlock() + + s.showWhenReady(w) + if ready { + s.app.Event.Emit(event) + } +} + func (s *WindowManager) MainWindow() *application.WebviewWindow { s.mu.Lock() factory := s.newMain @@ -533,6 +556,7 @@ func (s *WindowManager) forgetWindowLocked(w *application.WebviewWindow) { delete(s.ready, id) delete(s.showPending, id) delete(s.pendingTab, id) + delete(s.pendingEmits, id) kept := s.hiddenForLogin[:0] for _, hidden := range s.hiddenForLogin { @@ -563,12 +587,14 @@ func (s *WindowManager) markReady(w *application.WebviewWindow) { s.ready[id] = true wanted := s.showPending[id] tab, hasTab := s.pendingTab[id] + emits := s.pendingEmits[id] if timer := s.fallbackTimers[id]; timer != nil { timer.Stop() delete(s.fallbackTimers, id) } delete(s.showPending, id) delete(s.pendingTab, id) + delete(s.pendingEmits, id) s.mu.Unlock() if already { @@ -582,6 +608,10 @@ func (s *WindowManager) markReady(w *application.WebviewWindow) { if wanted { s.showNow(w) } + + for _, event := range emits { + s.app.Event.Emit(event) + } } func (s *WindowManager) showWhenReady(w *application.WebviewWindow) { diff --git a/client/ui/tray.go b/client/ui/tray.go index d27ebca20..437cbeabc 100644 --- a/client/ui/tray.go +++ b/client/ui/tray.go @@ -284,6 +284,15 @@ func (t *Tray) showMain() { } } +func (t *Tray) showMainAndEmit(event string) { + if t.svc.WindowManager != nil { + t.svc.WindowManager.ShowMainAndEmit(event) + return + } + t.showMain() + t.app.Event.Emit(event) +} + // applyLanguage re-renders every translated surface in the Localizer's current // language. Wails dispatches menu/tray APIs onto the UI thread internally, so // calling them from the Localizer's background goroutine is safe; profileLoadMu diff --git a/client/ui/tray_session.go b/client/ui/tray_session.go index e0dec949f..6e5d07740 100644 --- a/client/ui/tray_session.go +++ b/client/ui/tray_session.go @@ -304,8 +304,7 @@ func (t *Tray) openSessionExtendFlow() { } seconds := int(time.Until(deadline).Seconds()) if seconds <= 0 { - t.showMain() - t.app.Event.Emit(services.EventTriggerLogin) + t.showMainAndEmit(services.EventTriggerLogin) return } if t.svc.WindowManager == nil {