@remit/web-client 0.0.321 → 0.0.323

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.
Files changed (190) hide show
  1. package/package.json +4 -4
  2. package/src/auth/SignInAgain.tsx +18 -0
  3. package/src/auth/amplify-config.test.ts +1 -107
  4. package/src/auth/auth-interceptor.test.ts +0 -6
  5. package/src/auth/cognito/cognito-token.test.ts +2 -4
  6. package/src/components/calendar/AgendaStrip.tsx +1 -1
  7. package/src/components/calendar/CalendarWorkspace.tsx +1 -1
  8. package/src/components/calendar/ChangeCalendarSuggestion.tsx +1 -1
  9. package/src/components/calendar/OpenCalendarEvent.tsx +1 -18
  10. package/src/components/compose/ComposeForm.tsx +11 -6
  11. package/src/components/compose/FullCompose.tsx +1 -1
  12. package/src/components/compose/MobileComposeSheet.tsx +1 -2
  13. package/src/components/compose/compose.stories.tsx +18 -0
  14. package/src/components/compose/draft-discard-failure.render.test.ts +138 -0
  15. package/src/components/config-import/config-import.fixtures.ts +8 -9
  16. package/src/components/config-import/config-import.stories.tsx +2 -2
  17. package/src/components/layout/MailTopBar.tsx +3 -0
  18. package/src/components/mail/BriefPane.tsx +0 -3
  19. package/src/components/mail/ConversationView.tsx +1 -3
  20. package/src/components/mail/DailyBrief.tsx +5 -28
  21. package/src/components/mail/FlaggedList.tsx +3 -4
  22. package/src/components/mail/FlaggedPane.tsx +0 -3
  23. package/src/components/mail/IntelligenceDrawer.tsx +0 -3
  24. package/src/components/mail/IntelligencePane.render.test.ts +0 -1
  25. package/src/components/mail/IntelligencePane.test.ts +1 -95
  26. package/src/components/mail/IntelligencePane.tsx +3 -93
  27. package/src/components/mail/MailListHeader.tsx +12 -43
  28. package/src/components/mail/MailNav.tsx +6 -2
  29. package/src/components/mail/MailViewChrome.tsx +1 -11
  30. package/src/components/mail/MailboxPane.tsx +4 -34
  31. package/src/components/mail/MessageActionMenu.tsx +6 -6
  32. package/src/components/mail/MessageAttachments.tsx +35 -0
  33. package/src/components/mail/MessageBody.tsx +1 -0
  34. package/src/components/mail/MessageBodyErrorBanner.test.ts +0 -30
  35. package/src/components/mail/MessageCard.tsx +16 -4
  36. package/src/components/mail/MessageList.run-reporting.test.ts +2 -46
  37. package/src/components/mail/MessageList.tsx +23 -11
  38. package/src/components/mail/MessageRow.render.test.ts +0 -7
  39. package/src/components/mail/MoveToTrigger.tsx +1 -1
  40. package/src/components/mail/RawMessageView.tsx +1 -1
  41. package/src/components/mail/SelectionWizardHost.search-entry.test.ts +0 -1
  42. package/src/components/mail/SelectionWizardHost.tsx +16 -102
  43. package/src/components/mail/authenticity-warning.stories.tsx +0 -21
  44. package/src/components/mail/daily-brief.stories.tsx +31 -17
  45. package/src/components/mail/enter-cursor-late-open.render.test.ts +197 -0
  46. package/src/components/mail/intelligence-drawer.stories.tsx +0 -2
  47. package/src/components/mail/message-gone.stories.tsx +44 -0
  48. package/src/components/mail/offer-times-reply.render.test.ts +25 -1
  49. package/src/components/mail/reclassify-remove-override.render.test.ts +2 -6
  50. package/src/components/overlay-escape.render.test.ts +7 -3
  51. package/src/components/settings/AccountFormPanel.render.test.ts +26 -3
  52. package/src/components/settings/AccountFormPanel.test.ts +1 -36
  53. package/src/components/settings/AccountFormPanel.tsx +5 -25
  54. package/src/components/settings/AccountServices.tsx +1 -1
  55. package/src/components/settings/AdvancedNavIcon.tsx +3 -3
  56. package/src/components/settings/CalendarFeedCard.tsx +2 -1
  57. package/src/components/settings/CalendarSubscribeCard.tsx +1 -1
  58. package/src/components/settings/CalendarSubscriptionStatus.tsx +1 -2
  59. package/src/components/settings/FilterActionError.tsx +1 -1
  60. package/src/components/settings/FilterEditor.render.test.ts +3 -27
  61. package/src/components/settings/FilterEditor.tsx +4 -24
  62. package/src/components/settings/FilterEditorSurface.tsx +0 -3
  63. package/src/components/settings/FiltersList.render.test.ts +5 -24
  64. package/src/components/settings/FiltersList.stories.tsx +0 -1
  65. package/src/components/settings/FiltersList.tsx +10 -35
  66. package/src/components/settings/WarningsNavIcon.tsx +12 -0
  67. package/src/components/settings/{QuarantinePanel.delimiter.test.ts → WarningsPanel.dom.test.ts} +64 -5
  68. package/src/components/settings/WarningsPanel.render.test.ts +56 -0
  69. package/src/components/settings/WarningsPanel.tsx +244 -0
  70. package/src/components/settings/accounts.stories.tsx +18 -0
  71. package/src/components/settings/advanced.stories.tsx +3 -125
  72. package/src/components/settings/settings-filter.stories.tsx +2 -42
  73. package/src/components/settings/warnings.stories.tsx +290 -0
  74. package/src/components/ui/ErrorBannerProvider.tsx +5 -3
  75. package/src/components/ui/error-banners.test.ts +1 -22
  76. package/src/components/ui/error-banners.ts +9 -20
  77. package/src/hooks/calendar/useCalendarCollectionWrites.ts +2 -1
  78. package/src/hooks/calendar/useCalendarSuggestions.ts +2 -1
  79. package/src/hooks/calendar/useCalendarWrites.ts +2 -1
  80. package/src/hooks/intelligence-calendar.render.test.ts +0 -1
  81. package/src/hooks/useDeleteMessages.test.ts +1 -42
  82. package/src/hooks/useFilters.test.ts +0 -8
  83. package/src/hooks/useFollowFocusOpen.render.test.ts +1 -11
  84. package/src/hooks/useIntelligenceData.test.ts +9 -47
  85. package/src/hooks/useIntelligenceData.ts +7 -100
  86. package/src/hooks/useLayoutTier.test.ts +0 -48
  87. package/src/hooks/useReportSpam.ts +1 -1
  88. package/src/hooks/useRescueCandidates.test.ts +0 -23
  89. package/src/hooks/useRulePreview.ts +8 -7
  90. package/src/hooks/useSearchTokenContext.ts +2 -3
  91. package/src/hooks/useStaleAccountSync.test.ts +0 -17
  92. package/src/hooks/useStarredThreads.test.ts +0 -10
  93. package/src/hooks/useTriggerSync.test.ts +0 -6
  94. package/src/hooks/useVisualViewport.render.test.ts +63 -0
  95. package/src/hooks/useWarningsAttention.ts +37 -0
  96. package/src/lib/account-order.test.ts +0 -10
  97. package/src/lib/adjacent-message.test.ts +0 -9
  98. package/src/lib/app-info.ts +0 -3
  99. package/src/lib/autodiscovery.test.ts +0 -47
  100. package/src/lib/bug-report.test.ts +38 -0
  101. package/src/lib/bug-report.ts +10 -7
  102. package/src/lib/bulk-action-copy.ts +1 -1
  103. package/src/lib/calendar-route.test.ts +0 -5
  104. package/src/lib/config-import.test.ts +3 -39
  105. package/src/lib/dedupe-thread-messages.test.ts +0 -4
  106. package/src/lib/desktop-breakpoint.test.ts +0 -8
  107. package/src/lib/drafts.test.ts +0 -23
  108. package/src/lib/fatal-error.test.ts +0 -19
  109. package/src/lib/folder-roles.test.ts +0 -4
  110. package/src/lib/hot-sync-window.test.ts +0 -5
  111. package/src/lib/list-header-chrome.ts +1 -1
  112. package/src/lib/mail-context.ts +1 -1
  113. package/src/lib/message-attachments.test.ts +77 -0
  114. package/src/lib/message-attachments.ts +31 -15
  115. package/src/lib/message-body-source.test.ts +21 -4
  116. package/src/lib/message-body-source.ts +5 -2
  117. package/src/lib/onboarding-completion.test.ts +0 -16
  118. package/src/lib/organize/filter-edit-model.test.ts +2 -32
  119. package/src/lib/organize/filter-edit-model.ts +6 -16
  120. package/src/lib/organize/organize-model.test.ts +7 -40
  121. package/src/lib/organize/organize-model.ts +16 -18
  122. package/src/lib/organize/rule-model.test.ts +24 -202
  123. package/src/lib/organize/rule-model.ts +19 -166
  124. package/src/lib/organize/search-to-rule.test.ts +2 -36
  125. package/src/lib/organize/search-to-rule.ts +1 -16
  126. package/src/lib/provider-presets.test.ts +1 -77
  127. package/src/lib/query-error-handler.test.ts +0 -61
  128. package/src/lib/query-escalation.integration.test.ts +4 -4
  129. package/src/lib/result-count.ts +1 -1
  130. package/src/lib/result-folder.ts +1 -1
  131. package/src/lib/rum-adapter.test.ts +0 -104
  132. package/src/lib/search-query.test.ts +0 -11
  133. package/src/lib/search-result.test.ts +2 -142
  134. package/src/lib/search-result.ts +3 -73
  135. package/src/lib/search-scope.test.ts +1 -92
  136. package/src/lib/search-scope.ts +0 -29
  137. package/src/lib/search-surface.ts +2 -2
  138. package/src/lib/search-token-index.test.ts +0 -8
  139. package/src/lib/search-tokens.ts +15 -16
  140. package/src/lib/starred-rows.test.ts +0 -4
  141. package/src/lib/sync-warning-entries.ts +33 -0
  142. package/src/lib/wizard-selection.ts +1 -1
  143. package/src/mocks/story-frame/mail-handlers.ts +12 -8
  144. package/src/mocks/story-frame/mail-world.ts +5 -0
  145. package/src/routeTree.gen.ts +21 -0
  146. package/src/routes/calendar/$view.$date/index.tsx +1 -1
  147. package/src/routes/calendar.tsx +2 -1
  148. package/src/routes/mail/brief.tsx +1 -1
  149. package/src/routes/mail/flagged.tsx +1 -1
  150. package/src/routes/mail.tsx +5 -3
  151. package/src/routes/settings/-accounts.test.ts +0 -5
  152. package/src/routes/settings/-filters.test.ts +0 -1
  153. package/src/routes/settings/-shared/help-copy.tsx +15 -0
  154. package/src/routes/settings/accounts.tsx +1 -1
  155. package/src/routes/settings/advanced.tsx +1 -4
  156. package/src/routes/settings/filters.tsx +1 -2
  157. package/src/routes/settings/folders.tsx +1 -1
  158. package/src/routes/settings/labels.tsx +1 -1
  159. package/src/routes/settings/senders.tsx +1 -1
  160. package/src/routes/settings/warnings.tsx +36 -0
  161. package/src/routes/settings.tsx +7 -0
  162. package/src/routing/wizard-history.test.ts +1 -45
  163. package/src/runtime-config.ts +5 -0
  164. package/src/test-support/fixtures.ts +1 -1
  165. package/src/components/compose/draft-discard-banner.test.ts +0 -76
  166. package/src/components/mail/ReadingPaneEmpty.render.test.ts +0 -42
  167. package/src/components/mail/SpamRescue.render.test.ts +0 -35
  168. package/src/components/mail/intelligence-semantic-off.render.test.ts +0 -111
  169. package/src/components/mail/selection-surface-rollcall.test.ts +0 -270
  170. package/src/components/settings/QuarantinePanel.render.test.ts +0 -71
  171. package/src/components/settings/QuarantinePanel.tsx +0 -178
  172. package/src/components/ui/DropdownMenu.tsx +0 -99
  173. package/src/components/ui/EmptyState.tsx +0 -11
  174. package/src/components/ui/ErrorBanner.render.test.ts +0 -104
  175. package/src/components/ui/ErrorBanner.stories.tsx +0 -132
  176. package/src/components/ui/ErrorBanner.tsx +0 -106
  177. package/src/components/ui/ErrorBannerStack.tsx +0 -35
  178. package/src/components/ui/ErrorState.render.test.ts +0 -82
  179. package/src/components/ui/ErrorState.tsx +0 -80
  180. package/src/components/ui/KeyboardShortcutsModal.tsx +0 -113
  181. package/src/hooks/useMoveMessages.test.ts +0 -63
  182. package/src/hooks/useOrganizeWiden.test.ts +0 -217
  183. package/src/hooks/useOrganizeWiden.ts +0 -112
  184. package/src/hooks/useSemanticSearch.ts +0 -132
  185. package/src/hooks/useSemanticSearchEnabled.ts +0 -23
  186. package/src/hooks/useVisualViewport.test.ts +0 -126
  187. package/src/lib/organize/sender-fallback.test.ts +0 -52
  188. package/src/lib/organize/sender-fallback.ts +0 -38
  189. package/src/lib/rescue-telemetry.test.ts +0 -75
  190. package/src/router.test.ts +0 -81
