@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
@@ -1,201 +1,204 @@
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 { Label, createLabel, deleteLabel, listLabels, updateLabel } from "@rapidmx/react-shared/mail/labelsApi.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
- import FormField from "@rapidmx/react-shared/components/forms/FormField.js";
12
- import Modal from "@rapidmx/react-shared/components/overlays/Modal.js";
13
-
14
- export type SettingsLabelsPageProps = Omit<SettingsShellProps, "active">;
15
-
16
- export default function SettingsLabelsPage(props: SettingsLabelsPageProps) {
17
- return (
18
- <SettingsShell {...props} active="labels">
19
- <LabelsContent />
20
- </SettingsShell>
21
- );
22
- }
23
-
24
- const DEFAULT_COLOR = "#6366f1";
25
-
26
- function LabelsContent() {
27
- const { mailboxUid } = useSettingsShell();
28
- const [labels, setLabels] = useState<Label[]>([]);
29
- const [loading, setLoading] = useState(true);
30
- const [error, setError] = useState<string | null>(null);
31
-
32
- // `editing === null` means the create form; otherwise the label currently being renamed/recolored.
33
- const [editing, setEditing] = useState<Label | null>(null);
34
- const [formOpen, setFormOpen] = useState(false);
35
- const [formName, setFormName] = useState("");
36
- const [formColor, setFormColor] = useState(DEFAULT_COLOR);
37
- const [formError, setFormError] = useState<string | null>(null);
38
- const [saving, setSaving] = useState(false);
39
-
40
- const [deleting, setDeleting] = useState<Label | null>(null);
41
- const [deleteError, setDeleteError] = useState<string | null>(null);
42
- const [deleteBusy, setDeleteBusy] = useState(false);
43
-
44
- // `SettingsShell` only ever renders its children once `mailboxUid` has resolved — same established
45
- // non-null pattern as `apps/www/settings/auto-reply/index.tsx`.
46
- useEffect(() => {
47
- listLabels(mailboxUid!)
48
- .then(setLabels)
49
- .catch((err) => setError(err instanceof ApiRequestError ? err.message : "Could not load labels."))
50
- .finally(() => setLoading(false));
51
- }, [mailboxUid]);
52
-
53
- function openCreateForm() {
54
- setEditing(null);
55
- setFormName("");
56
- setFormColor(DEFAULT_COLOR);
57
- setFormError(null);
58
- setFormOpen(true);
59
- }
60
-
61
- function openEditForm(label: Label) {
62
- setEditing(label);
63
- setFormName(label.name);
64
- setFormColor(label.color ?? DEFAULT_COLOR);
65
- setFormError(null);
66
- setFormOpen(true);
67
- }
68
-
69
- async function handleSave() {
70
- setSaving(true);
71
- setFormError(null);
72
- try {
73
- if (editing) {
74
- const updated = await updateLabel({ uid: editing.uid, version: editing.version, name: formName, color: formColor });
75
- setLabels((prev) => prev.map((l) => (l.uid === updated.uid ? updated : l)));
76
- } else {
77
- const created = await createLabel({ mailboxUid: mailboxUid!, name: formName, color: formColor });
78
- setLabels((prev) => [...prev, created]);
79
- }
80
- setFormOpen(false);
81
- } catch (err) {
82
- setFormError(err instanceof ApiRequestError ? err.message : "Could not save this label.");
83
- } finally {
84
- setSaving(false);
85
- }
86
- }
87
-
88
- async function handleDelete() {
89
- setDeleteBusy(true);
90
- setDeleteError(null);
91
- try {
92
- await deleteLabel(deleting!.uid, deleting!.version);
93
- setLabels((prev) => prev.filter((l) => l.uid !== deleting!.uid));
94
- setDeleting(null);
95
- } catch (err) {
96
- setDeleteError(err instanceof ApiRequestError ? err.message : "Could not delete this label.");
97
- } finally {
98
- setDeleteBusy(false);
99
- }
100
- }
101
-
102
- return (
103
- <div className="flex-1 min-w-0 overflow-y-auto p-6">
104
- <div className="max-w-3xl">
105
- <div className="flex items-center justify-between mb-5">
106
- <h1 className="text-lg font-bold tracking-tight">Labels</h1>
107
- <Button type="button" className="!w-auto" onClick={openCreateForm}>
108
- + New label
109
- </Button>
110
- </div>
111
-
112
- {error && <Alert>{error}</Alert>}
113
-
114
- {loading ? (
115
- <p className="text-sm text-text-muted">Loading&hellip;</p>
116
- ) : labels.length === 0 ? (
117
- <p className="text-sm text-text-muted">No labels yet.</p>
118
- ) : (
119
- <ul className="flex flex-col gap-2">
120
- {labels.map((label) => (
121
- <li key={label.uid} className="flex items-center justify-between border border-border rounded-md p-3">
122
- <div className="flex items-center gap-2">
123
- <span
124
- className="w-3 h-3 rounded-full shrink-0"
125
- style={{ backgroundColor: label.color ?? DEFAULT_COLOR }}
126
- aria-hidden="true"
127
- />
128
- <span className="text-sm font-semibold">{label.name}</span>
129
- </div>
130
- <div className="flex items-center gap-3">
131
- <button
132
- type="button"
133
- className="text-primary-dark hover:underline font-medium text-sm"
134
- onClick={() => openEditForm(label)}
135
- >
136
- Edit
137
- </button>
138
- <button
139
- type="button"
140
- className="text-danger hover:underline font-medium text-sm"
141
- onClick={() => {
142
- setDeleting(label);
143
- setDeleteError(null);
144
- }}
145
- >
146
- Delete
147
- </button>
148
- </div>
149
- </li>
150
- ))}
151
- </ul>
152
- )}
153
- </div>
154
-
155
- <Modal open={formOpen} onClose={() => setFormOpen(false)} title={editing ? "Edit label" : "New label"}>
156
- {formError && <Alert>{formError}</Alert>}
157
- <FormField label="Name" htmlFor="label-name">
158
- <input
159
- id="label-name"
160
- type="text"
161
- value={formName}
162
- onChange={(e) => setFormName(e.target.value)}
163
- className="w-full text-sm px-3 py-1.5 rounded-md border border-border bg-surface"
164
- />
165
- </FormField>
166
- <FormField label="Color" htmlFor="label-color">
167
- <input
168
- id="label-color"
169
- type="color"
170
- value={formColor}
171
- onChange={(e) => setFormColor(e.target.value)}
172
- className="h-9 w-16 rounded-md border border-border bg-surface"
173
- />
174
- </FormField>
175
- <div className="flex justify-end gap-2 mt-2">
176
- <Button type="button" variant="secondary" className="!w-auto" onClick={() => setFormOpen(false)}>
177
- Cancel
178
- </Button>
179
- <Button type="button" className="!w-auto" loading={saving} disabled={saving || !formName.trim()} onClick={handleSave}>
180
- Save
181
- </Button>
182
- </div>
183
- </Modal>
184
-
185
- <Modal open={!!deleting} onClose={() => setDeleting(null)} title="Delete this label?">
186
- {deleteError && <Alert>{deleteError}</Alert>}
187
- <p className="text-sm text-text-muted mb-4">
188
- This removes &ldquo;{deleting?.name}&rdquo; from every message it's applied to. This can't be undone.
189
- </p>
190
- <div className="flex justify-end gap-2">
191
- <Button type="button" variant="secondary" className="!w-auto" onClick={() => setDeleting(null)}>
192
- Cancel
193
- </Button>
194
- <Button type="button" className="!w-auto" loading={deleteBusy} disabled={deleteBusy} onClick={handleDelete}>
195
- Delete label
196
- </Button>
197
- </div>
198
- </Modal>
199
- </div>
200
- );
201
- }
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 { Label, createLabel, deleteLabel, listLabels, updateLabel } from "@rapidmx/react-shared/mail/labelsApi.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
+ import FormField from "@rapidmx/react-shared/components/forms/FormField.js";
13
+ import Modal from "@rapidmx/react-shared/components/overlays/Modal.js";
14
+
15
+ export type SettingsLabelsPageProps = Omit<SettingsShellProps, "active">;
16
+
17
+ function SettingsLabelsPage(props: SettingsLabelsPageProps) {
18
+ return (
19
+ <SettingsShell {...props} active="labels">
20
+ <LabelsContent />
21
+ </SettingsShell>
22
+ );
23
+ }
24
+
25
+ const DEFAULT_COLOR = "#6366f1";
26
+
27
+ function LabelsContent() {
28
+ const { mailboxUid } = useSettingsShell();
29
+ const [labels, setLabels] = useState<Label[]>([]);
30
+ const [loading, setLoading] = useState(true);
31
+ const [error, setError] = useState<string | null>(null);
32
+
33
+ // `editing === null` means the create form; otherwise the label currently being renamed/recolored.
34
+ const [editing, setEditing] = useState<Label | null>(null);
35
+ const [formOpen, setFormOpen] = useState(false);
36
+ const [formName, setFormName] = useState("");
37
+ const [formColor, setFormColor] = useState(DEFAULT_COLOR);
38
+ const [formError, setFormError] = useState<string | null>(null);
39
+ const [saving, setSaving] = useState(false);
40
+
41
+ const [deleting, setDeleting] = useState<Label | null>(null);
42
+ const [deleteError, setDeleteError] = useState<string | null>(null);
43
+ const [deleteBusy, setDeleteBusy] = useState(false);
44
+
45
+ // `SettingsShell` only ever renders its children once `mailboxUid` has resolved — same established
46
+ // non-null pattern as `apps/www/settings/auto-reply/index.tsx`.
47
+ useEffect(() => {
48
+ listLabels(mailboxUid!)
49
+ .then(setLabels)
50
+ .catch((err) => setError(err instanceof ApiRequestError ? err.message : "Could not load labels."))
51
+ .finally(() => setLoading(false));
52
+ }, [mailboxUid]);
53
+
54
+ function openCreateForm() {
55
+ setEditing(null);
56
+ setFormName("");
57
+ setFormColor(DEFAULT_COLOR);
58
+ setFormError(null);
59
+ setFormOpen(true);
60
+ }
61
+
62
+ function openEditForm(label: Label) {
63
+ setEditing(label);
64
+ setFormName(label.name);
65
+ setFormColor(label.color ?? DEFAULT_COLOR);
66
+ setFormError(null);
67
+ setFormOpen(true);
68
+ }
69
+
70
+ async function handleSave() {
71
+ setSaving(true);
72
+ setFormError(null);
73
+ try {
74
+ if (editing) {
75
+ const updated = await updateLabel({ uid: editing.uid, version: editing.version, name: formName, color: formColor });
76
+ setLabels((prev) => prev.map((l) => (l.uid === updated.uid ? updated : l)));
77
+ } else {
78
+ const created = await createLabel({ mailboxUid: mailboxUid!, name: formName, color: formColor });
79
+ setLabels((prev) => [...prev, created]);
80
+ }
81
+ setFormOpen(false);
82
+ } catch (err) {
83
+ setFormError(err instanceof ApiRequestError ? err.message : "Could not save this label.");
84
+ } finally {
85
+ setSaving(false);
86
+ }
87
+ }
88
+
89
+ async function handleDelete() {
90
+ setDeleteBusy(true);
91
+ setDeleteError(null);
92
+ try {
93
+ await deleteLabel(deleting!.uid, deleting!.version);
94
+ setLabels((prev) => prev.filter((l) => l.uid !== deleting!.uid));
95
+ setDeleting(null);
96
+ } catch (err) {
97
+ setDeleteError(err instanceof ApiRequestError ? err.message : "Could not delete this label.");
98
+ } finally {
99
+ setDeleteBusy(false);
100
+ }
101
+ }
102
+
103
+ return (
104
+ <div className="flex-1 min-w-0 overflow-y-auto p-6">
105
+ <div className="max-w-3xl">
106
+ <div className="flex items-center justify-between mb-5">
107
+ <h1 className="text-lg font-bold tracking-tight">Labels</h1>
108
+ <Button type="button" className="!w-auto" onClick={openCreateForm}>
109
+ + New label
110
+ </Button>
111
+ </div>
112
+
113
+ {error && <Alert>{error}</Alert>}
114
+
115
+ {loading ? (
116
+ <p className="text-sm text-text-muted">Loading&hellip;</p>
117
+ ) : labels.length === 0 ? (
118
+ <p className="text-sm text-text-muted">No labels yet.</p>
119
+ ) : (
120
+ <ul className="flex flex-col gap-2">
121
+ {labels.map((label) => (
122
+ <li key={label.uid} className="flex items-center justify-between border border-border rounded-md p-3">
123
+ <div className="flex items-center gap-2">
124
+ <span
125
+ className="w-3 h-3 rounded-full shrink-0"
126
+ style={{ backgroundColor: label.color ?? DEFAULT_COLOR }}
127
+ aria-hidden="true"
128
+ />
129
+ <span className="text-sm font-semibold">{label.name}</span>
130
+ </div>
131
+ <div className="flex items-center gap-3">
132
+ <button
133
+ type="button"
134
+ className="text-primary-dark hover:underline font-medium text-sm"
135
+ onClick={() => openEditForm(label)}
136
+ >
137
+ Edit
138
+ </button>
139
+ <button
140
+ type="button"
141
+ className="text-danger hover:underline font-medium text-sm"
142
+ onClick={() => {
143
+ setDeleting(label);
144
+ setDeleteError(null);
145
+ }}
146
+ >
147
+ Delete
148
+ </button>
149
+ </div>
150
+ </li>
151
+ ))}
152
+ </ul>
153
+ )}
154
+ </div>
155
+
156
+ <Modal open={formOpen} onClose={() => setFormOpen(false)} title={editing ? "Edit label" : "New label"}>
157
+ {formError && <Alert>{formError}</Alert>}
158
+ <FormField label="Name" htmlFor="label-name">
159
+ <input
160
+ id="label-name"
161
+ type="text"
162
+ value={formName}
163
+ onChange={(e) => setFormName(e.target.value)}
164
+ className="w-full text-sm px-3 py-1.5 rounded-md border border-border bg-surface"
165
+ />
166
+ </FormField>
167
+ <FormField label="Color" htmlFor="label-color">
168
+ <input
169
+ id="label-color"
170
+ type="color"
171
+ value={formColor}
172
+ onChange={(e) => setFormColor(e.target.value)}
173
+ className="h-9 w-16 rounded-md border border-border bg-surface"
174
+ />
175
+ </FormField>
176
+ <div className="flex justify-end gap-2 mt-2">
177
+ <Button type="button" variant="secondary" className="!w-auto" onClick={() => setFormOpen(false)}>
178
+ Cancel
179
+ </Button>
180
+ <Button type="button" className="!w-auto" loading={saving} disabled={saving || !formName.trim()} onClick={handleSave}>
181
+ Save
182
+ </Button>
183
+ </div>
184
+ </Modal>
185
+
186
+ <Modal open={!!deleting} onClose={() => setDeleting(null)} title="Delete this label?">
187
+ {deleteError && <Alert>{deleteError}</Alert>}
188
+ <p className="text-sm text-text-muted mb-4">
189
+ This removes &ldquo;{deleting?.name}&rdquo; from every message it's applied to. This can't be undone.
190
+ </p>
191
+ <div className="flex justify-end gap-2">
192
+ <Button type="button" variant="secondary" className="!w-auto" onClick={() => setDeleting(null)}>
193
+ Cancel
194
+ </Button>
195
+ <Button type="button" className="!w-auto" loading={deleteBusy} disabled={deleteBusy} onClick={handleDelete}>
196
+ Delete label
197
+ </Button>
198
+ </div>
199
+ </Modal>
200
+ </div>
201
+ );
202
+ }
203
+
204
+ export default routedPage("/settings/labels", SettingsLabelsPage);
@@ -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, { ChangeEvent, FormEvent, useEffect, useRef, useState } from "react";
6
7
  import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
