@rapidmx/web-client 0.9.0 → 0.11.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 (443) hide show
  1. package/README.md +373 -88
  2. package/apps/admin/_layout.tsx +8 -2
  3. package/apps/admin/index.tsx +7 -1
  4. package/apps/admin/ingest-queue/index.tsx +2 -1
  5. package/apps/admin/mailboxes/[uid].tsx +23 -10
  6. package/apps/admin/quarantine/index.tsx +2 -1
  7. package/apps/admin/signing-certificates/index.tsx +343 -0
  8. package/apps/escrow/_layout.tsx +8 -2
  9. package/apps/shared/appearance/AppearanceForm.tsx +392 -0
  10. package/apps/shared/appearance/AppearanceHead.tsx +49 -0
  11. package/apps/shared/appearance/AppearanceProvider.tsx +470 -0
  12. package/apps/shared/appearance/ColorField.tsx +107 -0
  13. package/apps/shared/appearance/appearanceCache.ts +106 -0
  14. package/apps/shared/appearance/appearanceContext.ts +77 -0
  15. package/apps/shared/appearance/bootScript.ts +35 -0
  16. package/apps/shared/appearance/color.ts +169 -0
  17. package/apps/shared/appearance/photo.ts +88 -0
  18. package/apps/shared/appearance/resolvedTheme.ts +67 -0
  19. package/apps/shared/appearance/theme.ts +355 -0
  20. package/apps/shared/auth/accountUrl.ts +9 -0
  21. package/apps/shared/auth/adminAccess.ts +99 -0
  22. package/apps/shared/components/admin/layout/AdminShell.tsx +22 -10
  23. package/apps/shared/components/admin/mailboxes/ShareAccessCard.tsx +107 -60
  24. package/apps/shared/components/admin/settings/BrandingForm.tsx +24 -0
  25. package/apps/shared/components/admin/settings/DomainDnsSetup.tsx +2 -1
  26. package/apps/shared/components/admin/settings/PluginsManager.tsx +228 -25
  27. package/apps/shared/components/admin/setup/SetupWizard.tsx +2 -1
  28. package/apps/shared/components/calendar/CalendarListSidebar.tsx +4 -5
  29. package/apps/shared/components/calendar/layout/CalendarShell.tsx +8 -2
  30. package/apps/shared/components/contacts/ContactForm.tsx +11 -5
  31. package/apps/shared/components/contacts/ContactsSidebar.tsx +5 -3
  32. package/apps/shared/components/contacts/ContactsToolbar.tsx +98 -115
  33. package/apps/shared/components/contacts/layout/ContactsShell.tsx +25 -8
  34. package/apps/shared/components/escrow/layout/EscrowShell.tsx +13 -6
  35. package/apps/shared/components/layout/AppShell.tsx +477 -288
  36. package/apps/shared/components/layout/BrandingChrome.tsx +200 -13
  37. package/apps/shared/components/layout/KeyEnrollmentGate.tsx +398 -389
  38. package/apps/shared/components/layout/MailboxProvisioning.tsx +168 -164
  39. package/apps/shared/components/layout/RailIcon.tsx +100 -0
  40. package/apps/shared/components/layout/ResponsiveToolbar.tsx +315 -0
  41. package/apps/shared/components/layout/ThemeSwitch.tsx +84 -0
  42. package/apps/shared/components/layout/UserMenu.tsx +372 -185
  43. package/apps/shared/components/mail/ConversationList.tsx +271 -262
  44. package/apps/shared/components/mail/ConversationThreadPane.tsx +147 -94
  45. package/apps/shared/components/mail/LazyReadingPane.tsx +113 -0
  46. package/apps/shared/components/mail/MailSelectionBar.tsx +227 -222
  47. package/apps/shared/components/mail/MessageDetailPane.tsx +1537 -1387
  48. package/apps/shared/components/mail/OutboxBadge.tsx +44 -0
  49. package/apps/shared/components/mail/OutboxRowStatus.tsx +31 -0
  50. package/apps/shared/components/mail/compose/ComposeContext.tsx +312 -160
  51. package/apps/shared/components/mail/compose/ComposeToolbar.tsx +422 -418
  52. package/apps/shared/components/mail/compose/ComposeWindow.tsx +1644 -1736
  53. package/apps/shared/components/mail/compose/ComposeWindowPlaceholder.tsx +111 -0
  54. package/apps/shared/components/mail/compose/RichTextEditor.tsx +147 -118
  55. package/apps/shared/components/mail/compose/composePerf.ts +46 -0
  56. package/apps/shared/components/mail/compose/encryptionRequirement.ts +86 -0
  57. package/apps/shared/components/mail/compose/quotedBody.ts +161 -100
  58. package/apps/shared/components/mail/layout/MailShell.tsx +490 -472
  59. package/apps/shared/components/mail/reading/EncryptedBody.tsx +65 -0
  60. package/apps/shared/components/mail/reading/EncryptedPreview.tsx +35 -0
  61. package/apps/shared/components/mail/reading/MessageBody.tsx +240 -0
  62. package/apps/shared/components/mail/reading/MessageCard.tsx +174 -0
  63. package/apps/shared/components/mail/reading/bodyContent.ts +100 -0
  64. package/apps/shared/components/mail/reading/bodyHtml.ts +298 -0
  65. package/apps/shared/components/mail/reading/color.ts +245 -0
  66. package/apps/shared/components/mail/reading/frameControl.ts +142 -0
  67. package/apps/shared/components/mail/reading/frameDocument.ts +123 -0
  68. package/apps/shared/components/mail/reading/safeDocument.ts +24 -0
  69. package/apps/shared/components/mail/reading/themeAdaptation.ts +221 -0
  70. package/apps/shared/components/mail/reading/themeSurface.ts +90 -0
  71. package/apps/shared/components/mail/reading/viewOriginal.ts +38 -0
  72. package/apps/shared/components/mail/unreadStyle.tsx +75 -0
  73. package/apps/shared/components/settings/SigningCertificateCard.tsx +344 -0
  74. package/apps/shared/components/settings/layout/SettingsShell.tsx +253 -240
  75. package/apps/shared/components/sharing/PrincipalPicker.tsx +145 -0
  76. package/apps/shared/components/tasks/TasksSidebar.tsx +5 -3
  77. package/apps/shared/components/tasks/layout/TasksShell.tsx +24 -8
  78. package/apps/shared/keyboard/GlobalShortcuts.tsx +51 -0
  79. package/apps/shared/keyboard/ShortcutProvider.tsx +62 -0
  80. package/apps/shared/keyboard/ShortcutsDialog.tsx +84 -0
  81. package/apps/shared/keyboard/dispatch.ts +124 -0
  82. package/apps/shared/keyboard/format.ts +89 -0
  83. package/apps/shared/keyboard/keymap.ts +114 -0
  84. package/apps/shared/keyboard/match.ts +44 -0
  85. package/apps/shared/keyboard/parse.ts +136 -0
  86. package/apps/shared/keyboard/platform.ts +34 -0
  87. package/apps/shared/keyboard/registry.ts +65 -0
  88. package/apps/shared/keyboard/targets.ts +79 -0
  89. package/apps/shared/keyboard/useShortcut.ts +50 -0
  90. package/apps/shared/keyboard/useShortcutProps.ts +17 -0
  91. package/apps/shared/mail/folderCounts.ts +308 -0
  92. package/apps/shared/mail/folderTree.ts +143 -0
  93. package/apps/shared/mail/listSnapshots.ts +87 -0
  94. package/apps/shared/mail/messageReadState.ts +85 -0
  95. package/apps/shared/mail/newMailNotifications.ts +183 -0
  96. package/apps/shared/mail/outbox/UnlockBridge.tsx +23 -0
  97. package/apps/shared/mail/outbox/composeBridge.ts +69 -0
  98. package/apps/shared/mail/outbox/outboxState.ts +88 -0
  99. package/apps/shared/mail/outbox/pendingSends.ts +146 -0
  100. package/apps/shared/mail/outbox/sendDecision.ts +157 -0
  101. package/apps/shared/mail/outbox/sendJob.ts +427 -0
  102. package/apps/shared/mail/outbox/sendOutcomes.ts +146 -0
  103. package/apps/shared/mail/outbox/sendState.ts +35 -0
  104. package/apps/shared/mail/outbox/useOutboxStatus.ts +61 -0
  105. package/apps/shared/mail/useMailConnection.ts +203 -0
  106. package/apps/shared/mail/useMailLiveUpdates.ts +277 -224
  107. package/apps/shared/mail/useMarkMessageRead.ts +47 -0
  108. package/apps/shared/mail/useNewMailNotifications.ts +178 -0
  109. package/apps/shared/mail/useUnreadTitle.ts +42 -0
  110. package/apps/shared/navigation/AppRouter.tsx +300 -0
  111. package/apps/shared/navigation/appHrefs.ts +23 -0
  112. package/apps/shared/navigation/frameContext.tsx +35 -0
  113. package/apps/shared/navigation/idle.ts +45 -0
  114. package/apps/shared/navigation/routerContext.tsx +83 -0
  115. package/apps/shared/navigation/routes.ts +72 -0
  116. package/apps/shared/notifications/NotificationCenter.tsx +215 -0
  117. package/apps/shared/notifications/NotificationHistoryDialog.tsx +90 -0
  118. package/apps/shared/notifications/apiErrors.ts +98 -0
  119. package/apps/shared/notifications/headerOffset.ts +35 -0
  120. package/apps/shared/notifications/pushStatus.ts +56 -0
  121. package/apps/shared/notifications/store.ts +549 -0
  122. package/apps/shared/notifications/systemErrors.ts +63 -0
  123. package/apps/shared/notifications/useNotifications.ts +41 -0
  124. package/apps/shared/search/LocalIndexLifecycle.tsx +114 -98
  125. package/apps/shared/signing/enrollmentStorage.ts +33 -0
  126. package/apps/shared/signing/enrollmentTracker.ts +385 -0
  127. package/apps/shared/signing/enrollmentView.ts +251 -0
  128. package/apps/shared/signing/signingInfo.ts +58 -0
  129. package/apps/shared/signing/useNow.ts +19 -0
  130. package/apps/shared/signing/useSigningEnrollmentWatcher.ts +90 -0
  131. package/apps/shared/styles/app.css +108 -10
  132. package/apps/www/_layout.tsx +8 -2
  133. package/apps/www/_routedPage.tsx +24 -0
  134. package/apps/www/_routes.ts +36 -0
  135. package/apps/www/calendar/index.tsx +64 -33
  136. package/apps/www/contacts/[uid].tsx +109 -107
  137. package/apps/www/contacts/index.tsx +62 -42
  138. package/apps/www/index.tsx +2291 -1917
  139. package/apps/www/messages/[uid].tsx +108 -101
  140. package/apps/www/settings/appearance/index.tsx +21 -0
  141. package/apps/www/settings/auto-reply/index.tsx +7 -8
  142. package/apps/www/settings/encryption/index.tsx +1287 -1249
  143. package/apps/www/settings/filters/[uid].tsx +7 -2
  144. package/apps/www/settings/filters/index.tsx +102 -99
  145. package/apps/www/settings/filters/new/index.tsx +140 -133
  146. package/apps/www/settings/labels/index.tsx +204 -201
  147. package/apps/www/settings/privacy/index.tsx +7 -9
  148. package/apps/www/settings/read-receipts/index.tsx +7 -8
  149. package/apps/www/settings/sharing/index.tsx +256 -274
  150. package/apps/www/settings/signatures/[uid].tsx +172 -167
  151. package/apps/www/settings/signatures/index.tsx +88 -85
  152. package/apps/www/settings/signatures/new/index.tsx +135 -129
  153. package/apps/www/tasks/index.tsx +29 -18
  154. package/dist/apps/admin/_layout.d.ts +5 -1
  155. package/dist/apps/admin/_layout.js +3 -2
  156. package/dist/apps/admin/index.js +3 -2
  157. package/dist/apps/admin/ingest-queue/index.js +2 -1
  158. package/dist/apps/admin/mailboxes/[uid].js +9 -6
  159. package/dist/apps/admin/quarantine/index.js +2 -1
  160. package/dist/apps/admin/signing-certificates/index.d.ts +3 -0
  161. package/dist/apps/admin/signing-certificates/index.js +161 -0
  162. package/dist/apps/escrow/_layout.d.ts +5 -1
  163. package/dist/apps/escrow/_layout.js +3 -2
  164. package/dist/apps/shared/appearance/AppearanceForm.d.ts +7 -0
  165. package/dist/apps/shared/appearance/AppearanceForm.js +131 -0
  166. package/dist/apps/shared/appearance/AppearanceHead.d.ts +15 -0
  167. package/dist/apps/shared/appearance/AppearanceHead.js +31 -0
  168. package/dist/apps/shared/appearance/AppearanceProvider.d.ts +32 -0
  169. package/dist/apps/shared/appearance/AppearanceProvider.js +390 -0
  170. package/dist/apps/shared/appearance/ColorField.d.ts +22 -0
  171. package/dist/apps/shared/appearance/ColorField.js +40 -0
  172. package/dist/apps/shared/appearance/appearanceCache.d.ts +45 -0
  173. package/dist/apps/shared/appearance/appearanceCache.js +80 -0
  174. package/dist/apps/shared/appearance/appearanceContext.d.ts +45 -0
  175. package/dist/apps/shared/appearance/appearanceContext.js +29 -0
  176. package/dist/apps/shared/appearance/bootScript.d.ts +16 -0
  177. package/dist/apps/shared/appearance/bootScript.js +33 -0
  178. package/dist/apps/shared/appearance/color.d.ts +65 -0
  179. package/dist/apps/shared/appearance/color.js +132 -0
  180. package/dist/apps/shared/appearance/photo.d.ts +17 -0
  181. package/dist/apps/shared/appearance/photo.js +79 -0
  182. package/dist/apps/shared/appearance/resolvedTheme.d.ts +21 -0
  183. package/dist/apps/shared/appearance/resolvedTheme.js +55 -0
  184. package/dist/apps/shared/appearance/theme.d.ts +111 -0
  185. package/dist/apps/shared/appearance/theme.js +251 -0
  186. package/dist/apps/shared/auth/accountUrl.d.ts +2 -0
  187. package/dist/apps/shared/auth/accountUrl.js +8 -0
  188. package/dist/apps/shared/auth/adminAccess.d.ts +30 -0
  189. package/dist/apps/shared/auth/adminAccess.js +89 -0
  190. package/dist/apps/shared/components/admin/layout/AdminShell.d.ts +1 -1
  191. package/dist/apps/shared/components/admin/layout/AdminShell.js +16 -6
  192. package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.d.ts +10 -5
  193. package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.js +49 -34
  194. package/dist/apps/shared/components/admin/settings/BrandingForm.d.ts +2 -0
  195. package/dist/apps/shared/components/admin/settings/BrandingForm.js +7 -1
  196. package/dist/apps/shared/components/admin/settings/DomainDnsSetup.js +1 -1
  197. package/dist/apps/shared/components/admin/settings/PluginsManager.js +106 -20
  198. package/dist/apps/shared/components/admin/setup/SetupWizard.js +2 -1
  199. package/dist/apps/shared/components/calendar/CalendarListSidebar.js +8 -8
  200. package/dist/apps/shared/components/calendar/layout/CalendarShell.d.ts +1 -1
  201. package/dist/apps/shared/components/calendar/layout/CalendarShell.js +8 -4
  202. package/dist/apps/shared/components/contacts/ContactForm.js +1 -1
  203. package/dist/apps/shared/components/contacts/ContactsSidebar.js +4 -2
  204. package/dist/apps/shared/components/contacts/ContactsToolbar.d.ts +6 -2
  205. package/dist/apps/shared/components/contacts/ContactsToolbar.js +28 -10
  206. package/dist/apps/shared/components/contacts/layout/ContactsShell.d.ts +1 -1
  207. package/dist/apps/shared/components/contacts/layout/ContactsShell.js +19 -6
  208. package/dist/apps/shared/components/escrow/layout/EscrowShell.js +7 -4
  209. package/dist/apps/shared/components/layout/AppShell.d.ts +34 -4
  210. package/dist/apps/shared/components/layout/AppShell.js +122 -17
  211. package/dist/apps/shared/components/layout/BrandingChrome.d.ts +69 -10
  212. package/dist/apps/shared/components/layout/BrandingChrome.js +140 -12
  213. package/dist/apps/shared/components/layout/KeyEnrollmentGate.js +10 -9
  214. package/dist/apps/shared/components/layout/MailboxProvisioning.js +3 -1
  215. package/dist/apps/shared/components/layout/RailIcon.d.ts +21 -0
  216. package/dist/apps/shared/components/layout/RailIcon.js +85 -0
  217. package/dist/apps/shared/components/layout/ResponsiveToolbar.d.ts +55 -0
  218. package/dist/apps/shared/components/layout/ResponsiveToolbar.js +199 -0
  219. package/dist/apps/shared/components/layout/ThemeSwitch.d.ts +9 -0
  220. package/dist/apps/shared/components/layout/ThemeSwitch.js +54 -0
  221. package/dist/apps/shared/components/layout/UserMenu.d.ts +25 -3
  222. package/dist/apps/shared/components/layout/UserMenu.js +88 -7
  223. package/dist/apps/shared/components/mail/ConversationList.js +5 -11
  224. package/dist/apps/shared/components/mail/ConversationThreadPane.d.ts +6 -2
  225. package/dist/apps/shared/components/mail/ConversationThreadPane.js +87 -40
  226. package/dist/apps/shared/components/mail/LazyReadingPane.d.ts +9 -0
  227. package/dist/apps/shared/components/mail/LazyReadingPane.js +83 -0
  228. package/dist/apps/shared/components/mail/MailSelectionBar.d.ts +6 -4
  229. package/dist/apps/shared/components/mail/MailSelectionBar.js +11 -4
  230. package/dist/apps/shared/components/mail/MessageDetailPane.d.ts +25 -8
  231. package/dist/apps/shared/components/mail/MessageDetailPane.js +167 -73
  232. package/dist/apps/shared/components/mail/OutboxBadge.d.ts +17 -0
  233. package/dist/apps/shared/components/mail/OutboxBadge.js +18 -0
  234. package/dist/apps/shared/components/mail/OutboxRowStatus.d.ts +10 -0
  235. package/dist/apps/shared/components/mail/OutboxRowStatus.js +19 -0
  236. package/dist/apps/shared/components/mail/compose/ComposeContext.d.ts +38 -0
  237. package/dist/apps/shared/components/mail/compose/ComposeContext.js +97 -6
  238. package/dist/apps/shared/components/mail/compose/ComposeToolbar.js +4 -3
  239. package/dist/apps/shared/components/mail/compose/ComposeWindow.d.ts +3 -2
  240. package/dist/apps/shared/components/mail/compose/ComposeWindow.js +316 -339
  241. package/dist/apps/shared/components/mail/compose/ComposeWindowPlaceholder.d.ts +19 -0
  242. package/dist/apps/shared/components/mail/compose/ComposeWindowPlaceholder.js +33 -0
  243. package/dist/apps/shared/components/mail/compose/RichTextEditor.d.ts +9 -1
  244. package/dist/apps/shared/components/mail/compose/RichTextEditor.js +20 -2
  245. package/dist/apps/shared/components/mail/compose/composePerf.d.ts +16 -0
  246. package/dist/apps/shared/components/mail/compose/composePerf.js +41 -0
  247. package/dist/apps/shared/components/mail/compose/encryptionRequirement.d.ts +49 -0
  248. package/dist/apps/shared/components/mail/compose/encryptionRequirement.js +35 -0
  249. package/dist/apps/shared/components/mail/compose/quotedBody.d.ts +13 -0
  250. package/dist/apps/shared/components/mail/compose/quotedBody.js +67 -11
  251. package/dist/apps/shared/components/mail/layout/MailShell.d.ts +14 -4
  252. package/dist/apps/shared/components/mail/layout/MailShell.js +90 -104
  253. package/dist/apps/shared/components/mail/reading/EncryptedBody.d.ts +21 -0
  254. package/dist/apps/shared/components/mail/reading/EncryptedBody.js +22 -0
  255. package/dist/apps/shared/components/mail/reading/EncryptedPreview.d.ts +18 -0
  256. package/dist/apps/shared/components/mail/reading/EncryptedPreview.js +22 -0
  257. package/dist/apps/shared/components/mail/reading/MessageBody.d.ts +32 -0
  258. package/dist/apps/shared/components/mail/reading/MessageBody.js +136 -0
  259. package/dist/apps/shared/components/mail/reading/MessageCard.d.ts +62 -0
  260. package/dist/apps/shared/components/mail/reading/MessageCard.js +58 -0
  261. package/dist/apps/shared/components/mail/reading/bodyContent.d.ts +38 -0
  262. package/dist/apps/shared/components/mail/reading/bodyContent.js +84 -0
  263. package/dist/apps/shared/components/mail/reading/bodyHtml.d.ts +47 -0
  264. package/dist/apps/shared/components/mail/reading/bodyHtml.js +267 -0
  265. package/dist/apps/shared/components/mail/reading/color.d.ts +63 -0
  266. package/dist/apps/shared/components/mail/reading/color.js +201 -0
  267. package/dist/apps/shared/components/mail/reading/frameControl.d.ts +33 -0
  268. package/dist/apps/shared/components/mail/reading/frameControl.js +120 -0
  269. package/dist/apps/shared/components/mail/reading/frameDocument.d.ts +52 -0
  270. package/dist/apps/shared/components/mail/reading/frameDocument.js +98 -0
  271. package/dist/apps/shared/components/mail/reading/safeDocument.d.ts +3 -0
  272. package/dist/apps/shared/components/mail/reading/safeDocument.js +12 -0
  273. package/dist/apps/shared/components/mail/reading/themeAdaptation.d.ts +56 -0
  274. package/dist/apps/shared/components/mail/reading/themeAdaptation.js +144 -0
  275. package/dist/apps/shared/components/mail/reading/themeSurface.d.ts +17 -0
  276. package/dist/apps/shared/components/mail/reading/themeSurface.js +83 -0
  277. package/dist/apps/shared/components/mail/reading/viewOriginal.d.ts +9 -0
  278. package/dist/apps/shared/components/mail/reading/viewOriginal.js +32 -0
  279. package/dist/apps/shared/components/mail/unreadStyle.d.ts +45 -0
  280. package/dist/apps/shared/components/mail/unreadStyle.js +61 -0
  281. package/dist/apps/shared/components/settings/SigningCertificateCard.d.ts +32 -0
  282. package/dist/apps/shared/components/settings/SigningCertificateCard.js +98 -0
  283. package/dist/apps/shared/components/settings/layout/SettingsShell.d.ts +1 -1
  284. package/dist/apps/shared/components/settings/layout/SettingsShell.js +16 -5
  285. package/dist/apps/shared/components/sharing/PrincipalPicker.d.ts +26 -0
  286. package/dist/apps/shared/components/sharing/PrincipalPicker.js +77 -0
  287. package/dist/apps/shared/components/tasks/TasksSidebar.js +4 -2
  288. package/dist/apps/shared/components/tasks/layout/TasksShell.d.ts +1 -1
  289. package/dist/apps/shared/components/tasks/layout/TasksShell.js +18 -6
  290. package/dist/apps/shared/keyboard/GlobalShortcuts.d.ts +14 -0
  291. package/dist/apps/shared/keyboard/GlobalShortcuts.js +37 -0
  292. package/dist/apps/shared/keyboard/ShortcutProvider.d.ts +20 -0
  293. package/dist/apps/shared/keyboard/ShortcutProvider.js +52 -0
  294. package/dist/apps/shared/keyboard/ShortcutsDialog.d.ts +14 -0
  295. package/dist/apps/shared/keyboard/ShortcutsDialog.js +42 -0
  296. package/dist/apps/shared/keyboard/dispatch.d.ts +16 -0
  297. package/dist/apps/shared/keyboard/dispatch.js +109 -0
  298. package/dist/apps/shared/keyboard/format.d.ts +12 -0
  299. package/dist/apps/shared/keyboard/format.js +74 -0
  300. package/dist/apps/shared/keyboard/keymap.d.ts +294 -0
  301. package/dist/apps/shared/keyboard/keymap.js +84 -0
  302. package/dist/apps/shared/keyboard/match.d.ts +20 -0
  303. package/dist/apps/shared/keyboard/match.js +31 -0
  304. package/dist/apps/shared/keyboard/parse.d.ts +31 -0
  305. package/dist/apps/shared/keyboard/parse.js +107 -0
  306. package/dist/apps/shared/keyboard/platform.d.ts +15 -0
  307. package/dist/apps/shared/keyboard/platform.js +22 -0
  308. package/dist/apps/shared/keyboard/registry.d.ts +39 -0
  309. package/dist/apps/shared/keyboard/registry.js +33 -0
  310. package/dist/apps/shared/keyboard/targets.d.ts +14 -0
  311. package/dist/apps/shared/keyboard/targets.js +67 -0
  312. package/dist/apps/shared/keyboard/useShortcut.d.ts +19 -0
  313. package/dist/apps/shared/keyboard/useShortcut.js +35 -0
  314. package/dist/apps/shared/keyboard/useShortcutProps.d.ts +10 -0
  315. package/dist/apps/shared/keyboard/useShortcutProps.js +15 -0
  316. package/dist/apps/shared/mail/folderCounts.d.ts +78 -0
  317. package/dist/apps/shared/mail/folderCounts.js +218 -0
  318. package/dist/apps/shared/mail/folderTree.d.ts +55 -0
  319. package/dist/apps/shared/mail/folderTree.js +116 -0
  320. package/dist/apps/shared/mail/listSnapshots.d.ts +46 -0
  321. package/dist/apps/shared/mail/listSnapshots.js +43 -0
  322. package/dist/apps/shared/mail/messageReadState.d.ts +32 -0
  323. package/dist/apps/shared/mail/messageReadState.js +63 -0
  324. package/dist/apps/shared/mail/newMailNotifications.d.ts +62 -0
  325. package/dist/apps/shared/mail/newMailNotifications.js +138 -0
  326. package/dist/apps/shared/mail/outbox/UnlockBridge.d.ts +5 -0
  327. package/dist/apps/shared/mail/outbox/UnlockBridge.js +18 -0
  328. package/dist/apps/shared/mail/outbox/composeBridge.d.ts +34 -0
  329. package/dist/apps/shared/mail/outbox/composeBridge.js +32 -0
  330. package/dist/apps/shared/mail/outbox/outboxState.d.ts +41 -0
  331. package/dist/apps/shared/mail/outbox/outboxState.js +42 -0
  332. package/dist/apps/shared/mail/outbox/pendingSends.d.ts +39 -0
  333. package/dist/apps/shared/mail/outbox/pendingSends.js +109 -0
  334. package/dist/apps/shared/mail/outbox/sendDecision.d.ts +80 -0
  335. package/dist/apps/shared/mail/outbox/sendDecision.js +94 -0
  336. package/dist/apps/shared/mail/outbox/sendJob.d.ts +68 -0
  337. package/dist/apps/shared/mail/outbox/sendJob.js +319 -0
  338. package/dist/apps/shared/mail/outbox/sendOutcomes.d.ts +3 -0
  339. package/dist/apps/shared/mail/outbox/sendOutcomes.js +141 -0
  340. package/dist/apps/shared/mail/outbox/sendState.d.ts +25 -0
  341. package/dist/apps/shared/mail/outbox/sendState.js +8 -0
  342. package/dist/apps/shared/mail/outbox/useOutboxStatus.d.ts +11 -0
  343. package/dist/apps/shared/mail/outbox/useOutboxStatus.js +49 -0
  344. package/dist/apps/shared/mail/useMailConnection.d.ts +56 -0
  345. package/dist/apps/shared/mail/useMailConnection.js +138 -0
  346. package/dist/apps/shared/mail/useMailLiveUpdates.d.ts +29 -9
  347. package/dist/apps/shared/mail/useMailLiveUpdates.js +60 -32
  348. package/dist/apps/shared/mail/useMarkMessageRead.d.ts +12 -0
  349. package/dist/apps/shared/mail/useMarkMessageRead.js +44 -0
  350. package/dist/apps/shared/mail/useNewMailNotifications.d.ts +35 -0
  351. package/dist/apps/shared/mail/useNewMailNotifications.js +130 -0
  352. package/dist/apps/shared/mail/useUnreadTitle.d.ts +18 -0
  353. package/dist/apps/shared/mail/useUnreadTitle.js +31 -0
  354. package/dist/apps/shared/navigation/AppRouter.d.ts +52 -0
  355. package/dist/apps/shared/navigation/AppRouter.js +242 -0
  356. package/dist/apps/shared/navigation/appHrefs.d.ts +14 -0
  357. package/dist/apps/shared/navigation/appHrefs.js +20 -0
  358. package/dist/apps/shared/navigation/frameContext.d.ts +19 -0
  359. package/dist/apps/shared/navigation/frameContext.js +24 -0
  360. package/dist/apps/shared/navigation/idle.d.ts +14 -0
  361. package/dist/apps/shared/navigation/idle.js +43 -0
  362. package/dist/apps/shared/navigation/routerContext.d.ts +37 -0
  363. package/dist/apps/shared/navigation/routerContext.js +56 -0
  364. package/dist/apps/shared/navigation/routes.d.ts +32 -0
  365. package/dist/apps/shared/navigation/routes.js +37 -0
  366. package/dist/apps/shared/notifications/NotificationCenter.d.ts +19 -0
  367. package/dist/apps/shared/notifications/NotificationCenter.js +90 -0
  368. package/dist/apps/shared/notifications/NotificationHistoryDialog.d.ts +10 -0
  369. package/dist/apps/shared/notifications/NotificationHistoryDialog.js +36 -0
  370. package/dist/apps/shared/notifications/apiErrors.d.ts +19 -0
  371. package/dist/apps/shared/notifications/apiErrors.js +86 -0
  372. package/dist/apps/shared/notifications/headerOffset.d.ts +8 -0
  373. package/dist/apps/shared/notifications/headerOffset.js +33 -0
  374. package/dist/apps/shared/notifications/pushStatus.d.ts +9 -0
  375. package/dist/apps/shared/notifications/pushStatus.js +54 -0
  376. package/dist/apps/shared/notifications/store.d.ts +136 -0
  377. package/dist/apps/shared/notifications/store.js +416 -0
  378. package/dist/apps/shared/notifications/systemErrors.d.ts +9 -0
  379. package/dist/apps/shared/notifications/systemErrors.js +58 -0
  380. package/dist/apps/shared/notifications/useNotifications.d.ts +17 -0
  381. package/dist/apps/shared/notifications/useNotifications.js +18 -0
  382. package/dist/apps/shared/search/LocalIndexLifecycle.js +17 -3
  383. package/dist/apps/shared/signing/enrollmentStorage.d.ts +10 -0
  384. package/dist/apps/shared/signing/enrollmentStorage.js +33 -0
  385. package/dist/apps/shared/signing/enrollmentTracker.d.ts +73 -0
  386. package/dist/apps/shared/signing/enrollmentTracker.js +307 -0
  387. package/dist/apps/shared/signing/enrollmentView.d.ts +57 -0
  388. package/dist/apps/shared/signing/enrollmentView.js +214 -0
  389. package/dist/apps/shared/signing/signingInfo.d.ts +11 -0
  390. package/dist/apps/shared/signing/signingInfo.js +48 -0
  391. package/dist/apps/shared/signing/useNow.d.ts +2 -0
  392. package/dist/apps/shared/signing/useNow.js +18 -0
  393. package/dist/apps/shared/signing/useSigningEnrollmentWatcher.d.ts +16 -0
  394. package/dist/apps/shared/signing/useSigningEnrollmentWatcher.js +87 -0
  395. package/dist/apps/shared/styles/app.css +108 -10
  396. package/dist/apps/www/_layout.d.ts +5 -1
  397. package/dist/apps/www/_layout.js +3 -2
  398. package/dist/apps/www/_routedPage.d.ts +12 -0
  399. package/dist/apps/www/_routedPage.js +19 -0
  400. package/dist/apps/www/_routes.d.ts +11 -0
  401. package/dist/apps/www/_routes.js +30 -0
  402. package/dist/apps/www/calendar/index.d.ts +2 -2
  403. package/dist/apps/www/calendar/index.js +41 -11
  404. package/dist/apps/www/contacts/[uid].d.ts +3 -9
  405. package/dist/apps/www/contacts/[uid].js +11 -9
  406. package/dist/apps/www/contacts/index.d.ts +2 -2
  407. package/dist/apps/www/contacts/index.js +30 -24
  408. package/dist/apps/www/index.d.ts +2 -2
  409. package/dist/apps/www/index.js +332 -48
  410. package/dist/apps/www/messages/[uid].d.ts +3 -7
  411. package/dist/apps/www/messages/[uid].js +10 -5
  412. package/dist/apps/www/settings/appearance/index.d.ts +4 -0
  413. package/dist/apps/www/settings/appearance/index.js +13 -0
  414. package/dist/apps/www/settings/auto-reply/index.d.ts +2 -2
  415. package/dist/apps/www/settings/auto-reply/index.js +6 -7
  416. package/dist/apps/www/settings/encryption/index.d.ts +2 -2
  417. package/dist/apps/www/settings/encryption/index.js +106 -96
  418. package/dist/apps/www/settings/filters/[uid].d.ts +2 -2
  419. package/dist/apps/www/settings/filters/[uid].js +6 -2
  420. package/dist/apps/www/settings/filters/index.d.ts +2 -2
  421. package/dist/apps/www/settings/filters/index.js +3 -1
  422. package/dist/apps/www/settings/filters/new/index.d.ts +2 -2
  423. package/dist/apps/www/settings/filters/new/index.js +9 -3
  424. package/dist/apps/www/settings/labels/index.d.ts +2 -2
  425. package/dist/apps/www/settings/labels/index.js +3 -1
  426. package/dist/apps/www/settings/privacy/index.d.ts +2 -2
  427. package/dist/apps/www/settings/privacy/index.js +8 -9
  428. package/dist/apps/www/settings/read-receipts/index.d.ts +2 -2
  429. package/dist/apps/www/settings/read-receipts/index.js +6 -7
  430. package/dist/apps/www/settings/sharing/index.d.ts +2 -2
  431. package/dist/apps/www/settings/sharing/index.js +22 -34
  432. package/dist/apps/www/settings/signatures/[uid].d.ts +2 -2
  433. package/dist/apps/www/settings/signatures/[uid].js +6 -2
  434. package/dist/apps/www/settings/signatures/index.d.ts +2 -2
  435. package/dist/apps/www/settings/signatures/index.js +3 -1
  436. package/dist/apps/www/settings/signatures/new/index.d.ts +2 -2
  437. package/dist/apps/www/settings/signatures/new/index.js +9 -4
  438. package/dist/apps/www/tasks/index.d.ts +2 -2
  439. package/dist/apps/www/tasks/index.js +22 -16
  440. package/package.json +2 -2
  441. package/apps/shared/components/mail/compose/SendFailureAlert.tsx +0 -48
  442. package/dist/apps/shared/components/mail/compose/SendFailureAlert.d.ts +0 -14
  443. package/dist/apps/shared/components/mail/compose/SendFailureAlert.js +0 -11