@@ -1,52 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { describe, it } from "node:test";
3
- import { buildSenderFallbackDraft } from "./sender-fallback";
4
-
5
- describe("buildSenderFallbackDraft", () => {
6
- it("combines the sender clauses with Or and carries no anchor", () => {
7
- const draft = buildSenderFallbackDraft(
8
- ["npm@github.com", "a@x.com"],
9
- ["Deploy failed", "Standup notes"],
10
- );
11
- assert.equal(draft.matchOperator, "Or");
12
- assert.equal(draft.anchorMessageId, undefined);
13
- assert.deepEqual(draft.literalClauses, [
14
- { field: "From", value: "npm@github.com" },
15
- { field: "From", value: "a@x.com" },
16
- ]);
17
- });
18
-
19
- it("carries a single FromDomain clause when the senders share a domain", () => {
20
- const draft = buildSenderFallbackDraft(
21
- ["npm@github.com", "ci@github.com"],
22
- ["Deploy failed", "Standup notes"],
23
- );
24
- assert.equal(draft.matchOperator, "Or");
25
- assert.deepEqual(draft.literalClauses, [
26
- { field: "FromDomain", value: "github.com" },
27
- ]);
28
- });
29
-
30
- it("falls back to a shared subject when mixed senders share no domain (#458)", () => {
31
- const draft = buildSenderFallbackDraft(
32
- ["billing@acme.test", "accounts@globex.test"],
33
- ["Invoice 1841", "Invoice 1902"],
34
- );
35
- assert.equal(draft.matchOperator, "Or");
36
- assert.deepEqual(draft.literalClauses, [
37
- { field: "Subject", value: "Invoice" },
38
- ]);
39
- });
40
-
41
- it("offers one sender chip each when mixed senders share no subject either", () => {
42
- const draft = buildSenderFallbackDraft(
43
- ["billing@acme.test", "accounts@globex.test"],
44
- ["Invoice 1841", "Standup notes"],
45
- );
46
- assert.equal(draft.matchOperator, "Or");
47
- assert.deepEqual(draft.literalClauses, [
48
- { field: "From", value: "billing@acme.test" },
49
- { field: "From", value: "accounts@globex.test" },
50
- ]);
51
- });
52
- });
@@ -1,38 +0,0 @@
1
- import type { RemitImapOrganizeInput } from "@remit/api-http-client/types.gen.ts";
2
- import { derivePropertyClauses } from "@remit/ui";
3
- import type { OrganizeDraft } from "./organize-model";
4
-
5
- /**
6
- * The widen fallback for a deployment that ships no vector pipeline (self-host
7
- * sqlite — semantic-capability.ts). The semantic anchor matches nothing there,
8
- * so a widen degrades to the literal vocabulary RFC 031 already matches
9
- * vector-free: what the whole selection agrees on, combined with `Or`, no
10
- * anchor (#458) — the same evidence order the properties door prefills from,
11
- * so a selection with nothing in common on sender still falls back to a
12
- * subject it shares rather than one `From` chip per message. The same
13
- * predicate matches at index time (RFC 034), so a standing filter built from
14
- * it keeps working on future mail.
15
- */
16
-
17
- /**
18
- * The literal predicate that stands in for the semantic anchor: the clauses
19
- * the selection agrees on, combined with `Or` and no anchor. The preview, the
20
- * one-time back-apply, and the standing filter all carry exactly this.
21
- */
22
- export const buildSenderFallbackDraft = (
23
- senders: readonly string[],
24
- subjects: readonly string[],
25
- ): OrganizeDraft => ({
26
- matchOperator: "Or",
27
- literalClauses: derivePropertyClauses(senders, subjects),
28
- });
29
-
30
- /**
31
- * The predicate the widen previewed, handed to the organize sentence so the set
32
- * it previews equals the set every commit scope acts on. Either the semantic
33
- * anchor or the sender-derived literal fallback, never both.
34
- */
35
- export type OrganizeMatchPredicate = Pick<
36
- RemitImapOrganizeInput,
37
- "anchorMessageId" | "matchOperator" | "literalClauses"
38
- >;
@@ -1,75 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { describe, it } from "node:test";
3
- import type { Telemetry } from "@/lib/telemetry";
4
- import {
5
- recordRescueCandidatesSurfaced,
6
- recordRescueCommitted,
7
- recordRescueFlowOpened,
8
- recordRescueSentToJunk,
9
- } from "./rescue-telemetry";
10
-
11
- interface RecordedEvent {
12
- name: string;
13
- attributes?: Record<string, string>;
14
- }
15
-
16
- function stubTelemetry(): { telemetry: Telemetry; events: RecordedEvent[] } {
17
- const events: RecordedEvent[] = [];
18
- const telemetry: Telemetry = {
19
- recordPageView: () => undefined,
20
- recordError: () => undefined,
21
- recordTiming: () => undefined,
22
- recordEvent: (name, attributes) => events.push({ name, attributes }),
23
- };
24
- return { telemetry, events };
25
- }
26
-
27
- describe("rescue telemetry", () => {
28
- it("emits candidates surfaced with the count", () => {
29
- const { telemetry, events } = stubTelemetry();
30
- recordRescueCandidatesSurfaced(telemetry, 7);
31
- assert.deepEqual(events, [
32
- { name: "rescue.candidates_surfaced", attributes: { count: "7" } },
33
- ]);
34
- });
35
-
36
- it("emits flow opened with the count", () => {
37
- const { telemetry, events } = stubTelemetry();
38
- recordRescueFlowOpened(telemetry, 3);
39
- assert.deepEqual(events, [
40
- { name: "rescue.flow_opened", attributes: { count: "3" } },
41
- ]);
42
- });
43
-
44
- it("emits committed with selected vs total and an inbox destination", () => {
45
- const { telemetry, events } = stubTelemetry();
46
- recordRescueCommitted(telemetry, { selected: 2, total: 5, toInbox: true });
47
- assert.deepEqual(events, [
48
- {
49
- name: "rescue.committed",
50
- attributes: { selected: "2", total: "5", destination: "inbox" },
51
- },
52
- ]);
53
- });
54
-
55
- it("marks a non-inbox destination as other", () => {
56
- const { telemetry, events } = stubTelemetry();
57
- recordRescueCommitted(telemetry, { selected: 1, total: 1, toInbox: false });
58
- assert.equal(events[0]?.attributes?.destination, "other");
59
- });
60
-
61
- it("emits the reverse signal with trust and rescuable flag", () => {
62
- const { telemetry, events } = stubTelemetry();
63
- recordRescueSentToJunk(telemetry, {
64
- count: 1,
65
- senderTrust: "vip",
66
- wasRescuable: true,
67
- });
68
- assert.deepEqual(events, [
69
- {
70
- name: "rescue.sent_to_junk",
71
- attributes: { count: "1", senderTrust: "vip", wasRescuable: "true" },
72
- },
73
- ]);
74
- });
75
- });
@@ -1,81 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { describe, it } from "node:test";
3
- import type { Telemetry } from "./lib/telemetry";
4
-
5
- interface SubscribeCall {
6
- eventType: string;
7
- fn: (event: { toLocation: { pathname: string } }) => void;
8
- }
9
-
10
- interface RouterStub {
11
- subscribeCalls: SubscribeCall[];
12
- subscribe(
13
- eventType: string,
14
- fn: (event: { toLocation: { pathname: string } }) => void,
15
- ): () => void;
16
- }
17
-
18
- function makeRouterStub(): RouterStub {
19
- const stub: RouterStub = {
20
- subscribeCalls: [],
21
- subscribe(eventType, fn) {
22
- stub.subscribeCalls.push({ eventType, fn });
23
- return () => undefined;
24
- },
25
- };
26
- return stub;
27
- }
28
-
29
- function makeTelemetryStub(): Telemetry & { pageViews: string[] } {
30
- const stub = {
31
- pageViews: [] as string[],
32
- recordPageView(path: string) {
33
- stub.pageViews.push(path);
34
- },
35
- recordError: () => undefined,
36
- recordEvent: () => undefined,
37
- recordTiming: () => undefined,
38
- };
39
- return stub;
40
- }
41
-
42
- function wirePageViews(router: RouterStub, telemetry: Telemetry): void {
43
- router.subscribe("onResolved", (event) => {
44
- telemetry.recordPageView(event.toLocation.pathname);
45
- });
46
- }
47
-
48
- describe("router page-view telemetry wiring", () => {
49
- it("subscribes to onResolved", () => {
50
- const router = makeRouterStub();
51
- const telemetry = makeTelemetryStub();
52
- wirePageViews(router, telemetry);
53
- assert.equal(router.subscribeCalls.length, 1);
54
- assert.equal(router.subscribeCalls[0]?.eventType, "onResolved");
55
- });
56
-
57
- it("calls recordPageView with pathname on each navigation", () => {
58
- const router = makeRouterStub();
59
- const telemetry = makeTelemetryStub();
60
- wirePageViews(router, telemetry);
61
-
62
- const handler = router.subscribeCalls[0]?.fn;
63
- assert.ok(handler, "Expected a subscribe handler");
64
-
65
- handler({ toLocation: { pathname: "/mail" } });
66
- handler({ toLocation: { pathname: "/settings" } });
67
-
68
- assert.deepEqual(telemetry.pageViews, ["/mail", "/settings"]);
69
- });
70
-
71
- it("is a no-op when telemetry is noopTelemetry (does not throw)", () => {
72
- const router = makeRouterStub();
73
- const telemetry = makeTelemetryStub();
74
- telemetry.recordPageView = () => undefined;
75
- wirePageViews(router, telemetry);
76
-
77
- const handler = router.subscribeCalls[0]?.fn;
78
- assert.ok(handler);
79
- assert.doesNotThrow(() => handler({ toLocation: { pathname: "/mail" } }));
80
- });
81
- });