mirror of
https://github.com/netbirdio/netbird.git
synced 2026-08-31 03:51:29 +02:00
Reorder UI service files to follow member-ordering convention
Group all type declarations at the top, keep each type's methods with it, move package-level helpers and static functions to the end of the file. - connection.go: ClientError methods sit directly under the struct; the classifyDaemonError / translateShort helpers move to the file end. - windowmanager.go: the title / retitleAll / hideOtherWindowsLocked / restoreHiddenWindowsLocked / getScreenBasedOnCursorPosition helpers and the errorDialogURL / u32ptr static functions move to the file end; u32ptr no longer splits the const/var block.
This commit is contained in:
@@ -58,66 +58,6 @@ func (e *ClientError) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal((*alias)(e))
|
||||
}
|
||||
|
||||
// classifyDaemonError maps a gRPC error to a ClientError by matching known
|
||||
// substrings to a stable code. A missing locale entry surfaces as a visible
|
||||
// "error.<code>" string — a deliberate fail-loud signal to update the bundle.
|
||||
func (s *Connection) classifyDaemonError(err error) *ClientError {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
msg := err.Error()
|
||||
if st, ok := gstatus.FromError(err); ok {
|
||||
msg = st.Message()
|
||||
}
|
||||
lower := strings.ToLower(msg)
|
||||
|
||||
code := "unknown"
|
||||
switch {
|
||||
case strings.Contains(lower, "token used before issued"),
|
||||
strings.Contains(lower, "token is not valid yet"):
|
||||
code = "jwt_clock_skew"
|
||||
case strings.Contains(lower, "token is expired"),
|
||||
strings.Contains(lower, "token has expired"):
|
||||
code = "jwt_expired"
|
||||
case strings.Contains(lower, "token signature is invalid"):
|
||||
code = "jwt_signature_invalid"
|
||||
case strings.Contains(lower, "peer login has expired"):
|
||||
code = "session_expired"
|
||||
case strings.Contains(lower, "invalid setup-key"),
|
||||
strings.Contains(lower, "invalid setup key"):
|
||||
code = "invalid_setup_key"
|
||||
case strings.Contains(lower, "permission denied"):
|
||||
code = "permission_denied"
|
||||
case strings.Contains(lower, "no connection could be made"),
|
||||
strings.Contains(lower, "connection refused"),
|
||||
strings.Contains(lower, "context deadline exceeded"):
|
||||
code = "daemon_unreachable"
|
||||
}
|
||||
|
||||
return &ClientError{
|
||||
Code: code,
|
||||
Short: s.translateShort(code),
|
||||
Long: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// translateShort resolves the localised short message for code, returning the
|
||||
// bare "error.<code>" key when no translation is available so the gap stays visible.
|
||||
func (s *Connection) translateShort(code string) string {
|
||||
key := "error." + code
|
||||
if s.translator == nil {
|
||||
return key
|
||||
}
|
||||
lang := i18n.DefaultLanguage
|
||||
if s.prefs != nil {
|
||||
if pref := s.prefs.Get().Language; pref != "" {
|
||||
lang = pref
|
||||
}
|
||||
}
|
||||
return s.translator.Translate(lang, key)
|
||||
}
|
||||
|
||||
// LoginParams are the inputs to Login.
|
||||
type LoginParams struct {
|
||||
ProfileName string `json:"profileName"`
|
||||
@@ -317,3 +257,63 @@ func (s *Connection) Logout(ctx context.Context, p LogoutParams) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// classifyDaemonError maps a gRPC error to a ClientError by matching known
|
||||
// substrings to a stable code. A missing locale entry surfaces as a visible
|
||||
// "error.<code>" string — a deliberate fail-loud signal to update the bundle.
|
||||
func (s *Connection) classifyDaemonError(err error) *ClientError {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
msg := err.Error()
|
||||
if st, ok := gstatus.FromError(err); ok {
|
||||
msg = st.Message()
|
||||
}
|
||||
lower := strings.ToLower(msg)
|
||||
|
||||
code := "unknown"
|
||||
switch {
|
||||
case strings.Contains(lower, "token used before issued"),
|
||||
strings.Contains(lower, "token is not valid yet"):
|
||||
code = "jwt_clock_skew"
|
||||
case strings.Contains(lower, "token is expired"),
|
||||
strings.Contains(lower, "token has expired"):
|
||||
code = "jwt_expired"
|
||||
case strings.Contains(lower, "token signature is invalid"):
|
||||
code = "jwt_signature_invalid"
|
||||
case strings.Contains(lower, "peer login has expired"):
|
||||
code = "session_expired"
|
||||
case strings.Contains(lower, "invalid setup-key"),
|
||||
strings.Contains(lower, "invalid setup key"):
|
||||
code = "invalid_setup_key"
|
||||
case strings.Contains(lower, "permission denied"):
|
||||
code = "permission_denied"
|
||||
case strings.Contains(lower, "no connection could be made"),
|
||||
strings.Contains(lower, "connection refused"),
|
||||
strings.Contains(lower, "context deadline exceeded"):
|
||||
code = "daemon_unreachable"
|
||||
}
|
||||
|
||||
return &ClientError{
|
||||
Code: code,
|
||||
Short: s.translateShort(code),
|
||||
Long: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// translateShort resolves the localised short message for code, returning the
|
||||
// bare "error.<code>" key when no translation is available so the gap stays visible.
|
||||
func (s *Connection) translateShort(code string) string {
|
||||
key := "error." + code
|
||||
if s.translator == nil {
|
||||
return key
|
||||
}
|
||||
lang := i18n.DefaultLanguage
|
||||
if s.prefs != nil {
|
||||
if pref := s.prefs.Get().Language; pref != "" {
|
||||
lang = pref
|
||||
}
|
||||
}
|
||||
return s.translator.Translate(lang, key)
|
||||
}
|
||||
|
||||
@@ -44,8 +44,6 @@ const WindowHeight = 660
|
||||
// Wails reads CustomTheme colours as 0x00BBGGRR (RGB byte order reversed).
|
||||
// Border/title bar match AppRightPanel bg-nb-gray-940 (#1C1E21); title text
|
||||
// matches text-nb-gray-100 (#E4E7E9).
|
||||
func u32ptr(v uint32) *uint32 { return &v }
|
||||
|
||||
var microsoftWindowsTheme = &application.WindowTheme{
|
||||
BorderColour: u32ptr(0x00211E1C),
|
||||
TitleBarColour: u32ptr(0x00211E1C),
|
||||
@@ -144,21 +142,6 @@ type WindowManager struct {
|
||||
recenterOnShow func() bool
|
||||
}
|
||||
|
||||
// title resolves a window-title i18n key in the user's current language.
|
||||
// Falls back to the raw key when translator or prefs are missing.
|
||||
func (s *WindowManager) title(key string) string {
|
||||
if s.translator == nil {
|
||||
return key
|
||||
}
|
||||
lang := i18n.DefaultLanguage
|
||||
if s.prefs != nil {
|
||||
if pref := s.prefs.Get().Language; pref != "" {
|
||||
lang = pref
|
||||
}
|
||||
}
|
||||
return s.translator.Translate(lang, key)
|
||||
}
|
||||
|
||||
// NewWindowManager wires the manager to the main app. translator and prefs may
|
||||
// be nil (tests), in which case title() falls back to the raw i18n key.
|
||||
//
|
||||
@@ -210,32 +193,6 @@ func NewWindowManager(app *application.App, mainWindow *application.WebviewWindo
|
||||
return s
|
||||
}
|
||||
|
||||
// retitleAll re-applies the localised title to every alive auxiliary window.
|
||||
// Snapshots the window pointers under s.mu so a concurrent Open*/Close* can't
|
||||
// race; SetTitle dispatches to the OS UI thread, so the calls are safe to make
|
||||
// after releasing the lock.
|
||||
func (s *WindowManager) retitleAll() {
|
||||
s.mu.Lock()
|
||||
type pair struct {
|
||||
win *application.WebviewWindow
|
||||
key string
|
||||
}
|
||||
wins := []pair{
|
||||
{s.settings, "window.title.settings"},
|
||||
{s.browserLogin, "window.title.signIn"},
|
||||
{s.sessionExpiration, "window.title.sessionExpiration"},
|
||||
{s.installProgress, "window.title.updating"},
|
||||
{s.welcome, "window.title.welcome"},
|
||||
{s.errorDialog, "window.title.error"},
|
||||
}
|
||||
s.mu.Unlock()
|
||||
for _, p := range wins {
|
||||
if p.win != nil {
|
||||
p.win.SetTitle(s.title(p.key))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// OpenSettings shows the settings window on tab (empty → General).
|
||||
//
|
||||
// The window keeps a single URL (/#/settings) for its lifetime: SetURL per open
|
||||
@@ -305,33 +262,6 @@ func (s *WindowManager) OpenBrowserLogin(uri string) {
|
||||
s.centerWhenReady(s.browserLogin)
|
||||
}
|
||||
|
||||
// hideOtherWindowsLocked hides every visible window except keepName, recording
|
||||
// them in hiddenForLogin for restoreHiddenWindowsLocked. Caller must hold s.mu.
|
||||
func (s *WindowManager) hideOtherWindowsLocked(keepName string) {
|
||||
for _, w := range s.app.Window.GetAll() {
|
||||
if w == nil || w.Name() == keepName {
|
||||
continue
|
||||
}
|
||||
if !w.IsVisible() {
|
||||
continue
|
||||
}
|
||||
w.Hide()
|
||||
s.hiddenForLogin = append(s.hiddenForLogin, w)
|
||||
}
|
||||
}
|
||||
|
||||
// restoreHiddenWindowsLocked re-shows windows hidden by
|
||||
// hideOtherWindowsLocked. Caller must hold s.mu.
|
||||
func (s *WindowManager) restoreHiddenWindowsLocked() {
|
||||
for _, w := range s.hiddenForLogin {
|
||||
if w == nil {
|
||||
continue
|
||||
}
|
||||
w.Show()
|
||||
}
|
||||
s.hiddenForLogin = nil
|
||||
}
|
||||
|
||||
// BrowserLoginWindow returns the live SSO popup, or nil if no SSO flow is in
|
||||
// progress. While non-nil it is the app's focal window: tray "Open" and
|
||||
// dock/taskbar activation hand off to it instead of the main window.
|
||||
@@ -508,24 +438,6 @@ func (s *WindowManager) OpenError(title, message string) {
|
||||
s.centerWhenReady(s.errorDialog)
|
||||
}
|
||||
|
||||
// errorDialogURL builds the error window's hash-route start URL with title and
|
||||
// message as query params, escaped so newlines and ampersands common in
|
||||
// formatted daemon errors survive into useSearchParams.
|
||||
func errorDialogURL(title, message string) string {
|
||||
q := url.Values{}
|
||||
if title != "" {
|
||||
q.Set("title", title)
|
||||
}
|
||||
if message != "" {
|
||||
q.Set("message", message)
|
||||
}
|
||||
startURL := "/#/dialog/error"
|
||||
if enc := q.Encode(); enc != "" {
|
||||
startURL += "?" + enc
|
||||
}
|
||||
return startURL
|
||||
}
|
||||
|
||||
func (s *WindowManager) CloseError() {
|
||||
s.mu.Lock()
|
||||
w := s.errorDialog
|
||||
@@ -564,27 +476,6 @@ func (s *WindowManager) SetRecenterOnShow(pred func() bool) {
|
||||
s.recenterOnShow = pred
|
||||
}
|
||||
|
||||
// getScreenBasedOnCursorPosition returns the display the OS cursor is on,
|
||||
// falling back to the main-window screen, then nil (OS-default placement).
|
||||
// On Linux the cursor query uses XQueryPointer, which works on Wayland via
|
||||
// XWayland.
|
||||
func (s *WindowManager) getScreenBasedOnCursorPosition() *application.Screen {
|
||||
if s.app == nil || s.app.Screen == nil {
|
||||
return nil
|
||||
}
|
||||
if p, ok := getCursorPosition(s.app); ok {
|
||||
if sc := s.app.Screen.ScreenNearestDipPoint(p); sc != nil {
|
||||
return sc
|
||||
}
|
||||
}
|
||||
if s.mainWindow != nil {
|
||||
if sc, err := s.mainWindow.GetScreen(); err == nil {
|
||||
return sc
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// centerWhenReady centers w once its native window exists, but only where the
|
||||
// WM won't (recenterOnShow); otherwise it returns immediately so it never fights
|
||||
// a user-moved window.
|
||||
@@ -648,3 +539,113 @@ func (s *WindowManager) centerOnCursorScreen(w *application.WebviewWindow) {
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// title resolves a window-title i18n key in the user's current language.
|
||||
// Falls back to the raw key when translator or prefs are missing.
|
||||
func (s *WindowManager) title(key string) string {
|
||||
if s.translator == nil {
|
||||
return key
|
||||
}
|
||||
lang := i18n.DefaultLanguage
|
||||
if s.prefs != nil {
|
||||
if pref := s.prefs.Get().Language; pref != "" {
|
||||
lang = pref
|
||||
}
|
||||
}
|
||||
return s.translator.Translate(lang, key)
|
||||
}
|
||||
|
||||
// retitleAll re-applies the localised title to every alive auxiliary window.
|
||||
// Snapshots the window pointers under s.mu so a concurrent Open*/Close* can't
|
||||
// race; SetTitle dispatches to the OS UI thread, so the calls are safe to make
|
||||
// after releasing the lock.
|
||||
func (s *WindowManager) retitleAll() {
|
||||
s.mu.Lock()
|
||||
type pair struct {
|
||||
win *application.WebviewWindow
|
||||
key string
|
||||
}
|
||||
wins := []pair{
|
||||
{s.settings, "window.title.settings"},
|
||||
{s.browserLogin, "window.title.signIn"},
|
||||
{s.sessionExpiration, "window.title.sessionExpiration"},
|
||||
{s.installProgress, "window.title.updating"},
|
||||
{s.welcome, "window.title.welcome"},
|
||||
{s.errorDialog, "window.title.error"},
|
||||
}
|
||||
s.mu.Unlock()
|
||||
for _, p := range wins {
|
||||
if p.win != nil {
|
||||
p.win.SetTitle(s.title(p.key))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// hideOtherWindowsLocked hides every visible window except keepName, recording
|
||||
// them in hiddenForLogin for restoreHiddenWindowsLocked. Caller must hold s.mu.
|
||||
func (s *WindowManager) hideOtherWindowsLocked(keepName string) {
|
||||
for _, w := range s.app.Window.GetAll() {
|
||||
if w == nil || w.Name() == keepName {
|
||||
continue
|
||||
}
|
||||
if !w.IsVisible() {
|
||||
continue
|
||||
}
|
||||
w.Hide()
|
||||
s.hiddenForLogin = append(s.hiddenForLogin, w)
|
||||
}
|
||||
}
|
||||
|
||||
// restoreHiddenWindowsLocked re-shows windows hidden by
|
||||
// hideOtherWindowsLocked. Caller must hold s.mu.
|
||||
func (s *WindowManager) restoreHiddenWindowsLocked() {
|
||||
for _, w := range s.hiddenForLogin {
|
||||
if w == nil {
|
||||
continue
|
||||
}
|
||||
w.Show()
|
||||
}
|
||||
s.hiddenForLogin = nil
|
||||
}
|
||||
|
||||
// getScreenBasedOnCursorPosition returns the display the OS cursor is on,
|
||||
// falling back to the main-window screen, then nil (OS-default placement).
|
||||
// On Linux the cursor query uses XQueryPointer, which works on Wayland via
|
||||
// XWayland.
|
||||
func (s *WindowManager) getScreenBasedOnCursorPosition() *application.Screen {
|
||||
if s.app == nil || s.app.Screen == nil {
|
||||
return nil
|
||||
}
|
||||
if p, ok := getCursorPosition(s.app); ok {
|
||||
if sc := s.app.Screen.ScreenNearestDipPoint(p); sc != nil {
|
||||
return sc
|
||||
}
|
||||
}
|
||||
if s.mainWindow != nil {
|
||||
if sc, err := s.mainWindow.GetScreen(); err == nil {
|
||||
return sc
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// errorDialogURL builds the error window's hash-route start URL with title and
|
||||
// message as query params, escaped so newlines and ampersands common in
|
||||
// formatted daemon errors survive into useSearchParams.
|
||||
func errorDialogURL(title, message string) string {
|
||||
q := url.Values{}
|
||||
if title != "" {
|
||||
q.Set("title", title)
|
||||
}
|
||||
if message != "" {
|
||||
q.Set("message", message)
|
||||
}
|
||||
startURL := "/#/dialog/error"
|
||||
if enc := q.Encode(); enc != "" {
|
||||
startURL += "?" + enc
|
||||
}
|
||||
return startURL
|
||||
}
|
||||
|
||||
// u32ptr returns a pointer to v, for the optional *uint32 Wails theme fields.
|
||||
func u32ptr(v uint32) *uint32 { return &v }
|
||||
|
||||
Reference in New Issue
Block a user