@rapidmx/web-client 0.9.0 → 0.10.0

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 (226) hide show
  1. package/README.md +192 -88
  2. package/apps/shared/auth/accountUrl.ts +9 -0
  3. package/apps/shared/auth/adminAccess.ts +99 -0
  4. package/apps/shared/components/admin/settings/DomainDnsSetup.tsx +2 -1
  5. package/apps/shared/components/calendar/layout/CalendarShell.tsx +8 -2
  6. package/apps/shared/components/contacts/ContactsToolbar.tsx +125 -115
  7. package/apps/shared/components/contacts/layout/ContactsShell.tsx +15 -3
  8. package/apps/shared/components/layout/AppShell.tsx +411 -288
  9. package/apps/shared/components/layout/KeyEnrollmentGate.tsx +398 -389
  10. package/apps/shared/components/layout/MailboxProvisioning.tsx +168 -164
  11. package/apps/shared/components/layout/UserMenu.tsx +303 -185
  12. package/apps/shared/components/mail/ConversationList.tsx +269 -262
  13. package/apps/shared/components/mail/ConversationThreadPane.tsx +475 -434
  14. package/apps/shared/components/mail/LazyReadingPane.tsx +115 -0
  15. package/apps/shared/components/mail/MailSelectionBar.tsx +235 -222
  16. package/apps/shared/components/mail/MessageDetailPane.tsx +1458 -1387
  17. package/apps/shared/components/mail/NewMailToasts.tsx +141 -0
  18. package/apps/shared/components/mail/compose/ComposeContext.tsx +279 -160
  19. package/apps/shared/components/mail/compose/ComposeToolbar.tsx +422 -418
  20. package/apps/shared/components/mail/compose/ComposeWindow.tsx +1842 -1736
  21. package/apps/shared/components/mail/compose/ComposeWindowPlaceholder.tsx +111 -0
  22. package/apps/shared/components/mail/compose/RichTextEditor.tsx +147 -118
  23. package/apps/shared/components/mail/compose/composePerf.ts +46 -0
  24. package/apps/shared/components/mail/compose/quotedBody.ts +161 -100
  25. package/apps/shared/components/mail/layout/MailShell.tsx +476 -472
  26. package/apps/shared/components/mail/unreadStyle.tsx +75 -0
  27. package/apps/shared/components/settings/layout/SettingsShell.tsx +252 -240
  28. package/apps/shared/components/tasks/layout/TasksShell.tsx +15 -3
  29. package/apps/shared/keyboard/GlobalShortcuts.tsx +51 -0
  30. package/apps/shared/keyboard/ShortcutProvider.tsx +62 -0
  31. package/apps/shared/keyboard/ShortcutsDialog.tsx +84 -0
  32. package/apps/shared/keyboard/dispatch.ts +124 -0
  33. package/apps/shared/keyboard/format.ts +89 -0
  34. package/apps/shared/keyboard/keymap.ts +114 -0
  35. package/apps/shared/keyboard/match.ts +44 -0
  36. package/apps/shared/keyboard/parse.ts +136 -0
  37. package/apps/shared/keyboard/platform.ts +34 -0
  38. package/apps/shared/keyboard/registry.ts +65 -0
  39. package/apps/shared/keyboard/targets.ts +79 -0
  40. package/apps/shared/keyboard/useShortcut.ts +50 -0
  41. package/apps/shared/keyboard/useShortcutProps.ts +17 -0
  42. package/apps/shared/mail/folderCounts.ts +302 -0
  43. package/apps/shared/mail/listSnapshots.ts +87 -0
  44. package/apps/shared/mail/messageReadState.ts +85 -0
  45. package/apps/shared/mail/newMailNotifications.ts +183 -0
  46. package/apps/shared/mail/useMailConnection.ts +150 -0
  47. package/apps/shared/mail/useMailLiveUpdates.ts +232 -224
  48. package/apps/shared/mail/useMarkMessageRead.ts +47 -0
  49. package/apps/shared/mail/useNewMailNotifications.ts +163 -0
  50. package/apps/shared/mail/useUnreadTitle.ts +42 -0
  51. package/apps/shared/navigation/AppRouter.tsx +300 -0
  52. package/apps/shared/navigation/appHrefs.ts +23 -0
  53. package/apps/shared/navigation/frameContext.tsx +35 -0
  54. package/apps/shared/navigation/idle.ts +45 -0
  55. package/apps/shared/navigation/routerContext.tsx +83 -0
  56. package/apps/shared/navigation/routes.ts +72 -0
  57. package/apps/shared/search/LocalIndexLifecycle.tsx +114 -98
  58. package/apps/shared/styles/app.css +28 -10
  59. package/apps/www/_routedPage.tsx +24 -0
  60. package/apps/www/_routes.ts +35 -0
  61. package/apps/www/calendar/index.tsx +55 -19
  62. package/apps/www/contacts/[uid].tsx +112 -107
  63. package/apps/www/contacts/index.tsx +584 -567
  64. package/apps/www/index.tsx +2280 -1917
  65. package/apps/www/messages/[uid].tsx +106 -101
  66. package/apps/www/settings/auto-reply/index.tsx +4 -1
  67. package/apps/www/settings/encryption/index.tsx +1252 -1249
  68. package/apps/www/settings/filters/[uid].tsx +4 -1
  69. package/apps/www/settings/filters/index.tsx +102 -99
  70. package/apps/www/settings/filters/new/index.tsx +138 -133
  71. package/apps/www/settings/labels/index.tsx +204 -201
  72. package/apps/www/settings/privacy/index.tsx +4 -1
  73. package/apps/www/settings/read-receipts/index.tsx +4 -1
  74. package/apps/www/settings/sharing/index.tsx +277 -274
  75. package/apps/www/settings/signatures/[uid].tsx +170 -167
  76. package/apps/www/settings/signatures/index.tsx +88 -85
  77. package/apps/www/settings/signatures/new/index.tsx +134 -129
  78. package/apps/www/tasks/index.tsx +18 -2
  79. package/dist/apps/shared/auth/accountUrl.d.ts +2 -0
  80. package/dist/apps/shared/auth/accountUrl.js +8 -0
  81. package/dist/apps/shared/auth/adminAccess.d.ts +30 -0
  82. package/dist/apps/shared/auth/adminAccess.js +89 -0
  83. package/dist/apps/shared/components/admin/settings/DomainDnsSetup.js +1 -1
  84. package/dist/apps/shared/components/calendar/layout/CalendarShell.d.ts +1 -1
  85. package/dist/apps/shared/components/calendar/layout/CalendarShell.js +8 -4
  86. package/dist/apps/shared/components/contacts/ContactsToolbar.d.ts +3 -1
  87. package/dist/apps/shared/components/contacts/ContactsToolbar.js +7 -4
  88. package/dist/apps/shared/components/contacts/layout/ContactsShell.d.ts +1 -1
  89. package/dist/apps/shared/components/contacts/layout/ContactsShell.js +15 -5
  90. package/dist/apps/shared/components/layout/AppShell.d.ts +29 -4
  91. package/dist/apps/shared/components/layout/AppShell.js +73 -14
  92. package/dist/apps/shared/components/layout/KeyEnrollmentGate.js +10 -9
  93. package/dist/apps/shared/components/layout/MailboxProvisioning.js +3 -1
  94. package/dist/apps/shared/components/layout/UserMenu.d.ts +19 -3
  95. package/dist/apps/shared/components/layout/UserMenu.js +52 -5
  96. package/dist/apps/shared/components/mail/ConversationList.js +4 -11
  97. package/dist/apps/shared/components/mail/ConversationThreadPane.d.ts +4 -1
  98. package/dist/apps/shared/components/mail/ConversationThreadPane.js +52 -27
  99. package/dist/apps/shared/components/mail/LazyReadingPane.d.ts +9 -0
  100. package/dist/apps/shared/components/mail/LazyReadingPane.js +82 -0
  101. package/dist/apps/shared/components/mail/MailSelectionBar.d.ts +4 -1
  102. package/dist/apps/shared/components/mail/MailSelectionBar.js +11 -3
  103. package/dist/apps/shared/components/mail/MessageDetailPane.d.ts +7 -1
  104. package/dist/apps/shared/components/mail/MessageDetailPane.js +83 -34
  105. package/dist/apps/shared/components/mail/NewMailToasts.d.ts +19 -0
  106. package/dist/apps/shared/components/mail/NewMailToasts.js +55 -0
  107. package/dist/apps/shared/components/mail/compose/ComposeContext.d.ts +30 -0
  108. package/dist/apps/shared/components/mail/compose/ComposeContext.js +71 -5
  109. package/dist/apps/shared/components/mail/compose/ComposeToolbar.js +4 -3
  110. package/dist/apps/shared/components/mail/compose/ComposeWindow.d.ts +1 -1
  111. package/dist/apps/shared/components/mail/compose/ComposeWindow.js +98 -22
  112. package/dist/apps/shared/components/mail/compose/ComposeWindowPlaceholder.d.ts +19 -0
  113. package/dist/apps/shared/components/mail/compose/ComposeWindowPlaceholder.js +33 -0
  114. package/dist/apps/shared/components/mail/compose/RichTextEditor.d.ts +9 -1
  115. package/dist/apps/shared/components/mail/compose/RichTextEditor.js +20 -2
  116. package/dist/apps/shared/components/mail/compose/composePerf.d.ts +16 -0
  117. package/dist/apps/shared/components/mail/compose/composePerf.js +41 -0
  118. package/dist/apps/shared/components/mail/compose/quotedBody.d.ts +13 -0
  119. package/dist/apps/shared/components/mail/compose/quotedBody.js +67 -11
  120. package/dist/apps/shared/components/mail/layout/MailShell.d.ts +12 -4
  121. package/dist/apps/shared/components/mail/layout/MailShell.js +76 -96
  122. package/dist/apps/shared/components/mail/unreadStyle.d.ts +45 -0
  123. package/dist/apps/shared/components/mail/unreadStyle.js +61 -0
  124. package/dist/apps/shared/components/settings/layout/SettingsShell.d.ts +1 -1
  125. package/dist/apps/shared/components/settings/layout/SettingsShell.js +15 -5
  126. package/dist/apps/shared/components/tasks/layout/TasksShell.d.ts +1 -1
  127. package/dist/apps/shared/components/tasks/layout/TasksShell.js +15 -5
  128. package/dist/apps/shared/keyboard/GlobalShortcuts.d.ts +14 -0
  129. package/dist/apps/shared/keyboard/GlobalShortcuts.js +37 -0
  130. package/dist/apps/shared/keyboard/ShortcutProvider.d.ts +20 -0
  131. package/dist/apps/shared/keyboard/ShortcutProvider.js +52 -0
  132. package/dist/apps/shared/keyboard/ShortcutsDialog.d.ts +14 -0
  133. package/dist/apps/shared/keyboard/ShortcutsDialog.js +42 -0
  134. package/dist/apps/shared/keyboard/dispatch.d.ts +16 -0
  135. package/dist/apps/shared/keyboard/dispatch.js +109 -0
  136. package/dist/apps/shared/keyboard/format.d.ts +12 -0
  137. package/dist/apps/shared/keyboard/format.js +74 -0
  138. package/dist/apps/shared/keyboard/keymap.d.ts +294 -0
  139. package/dist/apps/shared/keyboard/keymap.js +84 -0
  140. package/dist/apps/shared/keyboard/match.d.ts +20 -0
  141. package/dist/apps/shared/keyboard/match.js +31 -0
  142. package/dist/apps/shared/keyboard/parse.d.ts +31 -0
  143. package/dist/apps/shared/keyboard/parse.js +107 -0
  144. package/dist/apps/shared/keyboard/platform.d.ts +15 -0
  145. package/dist/apps/shared/keyboard/platform.js +22 -0
  146. package/dist/apps/shared/keyboard/registry.d.ts +39 -0
  147. package/dist/apps/shared/keyboard/registry.js +33 -0
  148. package/dist/apps/shared/keyboard/targets.d.ts +14 -0
  149. package/dist/apps/shared/keyboard/targets.js +67 -0
  150. package/dist/apps/shared/keyboard/useShortcut.d.ts +19 -0
  151. package/dist/apps/shared/keyboard/useShortcut.js +35 -0
  152. package/dist/apps/shared/keyboard/useShortcutProps.d.ts +10 -0
  153. package/dist/apps/shared/keyboard/useShortcutProps.js +15 -0
  154. package/dist/apps/shared/mail/folderCounts.d.ts +78 -0
  155. package/dist/apps/shared/mail/folderCounts.js +212 -0
  156. package/dist/apps/shared/mail/listSnapshots.d.ts +46 -0
  157. package/dist/apps/shared/mail/listSnapshots.js +43 -0
  158. package/dist/apps/shared/mail/messageReadState.d.ts +32 -0
  159. package/dist/apps/shared/mail/messageReadState.js +63 -0
  160. package/dist/apps/shared/mail/newMailNotifications.d.ts +62 -0
  161. package/dist/apps/shared/mail/newMailNotifications.js +138 -0
  162. package/dist/apps/shared/mail/useMailConnection.d.ts +55 -0
  163. package/dist/apps/shared/mail/useMailConnection.js +96 -0
  164. package/dist/apps/shared/mail/useMailLiveUpdates.d.ts +13 -6
  165. package/dist/apps/shared/mail/useMailLiveUpdates.js +28 -26
  166. package/dist/apps/shared/mail/useMarkMessageRead.d.ts +12 -0
  167. package/dist/apps/shared/mail/useMarkMessageRead.js +44 -0
  168. package/dist/apps/shared/mail/useNewMailNotifications.d.ts +41 -0
  169. package/dist/apps/shared/mail/useNewMailNotifications.js +110 -0
  170. package/dist/apps/shared/mail/useUnreadTitle.d.ts +18 -0
  171. package/dist/apps/shared/mail/useUnreadTitle.js +31 -0
  172. package/dist/apps/shared/navigation/AppRouter.d.ts +52 -0
  173. package/dist/apps/shared/navigation/AppRouter.js +242 -0
  174. package/dist/apps/shared/navigation/appHrefs.d.ts +14 -0
  175. package/dist/apps/shared/navigation/appHrefs.js +20 -0
  176. package/dist/apps/shared/navigation/frameContext.d.ts +19 -0
  177. package/dist/apps/shared/navigation/frameContext.js +24 -0
  178. package/dist/apps/shared/navigation/idle.d.ts +14 -0
  179. package/dist/apps/shared/navigation/idle.js +43 -0
  180. package/dist/apps/shared/navigation/routerContext.d.ts +37 -0
  181. package/dist/apps/shared/navigation/routerContext.js +56 -0
  182. package/dist/apps/shared/navigation/routes.d.ts +32 -0
  183. package/dist/apps/shared/navigation/routes.js +37 -0
  184. package/dist/apps/shared/search/LocalIndexLifecycle.js +17 -3
  185. package/dist/apps/shared/styles/app.css +28 -10
  186. package/dist/apps/www/_routedPage.d.ts +12 -0
  187. package/dist/apps/www/_routedPage.js +19 -0
  188. package/dist/apps/www/_routes.d.ts +11 -0
  189. package/dist/apps/www/_routes.js +29 -0
  190. package/dist/apps/www/calendar/index.d.ts +2 -2
  191. package/dist/apps/www/calendar/index.js +39 -8
  192. package/dist/apps/www/contacts/[uid].d.ts +3 -9
  193. package/dist/apps/www/contacts/[uid].js +6 -2
  194. package/dist/apps/www/contacts/index.d.ts +2 -2
  195. package/dist/apps/www/contacts/index.js +17 -4
  196. package/dist/apps/www/index.d.ts +2 -2
  197. package/dist/apps/www/index.js +301 -35
  198. package/dist/apps/www/messages/[uid].d.ts +3 -7
  199. package/dist/apps/www/messages/[uid].js +7 -4
  200. package/dist/apps/www/settings/auto-reply/index.d.ts +2 -2
  201. package/dist/apps/www/settings/auto-reply/index.js +3 -1
  202. package/dist/apps/www/settings/encryption/index.d.ts +2 -2
  203. package/dist/apps/www/settings/encryption/index.js +3 -1
  204. package/dist/apps/www/settings/filters/[uid].d.ts +2 -2
  205. package/dist/apps/www/settings/filters/[uid].js +3 -1
  206. package/dist/apps/www/settings/filters/index.d.ts +2 -2
  207. package/dist/apps/www/settings/filters/index.js +3 -1
  208. package/dist/apps/www/settings/filters/new/index.d.ts +2 -2
  209. package/dist/apps/www/settings/filters/new/index.js +6 -2
  210. package/dist/apps/www/settings/labels/index.d.ts +2 -2
  211. package/dist/apps/www/settings/labels/index.js +3 -1
  212. package/dist/apps/www/settings/privacy/index.d.ts +2 -2
  213. package/dist/apps/www/settings/privacy/index.js +3 -1
  214. package/dist/apps/www/settings/read-receipts/index.d.ts +2 -2
  215. package/dist/apps/www/settings/read-receipts/index.js +3 -1
  216. package/dist/apps/www/settings/sharing/index.d.ts +2 -2
  217. package/dist/apps/www/settings/sharing/index.js +3 -1
  218. package/dist/apps/www/settings/signatures/[uid].d.ts +2 -2
  219. package/dist/apps/www/settings/signatures/[uid].js +3 -1
  220. package/dist/apps/www/settings/signatures/index.d.ts +2 -2
  221. package/dist/apps/www/settings/signatures/index.js +3 -1
  222. package/dist/apps/www/settings/signatures/new/index.d.ts +2 -2
  223. package/dist/apps/www/settings/signatures/new/index.js +6 -2
  224. package/dist/apps/www/tasks/index.d.ts +2 -2
  225. package/dist/apps/www/tasks/index.js +15 -3
  226. package/package.json +2 -2
