mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-02 08:15:55 +00:00
Add an "Interface layout" setting at the top of Settings > Layout with two options, Default and Mountain View. Default is the existing layout and stays the default, so nothing changes unless a user opts in. Mountain View rearranges the chrome while keeping the theme's colours, icons and type: - desktop: a top bar with a wide search field (filters drop from the field), an apps grid and the account avatar instead of the left app rail; flat navigation with Starred under Inbox and the rest behind "More"; a Compose pill; one list toolbar with select-all, refresh, overflow menu, a 1-N of total range and the batch actions; rows led by a checkbox and a star; an icon-only message toolbar with Reply / Reply all / Forward under the message; compose in a docked window; an account popover with per-account unread counts; j/k cursor, x to select and g-sequences - phone: rounded search header, compact rows with the star on the preview line, and Reply / Reply all / Forward at the end of a message instead of the bottom action bar It is one setting (`interfaceLayout`), written to a data attribute on <html> that keys a block of CSS tokens in globals.css, plus a few components gated on the flag. The mail layout in force is derived (effectiveMailLayout), so the user's saved mail layout is never rewritten. The setting is listed in the admin policy tab, and the new strings are translated in all locales. Includes unit tests and a Playwright smoke test against a running instance (scripts/mountain-view-smoke/).
36 lines
1.5 KiB
TypeScript
36 lines
1.5 KiB
TypeScript
import type { Mailbox } from "@/lib/jmap/types";
|
|
|
|
/**
|
|
* Unread messages waiting in an account's own inbox.
|
|
*
|
|
* Mountain View's account popover puts this number next to every address, so
|
|
* that switching account is a decision one can take from the popover instead
|
|
* of having to visit each account in turn.
|
|
*
|
|
* Shared folders are skipped: they are someone else's inbox, they are listed
|
|
* under their owner in the sidebar, and counting them here would attribute
|
|
* another person's unread mail to this account. `undefined` means "not known
|
|
* yet" - the account's folders have not been fetched - and is deliberately
|
|
* distinct from a known zero, which the caller should render as no badge
|
|
* rather than as a "0".
|
|
*/
|
|
export function inboxUnread(mailboxes: Mailbox[] | undefined): number | undefined {
|
|
if (!mailboxes || mailboxes.length === 0) return undefined;
|
|
const inbox = mailboxes.find((mailbox) => mailbox.role === "inbox" && !mailbox.isShared);
|
|
return inbox ? inbox.unreadEmails : undefined;
|
|
}
|
|
|
|
/**
|
|
* What "All inboxes" carries: the unread of every listed account's inbox.
|
|
*
|
|
* Accounts whose folders are still loading contribute nothing rather than
|
|
* blocking the total, so the badge grows as the fetches land instead of
|
|
* appearing all at once several seconds in.
|
|
*/
|
|
export function totalInboxUnread(
|
|
accountIds: readonly string[],
|
|
mailboxesByAccount: Record<string, Mailbox[] | undefined>
|
|
): number {
|
|
return accountIds.reduce((total, id) => total + (inboxUnread(mailboxesByAccount[id]) ?? 0), 0);
|
|
}
|