@rapidmx/web-client 0.14.1 → 0.16.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 (314) hide show
  1. package/README.md +373 -373
  2. package/apps/admin/branding/index.tsx +39 -39
  3. package/apps/admin/data-requests/index.tsx +482 -480
  4. package/apps/admin/domains/[uid].tsx +164 -163
  5. package/apps/admin/escrow-scopes/[uid].tsx +348 -348
  6. package/apps/admin/index.tsx +127 -124
  7. package/apps/admin/mailboxes/[uid].tsx +269 -224
  8. package/apps/admin/mailboxes/new/index.tsx +28 -28
  9. package/apps/admin/plugins/index.tsx +15 -15
  10. package/apps/admin/retention-policy/index.tsx +39 -39
  11. package/apps/admin/signing-certificates/index.tsx +343 -343
  12. package/apps/escrow/audit-log/index.tsx +196 -196
  13. package/apps/escrow/matters/[uid].tsx +621 -621
  14. package/apps/shared/appearance/AppearanceForm.tsx +9 -1
  15. package/apps/shared/auth/adminAccess.ts +99 -99
  16. package/apps/shared/calendar/bookingPlugin.ts +100 -0
  17. package/apps/shared/calendar/calendarLiveUpdates.ts +27 -0
  18. package/apps/shared/components/admin/layout/AdminShell.tsx +378 -346
  19. package/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.tsx +238 -0
  20. package/apps/shared/components/admin/mailboxes/EscrowScopeCard.tsx +152 -152
  21. package/apps/shared/components/admin/mailboxes/LeftoverMailboxesSection.tsx +194 -0
  22. package/apps/shared/components/admin/settings/BrandingForm.tsx +423 -423
  23. package/apps/shared/components/admin/settings/EncryptionPolicyForm.tsx +119 -119
  24. package/apps/shared/components/admin/settings/MailboxCreateForm.tsx +53 -4
  25. package/apps/shared/components/admin/settings/MailboxPolicyForm.tsx +198 -198
  26. package/apps/shared/components/admin/settings/PluginsManager.tsx +64 -10
  27. package/apps/shared/components/admin/settings/RetentionPolicyForm.tsx +182 -182
  28. package/apps/shared/components/admin/setup/EscrowSetupStep.tsx +288 -288
  29. package/apps/shared/components/admin/setup/SetupWizard.tsx +444 -444
  30. package/apps/shared/components/admin/usePagedList.tsx +129 -129
  31. package/apps/shared/components/calendar/AppointmentQuickForm.tsx +327 -0
  32. package/apps/shared/components/calendar/DescriptionEditor.tsx +274 -0
  33. package/apps/shared/components/calendar/EventDescriptionView.tsx +70 -0
  34. package/apps/shared/components/calendar/EventDetails.tsx +401 -0
  35. package/apps/shared/components/calendar/EventEditor.tsx +635 -0
  36. package/apps/shared/components/calendar/EventExpandedForm.tsx +210 -0
  37. package/apps/shared/components/calendar/EventFormParts.tsx +591 -0
  38. package/apps/shared/components/calendar/EventModal.tsx +206 -946
  39. package/apps/shared/components/calendar/EventQuickForm.tsx +116 -0
  40. package/apps/shared/components/calendar/EventShell.tsx +202 -0
  41. package/apps/shared/components/calendar/FindATime.tsx +379 -0
  42. package/apps/shared/components/calendar/GuestInput.tsx +100 -0
  43. package/apps/shared/components/calendar/LazyDescriptionEditor.tsx +70 -0
  44. package/apps/shared/components/calendar/MonthView.tsx +185 -173
  45. package/apps/shared/components/calendar/QuickCreateFaces.tsx +87 -0
  46. package/apps/shared/components/calendar/QuickCreateTabs.tsx +81 -0
  47. package/apps/shared/components/calendar/QuickFaceFrame.tsx +81 -0
  48. package/apps/shared/components/calendar/RecurrenceEditor.tsx +227 -222
  49. package/apps/shared/components/calendar/RequestChangeForm.tsx +240 -0
  50. package/apps/shared/components/calendar/SplitDayView.tsx +144 -143
  51. package/apps/shared/components/calendar/TaskQuickForm.tsx +289 -0
  52. package/apps/shared/components/calendar/TimeGridView.tsx +246 -244
  53. package/apps/shared/components/calendar/allDay.ts +124 -124
  54. package/apps/shared/components/calendar/eventDialogFields.ts +86 -0
  55. package/apps/shared/components/calendar/eventForm.ts +108 -0
  56. package/apps/shared/components/calendar/eventFormat.ts +210 -0
  57. package/apps/shared/components/calendar/findTimeGrid.ts +90 -0
  58. package/apps/shared/components/calendar/layout/CalendarShell.tsx +201 -200
  59. package/apps/shared/components/calendar/swipeNavigation.ts +14 -0
  60. package/apps/shared/components/contacts/ContactForm.tsx +383 -383
  61. package/apps/shared/components/contacts/ContactsSidebar.tsx +1 -1
  62. package/apps/shared/components/contacts/ContactsToolbar.tsx +103 -98
  63. package/apps/shared/components/contacts/layout/ContactsShell.tsx +217 -216
  64. package/apps/shared/components/escrow/layout/EscrowShell.tsx +155 -155
  65. package/apps/shared/components/layout/AppShell.tsx +480 -480
  66. package/apps/shared/components/layout/FloatingActionButton.tsx +31 -0
  67. package/apps/shared/components/layout/KeyEnrollmentGate.tsx +398 -398
  68. package/apps/shared/components/layout/MailboxProvisioning.tsx +168 -168
  69. package/apps/shared/components/layout/ResponsiveToolbar.tsx +315 -315
  70. package/apps/shared/components/layout/ThemeSwitch.tsx +84 -84
  71. package/apps/shared/components/layout/UserMenu.tsx +439 -372
  72. package/apps/shared/components/mail/ConversationList.tsx +323 -271
  73. package/apps/shared/components/mail/ConversationThreadPane.tsx +130 -5
  74. package/apps/shared/components/mail/InviteCard.tsx +387 -0
  75. package/apps/shared/components/mail/MailSelectionBar.tsx +236 -227
  76. package/apps/shared/components/mail/MessageDetailPane.tsx +1559 -1537
  77. package/apps/shared/components/mail/SwipeRow.tsx +120 -0
  78. package/apps/shared/components/mail/compose/ComposeContext.tsx +312 -312
  79. package/apps/shared/components/mail/compose/ComposeToolbar.tsx +422 -422
  80. package/apps/shared/components/mail/compose/ComposeWindow.tsx +1681 -1673
  81. package/apps/shared/components/mail/compose/RecipientInput.tsx +54 -11
  82. package/apps/shared/components/mail/compose/RichTextEditor.tsx +147 -147
  83. package/apps/shared/components/mail/compose/composeFlushRegistry.ts +60 -60
  84. package/apps/shared/components/mail/compose/quotedBody.ts +161 -161
  85. package/apps/shared/components/mail/invite/InviteRowChip.tsx +104 -0
  86. package/apps/shared/components/mail/invite/InviteRsvpPopover.tsx +271 -0
  87. package/apps/shared/components/mail/invite/InviteTimeline.tsx +200 -0
  88. package/apps/shared/components/mail/invite/ProposeTimeForm.tsx +109 -0
  89. package/apps/shared/components/mail/invite/inviteFormat.ts +110 -0
  90. package/apps/shared/components/mail/invite/inviteStore.ts +114 -0
  91. package/apps/shared/components/mail/layout/FolderBadgeChip.tsx +21 -0
  92. package/apps/shared/components/mail/layout/MailShell.tsx +546 -490
  93. package/apps/shared/components/mail/layout/SidebarSection.tsx +66 -0
  94. package/apps/shared/components/mail/reading/PendingMessageCard.tsx +68 -0
  95. package/apps/shared/components/mail/verificationSeals.ts +125 -125
  96. package/apps/shared/components/rules/RuleBuilder.tsx +311 -311
  97. package/apps/shared/components/settings/SigningCertificateCard.tsx +344 -344
  98. package/apps/shared/components/settings/layout/SettingsShell.tsx +255 -253
  99. package/apps/shared/components/tasks/TasksSidebar.tsx +1 -1
  100. package/apps/shared/components/tasks/layout/TasksShell.tsx +218 -217
  101. package/apps/shared/gestures/useEnterSlide.ts +54 -0
  102. package/apps/shared/gestures/useSwipe.ts +114 -0
  103. package/apps/shared/gestures/useSwipeSlide.ts +113 -0
  104. package/apps/shared/gestures/useWheelPaging.ts +107 -0
  105. package/apps/shared/keyboard/GlobalShortcuts.tsx +51 -51
  106. package/apps/shared/keyboard/ShortcutProvider.tsx +62 -62
  107. package/apps/shared/keyboard/ShortcutsDialog.tsx +84 -84
  108. package/apps/shared/keyboard/dispatch.ts +124 -124
  109. package/apps/shared/keyboard/format.ts +89 -89
  110. package/apps/shared/keyboard/keymap.ts +114 -114
  111. package/apps/shared/keyboard/registry.ts +65 -65
  112. package/apps/shared/keyboard/targets.ts +79 -79
  113. package/apps/shared/mail/folderCounts.ts +17 -0
  114. package/apps/shared/mail/folderTree.ts +143 -143
  115. package/apps/shared/mail/listAllPages.ts +39 -39
  116. package/apps/shared/mail/newMailNotifications.ts +3 -15
  117. package/apps/shared/mail/outbox/outgoingReplies.ts +192 -0
  118. package/apps/shared/mail/outbox/sendJob.ts +19 -1
  119. package/apps/shared/mail/outbox/sendOutcomes.ts +13 -4
  120. package/apps/shared/mail/primaryMailbox.ts +71 -0
  121. package/apps/shared/mail/useCollapsedSections.ts +146 -0
  122. package/apps/shared/mail/useMailConnection.ts +205 -203
  123. package/apps/shared/mail/useMailLiveUpdates.ts +277 -277
  124. package/apps/shared/mail/useMarkMessageRead.ts +47 -47
  125. package/apps/shared/mail/useNewMailNotifications.ts +2 -2
  126. package/apps/shared/navigation/AppRouter.tsx +300 -300
  127. package/apps/shared/navigation/routerContext.tsx +83 -83
  128. package/apps/shared/notifications/preferences.ts +36 -0
  129. package/apps/shared/notifications/store.ts +9 -0
  130. package/apps/shared/search/LocalIndexLifecycle.tsx +114 -114
  131. package/apps/shared/search/crossMailboxSearch.ts +169 -0
  132. package/apps/shared/search/localIndexBuilder.ts +481 -481
  133. package/apps/shared/signing/enrollmentStorage.ts +33 -33
  134. package/apps/shared/signing/enrollmentTracker.ts +385 -385
  135. package/apps/shared/signing/enrollmentView.ts +251 -251
  136. package/apps/shared/signing/useNow.ts +19 -19
  137. package/apps/shared/signing/useSigningEnrollmentWatcher.ts +90 -90
  138. package/apps/shared/styles/app.css +396 -386
  139. package/apps/www/_routes.ts +1 -0
  140. package/apps/www/calendar/index.tsx +578 -469
  141. package/apps/www/contacts/[uid].tsx +109 -109
  142. package/apps/www/contacts/index.tsx +6 -0
  143. package/apps/www/index.tsx +2847 -2332
  144. package/apps/www/messages/[uid].tsx +135 -108
  145. package/apps/www/settings/auto-reply/index.tsx +133 -133
  146. package/apps/www/settings/encryption/index.tsx +1287 -1287
  147. package/apps/www/settings/filters/[uid].tsx +176 -176
  148. package/apps/www/settings/filters/index.tsx +102 -102
  149. package/apps/www/settings/filters/new/index.tsx +140 -140
  150. package/apps/www/settings/labels/index.tsx +204 -204
  151. package/apps/www/settings/privacy/index.tsx +492 -492
  152. package/apps/www/settings/profile/index.tsx +248 -0
  153. package/apps/www/settings/read-receipts/index.tsx +147 -147
  154. package/apps/www/settings/sharing/index.tsx +256 -256
  155. package/apps/www/settings/signatures/[uid].tsx +172 -172
  156. package/apps/www/settings/signatures/index.tsx +88 -88
  157. package/apps/www/settings/signatures/new/index.tsx +135 -135
  158. package/apps/www/tasks/index.tsx +649 -649
  159. package/dist/apps/admin/data-requests/index.js +1 -1
  160. package/dist/apps/admin/domains/[uid].js +4 -2
  161. package/dist/apps/admin/index.js +2 -1
  162. package/dist/apps/admin/mailboxes/[uid].js +12 -2
  163. package/dist/apps/shared/appearance/AppearanceForm.js +9 -1
  164. package/dist/apps/shared/calendar/bookingPlugin.d.ts +50 -0
  165. package/dist/apps/shared/calendar/bookingPlugin.js +57 -0
  166. package/dist/apps/shared/calendar/calendarLiveUpdates.d.ts +11 -0
  167. package/dist/apps/shared/calendar/calendarLiveUpdates.js +23 -0
  168. package/dist/apps/shared/components/admin/layout/AdminShell.d.ts +1 -1
  169. package/dist/apps/shared/components/admin/layout/AdminShell.js +7 -3
  170. package/dist/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.d.ts +35 -0
  171. package/dist/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.js +115 -0
  172. package/dist/apps/shared/components/admin/mailboxes/LeftoverMailboxesSection.d.ts +16 -0
  173. package/dist/apps/shared/components/admin/mailboxes/LeftoverMailboxesSection.js +92 -0
  174. package/dist/apps/shared/components/admin/settings/BrandingForm.js +3 -3
  175. package/dist/apps/shared/components/admin/settings/MailboxCreateForm.js +28 -4
  176. package/dist/apps/shared/components/admin/settings/PluginsManager.js +40 -6
  177. package/dist/apps/shared/components/calendar/AppointmentQuickForm.d.ts +47 -0
  178. package/dist/apps/shared/components/calendar/AppointmentQuickForm.js +135 -0
  179. package/dist/apps/shared/components/calendar/DescriptionEditor.d.ts +26 -0
  180. package/dist/apps/shared/components/calendar/DescriptionEditor.js +192 -0
  181. package/dist/apps/shared/components/calendar/EventDescriptionView.d.ts +14 -0
  182. package/dist/apps/shared/components/calendar/EventDescriptionView.js +39 -0
  183. package/dist/apps/shared/components/calendar/EventDetails.d.ts +33 -0
  184. package/dist/apps/shared/components/calendar/EventDetails.js +137 -0
  185. package/dist/apps/shared/components/calendar/EventEditor.d.ts +59 -0
  186. package/dist/apps/shared/components/calendar/EventEditor.js +544 -0
  187. package/dist/apps/shared/components/calendar/EventExpandedForm.d.ts +14 -0
  188. package/dist/apps/shared/components/calendar/EventExpandedForm.js +44 -0
  189. package/dist/apps/shared/components/calendar/EventFormParts.d.ts +70 -0
  190. package/dist/apps/shared/components/calendar/EventFormParts.js +191 -0
  191. package/dist/apps/shared/components/calendar/EventModal.d.ts +26 -13
  192. package/dist/apps/shared/components/calendar/EventModal.js +57 -459
  193. package/dist/apps/shared/components/calendar/EventQuickForm.d.ts +18 -0
  194. package/dist/apps/shared/components/calendar/EventQuickForm.js +25 -0
  195. package/dist/apps/shared/components/calendar/EventShell.d.ts +64 -0
  196. package/dist/apps/shared/components/calendar/EventShell.js +134 -0
  197. package/dist/apps/shared/components/calendar/FindATime.d.ts +18 -0
  198. package/dist/apps/shared/components/calendar/FindATime.js +190 -0
  199. package/dist/apps/shared/components/calendar/GuestInput.d.ts +45 -0
  200. package/dist/apps/shared/components/calendar/GuestInput.js +30 -0
  201. package/dist/apps/shared/components/calendar/LazyDescriptionEditor.d.ts +8 -0
  202. package/dist/apps/shared/components/calendar/LazyDescriptionEditor.js +44 -0
  203. package/dist/apps/shared/components/calendar/MonthView.d.ts +5 -1
  204. package/dist/apps/shared/components/calendar/MonthView.js +11 -5
  205. package/dist/apps/shared/components/calendar/QuickCreateFaces.d.ts +30 -0
  206. package/dist/apps/shared/components/calendar/QuickCreateFaces.js +44 -0
  207. package/dist/apps/shared/components/calendar/QuickCreateTabs.d.ts +24 -0
  208. package/dist/apps/shared/components/calendar/QuickCreateTabs.js +46 -0
  209. package/dist/apps/shared/components/calendar/QuickFaceFrame.d.ts +25 -0
  210. package/dist/apps/shared/components/calendar/QuickFaceFrame.js +19 -0
  211. package/dist/apps/shared/components/calendar/RecurrenceEditor.d.ts +4 -1
  212. package/dist/apps/shared/components/calendar/RecurrenceEditor.js +2 -2
  213. package/dist/apps/shared/components/calendar/RequestChangeForm.d.ts +36 -0
  214. package/dist/apps/shared/components/calendar/RequestChangeForm.js +127 -0
  215. package/dist/apps/shared/components/calendar/SplitDayView.d.ts +3 -2
  216. package/dist/apps/shared/components/calendar/SplitDayView.js +3 -2
  217. package/dist/apps/shared/components/calendar/TaskQuickForm.d.ts +40 -0
  218. package/dist/apps/shared/components/calendar/TaskQuickForm.js +101 -0
  219. package/dist/apps/shared/components/calendar/TimeGridView.d.ts +4 -2
  220. package/dist/apps/shared/components/calendar/TimeGridView.js +3 -2
  221. package/dist/apps/shared/components/calendar/eventDialogFields.d.ts +28 -0
  222. package/dist/apps/shared/components/calendar/eventDialogFields.js +59 -0
  223. package/dist/apps/shared/components/calendar/eventForm.d.ts +98 -0
  224. package/dist/apps/shared/components/calendar/eventForm.js +1 -0
  225. package/dist/apps/shared/components/calendar/eventFormat.d.ts +80 -0
  226. package/dist/apps/shared/components/calendar/eventFormat.js +190 -0
  227. package/dist/apps/shared/components/calendar/findTimeGrid.d.ts +30 -0
  228. package/dist/apps/shared/components/calendar/findTimeGrid.js +83 -0
  229. package/dist/apps/shared/components/calendar/layout/CalendarShell.js +3 -2
  230. package/dist/apps/shared/components/calendar/swipeNavigation.d.ts +9 -0
  231. package/dist/apps/shared/components/calendar/swipeNavigation.js +8 -0
  232. package/dist/apps/shared/components/contacts/ContactsSidebar.js +1 -1
  233. package/dist/apps/shared/components/contacts/ContactsToolbar.d.ts +3 -1
  234. package/dist/apps/shared/components/contacts/ContactsToolbar.js +4 -2
  235. package/dist/apps/shared/components/contacts/layout/ContactsShell.js +4 -3
  236. package/dist/apps/shared/components/layout/FloatingActionButton.d.ts +13 -0
  237. package/dist/apps/shared/components/layout/FloatingActionButton.js +9 -0
  238. package/dist/apps/shared/components/layout/UserMenu.d.ts +14 -9
  239. package/dist/apps/shared/components/layout/UserMenu.js +74 -26
  240. package/dist/apps/shared/components/mail/ConversationList.d.ts +12 -1
  241. package/dist/apps/shared/components/mail/ConversationList.js +21 -4
  242. package/dist/apps/shared/components/mail/ConversationThreadPane.d.ts +4 -1
  243. package/dist/apps/shared/components/mail/ConversationThreadPane.js +127 -22
  244. package/dist/apps/shared/components/mail/InviteCard.d.ts +20 -0
  245. package/dist/apps/shared/components/mail/InviteCard.js +150 -0
  246. package/dist/apps/shared/components/mail/MailSelectionBar.d.ts +6 -1
  247. package/dist/apps/shared/components/mail/MailSelectionBar.js +3 -3
  248. package/dist/apps/shared/components/mail/MessageDetailPane.js +13 -2
  249. package/dist/apps/shared/components/mail/SwipeRow.d.ts +22 -0
  250. package/dist/apps/shared/components/mail/SwipeRow.js +71 -0
  251. package/dist/apps/shared/components/mail/compose/ComposeWindow.js +16 -7
  252. package/dist/apps/shared/components/mail/compose/RecipientInput.d.ts +21 -1
  253. package/dist/apps/shared/components/mail/compose/RecipientInput.js +18 -10
  254. package/dist/apps/shared/components/mail/invite/InviteRowChip.d.ts +23 -0
  255. package/dist/apps/shared/components/mail/invite/InviteRowChip.js +46 -0
  256. package/dist/apps/shared/components/mail/invite/InviteRsvpPopover.d.ts +20 -0
  257. package/dist/apps/shared/components/mail/invite/InviteRsvpPopover.js +141 -0
  258. package/dist/apps/shared/components/mail/invite/InviteTimeline.d.ts +27 -0
  259. package/dist/apps/shared/components/mail/invite/InviteTimeline.js +120 -0
  260. package/dist/apps/shared/components/mail/invite/ProposeTimeForm.d.ts +17 -0
  261. package/dist/apps/shared/components/mail/invite/ProposeTimeForm.js +45 -0
  262. package/dist/apps/shared/components/mail/invite/inviteFormat.d.ts +28 -0
  263. package/dist/apps/shared/components/mail/invite/inviteFormat.js +94 -0
  264. package/dist/apps/shared/components/mail/invite/inviteStore.d.ts +28 -0
  265. package/dist/apps/shared/components/mail/invite/inviteStore.js +95 -0
  266. package/dist/apps/shared/components/mail/layout/FolderBadgeChip.d.ts +6 -0
  267. package/dist/apps/shared/components/mail/layout/FolderBadgeChip.js +9 -0
  268. package/dist/apps/shared/components/mail/layout/MailShell.d.ts +10 -0
  269. package/dist/apps/shared/components/mail/layout/MailShell.js +76 -46
  270. package/dist/apps/shared/components/mail/layout/SidebarSection.d.ts +23 -0
  271. package/dist/apps/shared/components/mail/layout/SidebarSection.js +16 -0
  272. package/dist/apps/shared/components/mail/reading/PendingMessageCard.d.ts +14 -0
  273. package/dist/apps/shared/components/mail/reading/PendingMessageCard.js +16 -0
  274. package/dist/apps/shared/components/settings/layout/SettingsShell.js +7 -5
  275. package/dist/apps/shared/components/tasks/TasksSidebar.js +1 -1
  276. package/dist/apps/shared/components/tasks/layout/TasksShell.js +4 -3
  277. package/dist/apps/shared/gestures/useEnterSlide.d.ts +14 -0
  278. package/dist/apps/shared/gestures/useEnterSlide.js +47 -0
  279. package/dist/apps/shared/gestures/useSwipe.d.ts +45 -0
  280. package/dist/apps/shared/gestures/useSwipe.js +90 -0
  281. package/dist/apps/shared/gestures/useSwipeSlide.d.ts +40 -0
  282. package/dist/apps/shared/gestures/useSwipeSlide.js +95 -0
  283. package/dist/apps/shared/gestures/useWheelPaging.d.ts +32 -0
  284. package/dist/apps/shared/gestures/useWheelPaging.js +85 -0
  285. package/dist/apps/shared/mail/folderCounts.d.ts +10 -0
  286. package/dist/apps/shared/mail/folderCounts.js +12 -0
  287. package/dist/apps/shared/mail/newMailNotifications.d.ts +3 -8
  288. package/dist/apps/shared/mail/newMailNotifications.js +3 -12
  289. package/dist/apps/shared/mail/outbox/outgoingReplies.d.ts +80 -0
  290. package/dist/apps/shared/mail/outbox/outgoingReplies.js +131 -0
  291. package/dist/apps/shared/mail/outbox/sendJob.d.ts +5 -1
  292. package/dist/apps/shared/mail/outbox/sendJob.js +14 -1
  293. package/dist/apps/shared/mail/outbox/sendOutcomes.js +13 -4
  294. package/dist/apps/shared/mail/primaryMailbox.d.ts +29 -0
  295. package/dist/apps/shared/mail/primaryMailbox.js +61 -0
  296. package/dist/apps/shared/mail/useCollapsedSections.d.ts +32 -0
  297. package/dist/apps/shared/mail/useCollapsedSections.js +110 -0
  298. package/dist/apps/shared/mail/useMailConnection.js +3 -1
  299. package/dist/apps/shared/mail/useNewMailNotifications.js +3 -2
  300. package/dist/apps/shared/notifications/preferences.d.ts +13 -0
  301. package/dist/apps/shared/notifications/preferences.js +36 -0
  302. package/dist/apps/shared/notifications/store.d.ts +1 -0
  303. package/dist/apps/shared/notifications/store.js +7 -23
  304. package/dist/apps/shared/search/crossMailboxSearch.d.ts +72 -0
  305. package/dist/apps/shared/search/crossMailboxSearch.js +136 -0
  306. package/dist/apps/shared/styles/app.css +396 -386
  307. package/dist/apps/www/_routes.js +1 -0
  308. package/dist/apps/www/calendar/index.js +78 -7
  309. package/dist/apps/www/contacts/index.js +3 -1
  310. package/dist/apps/www/index.js +548 -210
  311. package/dist/apps/www/messages/[uid].js +13 -5
  312. package/dist/apps/www/settings/profile/index.d.ts +4 -0
  313. package/dist/apps/www/settings/profile/index.js +124 -0
  314. package/package.json +2 -2
