@remit/web-client 0.0.11 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/lib/brief.test.ts +17 -2
- package/src/lib/brief.ts +11 -3
- package/src/lib/display-category.test.ts +29 -0
- package/src/lib/display-category.ts +9 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remit/web-client",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Remit web client, published as composable primitives — the app shell, auth shells, and runtime config. A distributor imports what it composes and bundles it.",
|
|
6
6
|
"exports": {
|
package/src/lib/brief.test.ts
CHANGED
|
@@ -145,11 +145,26 @@ describe("groupBriefSections", () => {
|
|
|
145
145
|
|
|
146
146
|
// --- Fallback to personal ---
|
|
147
147
|
|
|
148
|
-
test("missing category
|
|
148
|
+
test("missing category lands in its own Unclassified section", () => {
|
|
149
|
+
// Never folded into Personal: unclassified mail is work the classifier
|
|
150
|
+
// has not done, and hiding it inside Personal is what made issue #45
|
|
151
|
+
// look like a working classifier with a huge personal inbox.
|
|
149
152
|
const r = row({ id: "1" });
|
|
150
153
|
const sections = groupBriefSections([r]);
|
|
151
154
|
assert.strictEqual(sections.length, 1);
|
|
152
|
-
assert.strictEqual(sections[0].id, "
|
|
155
|
+
assert.strictEqual(sections[0].id, "uncategorized");
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test("uncategorized rows do not inflate the personal section", () => {
|
|
159
|
+
const sections = groupBriefSections([
|
|
160
|
+
row({ id: "1", category: "personal" }),
|
|
161
|
+
row({ id: "2", category: "uncategorized" }),
|
|
162
|
+
row({ id: "3", category: "uncategorized" }),
|
|
163
|
+
]);
|
|
164
|
+
const personal = sections.find((s) => s.id === "personal");
|
|
165
|
+
const unclassified = sections.find((s) => s.id === "uncategorized");
|
|
166
|
+
assert.strictEqual(personal?.threads.length, 1);
|
|
167
|
+
assert.strictEqual(unclassified?.threads.length, 2);
|
|
153
168
|
});
|
|
154
169
|
|
|
155
170
|
// --- Starred is a row marker, not a section (Flagged lives in the nav) ---
|
package/src/lib/brief.ts
CHANGED
|
@@ -10,9 +10,12 @@
|
|
|
10
10
|
* 4. Marketing
|
|
11
11
|
* 5. Social
|
|
12
12
|
* 6. Automated
|
|
13
|
+
* 7. Unclassified
|
|
13
14
|
*
|
|
14
15
|
* Each row lands in the section for its category; a row with no category counts
|
|
15
|
-
* as `
|
|
16
|
+
* as `uncategorized`, which is its own section rather than being folded into
|
|
17
|
+
* Personal — unclassified mail is missing work, not a decision (issue #45).
|
|
18
|
+
* Starred mail is not a section —
|
|
16
19
|
* the star is a per-row marker, so a starred message stays in its category.
|
|
17
20
|
*
|
|
18
21
|
* Sender trust (vip/wellknown) no longer sections the brief — the signal is
|
|
@@ -87,6 +90,11 @@ const CATEGORY_SECTIONS: ReadonlyArray<{
|
|
|
87
90
|
{ id: "marketing", label: "Marketing", category: MessageCategory.marketing },
|
|
88
91
|
{ id: "social", label: "Social", category: MessageCategory.social },
|
|
89
92
|
{ id: "automated", label: "Automated", category: MessageCategory.automated },
|
|
93
|
+
{
|
|
94
|
+
id: "uncategorized",
|
|
95
|
+
label: "Unclassified",
|
|
96
|
+
category: MessageCategory.uncategorized,
|
|
97
|
+
},
|
|
90
98
|
];
|
|
91
99
|
|
|
92
100
|
/**
|
|
@@ -104,9 +112,9 @@ export function groupBriefSections(rows: ThreadRowData[]): ThreadSection[] {
|
|
|
104
112
|
);
|
|
105
113
|
|
|
106
114
|
for (const row of rows) {
|
|
107
|
-
const category = row.category ?? MessageCategory.
|
|
115
|
+
const category = row.category ?? MessageCategory.uncategorized;
|
|
108
116
|
const bucket =
|
|
109
|
-
byCategory.get(category) ?? byCategory.get(MessageCategory.
|
|
117
|
+
byCategory.get(category) ?? byCategory.get(MessageCategory.uncategorized);
|
|
110
118
|
bucket?.push(row);
|
|
111
119
|
}
|
|
112
120
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { describe, test } from "node:test";
|
|
3
|
+
import { toDisplayCategory } from "./display-category.js";
|
|
4
|
+
|
|
5
|
+
describe("toDisplayCategory", () => {
|
|
6
|
+
test("shows an unclassified message as unclassified, not personal", () => {
|
|
7
|
+
// Collapsing `uncategorized` into `personal` made "the classifier never
|
|
8
|
+
// ran" indistinguishable from "the classifier decided this is personal"
|
|
9
|
+
// (issue #45).
|
|
10
|
+
assert.strictEqual(toDisplayCategory("uncategorized"), "uncategorized");
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test("treats a row with no category as unclassified", () => {
|
|
14
|
+
assert.strictEqual(toDisplayCategory(undefined), "uncategorized");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("passes every classified category through unchanged", () => {
|
|
18
|
+
for (const category of [
|
|
19
|
+
"personal",
|
|
20
|
+
"newsletter",
|
|
21
|
+
"marketing",
|
|
22
|
+
"automated",
|
|
23
|
+
"transactional",
|
|
24
|
+
"social",
|
|
25
|
+
] as const) {
|
|
26
|
+
assert.strictEqual(toDisplayCategory(category), category);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -5,16 +5,18 @@ import type { ThreadCategory } from "@remit/ui";
|
|
|
5
5
|
* Map an API message category to a kit display category.
|
|
6
6
|
*
|
|
7
7
|
* The API category is total (RFC 032 Tier 2) and carries `uncategorized` for
|
|
8
|
-
* messages that are metadata-synced but not yet body-classified.
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
8
|
+
* messages that are metadata-synced but not yet body-classified. That state is
|
|
9
|
+
* shown as itself, not collapsed into `personal`: collapsing made an
|
|
10
|
+
* unclassified message identical on screen to one the classifier positively
|
|
11
|
+
* decided was personal, so a classification gap read as a large personal inbox
|
|
12
|
+
* rather than as missing work (issue #45).
|
|
13
|
+
*
|
|
14
|
+
* Pre-migration rows that read `undefined` carry no category either, so they
|
|
15
|
+
* map to `uncategorized` the same way.
|
|
14
16
|
*/
|
|
15
17
|
export function toDisplayCategory(
|
|
16
18
|
category: RemitImapMessageCategory | undefined,
|
|
17
19
|
): ThreadCategory {
|
|
18
|
-
if (category === undefined
|
|
20
|
+
if (category === undefined) return "uncategorized";
|
|
19
21
|
return category;
|
|
20
22
|
}
|