diff --git a/services/webmail/src/app/(user)/user/layout.tsx b/services/webmail/src/app/(user)/user/layout.tsx
index 14016fd..8a08a74 100644
--- a/services/webmail/src/app/(user)/user/layout.tsx
+++ b/services/webmail/src/app/(user)/user/layout.tsx
@@ -133,21 +133,10 @@ export default function UserWebmailLayout({ children }: { children: React.ReactN
{item.label}
- {/* The folder's size, shown quietly beside the badge: the
- sidebar reported unread only, so there was nowhere in the
- interface that said how much mail a folder holds. */}
- {/* The folder size, always in the same quiet style, so it
- can never be mistaken for an unread count. */}
- {badge.total > 0 && (
-
- {badge.total}
-
- )}
- {/* The unread count, only when there is unread mail, and
- visibly distinct from the size beside it. */}
+ {/* Only what is waiting to be read. The size of every folder
+ beside its name filled the rail with numbers that never
+ change and that nobody is waiting on; the folder heading
+ still says how much mail a folder holds. */}
{badge.showsUnread && (
{folder.name}
- {badge.total > 0 && (
- {badge.total}
- )}
{badge.showsUnread && (
{badge.unread}
diff --git a/services/webmail/src/mail-state-integrity.test.ts b/services/webmail/src/mail-state-integrity.test.ts
index 0beaa59..6097f86 100644
--- a/services/webmail/src/mail-state-integrity.test.ts
+++ b/services/webmail/src/mail-state-integrity.test.ts
@@ -36,17 +36,18 @@ describe("the counters the sidebar shows", () => {
expect(code(USER_LAYOUT)).not.toMatch(/unread_count\s*\?\?/);
});
- // The total was rendered twice for Drafts, where the badge and the size are
- // the same number: "Rascunhos 1 1".
- //
- // Two occurrences now, not one: the standard folders and the folders the
- // user created are rendered by separate templates. What must not happen is
- // one template printing it twice, which is what the defect was.
- it("renders the folder size once per folder template", () => {
+ // The sidebar shows what is waiting to be read and nothing else. It used to
+ // print the size of every folder beside its name - numbers that never change
+ // and that nobody is waiting on - and, worse, it printed the size in the
+ // unread badge's own place whenever nothing was unread, so an inbox of read
+ // mail showed "14" and reading another message never moved it.
+ it("renders only the unread count, never the folder size", () => {
const layout = code(USER_LAYOUT);
- expect(layout.match(/\{badge\.total\}/g) ?? []).toHaveLength(2);
- // And each template guards the unread badge, so the two numbers can never
- // be the same value shown twice.
+ // The size survives in the tooltip, which is not the rail.
+ const rendered = layout.match(/\{badge\.total\}/g) ?? [];
+ expect(rendered).toHaveLength(0);
+ // Both folder templates - the standard ones and the user's own - guard
+ // their badge on there actually being unread mail.
expect(layout.match(/badge\.showsUnread/g) ?? []).toHaveLength(2);
});
diff --git a/services/webmail/src/mailCounts.test.ts b/services/webmail/src/mailCounts.test.ts
index 1a7b4f2..9203dfc 100644
--- a/services/webmail/src/mailCounts.test.ts
+++ b/services/webmail/src/mailCounts.test.ts
@@ -175,39 +175,46 @@ describe("the badge a sidebar folder renders", () => {
{ special_use: "archive", name: "Archive", unread_count: 0, total_count: 0 },
];
- // The inbox showed "14" with nothing unread. Reading every message left it
- // at 14, because 14 was the total wearing the unread badge's clothes.
- it("does not show a total where an unread count belongs", () => {
- const badge = folderBadge(folders, "inbox");
- expect(badge.unread).toBe(0);
- expect(badge.showsUnread).toBe(false);
- });
-
- it("still reports the folder size, separately from the unread count", () => {
- expect(folderBadge(folders, "inbox").total).toBe(14);
- });
-
- // Drafts counted total as its badge, so total and badge were the same
- // number and the sidebar printed it twice: "Rascunhos 1 1".
- it("never renders the same number twice for drafts", () => {
- const badge = folderBadge(folders, "drafts");
- const rendered = [badge.showsUnread ? badge.unread : null, badge.total].filter(
- (v) => v !== null,
- );
- expect(rendered).toEqual([1]);
- });
-
- it("shows the unread count when there really is unread mail", () => {
+ // The sidebar shows what needs attention and nothing else. Printing the size
+ // of every folder beside its name filled the rail with numbers that never
+ // change and that nobody is waiting on.
+ it("shows the unread count when there is unread mail", () => {
const badge = folderBadge(folders, "junk");
expect(badge.showsUnread).toBe(true);
expect(badge.unread).toBe(3);
- expect(badge.total).toBe(9);
});
- it("shows nothing at all for an empty folder", () => {
- const badge = folderBadge(folders, "archive");
+ // An inbox whose mail has all been read shows nothing. This is also what the
+ // original defect looked like from the other side: it showed "14" - the
+ // total - and reading a message never moved it.
+ it("shows nothing when everything has been read", () => {
+ expect(folderBadge(folders, "inbox").showsUnread).toBe(false);
+ });
+
+ it("shows nothing for a folder nothing is delivered to", () => {
+ expect(folderBadge(folders, "sent").showsUnread).toBe(false);
+ });
+
+ it("shows nothing for an empty folder", () => {
+ expect(folderBadge(folders, "archive").showsUnread).toBe(false);
+ });
+
+ // Nothing marks a draft read, so its unread count is permanently zero. With
+ // only unread counts on show, Drafts carries no number at all.
+ it("shows nothing for drafts, which are never unread", () => {
+ expect(folderBadge(folders, "drafts").showsUnread).toBe(false);
+ });
+
+ // The total is still available to anything that wants it - the folder
+ // heading uses it - it is simply not what the rail displays.
+ it("still reports the folder size for callers that need it", () => {
+ expect(folderBadge(folders, "inbox").total).toBe(14);
+ });
+
+ it("copes with a folder that is not there", () => {
+ const badge = folderBadge(folders, "nao-existe");
expect(badge.showsUnread).toBe(false);
- expect(badge.total).toBe(0);
+ expect(badge.unread).toBe(0);
});
});
diff --git a/services/webmail/src/sidebarBadge.test.ts b/services/webmail/src/sidebarBadge.test.ts
new file mode 100644
index 0000000..1d8d4d3
--- /dev/null
+++ b/services/webmail/src/sidebarBadge.test.ts
@@ -0,0 +1,52 @@
+import { describe, it, expect } from "vitest";
+import { folderBadge, type FolderSummary } from "./lib/mailCounts";
+
+/**
+ * What the rail actually puts on screen, folder by folder.
+ *
+ * The component renders the unread count only when showsUnread is true, so
+ * this is the rendered result: a number, or nothing at all. Written as a table
+ * because the point of the change is what the rail looks like as a whole, not
+ * what one folder returns.
+ */
+function rendered(folders: FolderSummary[], route: string): string {
+ const badge = folderBadge(folders, route);
+ return badge.showsUnread ? String(badge.unread) : "";
+}
+
+describe("what each folder shows in the rail", () => {
+ const folders: FolderSummary[] = [
+ { special_use: "inbox", name: "INBOX", unread_count: 3, total_count: 17 },
+ { special_use: "sent", name: "Sent", unread_count: 0, total_count: 2 },
+ { special_use: "drafts", name: "Drafts", unread_count: 0, total_count: 1 },
+ { special_use: "junk", name: "Junk", unread_count: 0, total_count: 0 },
+ { special_use: "", name: "Faturas", unread_count: 5, total_count: 9 },
+ ];
+
+ it("shows only the unread count, never the size", () => {
+ expect(rendered(folders, "inbox")).toBe("3");
+ expect(rendered(folders, "inbox")).not.toContain("17");
+ });
+
+ it("shows nothing for folders with nothing unread", () => {
+ expect(rendered(folders, "sent")).toBe("");
+ expect(rendered(folders, "drafts")).toBe("");
+ expect(rendered(folders, "junk")).toBe("");
+ });
+
+ it("shows the unread count on a folder the user created", () => {
+ expect(rendered(folders, "Faturas")).toBe("5");
+ });
+
+ // The whole rail, as a reader sees it: a number only where mail is waiting.
+ it("leaves the rail carrying one number per folder with unread mail", () => {
+ const all = ["inbox", "sent", "drafts", "junk", "Faturas"].map((r) => rendered(folders, r));
+ expect(all.filter((v) => v !== "")).toEqual(["3", "5"]);
+ });
+
+ it("is completely bare when everything has been read", () => {
+ const read = folders.map((f) => ({ ...f, unread_count: 0 }));
+ const all = ["inbox", "sent", "drafts", "junk", "Faturas"].map((r) => rendered(read, r));
+ expect(all.every((v) => v === "")).toBe(true);
+ });
+});