@@ -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, useEffect, useState } from "react";
6
7
  import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
7
8
  import { Folder, listFolders } from "@rapidmx/react-shared/mail/mailApi.js";
@@ -18,13 +19,14 @@ import { MAIL_FILTER_CONDITION_FIELDS, buildMailFilterActionTypes } from "./_mai
18
19
  import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
19
20
  import Button from "@rapidmx/react-shared/components/buttons/Button.js";
20
21
  import FormField from "@rapidmx/react-shared/components/forms/FormField.js";
22
+ import { notifyApiError } from "../../../shared/notifications/apiErrors.js";
21
23
 
22
24
  const INPUT_CLASS =
23
25
  "w-full text-sm py-2.5 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
24
26
 
25
27
  export type MailFilterDetailPageProps = Omit<SettingsShellProps, "active"> & { params: { uid: string } };
26
28
 
27
- export default function MailFilterDetailPage(props: MailFilterDetailPageProps) {
29
+ function MailFilterDetailPage(props: MailFilterDetailPageProps) {
28
30
  return (
29
31
  <SettingsShell {...props} active="filters">
30
32
  <MailFilterDetailContent uid={props.params.uid} />
@@ -105,7 +107,8 @@ function MailFilterDetailContent({ uid }: { uid: string }) {
105
107
  setOriginal(updated);
106
108
  setSaved(true);
107
109
  } catch (err) {
108
- setError(err instanceof ApiRequestError ? err.message : "Could not save this mail filter.");
110
+ // A pop-up: `error` is only this form's own validation (and the load failure above).
111
+ notifyApiError(err, "Couldn't save the mail filter");
109
112
  } finally {
110
113
  setSaving(false);
111
114
  }
@@ -169,3 +172,5 @@ function MailFilterDetailContent({ uid }: { uid: string }) {
169
172
  </div>
170
173
  );
171
174
  }
175
+
176
+ export default routedPage("/settings/filters/:uid", MailFilterDetailPage);
@@ -1,99 +1,102 @@
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 { MailFilterRule, listMailFilterRules } from "@rapidmx/react-shared/mail/mailFilterRulesApi.js";
8
- import SettingsShell, { SettingsShellProps, useSettingsShell } from "../../../shared/components/settings/layout/SettingsShell.js";
9
- import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
10
- import Button from "@rapidmx/react-shared/components/buttons/Button.js";
11
-
12
- export type SettingsFiltersPageProps = Omit<SettingsShellProps, "active">;
13
-
14
- export default function SettingsFiltersPage(props: SettingsFiltersPageProps) {
15
- return (
16
- <SettingsShell {...props} active="filters">
17
- <FiltersContent />
18
- </SettingsShell>
19
- );
20
- }
21
-
22
- function FiltersContent() {
23
- const { mailboxUid } = useSettingsShell();
24
- const [rules, setRules] = useState<MailFilterRule[]>([]);
25
- const [loading, setLoading] = useState(true);
26
- const [error, setError] = useState<string | null>(null);
27
-
28
- // `SettingsShell` only ever renders its children once `mailboxUid` has resolved — same established
29
- // non-null pattern as `apps/www/settings/auto-reply/index.tsx`.
30
- useEffect(() => {
31
- listMailFilterRules(mailboxUid!)
32
- .then(setRules)
33
- .catch((err) => setError(err instanceof ApiRequestError ? err.message : "Could not load mail filters."))
34
- .finally(() => setLoading(false));
35
- }, [mailboxUid]);
36
-
37
- const sorted = [...rules].sort((a, b) => a.sequence - b.sequence);
38
-
39
- return (
40
- <div className="flex-1 min-w-0 overflow-y-auto p-6">
41
- <div className="max-w-3xl">
42
- <div className="flex items-center justify-between mb-5">
43
- <h1 className="text-lg font-bold tracking-tight">Mail Filters</h1>
44
- <a href={`/settings/filters/new?mailboxUid=${encodeURIComponent(mailboxUid!)}`}>
45
- <Button type="button" className="!w-auto">
46
- + New filter
47
- </Button>
48
- </a>
49
- </div>
50
- <p className="text-sm text-text-muted mb-4">
51
- Applied in order once a message is verdicted safe to deliver, before it reaches your Inbox.
52
- </p>
53
-
54
- {error && <Alert>{error}</Alert>}
55
-
56
- {loading ? (
57
- <p className="text-sm text-text-muted">Loading&hellip;</p>
58
- ) : sorted.length === 0 ? (
59
- <p className="text-sm text-text-muted">No mail filters yet.</p>
60
- ) : (
61
- <div className="overflow-x-auto">
62
- <table className="w-full text-sm border-collapse">
63
- <thead>
64
- <tr>
65
- {["Sequence", "Name", "Enabled", "Actions", ""].map((h) => (
66
- <th
67
- key={h}
68
- className="text-left text-xs uppercase tracking-wide text-text-muted py-2 px-2.5 border-b border-border"
69
- >
70
- {h}
71
- </th>
72
- ))}
73
- </tr>
74
- </thead>
75
- <tbody>
76
- {sorted.map((rule) => (
77
- <tr key={rule.uid}>
78
- <td className="py-2.5 px-2.5 border-b border-border">{rule.sequence}</td>
79
- <td className="py-2.5 px-2.5 border-b border-border">{rule.name}</td>
80
- <td className="py-2.5 px-2.5 border-b border-border">{rule.enabled ? "Yes" : "No"}</td>
81
- <td className="py-2.5 px-2.5 border-b border-border">{rule.actions.length}</td>
82
- <td className="py-2.5 px-2.5 border-b border-border text-right">
83
- <a
84
- href={`/settings/filters/${encodeURIComponent(rule.uid)}?mailboxUid=${encodeURIComponent(mailboxUid!)}`}
85
- className="text-primary-dark hover:underline font-medium"
86
- >
87
- View
88
- </a>
89
- </td>
90
- </tr>
91
- ))}
92
- </tbody>
93
- </table>
94
- </div>
95
- )}
96
- </div>
97
- </div>
98
- );
99
- }
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 { MailFilterRule, listMailFilterRules } from "@rapidmx/react-shared/mail/mailFilterRulesApi.js";
9
+ import SettingsShell, { SettingsShellProps, useSettingsShell } from "../../../shared/components/settings/layout/SettingsShell.js";
10
+ import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
11
+ import Button from "@rapidmx/react-shared/components/buttons/Button.js";
12
+
13
+ export type SettingsFiltersPageProps = Omit<SettingsShellProps, "active">;
14
+
15
+ function SettingsFiltersPage(props: SettingsFiltersPageProps) {
16
+ return (
17
+ <SettingsShell {...props} active="filters">
18
+ <FiltersContent />
19
+ </SettingsShell>
20
+ );
21
+ }
22
+
23
+ function FiltersContent() {
24
+ const { mailboxUid } = useSettingsShell();
25
+ const [rules, setRules] = useState<MailFilterRule[]>([]);
26
+ const [loading, setLoading] = useState(true);
27
+ const [error, setError] = useState<string | null>(null);
28
+
29
+ // `SettingsShell` only ever renders its children once `mailboxUid` has resolved — same established
30
+ // non-null pattern as `apps/www/settings/auto-reply/index.tsx`.
31
+ useEffect(() => {
32
+ listMailFilterRules(mailboxUid!)
33
+ .then(setRules)
34
+ .catch((err) => setError(err instanceof ApiRequestError ? err.message : "Could not load mail filters."))
35
+ .finally(() => setLoading(false));
36
+ }, [mailboxUid]);
37
+
38
+ const sorted = [...rules].sort((a, b) => a.sequence - b.sequence);
39
+
40
+ return (
41
+ <div className="flex-1 min-w-0 overflow-y-auto p-6">
42
+ <div className="max-w-3xl">
43
+ <div className="flex items-center justify-between mb-5">
44
+ <h1 className="text-lg font-bold tracking-tight">Mail Filters</h1>
45
+ <a href={`/settings/filters/new?mailboxUid=${encodeURIComponent(mailboxUid!)}`}>
46
+ <Button type="button" className="!w-auto">
47
+ + New filter
48
+ </Button>
49
+ </a>
50
+ </div>
51
+ <p className="text-sm text-text-muted mb-4">
52
+ Applied in order once a message is verdicted safe to deliver, before it reaches your Inbox.
53
+ </p>
54
+
55
+ {error && <Alert>{error}</Alert>}
56
+
57
+ {loading ? (
58
+ <p className="text-sm text-text-muted">Loading&hellip;</p>
59
+ ) : sorted.length === 0 ? (
60
+ <p className="text-sm text-text-muted">No mail filters yet.</p>
61
+ ) : (
62
+ <div className="overflow-x-auto">
63
+ <table className="w-full text-sm border-collapse">
64
+ <thead>
65
+ <tr>
66
+ {["Sequence", "Name", "Enabled", "Actions", ""].map((h) => (
67
+ <th
68
+ key={h}
69
+ className="text-left text-xs uppercase tracking-wide text-text-muted py-2 px-2.5 border-b border-border"
70
+ >
71
+ {h}
72
+ </th>
73
+ ))}
74
+ </tr>
75
+ </thead>
76
+ <tbody>
77
+ {sorted.map((rule) => (
78
+ <tr key={rule.uid}>
79
+ <td className="py-2.5 px-2.5 border-b border-border">{rule.sequence}</td>
80
+ <td className="py-2.5 px-2.5 border-b border-border">{rule.name}</td>
81
+ <td className="py-2.5 px-2.5 border-b border-border">{rule.enabled ? "Yes" : "No"}</td>
82
+ <td className="py-2.5 px-2.5 border-b border-border">{rule.actions.length}</td>
83
+ <td className="py-2.5 px-2.5 border-b border-border text-right">
84
+ <a
85
+ href={`/settings/filters/${encodeURIComponent(rule.uid)}?mailboxUid=${encodeURIComponent(mailboxUid!)}`}
86
+ className="text-primary-dark hover:underline font-medium"
87
+ >
88
+ View
89
+ </a>
90
+ </td>
91
+ </tr>
92
+ ))}
93
+ </tbody>
94
+ </table>
95
+ </div>
96
+ )}
97
+ </div>
98
+ </div>
99
+ );
100
+ }
101
+
102
+ export default routedPage("/settings/filters", SettingsFiltersPage);
@@ -1,133 +1,140 @@
1
- ///////////////////////////////////////////////////////////////////////////////
2
- // Copyright (C) 2026 Jean-Philippe Steinmetz
3
- // SPDX-License-Identifier: MPL-2.0
4
- ///////////////////////////////////////////////////////////////////////////////
5
- import React, { FormEvent, useEffect, useState } from "react";
6
- import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
7
- import { Folder, listFolders } from "@rapidmx/react-shared/mail/mailApi.js";
8
- import {
9
- MailFilterAction,
10
- MailFilterConditions,
11
- createMailFilterRule,
12
- } from "@rapidmx/react-shared/mail/mailFilterRulesApi.js";
13
- import SettingsShell, { SettingsShellProps, useSettingsShell } from "../../../../shared/components/settings/layout/SettingsShell.js";
14
- import RuleBuilder, { hasConditions, RuleBuilderValue } from "../../../../shared/components/rules/RuleBuilder.js";
15
- import { MAIL_FILTER_CONDITION_FIELDS, buildMailFilterActionTypes } from "../_mailFilterRuleConfig.js";
16
- import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
17
- import Button from "@rapidmx/react-shared/components/buttons/Button.js";
18
- import FormField from "@rapidmx/react-shared/components/forms/FormField.js";
19
-
20
- const INPUT_CLASS =
21
- "w-full text-sm py-2.5 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
22
-
23
- export type NewMailFilterPageProps = Omit<SettingsShellProps, "active">;
24
-
25
- export default function NewMailFilterPage(props: NewMailFilterPageProps) {
26
- return (
27
- <SettingsShell {...props} active="filters">
28
- <NewMailFilterForm />
29
- </SettingsShell>
30
- );
31
- }
32
-
33
- function NewMailFilterForm() {
34
- const { mailboxUid } = useSettingsShell();
35
- const [folders, setFolders] = useState<Folder[] | null>(null);
36
- const [folderError, setFolderError] = useState<string | null>(null);
37
- const [name, setName] = useState("");
38
- const [rule, setRule] = useState<RuleBuilderValue<MailFilterConditions, MailFilterAction>>({
39
- enabled: true,
40
- sequence: 0,
41
- stopProcessingRules: false,
42
- conditions: {},
43
- actions: [],
44
- });
45
- const [error, setError] = useState<string | null>(null);
46
- const [saving, setSaving] = useState(false);
47
- const [showValidation, setShowValidation] = useState(false);
48
-
49
- // `SettingsShell` only ever renders its children once `mailboxUid` has resolved — same established
50
- // non-null pattern as `apps/www/settings/auto-reply/index.tsx`.
51
- useEffect(() => {
52
- listFolders(mailboxUid!)
53
- .then(setFolders)
54
- .catch((err) => setFolderError(err instanceof ApiRequestError ? err.message : "Could not load this mailbox's folders."));
55
- }, [mailboxUid]);
56
-
57
- async function handleSubmit(e: FormEvent) {
58
- e.preventDefault();
59
- setError(null);
60
-
61
- if (!name.trim()) {
62
- setError("A name is required.");
63
- return;
64
- }
65
- if (!hasConditions(rule.conditions)) {
66
- setShowValidation(true);
67
- setError("Add at least one condition. A filter without conditions would apply to every message.");
68
- return;
69
- }
70
-
71
- setSaving(true);
72
- try {
73
- const created = await createMailFilterRule({ mailboxUid: mailboxUid!, name: name.trim(), ...rule });
74
- window.location.href = `/settings/filters/${encodeURIComponent(created.uid)}?mailboxUid=${encodeURIComponent(mailboxUid!)}`;
75
- } catch (err) {
76
- setError(err instanceof ApiRequestError ? err.message : "Could not create the mail filter.");
77
- } finally {
78
- setSaving(false);
79
- }
80
- }
81
-
82
- return (
83
- <div className="flex-1 min-w-0 overflow-y-auto p-6">
84
- <div className="max-w-3xl">
85
- <h1 className="text-lg font-bold tracking-tight mb-1">New mail filter</h1>
86
- <p className="text-sm text-text-muted mb-5">
87
- Applied in order once a message is verdicted safe to deliver, before it reaches your Inbox.
88
- </p>
89
-
90
- {error && <Alert>{error}</Alert>}
91
- {folderError && <Alert>{folderError}</Alert>}
92
-
93
- {folders === null ? (
94
- <p className="text-sm text-text-muted">Loading&hellip;</p>
95
- ) : (
96
- <form onSubmit={handleSubmit} className="flex flex-col gap-5">
97
- <div className="bg-surface border border-border rounded-md p-6">
98
- <FormField label="Name" htmlFor="name">
99
- <input
100
- id="name"
101
- type="text"
102
- className={INPUT_CLASS}
103
- value={name}
104
- onChange={(e) => setName(e.target.value)}
105
- placeholder="File newsletters"
106
- />
107
- </FormField>
108
- </div>
109
-
110
- <RuleBuilder
111
- value={rule}
112
- onChange={setRule}
113
- conditionFields={MAIL_FILTER_CONDITION_FIELDS}
114
- actionTypes={buildMailFilterActionTypes(folders)}
115
- showValidation={showValidation}
116
- />
117
-
118
- <div className="flex gap-3">
119
- <Button type="submit" loading={saving} disabled={saving} className="!w-auto">
120
- Create filter
121
- </Button>
122
- <a href={`/settings/filters?mailboxUid=${encodeURIComponent(mailboxUid!)}`}>
123
- <Button type="button" variant="secondary" className="!w-auto">
124
- Cancel
125
- </Button>
126
- </a>
127
- </div>
128
- </form>
129
- )}
130
- </div>
131
- </div>
132
- );
133
- }
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ import { routedPage } from "../../../_routedPage.js";
6
+ import { useNavigate } from "../../../../shared/navigation/AppRouter.js";
7
+ import React, { FormEvent, useEffect, useState } from "react";
8
+ import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
9
+ import { Folder, listFolders } from "@rapidmx/react-shared/mail/mailApi.js";
10
+ import {
11
+ MailFilterAction,
12
+ MailFilterConditions,
13
+ createMailFilterRule,
14
+ } from "@rapidmx/react-shared/mail/mailFilterRulesApi.js";
15
+ import SettingsShell, { SettingsShellProps, useSettingsShell } from "../../../../shared/components/settings/layout/SettingsShell.js";
16
+ import RuleBuilder, { hasConditions, RuleBuilderValue } from "../../../../shared/components/rules/RuleBuilder.js";
17
+ import { MAIL_FILTER_CONDITION_FIELDS, buildMailFilterActionTypes } from "../_mailFilterRuleConfig.js";
18
+ import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
19
+ import Button from "@rapidmx/react-shared/components/buttons/Button.js";
20
+ import FormField from "@rapidmx/react-shared/components/forms/FormField.js";
21
+ import { notifyApiError } from "../../../../shared/notifications/apiErrors.js";
22
+
23
+ const INPUT_CLASS =
24
+ "w-full text-sm py-2.5 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
25
+
26
+ export type NewMailFilterPageProps = Omit<SettingsShellProps, "active">;
27
+
28
+ function NewMailFilterPage(props: NewMailFilterPageProps) {
29
+ return (
30
+ <SettingsShell {...props} active="filters">
31
+ <NewMailFilterForm />
32
+ </SettingsShell>
33
+ );
34
+ }
35
+
36
+ function NewMailFilterForm() {
37
+ const navigate = useNavigate();
38
+ const { mailboxUid } = useSettingsShell();
39
+ const [folders, setFolders] = useState<Folder[] | null>(null);
40
+ const [folderError, setFolderError] = useState<string | null>(null);
41
+ const [name, setName] = useState("");
42
+ const [rule, setRule] = useState<RuleBuilderValue<MailFilterConditions, MailFilterAction>>({
43
+ enabled: true,
44
+ sequence: 0,
45
+ stopProcessingRules: false,
46
+ conditions: {},
47
+ actions: [],
48
+ });
49
+ const [error, setError] = useState<string | null>(null);
50
+ const [saving, setSaving] = useState(false);
51
+ const [showValidation, setShowValidation] = useState(false);
52
+
53
+ // `SettingsShell` only ever renders its children once `mailboxUid` has resolved — same established
54
+ // non-null pattern as `apps/www/settings/auto-reply/index.tsx`.
55
+ useEffect(() => {
56
+ listFolders(mailboxUid!)
57
+ .then(setFolders)
58
+ .catch((err) => setFolderError(err instanceof ApiRequestError ? err.message : "Could not load this mailbox's folders."));
59
+ }, [mailboxUid]);
60
+
61
+ async function handleSubmit(e: FormEvent) {
62
+ e.preventDefault();
63
+ setError(null);
64
+
65
+ if (!name.trim()) {
66
+ setError("A name is required.");
67
+ return;
68
+ }
69
+ if (!hasConditions(rule.conditions)) {
70
+ setShowValidation(true);
71
+ setError("Add at least one condition. A filter without conditions would apply to every message.");
72
+ return;
73
+ }
74
+
75
+ setSaving(true);
76
+ try {
77
+ const created = await createMailFilterRule({ mailboxUid: mailboxUid!, name: name.trim(), ...rule });
78
+ navigate(`/settings/filters/${encodeURIComponent(created.uid)}?mailboxUid=${encodeURIComponent(mailboxUid!)}`);
79
+ } catch (err) {
80
+ // A pop-up: `error` above is only the form's own validation.
81
+ notifyApiError(err, "Couldn't create the mail filter");
82
+ } finally {
83
+ setSaving(false);
84
+ }
85
+ }
86
+
87
+ return (
88
+ <div className="flex-1 min-w-0 overflow-y-auto p-6">
89
+ <div className="max-w-3xl">
90
+ <h1 className="text-lg font-bold tracking-tight mb-1">New mail filter</h1>
91
+ <p className="text-sm text-text-muted mb-5">
92
+ Applied in order once a message is verdicted safe to deliver, before it reaches your Inbox.
93
+ </p>
94
+
95
+ {error && <Alert>{error}</Alert>}
96
+ {folderError && <Alert>{folderError}</Alert>}
97
+
98
+ {folders === null ? (
99
+ <p className="text-sm text-text-muted">Loading&hellip;</p>
100
+ ) : (
101
+ <form onSubmit={handleSubmit} className="flex flex-col gap-5">
102
+ <div className="bg-surface border border-border rounded-md p-6">
103
+ <FormField label="Name" htmlFor="name">
104
+ <input
105
+ id="name"
106
+ type="text"
107
+ className={INPUT_CLASS}
108
+ value={name}
109
+ onChange={(e) => setName(e.target.value)}
110
+ placeholder="File newsletters"
111
+ />
112
+ </FormField>
113
+ </div>
114
+
115
+ <RuleBuilder
116
+ value={rule}
117
+ onChange={setRule}
118
+ conditionFields={MAIL_FILTER_CONDITION_FIELDS}
119
+ actionTypes={buildMailFilterActionTypes(folders)}
120
+ showValidation={showValidation}
121
+ />
122
+
123
+ <div className="flex gap-3">
124
+ <Button type="submit" loading={saving} disabled={saving} className="!w-auto">
125
+ Create filter
126
+ </Button>
127
+ <a href={`/settings/filters?mailboxUid=${encodeURIComponent(mailboxUid!)}`}>
128
+ <Button type="button" variant="secondary" className="!w-auto">
129
+ Cancel
130
+ </Button>
131
+ </a>
132
+ </div>
133
+ </form>
134
+ )}
135
+ </div>
136
+ </div>
137
+ );
138
+ }
139
+
140
+ export default routedPage("/settings/filters/new", NewMailFilterPage);