@@ -6,6 +6,7 @@ import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
6
6
  import {
7
7
  Attachment,
8
8
  ComposeRecipientInput,
9
+ DraftThreading,
9
10
  Mailbox,
10
11
  Message,
11
12
  assembleDraft,
@@ -26,6 +27,7 @@ import { policyCanAutoEncrypt } from "../../components/mail/compose/encryptionRe
26
27
  import { NotificationAction, dismiss, notify } from "../../notifications/store.js";
27
28
  import { INLINE_IMAGE_PATTERN, SendBlock, SendDecisionInput, decideSend } from "./sendDecision.js";
28
29
  import { beginPendingSend, finishPendingSend, setPendingStage } from "./pendingSends.js";
30
+ import { failOutgoing, forgetOutgoing, markOutgoingSending, markOutgoingSent, trackOutgoing } from "./outgoingReplies.js";
29
31
  import { openComposeFromOutside, requestUnlockFromOutside } from "./composeBridge.js";
30
32
  import { OutboxCountTracker, sendState } from "./sendState.js";
31
33
 
@@ -60,6 +62,10 @@ export interface SendRequest {
60
62
  forcePlaintext: boolean;
61
63
  /** Settles when the window's own autosaves have landed, so none can overwrite the assembly below. */
62
64
  saved?: Promise<unknown>;
65
+ /** The thread this message continues (a reply or forward), as the compose session was opened with it - what lets the conversation on screen
66
+ * show the message as soon as it is sent (see `outgoingReplies.ts`). Absent for a new message, and for a draft opened again, which the draft's own
67
+ * `inReplyTo`/`references` stand in for. */
68
+ threading?: DraftThreading;
63
69
  }
64
70
 
65
71
  /** How long a background send waits for the mailbox / encryption policy it did not have, before it goes ahead treating them as unknown. Nobody waits for it. */
@@ -311,7 +317,7 @@ export function describeMessage(subject: string, recipients: string): string {
311
317
 
312
318
  /** Re-opens the compose window around a request's message, with everything as it was typed. */
313
319
  export function openDraftFromRequest(request: SendRequest): boolean {
314
- return openComposeFromOutside({
320
+ const opened = openComposeFromOutside({
315
321
  mailboxUid: request.mailboxUid,
316
322
  resume: {
317
323
  draft: request.draft,
@@ -327,6 +333,11 @@ export function openDraftFromRequest(request: SendRequest): boolean {
327
333
  encryptRequested: request.encryptRequested,
328
334
  },
329
335
  });
336
+ // Being edited again, it is no longer a message on its way in the conversation.
337
+ if (opened) {
338
+ forgetOutgoing(request.draft.uid);
339
+ }
340
+ return opened;
330
341
  }
331
342
 
332
343
  function failureNotificationId(draftUid: string): string {
@@ -363,6 +374,8 @@ function notifyFailure(request: SendRequest, failure: Failure): void {
363
374
  details: failure.details,
364
375
  actions,
365
376
  });
377
+ // The conversation on screen shows the same failure under the message, with the same ways out.
378
+ failOutgoing(uid, failure.message, actions);
366
379
  }
367
380
 
368
381
  const SENT_BURST_MS = 6_000;
@@ -389,6 +402,8 @@ export function startSend(request: SendRequest): boolean {
389
402
  }
390
403
  // The failure of an earlier attempt is resolved by trying again.
391
404
  dismiss(failureNotificationId(uid));
405
+ // A reply or forward is on screen in its conversation from this moment, before any of the work below.
406
+ trackOutgoing(request);
392
407
  const tracker = sendState.countTracker?.(request.mailboxUid);
393
408
  void attempt(request).then((outcome) => {
394
409
  finishPendingSend(uid);
@@ -402,6 +417,8 @@ export function startSend(request: SendRequest): boolean {
402
417
  retain(request);
403
418
  sendState.queuedListener?.(request.mailboxUid);
404
419
  } else if (outcome.kind === "sent") {
420
+ // A server with no queue relayed it before answering: the Sent Items copy exists already.
421
+ markOutgoingSent(uid);
405
422
  notifySent();
406
423
  } else {
407
424
  notify({
@@ -420,6 +437,7 @@ export function startSend(request: SendRequest): boolean {
420
437
  export async function retrySend(request: SendRequest): Promise<void> {
421
438
  if (await alreadyAccepted(request)) {
422
439
  dismiss(failureNotificationId(request.draft.uid));
440
+ markOutgoingSending(request.draft.uid);
423
441
  notify({ id: `already-sent:${request.draft.uid}`, kind: "info", title: "This message is already on its way", timeoutMs: 5_000 });
424
442
  return;
425
443
  }
@@ -9,6 +9,7 @@ import { loadOriginalMessage } from "../../components/mail/compose/quotedBody.js
9
9
  import { notifyApiError } from "../../notifications/apiErrors.js";
10
10
  import { dismiss, notify } from "../../notifications/store.js";
11
11
  import { beginPendingSend, finishPendingSend, setPendingStage } from "./pendingSends.js";
12
+ import { failOutgoing, forgetOutgoing, markOutgoingSending, markOutgoingSent } from "./outgoingReplies.js";
12
13
  import { openComposeFromOutside } from "./composeBridge.js";
13
14
  import { describeMessage, forgetRetainedRequest, notifySent, openDraftFromRequest, retainedRequest } from "./sendJob.js";
14
15
 
@@ -45,6 +46,8 @@ function escapeHtml(text: string): string {
45
46
  async function openFailedDraft(event: SendEvent): Promise<void> {
46
47
  try {
47
48
  const moved = await moveBackToDrafts(event.uid, event.mailboxUid ?? "");
49
+ // Back in Drafts it is no longer a message on its way in its conversation.
50
+ forgetOutgoing(event.uid);
48
51
  const request = retainedRequest(event.uid);
49
52
  if (request) {
50
53
  openDraftFromRequest({ ...request, draft: moved });
@@ -90,6 +93,7 @@ async function retryFailed(event: SendEvent): Promise<void> {
90
93
  return;
91
94
  }
92
95
  dismiss(failedId(event.uid));
96
+ markOutgoingSending(event.uid);
93
97
  try {
94
98
  setPendingStage(event.uid, "queuing");
95
99
  const result = await queueMessageSend(event.uid);
@@ -108,6 +112,8 @@ export function handleSendEvent(event: SendEvent): void {
108
112
  if (event.action === "send-succeeded") {
109
113
  dismiss(retryId(event.uid));
110
114
  dismiss(failedId(event.uid));
115
+ // The conversation on screen swaps the message it drew for the Sent Items copy (a message this tab did not send has nothing drawn).
116
+ markOutgoingSent(event.uid);
111
117
  // The server reports every message it relays, scheduled ones from long ago included: only a message this tab queued in the background is
112
118
  // worth a confirmation (several are one pop-up counting up). Anything else - a send-later message going out, another tab's send - is quiet.
113
119
  if (retainedRequest(event.uid)) {
@@ -132,15 +138,18 @@ export function handleSendEvent(event: SendEvent): void {
132
138
  return;
133
139
  }
134
140
  dismiss(retryId(event.uid));
141
+ const actions = [
142
+ { label: "Retry", onClick: () => void retryFailed(event) },
143
+ { label: "Open draft", onClick: () => void openFailedDraft(event) },
144
+ ];
135
145
  notify({
136
146
  id: failedId(event.uid),
137
147
  kind: "error",
138
148
  title: "This message wasn't sent",
139
149
  message: `${what}: ${failure.message}`,
140
150
  details: failure.lines,
141
- actions: [
142
- { label: "Retry", onClick: () => void retryFailed(event) },
143
- { label: "Open draft", onClick: () => void openFailedDraft(event) },
144
- ],
151
+ actions,
145
152
  });
153
+ // The conversation on screen shows the same failure under the message, with the same ways out.
154
+ failOutgoing(event.uid, failure.message, actions);
146
155
  }
@@ -0,0 +1,71 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ /**
6
+ * Which of the caller's mailboxes is "theirs", and the order every list of them is shown in - the one definition the mail, calendar,
7
+ * contacts, tasks and settings shells, the compose window's From picker and the account menu all use, so a shared mailbox that happens
8
+ * to sort first (or that the server lists first) is never the one an app opens on or a picker defaults to.
9
+ *
10
+ * An **own** mailbox is one whose `ownerUserUid` is the signed-in user.
11
+ *
12
+ * A **shared** mailbox is any other one the caller merely has access to: an ownerless (shared/organization) mailbox, or a delegate grant on
13
+ * somebody else's (`isSharedWithMe()`).
14
+ *
15
+ * The **primary** mailbox is the caller's own mailbox - the earliest created, when they own several - else the first mailbox that isn't
16
+ * shared, else the first one there is.
17
+ *
18
+ * `orderMailboxes()` puts the primary mailbox first, then the caller's other mailboxes, then the shared ones, each group alphabetical.
19
+ */
20
+ import { isSharedWithMe, type Mailbox } from "@rapidmx/react-shared/mail/mailApi.js";
21
+
22
+ /** Whether `mailbox` is the signed-in user's own. */
23
+ export function isOwnMailbox(mailbox: Mailbox, userUid: string | undefined): boolean {
24
+ return !!userUid && mailbox.ownerUserUid === userUid;
25
+ }
26
+
27
+ /** Whether `mailbox` is one the caller only has access to (an ownerless one, or somebody else's shared with them). */
28
+ export function isSharedMailbox(mailbox: Mailbox, userUid: string | undefined): boolean {
29
+ if (isOwnMailbox(mailbox, userUid)) {
30
+ return false;
31
+ }
32
+ // The server says how the caller reaches a mailbox (`accessRole`); an older one doesn't, and then a mailbox that isn't the known
33
+ // caller's own is somebody else's.
34
+ return mailbox.accessRole ? isSharedWithMe(mailbox) : !!userUid || isSharedWithMe(mailbox);
35
+ }
36
+
37
+ function createdAt(mailbox: Mailbox): number {
38
+ const time = Date.parse(mailbox.dateCreated);
39
+ return Number.isNaN(time) ? Infinity : time;
40
+ }
41
+
42
+ /** The caller's own mailboxes, earliest created first (the given order among ones created together). */
43
+ export function ownMailboxes(mailboxes: readonly Mailbox[], userUid: string | undefined): Mailbox[] {
44
+ return mailboxes.filter((mailbox) => isOwnMailbox(mailbox, userUid)).sort((a, b) => createdAt(a) - createdAt(b));
45
+ }
46
+
47
+ /** The mailbox to open and to default pickers to: see the rules at the top of this file. `undefined` only for no mailboxes at all. */
48
+ export function primaryMailbox(mailboxes: readonly Mailbox[], userUid: string | undefined): Mailbox | undefined {
49
+ return ownMailboxes(mailboxes, userUid)[0] ?? mailboxes.find((mailbox) => !isSharedMailbox(mailbox, userUid)) ?? mailboxes[0];
50
+ }
51
+
52
+ /** `primaryMailbox()`'s uid. */
53
+ export function primaryMailboxUid(mailboxes: readonly Mailbox[], userUid: string | undefined): string | undefined {
54
+ return primaryMailbox(mailboxes, userUid)?.uid;
55
+ }
56
+
57
+ /** `mailboxes` in display order: the primary mailbox, then the caller's other mailboxes, then the shared ones - each group by name. Never
58
+ * changes the list it is given. */
59
+ export function orderMailboxes(mailboxes: readonly Mailbox[], userUid: string | undefined): Mailbox[] {
60
+ const primary = primaryMailbox(mailboxes, userUid);
61
+ const rank = (mailbox: Mailbox) => (mailbox === primary ? 0 : isSharedMailbox(mailbox, userUid) ? 2 : 1);
62
+ // Tolerates a mailbox that says nothing of its name or address (an older server's, or a test fixture's).
63
+ const name = (mailbox: Mailbox) => mailbox.displayName ?? "";
64
+ const address = (mailbox: Mailbox) => mailbox.primarySmtpAddress ?? "";
65
+ return [...mailboxes].sort(
66
+ (a, b) =>
67
+ rank(a) - rank(b) ||
68
+ name(a).localeCompare(name(b), undefined, { sensitivity: "base" }) ||
69
+ address(a).localeCompare(address(b)),
70
+ );
71
+ }
@@ -0,0 +1,146 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ /**
6
+ * Which of the Mail sidebar's sections - "All mailboxes" and one per mailbox - are open, and the reader's own choices about it.
7
+ *
8
+ * A section the reader has never touched follows the **default rule**: "All mailboxes" and the signed-in user's primary mailbox
9
+ * (`primaryMailboxUid()`) are open, every other mailbox is collapsed - so a mailbox that is newly shared with them arrives closed.
10
+ * Once they toggle a section the choice is remembered for that section, replacing the default, whatever the default later says.
11
+ *
12
+ * **Every** section can be collapsed, the one holding the open folder and the primary mailbox included, and a collapsed one stays collapsed
13
+ * across reloads even while its own folder is the selected one: the toggle always toggles and always remembers.
14
+ *
15
+ * The one exception is *navigation*: when the section holding the open folder (`activeId`) **changes** - the reader picks a folder in
16
+ * another section, or a search result or a link lands in one - that section is shown open, so the folder just highlighted is not hidden
17
+ * from them. That is an in-memory override of this page view, never written as the reader's choice: it does not apply to the first
18
+ * section the page resolves (a reload lands on whatever was chosen), and it ends when they navigate to yet another section. Toggling
19
+ * the section it opened forgets the override and remembers the reader's new choice (the opposite of what was showing).
20
+ *
21
+ * The choices are kept per signed-in user, per device, in `localStorage` (`rapidmx:mail-sidebar-collapsed:<userUid>`), the way
22
+ * `listPreferences.ts` keeps the list arrangement: `{ "<section id>": <collapsed> }`. Every read tolerates a missing, corrupt or
23
+ * blocked store (and keeps only the booleans of a partly-valid one), every write swallows its failure - a blocked store only means the
24
+ * choices do not survive a reload.
25
+ */
26
+ import { useCallback, useMemo, useRef, useState } from "react";
27
+
28
+ const STORAGE_KEY_PREFIX = "rapidmx:mail-sidebar-collapsed:";
29
+
30
+ /** The id of the "All mailboxes" section; every mailbox's section is its uid. */
31
+ export const ALL_MAILBOXES_SECTION = "all";
32
+
33
+ /** What the reader chose per section: `true` collapsed, `false` open. A section that is not there follows the default rule. */
34
+ export type SectionChoices = Record<string, boolean>;
35
+
36
+ /** The `localStorage` key one user's choices are kept under. */
37
+ export function collapsedSectionsKey(userUid: string): string {
38
+ return `${STORAGE_KEY_PREFIX}${userUid}`;
39
+ }
40
+
41
+ /** Reads a user's stored choices - none for an unknown user, a missing entry, unparsable JSON, something that is not an object, or a store that throws. */
42
+ export function readSectionChoices(userUid: string | undefined): SectionChoices {
43
+ if (!userUid) {
44
+ return {};
45
+ }
46
+ let stored: unknown;
47
+ try {
48
+ const raw = localStorage.getItem(collapsedSectionsKey(userUid));
49
+ stored = raw === null ? null : JSON.parse(raw);
50
+ } catch {
51
+ return {};
52
+ }
53
+ if (!stored || typeof stored !== "object" || Array.isArray(stored)) {
54
+ return {};
55
+ }
56
+ // Only what is a real choice: a value that is not a boolean falls back to the default for its own section, not for all of them.
57
+ return Object.fromEntries(Object.entries(stored).filter((entry): entry is [string, boolean] => typeof entry[1] === "boolean"));
58
+ }
59
+
60
+ /** Stores a user's choices. Best-effort - see this module's own doc comment. */
61
+ export function writeSectionChoices(userUid: string | undefined, choices: SectionChoices): void {
62
+ if (!userUid) {
63
+ return;
64
+ }
65
+ try {
66
+ localStorage.setItem(collapsedSectionsKey(userUid), JSON.stringify(choices));
67
+ } catch {
68
+ // Best-effort - see this module's own doc comment.
69
+ }
70
+ }
71
+
72
+ /** Whether a section is open by the reader's choice, or by the default rule where they have made none (a navigation's override not considered). */
73
+ export function sectionChosenOpen(id: string, choices: SectionChoices, primaryUid: string | undefined): boolean {
74
+ const collapsed = choices[id];
75
+ return collapsed === undefined ? id === ALL_MAILBOXES_SECTION || id === primaryUid : !collapsed;
76
+ }
77
+
78
+ export interface SidebarSectionsOptions {
79
+ /** The signed-in user, whose choices these are; `undefined` keeps them for this page view only. */
80
+ userUid: string | undefined;
81
+ /** The user's primary mailbox, which - with "All mailboxes" - is open until they choose otherwise. */
82
+ primaryUid: string | undefined;
83
+ /**
84
+ * The section holding the folder that is open: `ALL_MAILBOXES_SECTION` for an aggregate view, else that mailbox's uid; `undefined`
85
+ * while that is not known yet (the address not read, the mailboxes not loaded). Moving from one section to another opens the new one
86
+ * (see this module's doc comment); the first section that becomes known does not.
87
+ */
88
+ activeId: string | undefined;
89
+ }
90
+
91
+ export interface SidebarSections {
92
+ /** Whether the section shows its folders. */
93
+ isExpanded: (id: string) => boolean;
94
+ /** Opens a collapsed section, collapses an open one, and remembers it as the reader's choice. */
95
+ toggle: (id: string) => void;
96
+ }
97
+
98
+ /** See this module's doc comment. */
99
+ export function useSidebarSections({ userUid, primaryUid, activeId }: SidebarSectionsOptions): SidebarSections {
100
+ const [loaded, setLoaded] = useState(() => ({ userUid, choices: readSectionChoices(userUid) }));
101
+ let current = loaded;
102
+ if (loaded.userUid !== userUid) {
103
+ // Another user signed in on this page: theirs are not the last one's choices.
104
+ current = { userUid, choices: readSectionChoices(userUid) };
105
+ setLoaded(current);
106
+ }
107
+ // The section the last navigation opened, if any: derived while rendering from the change of `activeId` (no effect, so the section is
108
+ // already open in the render that highlights its folder). A first `activeId` (from undefined) is not a navigation.
109
+ const [nav, setNav] = useState<{ activeId: string | undefined; opened: string | undefined }>({ activeId, opened: undefined });
110
+ let currentNav = nav;
111
+ if (nav.activeId !== activeId) {
112
+ currentNav = { activeId, opened: nav.activeId === undefined ? undefined : activeId };
113
+ setNav(currentNav);
114
+ }
115
+ // Kept in step with what was last rendered, and moved on by a toggle at once, so two toggles before a re-render both count.
116
+ const choicesRef = useRef(current.choices);
117
+ choicesRef.current = current.choices;
118
+ const openedRef = useRef(currentNav.opened);
119
+ openedRef.current = currentNav.opened;
120
+
121
+ const toggle = useCallback(
122
+ (id: string) => {
123
+ const expanded = openedRef.current === id || sectionChosenOpen(id, choicesRef.current, primaryUid);
124
+ const next = { ...choicesRef.current, [id]: expanded };
125
+ choicesRef.current = next;
126
+ setLoaded({ userUid, choices: next });
127
+ writeSectionChoices(userUid, next);
128
+ if (openedRef.current === id) {
129
+ // The reader's own choice replaces the override that had opened it.
130
+ openedRef.current = undefined;
131
+ setNav({ activeId, opened: undefined });
132
+ }
133
+ },
134
+ [activeId, primaryUid, userUid],
135
+ );
136
+
137
+ const choices = current.choices;
138
+ const opened = currentNav.opened;
139
+ return useMemo(
140
+ () => ({
141
+ isExpanded: (id: string) => id === opened || sectionChosenOpen(id, choices, primaryUid),
142
+ toggle,
143
+ }),
144
+ [opened, choices, primaryUid, toggle],
145
+ );
146
+ }