7
8
  import {
@@ -27,6 +28,7 @@ import SettingsShell, { SettingsShellProps, useSettingsShell } from "../../../sh
27
28
  import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
28
29
  import Button from "@rapidmx/react-shared/components/buttons/Button.js";
29
30
  import Modal from "@rapidmx/react-shared/components/overlays/Modal.js";
31
+ import { notifyApiError } from "../../../shared/notifications/apiErrors.js";
30
32
 
31
33
  const NON_MAIL_FOLDER_TYPES = new Set(["calendar", "contacts", "tasks", "notes"]);
32
34
 
@@ -51,7 +53,7 @@ function statusLabel(status: string): string {
51
53
 
52
54
  export type SettingsPrivacyPageProps = Omit<SettingsShellProps, "active">;
53
55
 
54
- export default function SettingsPrivacyPage(props: SettingsPrivacyPageProps) {
56
+ function SettingsPrivacyPage(props: SettingsPrivacyPageProps) {
55
57
  return (
56
58
  <SettingsShell {...props} active="privacy">
57
59
  <PrivacyContent userUid={props.userUid} />
@@ -124,7 +126,6 @@ function ExportSection({ mailboxUid, mailboxLabel }: { mailboxUid?: string; mail
124
126
  const [loadError, setLoadError] = useState<string | null>(null);
125
127
  const [format, setFormat] = useState<DataExportFormat>("json");
126
128
  const [creating, setCreating] = useState(false);
127
- const [createError, setCreateError] = useState<string | null>(null);
128
129
 
129
130
  // The create form below isn't gated behind `loading`, so a fast create-then-reload can resolve before
130
131
  // the initial mount fetch does; without this guard the mount fetch's now-stale response would land
@@ -151,13 +152,12 @@ function ExportSection({ mailboxUid, mailboxLabel }: { mailboxUid?: string; mail
151
152
 
152
153
  async function handleCreate(e: FormEvent) {
153
154
  e.preventDefault();
154
- setCreateError(null);
155
155
  setCreating(true);
156
156
  try {
157
157
  await createExportRequest({ format });
158
158
  await loadRequests();
159
159
  } catch (err) {
160
- setCreateError(err instanceof ApiRequestError ? err.message : "Could not start this export.");
160
+ notifyApiError(err, "Couldn't start the export");
161
161
  } finally {
162
162
  setCreating(false);
163
163
  }
@@ -190,7 +190,6 @@ function ExportSection({ mailboxUid, mailboxLabel }: { mailboxUid?: string; mail
190
190
  </Button>
191
191
  </form>
192
192
 
193
- {createError && <Alert>{createError}</Alert>}
194
193
  {loadError && <Alert>{loadError}</Alert>}
195
194
 
196
195
  {loading ? (
@@ -241,7 +240,6 @@ function ImportSection({ mailboxUid }: { mailboxUid?: string }) {
241
240
  const [loading, setLoading] = useState(true);
242
241
  const [loadError, setLoadError] = useState<string | null>(null);
243
242
  const [uploading, setUploading] = useState(false);
244
- const [uploadError, setUploadError] = useState<string | null>(null);
245
243
  const fileInputRef = useRef<HTMLInputElement>(null);
246
244
 
247
245
  // Same stale-response guard as ExportSection's loadRequests() - the Upload button is enabled
@@ -286,11 +284,10 @@ function ImportSection({ mailboxUid }: { mailboxUid?: string }) {
286
284
  if (!file || !targetFolderUid) {
287
285
  return;
288
286
  }
289
- setUploadError(null);
290
287
  setUploading(true);
291
288
  uploadMailboxImport(file, { format: importFormatFromFilename(file.name), targetFolderUid })
292
289
  .then(() => loadRequests())
293
- .catch((err) => setUploadError(err instanceof ApiRequestError ? err.message : "Could not upload this file."))
290
+ .catch((err) => notifyApiError(err, "Couldn't upload the file"))
294
291
  .finally(() => setUploading(false));
295
292
  }
296
293
 
@@ -340,7 +337,6 @@ function ImportSection({ mailboxUid }: { mailboxUid?: string }) {
340
337
  />
341
338
 
342
339
  {foldersError && <Alert>{foldersError}</Alert>}
343
- {uploadError && <Alert>{uploadError}</Alert>}
344
340
  {loadError && <Alert>{loadError}</Alert>}
345
341
 
346
342
  {loading ? (
@@ -492,3 +488,5 @@ function ErasureSection({ mailboxLabel }: { mailboxLabel: string }) {
492
488
  </div>
493
489
  );
494
490
  }
491
+
492
+ export default routedPage("/settings/privacy", SettingsPrivacyPage);
@@ -2,16 +2,16 @@
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
- import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
7
7
  import { updateMailbox } from "@rapidmx/react-shared/mail/mailApi.js";
8
8
  import SettingsShell, { SettingsShellProps, useSettingsShell } from "../../../shared/components/settings/layout/SettingsShell.js";
9
- import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
10
9
  import Button from "@rapidmx/react-shared/components/buttons/Button.js";
10
+ import { notifyApiError } from "../../../shared/notifications/apiErrors.js";
11
11
 
12
12
  export type SettingsReadReceiptsPageProps = Omit<SettingsShellProps, "active">;
13
13
 
14
- export default function SettingsReadReceiptsPage(props: SettingsReadReceiptsPageProps) {
14
+ function SettingsReadReceiptsPage(props: SettingsReadReceiptsPageProps) {
15
15
  return (
16
16
  <SettingsShell {...props} active="read-receipts">
17
17
  <ReadReceiptsContent />
@@ -33,13 +33,11 @@ function ReadReceiptsContent() {
33
33
  const [autoSendReceiptsExternal, setAutoSendReceiptsExternal] = useState(mailbox.autoSendReceiptsExternal ?? false);
34
34
  // See auto-reply/index.tsx's identical note - later saves must carry the version the previous save returned.
35
35
  const [version, setVersion] = useState(mailbox.version);
36
- const [error, setError] = useState<string | null>(null);
37
36
  const [saving, setSaving] = useState(false);
38
37
  const [saved, setSaved] = useState(false);
39
38
 
40
39
  async function handleSubmit(e: FormEvent) {
41
40
  e.preventDefault();
42
- setError(null);
43
41
  setSaved(false);
44
42
  setSaving(true);
45
43
  try {
@@ -56,7 +54,7 @@ function ReadReceiptsContent() {
56
54
  setVersion(updated.version);
57
55
  setSaved(true);
58
56
  } catch (err) {
59
- setError(err instanceof ApiRequestError ? err.message : "Could not save read receipt settings.");
57
+ notifyApiError(err, "Couldn't save the read receipt settings");
60
58
  } finally {
61
59
  setSaving(false);
62
60
  }
@@ -72,8 +70,7 @@ function ReadReceiptsContent() {
72
70
  checkbox in Compose always overrides the request-side settings below for that one message.
73
71
  </p>
74
72
 
75
- {error && <Alert>{error}</Alert>}
76
- {saved && !error && <div className="mb-4 text-sm text-success font-medium">Saved.</div>}
73
+ {saved && <div className="mb-4 text-sm text-success font-medium">Saved.</div>}
77
74
 
78
75
  <form onSubmit={handleSubmit} className="flex flex-col gap-5">
79
76
  <fieldset className="flex flex-col gap-2">
@@ -146,3 +143,5 @@ function ReadReceiptsContent() {
146
143
  </div>
147
144
  );
148
145
  }
146
+
147
+ export default routedPage("/settings/read-receipts", SettingsReadReceiptsPage);