@@ -1,101 +1,106 @@
1
- ///////////////////////////////////////////////////////////////////////////////
2
- // Copyright (C) 2026 Jean-Philippe Steinmetz
3
- // SPDX-License-Identifier: MPL-2.0
4
- ///////////////////////////////////////////////////////////////////////////////
5
- import React, { useEffect, useState } from "react";
6
- import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
7
- import { Message, getMessage } from "@rapidmx/react-shared/mail/mailApi.js";
8
- import { Label, listLabels } from "@rapidmx/react-shared/mail/labelsApi.js";
9
- import { useMarkMessageRead, useMessageAttachments } from "@rapidmx/react-shared/mail/mailDetailHooks.js";
10
- import MailShell, { MailShellProps, useMailShell } from "../../shared/components/mail/layout/MailShell.js";
11
- import MessageDetailPane from "../../shared/components/mail/MessageDetailPane.js";
12
- import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
13
-
14
- /**
15
- * Only reached on mobile (below the `md` breakpoint) — desktop's `apps/www/index.tsx` keeps its existing
16
- * inline reading pane and never navigates here; see that file's `handleSelect`.
17
- */
18
- export default function MessageDetailPage(props: MailShellProps & { params: { uid: string } }) {
19
- return (
20
- <MailShell {...props}>
21
- <MessageDetailContent uid={props.params.uid} />
22
- </MailShell>
23
- );
24
- }
25
-
26
- function MessageDetailContent({ uid }: { uid: string }) {
27
- const { mailboxFolders, mailboxUid, onFolderCreated } = useMailShell();
28
- const [message, setMessage] = useState<Message | null>(null);
29
- const [loading, setLoading] = useState(true);
30
- const [error, setError] = useState<string | null>(null);
31
- const [labels, setLabels] = useState<Label[]>([]);
32
-
33
- useEffect(() => {
34
- setLoading(true);
35
- setError(null);
36
- getMessage(uid)
37
- .then(setMessage)
38
- .catch((err) => setError(err instanceof ApiRequestError ? err.message : "Could not load this message."))
39
- .finally(() => setLoading(false));
40
- }, [uid]);
41
-
42
- // See `apps/www/index.tsx`'s identical effect's own doc comment - a failure here just hides the
43
- // Labels control rather than blocking the rest of the page. Keyed on the message's own mailbox, not the
44
- // shell's ambient one: this route is reached from aggregate/search rows too, where they can differ.
45
- const messageMailboxUid = message?.mailboxUid;
46
- useEffect(() => {
47
- if (!messageMailboxUid) {
48
- return;
49
- }
50
- let cancelled = false;
51
- listLabels(messageMailboxUid, { limit: 200 })
52
- .then((result) => {
53
- if (!cancelled) {
54
- setLabels(result);
55
- }
56
- })
57
- .catch(() => undefined);
58
- return () => {
59
- cancelled = true;
60
- };
61
- }, [messageMailboxUid]);
62
-
63
- const attachments = useMessageAttachments(message);
64
- useMarkMessageRead(message, setMessage);
65
-
66
- if (loading) {
67
- return <p className="p-8 text-sm text-text-muted">Loading&hellip;</p>;
68
- }
69
- if (error || !message) {
70
- return <Alert>{error ?? "Message not found."}</Alert>;
71
- }
72
-
73
- // Looked up by the message's own mailboxUid, not the shell's ambient `mailboxUid` - the mobile detail
74
- // route reaches here from an aggregate ("All Inboxes" etc.) list row too, where there's no single
75
- // selected mailbox to fall back to.
76
- const folders = mailboxFolders.find((mf) => mf.mailbox.uid === message.mailboxUid)?.folders ?? [];
77
- const backHref = `/?mailboxUid=${encodeURIComponent(message.mailboxUid)}&folderUid=${encodeURIComponent(message.folderUid)}`;
78
- const isSentItems = folders.find((f) => f.uid === message.folderUid)?.type === "sent_items";
79
- const isOutbox = folders.find((f) => f.uid === message.folderUid)?.type === "outbox";
80
- const draftsFolderUid = folders.find((f) => f.type === "drafts")?.uid;
81
- return (
82
- <MessageDetailPane
83
- message={message}
84
- attachments={attachments}
85
- backHref={backHref}
86
- isSentItems={isSentItems}
87
- onRecalled={setMessage}
88
- isOutbox={isOutbox}
89
- onReceiptHandled={setMessage}
90
- draftsFolderUid={draftsFolderUid}
91
- folders={folders}
92
- onMoved={setMessage}
93
- onFolderCreated={onFolderCreated}
94
- onScheduledSendCanceled={setMessage}
95
- onArchived={setMessage}
96
- labels={labels}
97
- onLabelsChanged={setMessage}
98
- onLabelCreated={(label) => setLabels((prev) => [...prev, label])}
99
- />
100
- );
101
- }
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ import { routedPage } from "../_routedPage.js";
6
+ import React, { useEffect, useState } from "react";
7
+ import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
8
+ import { Message, getMessage } from "@rapidmx/react-shared/mail/mailApi.js";
9
+ import { Label, listLabels } from "@rapidmx/react-shared/mail/labelsApi.js";
10
+ import { useMessageAttachments } from "@rapidmx/react-shared/mail/mailDetailHooks.js";
11
+ import { useMarkMessageRead } from "../../shared/mail/useMarkMessageRead.js";
12
+ import MailShell, { MailShellProps, useMailShell } from "../../shared/components/mail/layout/MailShell.js";
13
+ import { LazyMessageDetailPane } from "../../shared/components/mail/LazyReadingPane.js";
14
+ import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
15
+
16
+ /**
17
+ * Only reached on mobile (below the `md` breakpoint) — desktop's `apps/www/index.tsx` keeps its existing
18
+ * inline reading pane and never navigates here; see that file's `handleSelect`.
19
+ */
20
+ function MessageDetailPage(props: MailShellProps & { params: { uid: string } }) {
21
+ return (
22
+ <MailShell {...props}>
23
+ <MessageDetailContent uid={props.params.uid} />
24
+ </MailShell>
25
+ );
26
+ }
27
+
28
+ function MessageDetailContent({ uid }: { uid: string }) {
29
+ const { mailboxFolders, mailboxUid, onFolderCreated } = useMailShell();
30
+ const [message, setMessage] = useState<Message | null>(null);
31
+ const [loading, setLoading] = useState(true);
32
+ const [error, setError] = useState<string | null>(null);
33
+ const [labels, setLabels] = useState<Label[]>([]);
34
+
35
+ useEffect(() => {
36
+ setLoading(true);
37
+ setError(null);
38
+ getMessage(uid)
39
+ .then(setMessage)
40
+ .catch((err) => setError(err instanceof ApiRequestError ? err.message : "Could not load this message."))
41
+ .finally(() => setLoading(false));
42
+ }, [uid]);
43
+
44
+ // See `apps/www/index.tsx`'s identical effect's own doc comment - a failure here just hides the
45
+ // Labels control rather than blocking the rest of the page. Keyed on the message's own mailbox, not the
46
+ // shell's ambient one: this route is reached from aggregate/search rows too, where they can differ.
47
+ const messageMailboxUid = message?.mailboxUid;
48
+ useEffect(() => {
49
+ if (!messageMailboxUid) {
50
+ return;
51
+ }
52
+ let cancelled = false;
53
+ listLabels(messageMailboxUid, { limit: 200 })
54
+ .then((result) => {
55
+ if (!cancelled) {
56
+ setLabels(result);
57
+ }
58
+ })
59
+ .catch(() => undefined);
60
+ return () => {
61
+ cancelled = true;
62
+ };
63
+ }, [messageMailboxUid]);
64
+
65
+ const attachments = useMessageAttachments(message);
66
+ useMarkMessageRead(message, setMessage);
67
+
68
+ if (loading) {
69
+ return <p className="p-8 text-sm text-text-muted">Loading&hellip;</p>;
70
+ }
71
+ if (error || !message) {
72
+ return <Alert>{error ?? "Message not found."}</Alert>;
73
+ }
74
+
75
+ // Looked up by the message's own mailboxUid, not the shell's ambient `mailboxUid` - the mobile detail
76
+ // route reaches here from an aggregate ("All Inboxes" etc.) list row too, where there's no single
77
+ // selected mailbox to fall back to.
78
+ const folders = mailboxFolders.find((mf) => mf.mailbox.uid === message.mailboxUid)?.folders ?? [];
79
+ const backHref = `/?mailboxUid=${encodeURIComponent(message.mailboxUid)}&folderUid=${encodeURIComponent(message.folderUid)}`;
80
+ const isSentItems = folders.find((f) => f.uid === message.folderUid)?.type === "sent_items";
81
+ const isOutbox = folders.find((f) => f.uid === message.folderUid)?.type === "outbox";
82
+ const draftsFolderUid = folders.find((f) => f.type === "drafts")?.uid;
83
+ return (
84
+ <LazyMessageDetailPane
85
+ shortcuts
86
+ message={message}
87
+ attachments={attachments}
88
+ backHref={backHref}
89
+ isSentItems={isSentItems}
90
+ onRecalled={setMessage}
91
+ isOutbox={isOutbox}
92
+ onReceiptHandled={setMessage}
93
+ draftsFolderUid={draftsFolderUid}
94
+ folders={folders}
95
+ onMoved={setMessage}
96
+ onFolderCreated={onFolderCreated}
97
+ onScheduledSendCanceled={setMessage}
98
+ onArchived={setMessage}
99
+ labels={labels}
100
+ onLabelsChanged={setMessage}
101
+ onLabelCreated={(label) => setLabels((prev) => [...prev, label])}
102
+ />
103
+ );
104
+ }
105
+
106
+ export default routedPage("/messages/:uid", MessageDetailPage);
@@ -2,6 +2,7 @@
2
2
  // Copyright (C) 2026 Jean-Philippe Steinmetz
3
3
  // SPDX-License-Identifier: MPL-2.0
4
4
  ///////////////////////////////////////////////////////////////////////////////
5
+ import { routedPage } from "../../_routedPage.js";
5
6
  import React, { FormEvent, useState } from "react";
6
7
  import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
7
8
  import { toDatetimeLocal } from "@rapidmx/react-shared/util/dateInput.js";
@@ -15,7 +16,7 @@ const INPUT_CLASS =
15
16
 
16
17
  export type SettingsAutoReplyPageProps = Omit<SettingsShellProps, "active">;
17
18
 
18
- export default function SettingsAutoReplyPage(props: SettingsAutoReplyPageProps) {
19
+ function SettingsAutoReplyPage(props: SettingsAutoReplyPageProps) {
19
20
  return (
20
21
  <SettingsShell {...props} active="auto-reply">
21
22
  <AutoReplyContent />
@@ -132,3 +133,5 @@ function AutoReplyContent() {
132
133
  </div>
133
134
  );
134
135
  }
136
+
137
+ export default routedPage("/settings/auto-reply", SettingsAutoReplyPage);