@remit/web-client 0.0.171 → 0.0.172

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remit/web-client",
3
- "version": "0.0.171",
3
+ "version": "0.0.172",
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": {
@@ -59,7 +59,12 @@ export const ComposeProvider = ({
59
59
  enabled: !!pollingMessageId,
60
60
  refetchInterval: (query) => {
61
61
  const status = query.state.data?.status;
62
- if (status === "sent" || status === "failed" || status === "blocked")
62
+ if (
63
+ status === "sent" ||
64
+ status === "unfiled" ||
65
+ status === "failed" ||
66
+ status === "blocked"
67
+ )
63
68
  return false;
64
69
  if (Date.now() - startedAtRef.current > MAX_POLL_DURATION_MS)
65
70
  return false;
@@ -72,6 +77,7 @@ export const ComposeProvider = ({
72
77
 
73
78
  const terminal =
74
79
  polledMessage.status === "sent" ||
80
+ polledMessage.status === "unfiled" ||
75
81
  polledMessage.status === "failed" ||
76
82
  polledMessage.status === "blocked";
77
83
 
@@ -59,7 +59,7 @@ import {
59
59
  matchesOutboxSearch,
60
60
  outboxQueryIsUnsupported,
61
61
  } from "@/lib/outbox-search";
62
- import { isOutboxListRow, isUnsendableStatus } from "@/lib/outbox-status";
62
+ import { isOutboxListRow, showsLastError } from "@/lib/outbox-status";
63
63
  import { normalizeSearchQuery } from "@/lib/search-query";
64
64
  import { parseSearchTokens } from "@/lib/search-tokens";
65
65
  import { useEditDraft, useRetainOpenPanels } from "@/routing";
@@ -87,6 +87,11 @@ const STATUS_CONFIG: Record<
87
87
  label: "Sent",
88
88
  className: "text-positive",
89
89
  },
90
+ unfiled: {
91
+ icon: AlertTriangle,
92
+ label: "Sent, not filed",
93
+ className: "text-warning",
94
+ },
90
95
  failed: {
91
96
  icon: AlertCircle,
92
97
  label: "Failed",
@@ -310,7 +315,7 @@ function OutboxMessageRow({
310
315
  >;
311
316
  if (!STATUS_CONFIG[status]) return null;
312
317
 
313
- const showError = isUnsendableStatus(message.status);
318
+ const showError = showsLastError(message.status);
314
319
 
315
320
  return (
316
321
  <OutboxRow
@@ -414,7 +419,7 @@ function OutboxMessageDetail({ message, onBack }: OutboxMessageDetailProps) {
414
419
  <span className={cn("text-xs font-medium", config.className)}>
415
420
  {config.label}
416
421
  </span>
417
- {isUnsendableStatus(message.status) && message.lastError && (
422
+ {showsLastError(message.status) && message.lastError && (
418
423
  <span className="text-xs text-fg-muted">
419
424
  — {message.lastError}
420
425
  </span>
@@ -3,7 +3,7 @@ import { describe, test } from "node:test";
3
3
  import {
4
4
  describeOutboxStatus,
5
5
  isOutboxListRow,
6
- isUnsendableStatus,
6
+ showsLastError,
7
7
  } from "./outbox-status.js";
8
8
 
9
9
  describe("describeOutboxStatus", () => {
@@ -25,6 +25,13 @@ describe("describeOutboxStatus", () => {
25
25
  assert.equal(desc.label, "Sent");
26
26
  });
27
27
 
28
+ test("unfiled says the message was sent but not filed", () => {
29
+ const desc = describeOutboxStatus("unfiled");
30
+ assert.ok(desc);
31
+ assert.equal(desc.tone, "warning");
32
+ assert.match(desc.label, /not filed/);
33
+ });
34
+
28
35
  test("failed is error tone (distinct from blocked)", () => {
29
36
  const desc = describeOutboxStatus("failed");
30
37
  assert.ok(desc);
@@ -33,19 +40,23 @@ describe("describeOutboxStatus", () => {
33
40
  });
34
41
  });
35
42
 
36
- describe("isUnsendableStatus", () => {
37
- test("sent must NOT be flagged as unsendable — never show error subtitle on success (issue #192)", () => {
38
- assert.equal(isUnsendableStatus("sent"), false);
43
+ describe("showsLastError", () => {
44
+ test("sent must NOT surface an error — never show an error subtitle on success (issue #192)", () => {
45
+ assert.equal(showsLastError("sent"), false);
39
46
  });
40
47
 
41
- test("failed and blocked are unsendable", () => {
42
- assert.equal(isUnsendableStatus("failed"), true);
43
- assert.equal(isUnsendableStatus("blocked"), true);
48
+ test("failed and blocked surface their error", () => {
49
+ assert.equal(showsLastError("failed"), true);
50
+ assert.equal(showsLastError("blocked"), true);
44
51
  });
45
52
 
46
- test("queued and sending are not unsendable", () => {
47
- assert.equal(isUnsendableStatus("queued"), false);
48
- assert.equal(isUnsendableStatus("sending"), false);
53
+ test("unfiled surfaces its reason the row exists only to carry it", () => {
54
+ assert.equal(showsLastError("unfiled"), true);
55
+ });
56
+
57
+ test("queued and sending carry no error", () => {
58
+ assert.equal(showsLastError("queued"), false);
59
+ assert.equal(showsLastError("sending"), false);
49
60
  });
50
61
  });
51
62
 
@@ -61,4 +72,8 @@ describe("isOutboxListRow", () => {
61
72
  assert.equal(isOutboxListRow("failed"), true);
62
73
  assert.equal(isOutboxListRow("blocked"), true);
63
74
  });
75
+
76
+ test("keeps an unfiled row listed — it is the only copy the user has", () => {
77
+ assert.equal(isOutboxListRow("unfiled"), true);
78
+ });
64
79
  });
@@ -29,6 +29,7 @@ const STATUS_DESCRIPTORS: Record<OutboxDisplayStatus, OutboxStatusDescriptor> =
29
29
  queued: { label: "Queued", tone: "neutral" },
30
30
  sending: { label: "Sending…", tone: "info" },
31
31
  sent: { label: "Sent", tone: "success" },
32
+ unfiled: { label: "Sent, not filed", tone: "warning" },
32
33
  failed: { label: "Failed", tone: "error" },
33
34
  blocked: { label: "Blocked", tone: "warning" },
34
35
  };
@@ -56,11 +57,12 @@ export const describeOutboxStatus = (
56
57
  *
57
58
  * Critically: `sent` is NOT included. A successful send must never display
58
59
  * a stale lastError — that is the contradictory "green check + 'SMTP not
59
- * configured'" UI bug from issue #192.
60
+ * configured'" UI bug from issue #192. `unfiled` is: the message was
61
+ * delivered but could not be filed, and the reason is the whole point of
62
+ * the row still being there.
60
63
  */
61
- export const isUnsendableStatus = (
62
- status: RemitImapOutboxMessageStatus,
63
- ): boolean => status === "failed" || status === "blocked";
64
+ export const showsLastError = (status: RemitImapOutboxMessageStatus): boolean =>
65
+ status === "failed" || status === "blocked" || status === "unfiled";
64
66
 
65
67
  /**
66
68
  * Whether the outbox list view should render this row.
@@ -68,7 +70,9 @@ export const isUnsendableStatus = (
68
70
  * `draft` belongs in the Drafts view. `sent` rows are deleted by the IMAP
69
71
  * APPEND handler (issue #178) — but until that happens we hide them from
70
72
  * the Outbox list so a successfully-sent message never appears in two
71
- * places at once (issue #193).
73
+ * places at once (issue #193). `unfiled` is the settled failure of that
74
+ * APPEND: the message is on no server folder, so the Outbox row is the only
75
+ * copy the user has and it stays listed.
72
76
  */
73
77
  export const isOutboxListRow = (
74
78
  status: RemitImapOutboxMessageStatus,