From 73d9c18bfe34f7b3cdb47360d8f39a755ff67f40 Mon Sep 17 00:00:00 2001 From: SUMAN JANA Date: Wed, 9 Sep 2026 15:29:47 +0000 Subject: [PATCH] fix(imap, unibox): show the server's text for a codeless IMAP error; contact rail starts closed - An IMAP NO/BAD without a response code (Gmail's "NO System Error") rendered as "Something went wrong: " in the mailbox's error list. Fall back to the server's text when the code is empty. - The unibox contact rail opened by itself on lg+ screens, putting the contact form in front of every thread the reader opened. It now starts closed at every width and opens from the header toggle. --- internal/client/smtpimap/imap/err.go | 9 ++++++++- web/src/components/app/unibox/ThreadView.tsx | 13 +++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/internal/client/smtpimap/imap/err.go b/internal/client/smtpimap/imap/err.go index 767bb509..53c0aaa3 100644 --- a/internal/client/smtpimap/imap/err.go +++ b/internal/client/smtpimap/imap/err.go @@ -21,7 +21,14 @@ func (c *Client) handleError(err error) *errx.MailError { case imap.ResponseCodeAuthorizationFailed: return errx.ErrMailAuthorizationFailed default: - return errx.ErrMailUnknownImapError(string(imapErr.Code)) + // A NO/BAD without a response code (Gmail's "NO System Error") + // has only its text; an empty code rendered as "Something went + // wrong: " in the mailbox's error list. + detail := string(imapErr.Code) + if detail == "" { + detail = imapErr.Text + } + return errx.ErrMailUnknownImapError(detail) } } diff --git a/web/src/components/app/unibox/ThreadView.tsx b/web/src/components/app/unibox/ThreadView.tsx index d7f1d4cd..8e55b361 100644 --- a/web/src/components/app/unibox/ThreadView.tsx +++ b/web/src/components/app/unibox/ThreadView.tsx @@ -160,14 +160,11 @@ export function ThreadView({ threadId, emailId }: ThreadViewProps) { const threadLabels = useThreadLabels(threadId); const [labelMenuOpen, setLabelMenuOpen] = React.useState(false); - // CRM context rail (right side). Open by default on wide screens (lg+), - // where it renders as a static rail. Below lg it renders as an overlay - // drawer, so it starts closed and is opened from the header toggle. - const [crmOpen, setCrmOpen] = React.useState( - () => - typeof window !== "undefined" && - window.matchMedia("(min-width: 1024px)").matches, - ); + // CRM context rail (right side). Starts closed at every width and is + // opened from the header toggle: on wide screens it renders as a static + // rail, below lg as an overlay drawer. It used to open by itself on lg+, + // which put the contact form in front of every thread the reader opened. + const [crmOpen, setCrmOpen] = React.useState(false); // `c` opens the label menu while a thread is open — ignored while // typing into the composer / any input so it never eats keystrokes.